These tests show that changing the system clock has an effect on std::future::wait_until when using std::chrono::system_clock but not when using std::chrono::steady_clock. Unfortunately these tests have a number of downsides:
1. Nothing that is attempting to keep the clock set correctly (ntpd, systemd-timesyncd) can be running at the same time. 2. The test process requires the CAP_SYS_TIME capability. 3. Other processes running concurrently may misbehave when the clock darts back and forth. 4. They are slow to run. As such, I don't think they are suitable for merging. I include them here because I wanted to document how I had tested the changes in the previous commits. --- libstdc++-v3/testsuite/30_threads/async/async.cc | 76 ++++++++++++++++++++++++ 1 file changed, 76 insertions(+) diff --git a/libstdc++-v3/testsuite/30_threads/async/async.cc b/libstdc++-v3/testsuite/30_threads/async/async.cc index 59905b4666f..0a3cb28fbda 100644 --- a/libstdc++-v3/testsuite/30_threads/async/async.cc +++ b/libstdc++-v3/testsuite/30_threads/async/async.cc @@ -25,6 +25,7 @@ #include <future> #include <testsuite_hooks.h> +#include <sys/time.h> using namespace std; @@ -94,11 +95,86 @@ void test03() VERIFY( elapsed < std::chrono::seconds(20) ); } +void perturb_system_clock(const std::chrono::seconds &seconds) +{ + struct timeval tv; + if (gettimeofday(&tv, NULL)) + abort(); + + tv.tv_sec += seconds.count(); + if (settimeofday(&tv, NULL)) + abort(); +} + +void work04() +{ + std::this_thread::sleep_for(std::chrono::seconds(10)); +} + +// Ensure that advancing CLOCK_REALTIME doesn't make any difference +// when we're waiting on std::chrono::steady_clock. +void test04() +{ + auto const start = chrono::steady_clock::now(); + future<void> f1 = async(launch::async, &work04); + + perturb_system_clock(chrono::seconds(20)); + + std::future_status status; + status = f1.wait_for(std::chrono::seconds(4)); + VERIFY( status == std::future_status::timeout ); + + status = f1.wait_until(start + std::chrono::seconds(6)); + VERIFY( status == std::future_status::timeout ); + + status = f1.wait_until(start + std::chrono::seconds(12)); + VERIFY( status == std::future_status::ready ); + + auto const elapsed = chrono::steady_clock::now() - start; + VERIFY( elapsed >= std::chrono::seconds(10) ); + VERIFY( elapsed < std::chrono::seconds(15) ); + + perturb_system_clock(chrono::seconds(-20)); +} + +void work05() +{ + std::this_thread::sleep_for(std::chrono::seconds(5)); + perturb_system_clock(chrono::seconds(60)); + std::this_thread::sleep_for(std::chrono::seconds(5)); +} + +// Ensure that advancing CLOCK_REALTIME does make a difference when +// we're waiting on std::chrono::system_clock. +void test05() +{ + auto const start = chrono::system_clock::now(); + auto const start_steady = chrono::steady_clock::now(); + + future<void> f1 = async(launch::async, &work05); + future_status status; + status = f1.wait_until(start + std::chrono::seconds(60)); + VERIFY( status == std::future_status::timeout ); + + auto const elapsed_steady = chrono::steady_clock::now() - start_steady; + VERIFY( elapsed_steady >= std::chrono::seconds(5) ); + VERIFY( elapsed_steady < std::chrono::seconds(10) ); + + status = f1.wait_until(start + std::chrono::seconds(75)); + VERIFY( status == std::future_status::ready ); + + perturb_system_clock(chrono::seconds(-60)); +} + int main() { test01(); test02(); test03<std::chrono::system_clock>(); test03<std::chrono::steady_clock>(); + if (geteuid() == 0) { + test04(); + test05(); + } return 0; } -- 2.11.0