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
This commit is contained in:
parent
5534096c09
commit
2f59343265
@ -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
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user