3
3
* (See accompanying file LICENSE_1_0.txt or copy at
4
4
* http://www.boost.org/LICENSE_1_0.txt)
5
5
*
6
- * Copyright (c) 2020 Andrey Semashev
6
+ * Copyright (c) 2020-2025 Andrey Semashev
7
7
*/
8
8
/* !
9
9
* \file atomic/detail/wait_ops_generic.hpp
10
10
*
11
11
* This header contains generic (lock-based) implementation of the waiting/notifying atomic operations.
12
+ *
13
+ * This backend is used when lock-free atomic operations are available but native waiting/notifying operations are not.
12
14
*/
13
15
14
16
#ifndef BOOST_ATOMIC_DETAIL_WAIT_OPS_GENERIC_HPP_INCLUDED_
15
17
#define BOOST_ATOMIC_DETAIL_WAIT_OPS_GENERIC_HPP_INCLUDED_
16
18
17
19
#include < cstddef>
20
+ #include < chrono>
18
21
#include < boost/memory_order.hpp>
22
+ #include < boost/atomic/wait_result.hpp>
19
23
#include < boost/atomic/detail/config.hpp>
24
+ #if !defined(BOOST_WINDOWS)
25
+ #include < time.h>
26
+ #include < type_traits>
27
+ #include < boost/atomic/posix_clock_traits_fwd.hpp>
28
+ #include < boost/atomic/detail/has_posix_clock_traits.hpp>
29
+ #endif
30
+ #include < boost/atomic/detail/chrono.hpp>
20
31
#include < boost/atomic/detail/pause.hpp>
21
32
#include < boost/atomic/detail/lock_pool.hpp>
22
33
#include < boost/atomic/detail/wait_operations_fwd.hpp>
@@ -38,19 +49,19 @@ template< typename Base >
38
49
struct wait_operations_generic < Base, false > :
39
50
public Base
40
51
{
41
- typedef Base base_type;
42
- typedef typename base_type::storage_type storage_type;
43
- typedef lock_pool::scoped_lock< base_type::storage_alignment, true > scoped_lock ;
44
- typedef lock_pool::scoped_wait_state< base_type::storage_alignment > scoped_wait_state ;
52
+ using base_type = Base ;
53
+ using storage_type = typename base_type::storage_type;
54
+ using scoped_lock = lock_pool::scoped_lock< base_type::storage_alignment, true >;
55
+ using scoped_wait_state = lock_pool::scoped_wait_state< base_type::storage_alignment >;
45
56
46
- static BOOST_CONSTEXPR_OR_CONST bool always_has_native_wait_notify = false ;
57
+ static constexpr bool always_has_native_wait_notify = false ;
47
58
48
- static BOOST_FORCEINLINE bool has_native_wait_notify (storage_type const volatile &) BOOST_NOEXCEPT
59
+ static BOOST_FORCEINLINE bool has_native_wait_notify (storage_type const volatile &) noexcept
49
60
{
50
61
return false ;
51
62
}
52
63
53
- static BOOST_FORCEINLINE storage_type wait (storage_type const volatile & storage, storage_type old_val, memory_order order) BOOST_NOEXCEPT
64
+ static BOOST_FORCEINLINE storage_type wait (storage_type const volatile & storage, storage_type old_val, memory_order order) noexcept
54
65
{
55
66
storage_type new_val = base_type::load (storage, order);
56
67
if (new_val == old_val)
@@ -67,13 +78,119 @@ struct wait_operations_generic< Base, false > :
67
78
return new_val;
68
79
}
69
80
70
- static BOOST_FORCEINLINE void notify_one (storage_type volatile & storage) BOOST_NOEXCEPT
81
+ private:
82
+ template < typename Clock >
83
+ static BOOST_FORCEINLINE wait_result< storage_type >
84
+ wait_until_fallback (storage_type const volatile & storage, storage_type old_val, typename Clock::time_point timeout, typename Clock::time_point now, memory_order order)
85
+ noexcept (noexcept (Clock::now()))
86
+ {
87
+ wait_result< storage_type > res{ base_type::load (storage, order), false };
88
+ if (res.value == old_val)
89
+ {
90
+ scoped_wait_state wait_state (&storage);
91
+ while (res.value == old_val)
92
+ {
93
+ const std::chrono::nanoseconds nsec = atomics::detail::chrono::ceil< std::chrono::nanoseconds >(timeout - now);
94
+ if (nsec.count () <= 0 )
95
+ {
96
+ res.timeout = true ;
97
+ break ;
98
+ }
99
+
100
+ wait_state.wait_for (nsec);
101
+
102
+ now = Clock::now ();
103
+ res.value = base_type::load (storage, order);
104
+ }
105
+ }
106
+
107
+ return res;
108
+ }
109
+
110
+ #if !defined(BOOST_WINDOWS)
111
+
112
+ template < typename Clock >
113
+ static BOOST_FORCEINLINE wait_result< storage_type >
114
+ wait_until_abs_timeout (storage_type const volatile & storage, storage_type old_val, typename Clock::time_point timeout, memory_order order) noexcept
115
+ {
116
+ wait_result< storage_type > res{ base_type::load (storage, order), false };
117
+ if (res.value == old_val)
118
+ {
119
+ scoped_wait_state wait_state (&storage);
120
+ const timespec abs_timeout (posix_clock_traits< Clock >::to_timespec (timeout));
121
+ if (BOOST_LIKELY (abs_timeout.tv_sec >= 0 ))
122
+ {
123
+ while (res.value == old_val)
124
+ {
125
+ res.timeout = wait_state.wait_until (posix_clock_traits< Clock >::clock_id, abs_timeout);
126
+ res.value = base_type::load (storage, order);
127
+
128
+ if (res.timeout )
129
+ goto timeout_expired;
130
+ }
131
+ }
132
+ else
133
+ {
134
+ timeout_expired:
135
+ res.timeout = !(res.value == old_val);
136
+ }
137
+ }
138
+
139
+ return res;
140
+ }
141
+
142
+ template < typename Clock >
143
+ static BOOST_FORCEINLINE wait_result< storage_type >
144
+ wait_until_dispatch (storage_type const volatile & storage, storage_type old_val, typename Clock::time_point timeout, memory_order order, std::true_type) noexcept
145
+ {
146
+ return wait_until_abs_timeout< Clock >(storage, old_val, timeout, order);
147
+ }
148
+
149
+ template < typename Clock >
150
+ static BOOST_FORCEINLINE wait_result< storage_type >
151
+ wait_until_dispatch (storage_type const volatile & storage, storage_type old_val, typename Clock::time_point timeout, memory_order order, std::false_type)
152
+ noexcept (noexcept (Clock::now()))
153
+ {
154
+ return wait_until_fallback< Clock >(storage, old_val, timeout, Clock::now (), order);
155
+ }
156
+
157
+ public:
158
+ template < typename Clock, typename Duration >
159
+ static BOOST_FORCEINLINE wait_result< storage_type >
160
+ wait_until (storage_type const volatile & storage, storage_type old_val, std::chrono::time_point< Clock, Duration > timeout, memory_order order)
161
+ noexcept (noexcept (wait_until_dispatch< Clock >(storage, old_val, timeout, order, std::integral_constant< bool , has_posix_clock_traits< Clock >::value >())))
162
+ {
163
+ return wait_until_dispatch< Clock >(storage, old_val, timeout, order, std::integral_constant< bool , has_posix_clock_traits< Clock >::value >());
164
+ }
165
+
166
+ #else // !defined(BOOST_WINDOWS)
167
+
168
+ public:
169
+ template < typename Clock, typename Duration >
170
+ static BOOST_FORCEINLINE wait_result< storage_type >
171
+ wait_until (storage_type const volatile & storage, storage_type old_val, std::chrono::time_point< Clock, Duration > timeout, memory_order order)
172
+ noexcept (noexcept (wait_until_fallback< Clock >(storage, old_val, timeout, Clock::now(), order)))
173
+ {
174
+ return wait_until_fallback< Clock >(storage, old_val, timeout, Clock::now (), order);
175
+ }
176
+
177
+ #endif // !defined(BOOST_WINDOWS)
178
+
179
+ template < typename Rep, typename Period >
180
+ static BOOST_FORCEINLINE wait_result< storage_type >
181
+ wait_for (storage_type const volatile & storage, storage_type old_val, std::chrono::duration< Rep, Period > timeout, memory_order order) noexcept
182
+ {
183
+ const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now ();
184
+ return wait_until_fallback< std::chrono::steady_clock >(storage, old_val, now + timeout, now, order);
185
+ }
186
+
187
+ static BOOST_FORCEINLINE void notify_one (storage_type volatile & storage) noexcept
71
188
{
72
189
scoped_lock lock (&storage);
73
190
lock_pool::notify_one (lock.get_lock_state (), &storage);
74
191
}
75
192
76
- static BOOST_FORCEINLINE void notify_all (storage_type volatile & storage) BOOST_NOEXCEPT
193
+ static BOOST_FORCEINLINE void notify_all (storage_type volatile & storage) noexcept
77
194
{
78
195
scoped_lock lock (&storage);
79
196
lock_pool::notify_all (lock.get_lock_state (), &storage);
@@ -87,19 +204,23 @@ struct wait_operations_generic< Base, true > :
87
204
typedef Base base_type;
88
205
typedef typename base_type::storage_type storage_type;
89
206
90
- static BOOST_CONSTEXPR_OR_CONST bool always_has_native_wait_notify = false ;
207
+ static constexpr bool always_has_native_wait_notify = false ;
208
+
209
+ private:
210
+ static constexpr unsigned int fast_loop_count = 16u ;
91
211
92
- static BOOST_FORCEINLINE bool has_native_wait_notify (storage_type const volatile &) BOOST_NOEXCEPT
212
+ public:
213
+ static BOOST_FORCEINLINE bool has_native_wait_notify (storage_type const volatile &) noexcept
93
214
{
94
215
return false ;
95
216
}
96
217
97
- static BOOST_FORCEINLINE storage_type wait (storage_type const volatile & storage, storage_type old_val, memory_order order) BOOST_NOEXCEPT
218
+ static BOOST_FORCEINLINE storage_type wait (storage_type const volatile & storage, storage_type old_val, memory_order order) noexcept
98
219
{
99
220
storage_type new_val = base_type::load (storage, order);
100
221
if (new_val == old_val)
101
222
{
102
- for (unsigned int i = 0u ; i < 16u ; ++i)
223
+ for (unsigned int i = 0u ; i < fast_loop_count ; ++i)
103
224
{
104
225
atomics::detail::pause ();
105
226
new_val = base_type::load (storage, order);
@@ -119,11 +240,72 @@ struct wait_operations_generic< Base, true > :
119
240
return new_val;
120
241
}
121
242
122
- static BOOST_FORCEINLINE void notify_one (storage_type volatile &) BOOST_NOEXCEPT
243
+ private:
244
+ template < typename Clock >
245
+ static BOOST_FORCEINLINE wait_result< storage_type >
246
+ wait_until_impl (storage_type const volatile & storage, storage_type old_val, typename Clock::time_point timeout, typename Clock::time_point now, memory_order order)
247
+ noexcept (noexcept (Clock::now()))
248
+ {
249
+ wait_result< storage_type > res{ base_type::load (storage, order), false };
250
+ if (res.value == old_val)
251
+ {
252
+ for (unsigned int i = 0u ; i < fast_loop_count; ++i)
253
+ {
254
+ if ((now - timeout).count () >= 0 )
255
+ {
256
+ res.timeout = true ;
257
+ goto finish;
258
+ }
259
+
260
+ atomics::detail::pause ();
261
+ now = Clock::now ();
262
+
263
+ res.value = base_type::load (storage, order);
264
+ if (res.value != old_val)
265
+ goto finish;
266
+ }
267
+
268
+ do
269
+ {
270
+ if ((now - timeout).count () >= 0 )
271
+ {
272
+ res.timeout = true ;
273
+ goto finish;
274
+ }
275
+
276
+ atomics::detail::wait_some ();
277
+ now = Clock::now ();
278
+ res.value = base_type::load (storage, order);
279
+ }
280
+ while (res.value == old_val);
281
+ }
282
+
283
+ finish:
284
+ return res;
285
+ }
286
+
287
+ public:
288
+ template < typename Clock, typename Duration >
289
+ static BOOST_FORCEINLINE wait_result< storage_type >
290
+ wait_until (storage_type const volatile & storage, storage_type old_val, std::chrono::time_point< Clock, Duration > timeout, memory_order order)
291
+ noexcept (noexcept (wait_until_impl< Clock >(storage, old_val, timeout, Clock::now(), order)))
292
+ {
293
+ return wait_until_impl< Clock >(storage, old_val, timeout, Clock::now (), order);
294
+ }
295
+
296
+ template < typename Rep, typename Period >
297
+ static BOOST_FORCEINLINE wait_result< storage_type >
298
+ wait_for (storage_type const volatile & storage, storage_type old_val, std::chrono::duration< Rep, Period > timeout, memory_order order) noexcept
299
+ {
300
+ const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now ();
301
+ return wait_until_impl< std::chrono::steady_clock >(storage, old_val, now + timeout, now, order);
302
+ }
303
+
304
+ static BOOST_FORCEINLINE void notify_one (storage_type volatile &) noexcept
123
305
{
124
306
}
125
307
126
- static BOOST_FORCEINLINE void notify_all (storage_type volatile &) BOOST_NOEXCEPT
308
+ static BOOST_FORCEINLINE void notify_all (storage_type volatile &) noexcept
127
309
{
128
310
}
129
311
};
0 commit comments