Skip to content

Commit 4b80c7c

Browse files
Wait on condition variables independently on system time (#4457)
Co-authored-by: Stephan T. Lavavej <[email protected]>
1 parent 79e79a2 commit 4b80c7c

File tree

13 files changed

+184
-139
lines changed

13 files changed

+184
-139
lines changed

stl/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ set(HEADERS
1919
${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_print.hpp
2020
${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_sanitizer_annotate_container.hpp
2121
${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_system_error_abi.hpp
22+
${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_threads_core.hpp
2223
${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_tzdb.hpp
2324
${CMAKE_CURRENT_LIST_DIR}/inc/__msvc_xlocinfo_types.hpp
2425
${CMAKE_CURRENT_LIST_DIR}/inc/algorithm

stl/inc/__msvc_chrono.hpp

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -715,30 +715,6 @@ namespace chrono {
715715
_EXPORT_STD using high_resolution_clock = steady_clock;
716716
} // namespace chrono
717717

718-
template <class _Rep, class _Period>
719-
_NODISCARD bool _To_timespec64_sys_10_day_clamped(
720-
_timespec64& _Ts64, const _CHRONO duration<_Rep, _Period>& _Rel_time) noexcept(is_arithmetic_v<_Rep>) {
721-
// Convert duration to _timespec64 representing system time, maximum 10 days from now, returns whether clamping
722-
// occurred. If clamped, timeouts will be transformed into spurious non-timeout wakes, due to ABI restrictions where
723-
// the other side of the DLL boundary overflows int32_t milliseconds.
724-
// Every function calling this one is TRANSITION, ABI
725-
constexpr _CHRONO nanoseconds _Ten_days{_CHRONO hours{24} * 10};
726-
constexpr _CHRONO duration<double> _Ten_days_d{_Ten_days};
727-
_CHRONO nanoseconds _Tx0 = _CHRONO system_clock::duration{_Xtime_get_ticks()};
728-
const bool _Clamped = _Ten_days_d < _Rel_time;
729-
if (_Clamped) {
730-
_Tx0 += _Ten_days;
731-
} else {
732-
_Tx0 += _CHRONO duration_cast<_CHRONO nanoseconds>(_Rel_time);
733-
}
734-
735-
const auto _Whole_seconds = _CHRONO duration_cast<_CHRONO seconds>(_Tx0);
736-
_Ts64.tv_sec = _Whole_seconds.count();
737-
_Tx0 -= _Whole_seconds;
738-
_Ts64.tv_nsec = static_cast<long>(_Tx0.count());
739-
return _Clamped;
740-
}
741-
742718
inline namespace literals {
743719
inline namespace chrono_literals {
744720
_EXPORT_STD _NODISCARD constexpr _CHRONO hours operator""h(unsigned long long _Val) noexcept

stl/inc/__msvc_threads_core.hpp

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
// __msvc_threads_core.hpp internal header (core)
2+
3+
// Copyright (c) Microsoft Corporation.
4+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
5+
6+
#ifndef __MSVC_THREADS_CORE_HPP
7+
#define __MSVC_THREADS_CORE_HPP
8+
#include <yvals_core.h>
9+
#if _STL_COMPILER_PREPROCESSOR
10+
#include <type_traits>
11+
12+
#pragma pack(push, _CRT_PACKING)
13+
#pragma warning(push, _STL_WARNING_LEVEL)
14+
#pragma warning(disable : _STL_DISABLED_WARNINGS)
15+
_STL_DISABLE_CLANG_WARNINGS
16+
#pragma push_macro("new")
17+
#undef new
18+
19+
extern "C" {
20+
using _Thrd_id_t = unsigned int;
21+
struct _Thrd_t { // thread identifier for Win32
22+
void* _Hnd; // Win32 HANDLE
23+
_Thrd_id_t _Id;
24+
};
25+
26+
using _Smtx_t = void*;
27+
28+
enum class _Thrd_result : int { _Success, _Nomem, _Timedout, _Busy, _Error };
29+
30+
struct _Stl_critical_section {
31+
void* _Unused = nullptr; // TRANSITION, ABI: was the vptr
32+
_Smtx_t _M_srw_lock = nullptr;
33+
};
34+
35+
struct _Mtx_internal_imp_t {
36+
#if defined(_CRT_WINDOWS) || defined(UNDOCKED_WINDOWS_UCRT)
37+
#ifdef _WIN64
38+
static constexpr size_t _Critical_section_size = 16;
39+
#else // ^^^ defined(_WIN64) / !defined(_WIN64) vvv
40+
static constexpr size_t _Critical_section_size = 8;
41+
#endif // ^^^ !defined(_WIN64) ^^^
42+
#else // ^^^ Windows private STL / public STL vvv
43+
#ifdef _WIN64
44+
static constexpr size_t _Critical_section_size = 64;
45+
#else // ^^^ defined(_WIN64) / !defined(_WIN64) vvv
46+
static constexpr size_t _Critical_section_size = 36;
47+
#endif // ^^^ !defined(_WIN64) ^^^
48+
#endif // ^^^ public STL ^^^
49+
50+
static constexpr size_t _Critical_section_align = alignof(void*);
51+
52+
int _Type{};
53+
union {
54+
_Stl_critical_section _Critical_section{};
55+
_STD _Aligned_storage_t<_Critical_section_size, _Critical_section_align> _Cs_storage;
56+
};
57+
long _Thread_id{};
58+
int _Count{};
59+
};
60+
61+
// Size and alignment for _Cnd_internal_imp_t
62+
#if defined(_CRT_WINDOWS) // for Windows-internal code
63+
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size = 2 * sizeof(void*);
64+
#elif defined(_WIN64) // ordinary 64-bit code
65+
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size = 72;
66+
#else // vvv ordinary 32-bit code vvv
67+
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size = 40;
68+
#endif // ^^^ ordinary 32-bit code ^^^
69+
70+
_INLINE_VAR constexpr size_t _Cnd_internal_imp_alignment = alignof(void*);
71+
72+
using _Mtx_t = _Mtx_internal_imp_t*;
73+
74+
#ifdef _M_CEE // avoid warning LNK4248: unresolved typeref token for '_Cnd_internal_imp_t'; image may not run
75+
using _Cnd_t = void*;
76+
#else // ^^^ defined(_M_CEE) / !defined(_M_CEE) vvv
77+
struct _Cnd_internal_imp_t;
78+
using _Cnd_t = _Cnd_internal_imp_t*;
79+
#endif // ^^^ !defined(_M_CEE) ^^^
80+
} // extern "C"
81+
82+
#pragma pop_macro("new")
83+
_STL_RESTORE_CLANG_WARNINGS
84+
#pragma warning(pop)
85+
#pragma pack(pop)
86+
#endif // _STL_COMPILER_PREPROCESSOR
87+
#endif // __MSVC_THREADS_CORE_HPP
88+
89+
/*
90+
* This file is derived from software bearing the following
91+
* restrictions:
92+
*
93+
* (c) Copyright William E. Kempf 2001
94+
*
95+
* Permission to use, copy, modify, distribute and sell this
96+
* software and its documentation for any purpose is hereby
97+
* granted without fee, provided that the above copyright
98+
* notice appear in all copies and that both that copyright
99+
* notice and this permission notice appear in supporting
100+
* documentation. William E. Kempf makes no representations
101+
* about the suitability of this software for any purpose.
102+
* It is provided "as is" without express or implied warranty.
103+
*/

stl/inc/condition_variable

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -124,12 +124,9 @@ public:
124124
return cv_status::timeout;
125125
}
126126

127-
// TRANSITION, ABI: The standard says that we should use a steady clock,
128-
// but unfortunately our ABI relies on the system clock.
129-
_timespec64 _Tgt;
130-
const bool _Clamped = _To_timespec64_sys_10_day_clamped(_Tgt, _Rel_time);
131-
const cv_status _Result = _Wait_until_sys_time(_Lck, &_Tgt);
132-
if (_Clamped) {
127+
const auto _Rel_time_ms = _Clamped_rel_time_ms_count(_Rel_time);
128+
const cv_status _Result = _Wait_for_ms_count(_Lck, _Rel_time_ms._Count);
129+
if (_Rel_time_ms._Clamped) {
133130
return cv_status::no_timeout;
134131
}
135132

@@ -206,12 +203,8 @@ public:
206203
break;
207204
}
208205

209-
const auto _Rel_time = _Abs_time - _Now;
210-
// TRANSITION, ABI: The standard says that we should use a steady clock,
211-
// but unfortunately our ABI relies on the system clock.
212-
_timespec64 _Tgt;
213-
(void) _To_timespec64_sys_10_day_clamped(_Tgt, _Rel_time);
214-
(void) _Cnd_timedwait(_Mycnd(), _Myptr->_Mymtx(), &_Tgt);
206+
const unsigned long _Rel_ms_count = _Clamped_rel_time_ms_count(_Abs_time - _Now)._Count;
207+
(void) _Cnd_timedwait_for(_Mycnd(), _Myptr->_Mymtx(), _Rel_ms_count);
215208
_Guard_unlocks_before_locking_outer.unlock();
216209
} // relock
217210

@@ -234,12 +227,12 @@ private:
234227
}
235228

236229
template <class _Lock>
237-
cv_status _Wait_until_sys_time(_Lock& _Lck, const _timespec64* const _Abs_time) {
230+
cv_status _Wait_for_ms_count(_Lock& _Lck, const unsigned int _Rel_ms_count) {
238231
// wait for signal with timeout
239232
const shared_ptr<mutex> _Ptr = _Myptr; // for immunity to *this destruction
240233
unique_lock<mutex> _Guard{*_Ptr};
241234
_Unlock_guard<_Lock> _Unlock_outer{_Lck};
242-
const _Thrd_result _Res = _Cnd_timedwait(_Mycnd(), _Ptr->_Mymtx(), _Abs_time);
235+
const _Thrd_result _Res = _Cnd_timedwait_for(_Mycnd(), _Ptr->_Mymtx(), _Rel_ms_count);
243236
_Guard.unlock();
244237

245238
if (_Res == _Thrd_result::_Success) {

stl/inc/header-units.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"__msvc_print.hpp",
1818
"__msvc_sanitizer_annotate_container.hpp",
1919
"__msvc_system_error_abi.hpp",
20+
"__msvc_threads_core.hpp",
2021
"__msvc_tzdb.hpp",
2122
"__msvc_xlocinfo_types.hpp",
2223
"algorithm",

stl/inc/mutex

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -601,11 +601,9 @@ public:
601601
return cv_status::timeout;
602602
}
603603

604-
// TRANSITION, ABI: should use a steady clock
605-
_timespec64 _Tgt;
606-
(void) _To_timespec64_sys_10_day_clamped(_Tgt, _Abs_time - _Now);
607-
// Nothing to do to comply with LWG-2135 because std::mutex lock/unlock are nothrow
608-
const _Thrd_result _Res = _Cnd_timedwait(_Mycnd(), _Lck.mutex()->_Mymtx(), &_Tgt);
604+
const unsigned long _Rel_ms_count = _Clamped_rel_time_ms_count(_Abs_time - _Now)._Count;
605+
606+
const _Thrd_result _Res = _Cnd_timedwait_for(_Mycnd(), _Lck.mutex()->_Mymtx(), _Rel_ms_count);
609607
if (_Res == _Thrd_result::_Success) {
610608
return cv_status::no_timeout;
611609
}

stl/inc/thread

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,24 @@ _NODISCARD auto _To_absolute_time(const chrono::duration<_Rep, _Period>& _Rel_ti
179179
return _Abs_time;
180180
}
181181

182+
struct _Clamped_rel_time_ms_count_result {
183+
unsigned long _Count;
184+
bool _Clamped;
185+
};
186+
187+
template <class _Duration>
188+
_NODISCARD _Clamped_rel_time_ms_count_result _Clamped_rel_time_ms_count(const _Duration& _Rel) {
189+
// _Clamp must be less than 2^32 - 1 (INFINITE) milliseconds, but is otherwise arbitrary.
190+
constexpr chrono::milliseconds _Clamp{chrono::hours{24}};
191+
192+
if (_Rel > _Clamp) {
193+
return {static_cast<unsigned long>(_Clamp.count()), true};
194+
} else {
195+
const auto _Rel_ms = chrono::ceil<chrono::milliseconds>(_Rel);
196+
return {static_cast<unsigned long>(_Rel_ms.count()), false};
197+
}
198+
}
199+
182200
namespace this_thread {
183201
_EXPORT_STD _NODISCARD thread::id get_id() noexcept;
184202

@@ -197,16 +215,8 @@ namespace this_thread {
197215
return;
198216
}
199217

200-
// _Clamp must be less than 2^32 - 1 (INFINITE) milliseconds, but is otherwise arbitrary.
201-
constexpr chrono::milliseconds _Clamp{chrono::hours{24}};
202-
203-
const auto _Rel = _Abs_time - _Now;
204-
if (_Rel >= _Clamp) {
205-
_Thrd_sleep_for(static_cast<unsigned long>(_Clamp.count()));
206-
} else {
207-
const auto _Rel_ms = chrono::ceil<chrono::milliseconds>(_Rel);
208-
_Thrd_sleep_for(static_cast<unsigned long>(_Rel_ms.count()));
209-
}
218+
const unsigned long _Rel_ms_count = _Clamped_rel_time_ms_count(_Abs_time - _Now)._Count;
219+
_Thrd_sleep_for(_Rel_ms_count);
210220
}
211221
}
212222

stl/inc/xthreads.h

Lines changed: 2 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77
#define _THR_XTHREADS_H
88
#include <yvals_core.h>
99
#if _STL_COMPILER_PREPROCESSOR
10+
#include <__msvc_threads_core.hpp>
1011
#include <climits>
11-
#include <type_traits>
1212
#include <xtimec.h>
1313

1414
#pragma pack(push, _CRT_PACKING)
@@ -19,67 +19,6 @@ _STL_DISABLE_CLANG_WARNINGS
1919
#undef new
2020

2121
extern "C" {
22-
using _Thrd_id_t = unsigned int;
23-
struct _Thrd_t { // thread identifier for Win32
24-
void* _Hnd; // Win32 HANDLE
25-
_Thrd_id_t _Id;
26-
};
27-
28-
using _Smtx_t = void*;
29-
30-
struct _Stl_critical_section {
31-
void* _Unused = nullptr; // TRANSITION, ABI: was the vptr
32-
_Smtx_t _M_srw_lock = nullptr;
33-
};
34-
35-
struct _Mtx_internal_imp_t {
36-
#if defined(_CRT_WINDOWS) || defined(UNDOCKED_WINDOWS_UCRT)
37-
#ifdef _WIN64
38-
static constexpr size_t _Critical_section_size = 16;
39-
#else // ^^^ defined(_WIN64) / !defined(_WIN64) vvv
40-
static constexpr size_t _Critical_section_size = 8;
41-
#endif // ^^^ !defined(_WIN64) ^^^
42-
#else // ^^^ Windows private STL / public STL vvv
43-
#ifdef _WIN64
44-
static constexpr size_t _Critical_section_size = 64;
45-
#else // ^^^ defined(_WIN64) / !defined(_WIN64) vvv
46-
static constexpr size_t _Critical_section_size = 36;
47-
#endif // ^^^ !defined(_WIN64) ^^^
48-
#endif // ^^^ public STL ^^^
49-
50-
static constexpr size_t _Critical_section_align = alignof(void*);
51-
52-
int _Type{};
53-
union {
54-
_Stl_critical_section _Critical_section{};
55-
_STD _Aligned_storage_t<_Critical_section_size, _Critical_section_align> _Cs_storage;
56-
};
57-
long _Thread_id{};
58-
int _Count{};
59-
};
60-
61-
// Size and alignment for _Cnd_internal_imp_t
62-
#if defined(_CRT_WINDOWS) // for Windows-internal code
63-
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size = 2 * sizeof(void*);
64-
#elif defined(_WIN64) // ordinary 64-bit code
65-
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size = 72;
66-
#else // vvv ordinary 32-bit code vvv
67-
_INLINE_VAR constexpr size_t _Cnd_internal_imp_size = 40;
68-
#endif // ^^^ ordinary 32-bit code ^^^
69-
70-
_INLINE_VAR constexpr size_t _Cnd_internal_imp_alignment = alignof(void*);
71-
72-
using _Mtx_t = _Mtx_internal_imp_t*;
73-
74-
#ifdef _M_CEE // avoid warning LNK4248: unresolved typeref token for '_Cnd_internal_imp_t'; image may not run
75-
using _Cnd_t = void*;
76-
#else // ^^^ defined(_M_CEE) / !defined(_M_CEE) vvv
77-
struct _Cnd_internal_imp_t;
78-
using _Cnd_t = _Cnd_internal_imp_t*;
79-
#endif // ^^^ !defined(_M_CEE) ^^^
80-
81-
enum class _Thrd_result : int { _Success, _Nomem, _Timedout, _Busy, _Error };
82-
8322
// threads
8423
_CRTIMP2_PURE _Thrd_result __cdecl _Thrd_detach(_Thrd_t) noexcept;
8524
_CRTIMP2_PURE _Thrd_result __cdecl _Thrd_join(_Thrd_t, int*) noexcept;
@@ -123,12 +62,12 @@ _CRTIMP2_PURE void __cdecl _Cnd_destroy(_Cnd_t) noexcept;
12362
_CRTIMP2_PURE void __cdecl _Cnd_init_in_situ(_Cnd_t) noexcept;
12463
_CRTIMP2_PURE void __cdecl _Cnd_destroy_in_situ(_Cnd_t) noexcept;
12564
_CRTIMP2_PURE _Thrd_result __cdecl _Cnd_wait(_Cnd_t, _Mtx_t) noexcept; // TRANSITION, ABI: Always succeeds
126-
_CRTIMP2_PURE _Thrd_result __cdecl _Cnd_timedwait(_Cnd_t, _Mtx_t, const _timespec64*) noexcept;
12765
_CRTIMP2_PURE _Thrd_result __cdecl _Cnd_broadcast(_Cnd_t) noexcept; // TRANSITION, ABI: Always succeeds
12866
_CRTIMP2_PURE _Thrd_result __cdecl _Cnd_signal(_Cnd_t) noexcept; // TRANSITION, ABI: Always succeeds
12967
_CRTIMP2_PURE void __cdecl _Cnd_register_at_thread_exit(_Cnd_t, _Mtx_t, int*) noexcept;
13068
_CRTIMP2_PURE void __cdecl _Cnd_unregister_at_thread_exit(_Mtx_t) noexcept;
13169
_CRTIMP2_PURE void __cdecl _Cnd_do_broadcast_at_thread_exit() noexcept;
70+
_Thrd_result __stdcall _Cnd_timedwait_for(_Cnd_t, _Mtx_t, unsigned int) noexcept;
13271
} // extern "C"
13372

13473
_STD_BEGIN

stl/src/cond.cpp

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <cstdlib>
55
#include <internal_shared.h>
6+
#include <new>
67
#include <type_traits>
78
#include <xthreads.h>
89
#include <xtimec.h>
@@ -11,17 +12,9 @@
1112

1213
extern "C" {
1314

14-
struct _Cnd_internal_imp_t {
15-
typename std::_Aligned_storage<_Cnd_internal_imp_size, _Cnd_internal_imp_alignment>::type cv;
16-
17-
[[nodiscard]] Concurrency::details::stl_condition_variable_win7* _get_cv() noexcept {
18-
// get pointer to implementation
19-
return reinterpret_cast<Concurrency::details::stl_condition_variable_win7*>(&cv);
20-
}
21-
};
2215

2316
_CRTIMP2_PURE void __cdecl _Cnd_init_in_situ(const _Cnd_t cond) noexcept { // initialize condition variable in situ
24-
Concurrency::details::create_stl_condition_variable(cond->_get_cv());
17+
new (cond->_get_cv()) Concurrency::details::stl_condition_variable_win7;
2518
}
2619

2720
_CRTIMP2_PURE void __cdecl _Cnd_destroy_in_situ(_Cnd_t) noexcept {} // destroy condition variable in situ
@@ -66,7 +59,7 @@ _CRTIMP2_PURE _Thrd_result __cdecl _Cnd_wait(const _Cnd_t cond, const _Mtx_t mtx
6659
return _Thrd_result::_Success; // TRANSITION, ABI: Always succeeds
6760
}
6861

69-
// wait until signaled or timeout
62+
// TRANSITION, ABI: preserved for compatibility; wait until signaled or timeout
7063
_CRTIMP2_PURE _Thrd_result __cdecl _Cnd_timedwait(
7164
const _Cnd_t cond, const _Mtx_t mtx, const _timespec64* const target) noexcept {
7265
_Thrd_result res = _Thrd_result::_Success;

0 commit comments

Comments
 (0)