Describe the bug
Internally, sleep_until calls _To_xtime_10_day_clamped to convert time point of any clock to time point of System clock. Then _Thrd_sleep sleeps on this system time point:
|
template <class _Clock, class _Duration>
|
|
void sleep_until(const chrono::time_point<_Clock, _Duration>& _Abs_time) {
|
|
for (;;) {
|
|
const auto _Now = _Clock::now();
|
|
if (_Abs_time <= _Now) {
|
|
return;
|
|
}
|
|
|
|
_CSTD xtime _Tgt;
|
|
(void) _To_xtime_10_day_clamped(_Tgt, _Abs_time - _Now);
|
|
_Thrd_sleep(&_Tgt);
|
|
}
|
|
}
|
_To_xtime_10_day_clamped uses system_clock
|
// HELPERS
|
|
template <class _Rep, class _Period>
|
|
_NODISCARD bool _To_xtime_10_day_clamped(_CSTD xtime& _Xt, const chrono::duration<_Rep, _Period>& _Rel_time) noexcept(
|
|
is_arithmetic_v<_Rep>) {
|
|
// Convert duration to xtime, maximum 10 days from now, returns whether clamping occurred.
|
|
// If clamped, timeouts will be transformed into spurious non-timeout wakes, due to ABI restrictions where
|
|
// the other side of the DLL boundary overflows int32_t milliseconds.
|
|
// Every function calling this one is TRANSITION, ABI
|
|
constexpr chrono::nanoseconds _Ten_days{chrono::hours{24} * 10};
|
|
constexpr chrono::duration<double> _Ten_days_d{_Ten_days};
|
|
chrono::nanoseconds _T0 = chrono::system_clock::now().time_since_epoch();
|
|
const bool _Clamped = _Ten_days_d < _Rel_time;
|
|
if (_Clamped) {
|
|
_T0 += _Ten_days;
|
|
} else {
|
|
_T0 += chrono::duration_cast<chrono::nanoseconds>(_Rel_time);
|
|
}
|
|
|
|
const auto _Whole_seconds = chrono::duration_cast<chrono::seconds>(_T0);
|
|
_Xt.sec = _Whole_seconds.count();
|
|
_T0 -= _Whole_seconds;
|
|
_Xt.nsec = static_cast<long>(_T0.count());
|
|
return _Clamped;
|
|
}
|
If system_clock is adjusted between _To_xtime_10_day_clamped and _Thrd_sleep, the sleep interval is wrong.
sleep_for is also subject to this problem as it is based on sleep_until
|
template <class _Rep, class _Period>
|
|
void sleep_for(const chrono::duration<_Rep, _Period>& _Rel_time) {
|
|
sleep_until(chrono::steady_clock::now() + _Rel_time);
|
|
}
|
Though standard says
The functions whose names end in _for take an argument that specifies a duration. These functions produce relative timeouts. Implementations should use a steady clock to measure time for these functions.
Command-line test case
Hard to build stable repro, but will try if needed.
Expected behavior
Neither sleep_until relative to a steady clock nor sleep_for should depend on system_clock.
GetTickCount64 is good enough steady clock, as Sleep API does not have granularity less than a millisecond anyway (calling undocumented NtDelayExecution is unacceptable). GetTickCount64 is very fast clock, it is basically reading a global variable.
STL version
Microsoft Visual Studio Professional 2019 Preview
Version 16.6.0 Preview 2.1
Additional context
Suspect that tests for #593 are failing due to timeout because other tests simultaneously tamper with system clock. Ok, this theory is defeated now, but the bug still looks valid
Describe the bug
Internally,
sleep_untilcalls_To_xtime_10_day_clampedto convert time point of any clock to time point of System clock. Then_Thrd_sleepsleeps on this system time point:STL/stl/inc/thread
Lines 154 to 166 in 3141965
_To_xtime_10_day_clampedusessystem_clockSTL/stl/inc/chrono
Lines 629 to 652 in 3141965
If
system_clockis adjusted between_To_xtime_10_day_clampedand_Thrd_sleep, the sleep interval is wrong.sleep_foris also subject to this problem as it is based onsleep_untilSTL/stl/inc/thread
Lines 168 to 171 in 3141965
Though standard says
Command-line test case
Hard to build stable repro, but will try if needed.
Expected behavior
Neither
sleep_untilrelative to a steady clock norsleep_forshould depend onsystem_clock.GetTickCount64is good enough steady clock, asSleepAPI does not have granularity less than a millisecond anyway (calling undocumentedNtDelayExecutionis unacceptable).GetTickCount64is very fast clock, it is basically reading a global variable.STL version
Additional context
Suspect that tests for #593 are failing due to timeout because other tests simultaneously tamper with system clock.Ok, this theory is defeated now, but the bug still looks valid