aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crowe <mac@mcrowe.com>2018-08-01 15:39:45 +0000
committerJonathan Wakely <redi@gcc.gnu.org>2018-08-01 16:39:45 +0100
commit2f5934326542b3f303db91cc4cd432cd488f0258 (patch)
tree1d909cf9f46064af55af5dae9bb10e0263463b8c
parent5534096c09d8d8bc4e12b73d1db179ccd7c60671 (diff)
downloadgcc-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/ChangeLog8
-rw-r--r--libstdc++-v3/include/std/condition_variable9
-rw-r--r--libstdc++-v3/testsuite/30_threads/condition_variable/members/2.cc52
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;
}