Skip to content

Commit 2f5b967

Browse files
Wait condition_variable_any for steady_clock (#4755)
Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent eaf7b31 commit 2f5b967

File tree

5 files changed

+54
-46
lines changed

5 files changed

+54
-46
lines changed

stl/inc/condition_variable

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -123,12 +123,26 @@ public:
123123
}
124124

125125
const auto _Rel_time_ms = _Clamped_rel_time_ms_count(_Rel_time);
126-
const cv_status _Result = _Wait_for_ms_count(_Lck, _Rel_time_ms._Count);
127-
if (_Rel_time_ms._Clamped) {
128-
return cv_status::no_timeout;
129-
}
130126

131-
return _Result;
127+
// wait for signal with timeout
128+
const shared_ptr<mutex> _Ptr = _Myptr; // for immunity to *this destruction
129+
const auto _Start = _CHRONO steady_clock::now();
130+
const auto _Target = _Start + _Rel_time;
131+
132+
unique_lock<mutex> _Guard{*_Ptr};
133+
_Unlock_guard<_Lock> _Unlock_outer{_Lck};
134+
const _Thrd_result _Res = _Cnd_timedwait_for_unchecked(_Mycnd(), _Ptr->_Mymtx(), _Rel_time_ms._Count);
135+
_Guard.unlock();
136+
137+
if (_Res == _Thrd_result::_Success) {
138+
return cv_status::no_timeout; // Real or WinAPI-internal spurious wake
139+
} else if (_Rel_time_ms._Clamped) {
140+
return cv_status::no_timeout; // Spuriously wake due to clamped timeout
141+
} else if (_CHRONO steady_clock::now() < _Target) {
142+
return cv_status::no_timeout; // Spuriously wake due to premature timeout
143+
} else {
144+
return cv_status::timeout;
145+
}
132146
}
133147

134148
template <class _Lock, class _Rep, class _Period, class _Predicate>
@@ -202,7 +216,7 @@ public:
202216
}
203217

204218
const unsigned long _Rel_ms_count = _Clamped_rel_time_ms_count(_Abs_time - _Now)._Count;
205-
(void) _Cnd_timedwait_for(_Mycnd(), _Myptr->_Mymtx(), _Rel_ms_count);
219+
(void) _Cnd_timedwait_for_unchecked(_Mycnd(), _Myptr->_Mymtx(), _Rel_ms_count);
206220
_Guard_unlocks_before_locking_outer.unlock();
207221
} // relock
208222

@@ -223,22 +237,6 @@ private:
223237
_NODISCARD _Cnd_t _Mycnd() noexcept {
224238
return &_Cnd_storage;
225239
}
226-
227-
template <class _Lock>
228-
cv_status _Wait_for_ms_count(_Lock& _Lck, const unsigned int _Rel_ms_count) {
229-
// wait for signal with timeout
230-
const shared_ptr<mutex> _Ptr = _Myptr; // for immunity to *this destruction
231-
unique_lock<mutex> _Guard{*_Ptr};
232-
_Unlock_guard<_Lock> _Unlock_outer{_Lck};
233-
const _Thrd_result _Res = _Cnd_timedwait_for(_Mycnd(), _Ptr->_Mymtx(), _Rel_ms_count);
234-
_Guard.unlock();
235-
236-
if (_Res == _Thrd_result::_Success) {
237-
return cv_status::no_timeout;
238-
} else {
239-
return cv_status::timeout;
240-
}
241-
}
242240
};
243241

244242
_EXPORT_STD inline void notify_all_at_thread_exit(condition_variable& _Cnd, unique_lock<mutex> _Lck) noexcept

stl/inc/mutex

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -599,7 +599,7 @@ public:
599599

600600
const unsigned long _Rel_ms_count = _Clamped_rel_time_ms_count(_Abs_time - _Now)._Count;
601601

602-
const _Thrd_result _Res = _Cnd_timedwait_for(_Mycnd(), _Lck.mutex()->_Mymtx(), _Rel_ms_count);
602+
const _Thrd_result _Res = _Cnd_timedwait_for_unchecked(_Mycnd(), _Lck.mutex()->_Mymtx(), _Rel_ms_count);
603603
if (_Res == _Thrd_result::_Success) {
604604
return cv_status::no_timeout;
605605
}

stl/inc/xthreads.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ _CRTIMP2_PURE _Thrd_result __cdecl _Cnd_signal(_Cnd_t) noexcept; // TRANSITION,
6262
_CRTIMP2_PURE void __cdecl _Cnd_register_at_thread_exit(_Cnd_t, _Mtx_t, int*) noexcept;
6363
_CRTIMP2_PURE void __cdecl _Cnd_unregister_at_thread_exit(_Mtx_t) noexcept;
6464
_CRTIMP2_PURE void __cdecl _Cnd_do_broadcast_at_thread_exit() noexcept;
65-
_Thrd_result __stdcall _Cnd_timedwait_for(_Cnd_t, _Mtx_t, unsigned int) noexcept;
65+
// '_unchecked' means it is not checked against the 'steady_clock', so may report timeout prematurely
66+
_Thrd_result __stdcall _Cnd_timedwait_for_unchecked(_Cnd_t, _Mtx_t, unsigned int) noexcept;
6667
} // extern "C"
6768

6869
_STD_BEGIN

stl/src/sharedmutex.cpp

Lines changed: 31 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -41,25 +41,42 @@ void __stdcall _Thrd_sleep_for(const unsigned long ms) noexcept { // suspend cur
4141
Sleep(ms);
4242
}
4343

44-
_Thrd_result __stdcall _Cnd_timedwait_for(const _Cnd_t cond, const _Mtx_t mtx, const unsigned int target_ms) noexcept {
45-
_Thrd_result res = _Thrd_result::_Success;
46-
const auto start_ms = GetTickCount64();
44+
namespace {
45+
_Thrd_result __stdcall _Cnd_timedwait_for_impl(
46+
const _Cnd_t cond, const _Mtx_t mtx, const unsigned int target_ms, const bool checked) noexcept {
47+
_Thrd_result res = _Thrd_result::_Success;
48+
unsigned long long start_ms = 0;
49+
50+
if (checked) {
51+
start_ms = GetTickCount64();
52+
}
4753

48-
// TRANSITION: replace with _Mtx_clear_owner(mtx);
49-
mtx->_Thread_id = -1;
50-
--mtx->_Count;
54+
// TRANSITION: replace with _Mtx_clear_owner(mtx);
55+
mtx->_Thread_id = -1;
56+
--mtx->_Count;
5157

52-
if (!_Primitive_wait_for(cond, mtx, target_ms)) { // report timeout
53-
const auto end_ms = GetTickCount64();
54-
if (end_ms - start_ms >= target_ms) {
55-
res = _Thrd_result::_Timedout;
58+
if (!_Primitive_wait_for(cond, mtx, target_ms)) { // report timeout
59+
if (!checked || GetTickCount64() - start_ms >= target_ms) {
60+
res = _Thrd_result::_Timedout;
61+
}
5662
}
63+
// TRANSITION: replace with _Mtx_reset_owner(mtx);
64+
mtx->_Thread_id = static_cast<long>(GetCurrentThreadId());
65+
++mtx->_Count;
66+
67+
return res;
5768
}
58-
// TRANSITION: replace with _Mtx_reset_owner(mtx);
59-
mtx->_Thread_id = static_cast<long>(GetCurrentThreadId());
60-
++mtx->_Count;
69+
} // namespace
6170

62-
return res;
71+
// TRANSITION, ABI: preserved for compatibility
72+
_Thrd_result __stdcall _Cnd_timedwait_for(const _Cnd_t cond, const _Mtx_t mtx, const unsigned int target_ms) noexcept {
73+
return _Cnd_timedwait_for_impl(cond, mtx, target_ms, true);
6374
}
6475

76+
_Thrd_result __stdcall _Cnd_timedwait_for_unchecked(
77+
const _Cnd_t cond, const _Mtx_t mtx, const unsigned int target_ms) noexcept {
78+
return _Cnd_timedwait_for_impl(cond, mtx, target_ms, false);
79+
}
80+
81+
6582
} // extern "C"

tests/libcxx/expected_results.txt

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -467,14 +467,6 @@ std/input.output/string.streams/stringbuf/stringbuf.members/view.pass.cpp FAIL
467467
std/input.output/syncstream/syncbuf/syncstream.syncbuf.cons/dtor.pass.cpp FAIL
468468
std/input.output/syncstream/syncbuf/syncstream.syncbuf.members/emit.pass.cpp FAIL
469469

470-
# GH-4723: <condition_variable>: condition_variable_any::wait_for returns cv_status::timeout when the elapsed time is shorter than requested
471-
std/thread/thread.condition/thread.condition.condvarany/notify_all.pass.cpp SKIPPED
472-
std/thread/thread.condition/thread.condition.condvarany/notify_one.pass.cpp SKIPPED
473-
std/thread/thread.condition/thread.condition.condvarany/wait_for_pred.pass.cpp SKIPPED
474-
std/thread/thread.condition/thread.condition.condvarany/wait_for.pass.cpp SKIPPED
475-
std/thread/thread.condition/thread.condition.condvarany/wait_until_pred.pass.cpp SKIPPED
476-
std/thread/thread.condition/thread.condition.condvarany/wait_until.pass.cpp SKIPPED
477-
478470

479471
# *** VCRUNTIME BUGS ***
480472
# DevCom-10373274 VSO-1824997 "vcruntime nothrow array operator new falls back on the wrong function"

0 commit comments

Comments
 (0)