diff options
author | Mike Crowe <mac@mcrowe.com> | 2018-08-01 15:39:45 +0000 |
---|---|---|
committer | Jonathan Wakely <redi@gcc.gnu.org> | 2018-08-01 16:39:45 +0100 |
commit | 2f5934326542b3f303db91cc4cd432cd488f0258 (patch) | |
tree | 1d909cf9f46064af55af5dae9bb10e0263463b8c | |
parent | 5534096c09d8d8bc4e12b73d1db179ccd7c60671 (diff) | |
download | gcc-2f5934326542b3f303db91cc4cd432cd488f0258.zip gcc-2f5934326542b3f303db91cc4cd432cd488f0258.tar.gz gcc-2f5934326542b3f303db91cc4cd432cd488f0258.tar.bz2 |
Report early wakeup of condition_variable::wait_until as no_timeout
As currently implemented, condition_variable always ultimately waits
against std::chrono::system_clock. This clock can be changed in arbitrary
ways by the user which may result in us waking up too early or too late
when measured against the caller-supplied clock.
We can't (yet) do much about waking up too late (PR 41861), but
if we wake up too early we must return cv_status::no_timeout to indicate a
spurious wakeup rather than incorrectly returning cv_status::timeout.
2018-08-01 Mike Crowe <mac@mcrowe.com>
* include/std/condition_variable (wait_until): Only report timeout
if we really have timed out when measured against the
caller-supplied clock.
* testsuite/30_threads/condition_variable/members/2.cc: Add test
case to confirm above behaviour.
From-SVN: r263224
-rw-r--r-- | libstdc++-v3/ChangeLog | 8 | ||||
-rw-r--r-- | libstdc++-v3/include/std/condition_variable | 9 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/30_threads/condition_variable/members/2.cc | 52 |
3 files changed, 68 insertions, 1 deletions
diff --git a/libstdc++-v3/ChangeLog b/libstdc++-v3/ChangeLog index 283babf..dc4cc14 100644 --- a/libstdc++-v3/ChangeLog +++ b/libstdc++-v3/ChangeLog @@ -1,3 +1,11 @@ +2018-08-01 Mike Crowe <mac@mcrowe.com> + + * include/std/condition_variable (wait_until): Only report timeout + if we really have timed out when measured against the + caller-supplied clock. + * testsuite/30_threads/condition_variable/members/2.cc: Add test + case to confirm above behaviour. + 2018-08-01 Jonathan Wakely <jwakely@redhat.com> PR libstdc++/60555 diff --git a/libstdc++-v3/include/std/condition_variable b/libstdc++-v3/include/std/condition_variable index 3f690c8..c00afa2 100644 --- a/libstdc++-v3/include/std/condition_variable +++ b/libstdc++-v3/include/std/condition_variable @@ -117,7 +117,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION const auto __delta = __atime - __c_entry; const auto __s_atime = __s_entry + __delta; - return __wait_until_impl(__lock, __s_atime); + if (__wait_until_impl(__lock, __s_atime) == cv_status::no_timeout) + return cv_status::no_timeout; + // We got a timeout when measured against __clock_t but + // we need to check against the caller-supplied clock + // to tell whether we should return a timeout. + if (_Clock::now() < __atime) + return cv_status::no_timeout; + return cv_status::timeout; } template<typename _Clock, typename _Duration, typename _Predicate> diff --git a/libstdc++-v3/testsuite/30_threads/condition_variable/members/2.cc b/libstdc++-v3/testsuite/30_threads/condition_variable/members/2.cc index 0a44c4f..09a7178 100644 --- a/libstdc++-v3/testsuite/30_threads/condition_variable/members/2.cc +++ b/libstdc++-v3/testsuite/30_threads/condition_variable/members/2.cc @@ -51,8 +51,60 @@ void test01() } } +struct slow_clock +{ + using rep = std::chrono::system_clock::rep; + using period = std::chrono::system_clock::period; + using duration = std::chrono::system_clock::duration; + using time_point = std::chrono::time_point<slow_clock, duration>; + static constexpr bool is_steady = false; + + static time_point now() + { + auto real = std::chrono::system_clock::now(); + return time_point{real.time_since_epoch() / 3}; + } +}; + + +void test01_alternate_clock() +{ + try + { + std::condition_variable c1; + std::mutex m; + std::unique_lock<std::mutex> l(m); + auto const expire = slow_clock::now() + std::chrono::seconds(1); + + while (slow_clock::now() < expire) + { + auto const result = c1.wait_until(l, expire); + + // If wait_until returns before the timeout has expired when + // measured against the supplied clock, then wait_until must + // return no_timeout. + if (slow_clock::now() < expire) + VERIFY(result == std::cv_status::no_timeout); + + // If wait_until returns timeout then the timeout must have + // expired. + if (result == std::cv_status::timeout) + VERIFY(slow_clock::now() >= expire); + } + } + catch (const std::system_error& e) + { + VERIFY( false ); + } + catch (...) + { + VERIFY( false ); + } +} + int main() { test01(); + test01_alternate_clock(); return 0; } |