libstdc++
atomic_timed_wait.h
Go to the documentation of this file.
1// -*- C++ -*- header.
2
3// Copyright (C) 2020-2022 Free Software Foundation, Inc.
4//
5// This file is part of the GNU ISO C++ Library. This library is free
6// software; you can redistribute it and/or modify it under the
7// terms of the GNU General Public License as published by the
8// Free Software Foundation; either version 3, or (at your option)
9// any later version.
10
11// This library is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// Under Section 7 of GPL version 3, you are granted additional
17// permissions described in the GCC Runtime Library Exception, version
18// 3.1, as published by the Free Software Foundation.
19
20// You should have received a copy of the GNU General Public License and
21// a copy of the GCC Runtime Library Exception along with this program;
22// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23// <http://www.gnu.org/licenses/>.
24
25/** @file bits/atomic_timed_wait.h
26 * This is an internal header file, included by other library headers.
27 * Do not attempt to use it directly. @headername{atomic}
28 */
29
30#ifndef _GLIBCXX_ATOMIC_TIMED_WAIT_H
31#define _GLIBCXX_ATOMIC_TIMED_WAIT_H 1
32
33#pragma GCC system_header
34
35#include <bits/atomic_wait.h>
36
37#if __cpp_lib_atomic_wait
40#include <bits/chrono.h>
41
42#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
43#include <sys/time.h>
44#endif
45
46namespace std _GLIBCXX_VISIBILITY(default)
47{
48_GLIBCXX_BEGIN_NAMESPACE_VERSION
49
50 namespace __detail
51 {
52 using __wait_clock_t = chrono::steady_clock;
53
54 template<typename _Clock, typename _Dur>
55 __wait_clock_t::time_point
56 __to_wait_clock(const chrono::time_point<_Clock, _Dur>& __atime) noexcept
57 {
58 const typename _Clock::time_point __c_entry = _Clock::now();
59 const __wait_clock_t::time_point __w_entry = __wait_clock_t::now();
60 const auto __delta = __atime - __c_entry;
61 using __w_dur = typename __wait_clock_t::duration;
62 return __w_entry + chrono::ceil<__w_dur>(__delta);
63 }
64
65 template<typename _Dur>
66 __wait_clock_t::time_point
67 __to_wait_clock(const chrono::time_point<__wait_clock_t,
68 _Dur>& __atime) noexcept
69 {
70 using __w_dur = typename __wait_clock_t::duration;
71 return chrono::ceil<__w_dur>(__atime);
72 }
73
74#ifdef _GLIBCXX_HAVE_LINUX_FUTEX
75#define _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT
76 // returns true if wait ended before timeout
77 template<typename _Dur>
78 bool
79 __platform_wait_until_impl(const __platform_wait_t* __addr,
80 __platform_wait_t __old,
81 const chrono::time_point<__wait_clock_t, _Dur>&
82 __atime) noexcept
83 {
84 auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
85 auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
86
87 struct timespec __rt =
88 {
89 static_cast<std::time_t>(__s.time_since_epoch().count()),
90 static_cast<long>(__ns.count())
91 };
92
93 auto __e = syscall (SYS_futex, __addr,
94 static_cast<int>(__futex_wait_flags::
95 __wait_bitset_private),
96 __old, &__rt, nullptr,
97 static_cast<int>(__futex_wait_flags::
98 __bitset_match_any));
99
100 if (__e)
101 {
102 if (errno == ETIMEDOUT)
103 return false;
104 if (errno != EINTR && errno != EAGAIN)
105 __throw_system_error(errno);
106 }
107 return true;
108 }
109
110 // returns true if wait ended before timeout
111 template<typename _Clock, typename _Dur>
112 bool
113 __platform_wait_until(const __platform_wait_t* __addr, __platform_wait_t __old,
114 const chrono::time_point<_Clock, _Dur>& __atime)
115 {
116 if constexpr (is_same_v<__wait_clock_t, _Clock>)
117 {
118 return __platform_wait_until_impl(__addr, __old, __atime);
119 }
120 else
121 {
122 if (!__platform_wait_until_impl(__addr, __old,
123 __to_wait_clock(__atime)))
124 {
125 // We got a timeout when measured against __clock_t but
126 // we need to check against the caller-supplied clock
127 // to tell whether we should return a timeout.
128 if (_Clock::now() < __atime)
129 return true;
130 }
131 return false;
132 }
133 }
134#else
135// define _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT and implement __platform_wait_until()
136// if there is a more efficient primitive supported by the platform
137// (e.g. __ulock_wait())which is better than pthread_cond_clockwait
138#endif // ! PLATFORM_TIMED_WAIT
139
140#ifdef _GLIBCXX_HAS_GTHREADS
141 // Returns true if wait ended before timeout.
142 // _Clock must be either steady_clock or system_clock.
143 template<typename _Clock, typename _Dur>
144 bool
145 __cond_wait_until_impl(__condvar& __cv, mutex& __mx,
146 const chrono::time_point<_Clock, _Dur>& __atime)
147 {
148 static_assert(std::__is_one_of<_Clock, chrono::steady_clock,
149 chrono::system_clock>::value);
150
151 auto __s = chrono::time_point_cast<chrono::seconds>(__atime);
152 auto __ns = chrono::duration_cast<chrono::nanoseconds>(__atime - __s);
153
154 __gthread_time_t __ts =
155 {
156 static_cast<std::time_t>(__s.time_since_epoch().count()),
157 static_cast<long>(__ns.count())
158 };
159
160#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
161 if constexpr (is_same_v<chrono::steady_clock, _Clock>)
162 __cv.wait_until(__mx, CLOCK_MONOTONIC, __ts);
163 else
164#endif
165 __cv.wait_until(__mx, __ts);
166 return _Clock::now() < __atime;
167 }
168
169 // returns true if wait ended before timeout
170 template<typename _Clock, typename _Dur>
171 bool
172 __cond_wait_until(__condvar& __cv, mutex& __mx,
173 const chrono::time_point<_Clock, _Dur>& __atime)
174 {
175#ifdef _GLIBCXX_USE_PTHREAD_COND_CLOCKWAIT
176 if constexpr (is_same_v<_Clock, chrono::steady_clock>)
177 return __detail::__cond_wait_until_impl(__cv, __mx, __atime);
178 else
179#endif
180 if constexpr (is_same_v<_Clock, chrono::system_clock>)
181 return __detail::__cond_wait_until_impl(__cv, __mx, __atime);
182 else
183 {
184 if (__cond_wait_until_impl(__cv, __mx,
185 __to_wait_clock(__atime)))
186 {
187 // We got a timeout when measured against __clock_t but
188 // we need to check against the caller-supplied clock
189 // to tell whether we should return a timeout.
190 if (_Clock::now() < __atime)
191 return true;
192 }
193 return false;
194 }
195 }
196#endif // _GLIBCXX_HAS_GTHREADS
197
198 struct __timed_waiter_pool : __waiter_pool_base
199 {
200 // returns true if wait ended before timeout
201 template<typename _Clock, typename _Dur>
202 bool
203 _M_do_wait_until(__platform_wait_t* __addr, __platform_wait_t __old,
204 const chrono::time_point<_Clock, _Dur>& __atime)
205 {
206#ifdef _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT
207 return __platform_wait_until(__addr, __old, __atime);
208#else
209 __platform_wait_t __val;
210 __atomic_load(__addr, &__val, __ATOMIC_RELAXED);
211 if (__val == __old)
212 {
213 lock_guard<mutex> __l(_M_mtx);
214 return __cond_wait_until(_M_cv, _M_mtx, __atime);
215 }
216 else
217 return true;
218#endif // _GLIBCXX_HAVE_PLATFORM_TIMED_WAIT
219 }
220 };
221
222 struct __timed_backoff_spin_policy
223 {
224 __wait_clock_t::time_point _M_deadline;
225 __wait_clock_t::time_point _M_t0;
226
227 template<typename _Clock, typename _Dur>
228 __timed_backoff_spin_policy(chrono::time_point<_Clock, _Dur>
229 __deadline = _Clock::time_point::max(),
230 chrono::time_point<_Clock, _Dur>
231 __t0 = _Clock::now()) noexcept
232 : _M_deadline(__to_wait_clock(__deadline))
233 , _M_t0(__to_wait_clock(__t0))
234 { }
235
236 bool
237 operator()() const noexcept
238 {
239 using namespace literals::chrono_literals;
240 auto __now = __wait_clock_t::now();
241 if (_M_deadline <= __now)
242 return false;
243
244 // FIXME: this_thread::sleep_for not available #ifdef _GLIBCXX_NO_SLEEP
245
246 auto __elapsed = __now - _M_t0;
247 if (__elapsed > 128ms)
248 {
250 }
251 else if (__elapsed > 64us)
252 {
253 this_thread::sleep_for(__elapsed / 2);
254 }
255 else if (__elapsed > 4us)
256 {
257 __thread_yield();
258 }
259 else
260 return false;
261 return true;
262 }
263 };
264
265 template<typename _EntersWait>
266 struct __timed_waiter : __waiter_base<__timed_waiter_pool>
267 {
268 using __base_type = __waiter_base<__timed_waiter_pool>;
269
270 template<typename _Tp>
271 __timed_waiter(const _Tp* __addr) noexcept
272 : __base_type(__addr)
273 {
274 if constexpr (_EntersWait::value)
275 _M_w._M_enter_wait();
276 }
277
278 ~__timed_waiter()
279 {
280 if constexpr (_EntersWait::value)
281 _M_w._M_leave_wait();
282 }
283
284 // returns true if wait ended before timeout
285 template<typename _Tp, typename _ValFn,
286 typename _Clock, typename _Dur>
287 bool
288 _M_do_wait_until_v(_Tp __old, _ValFn __vfn,
289 const chrono::time_point<_Clock, _Dur>&
290 __atime) noexcept
291 {
292 __platform_wait_t __val;
293 if (_M_do_spin(__old, std::move(__vfn), __val,
294 __timed_backoff_spin_policy(__atime)))
295 return true;
296 return __base_type::_M_w._M_do_wait_until(__base_type::_M_addr, __val, __atime);
297 }
298
299 // returns true if wait ended before timeout
300 template<typename _Pred,
301 typename _Clock, typename _Dur>
302 bool
303 _M_do_wait_until(_Pred __pred, __platform_wait_t __val,
304 const chrono::time_point<_Clock, _Dur>&
305 __atime) noexcept
306 {
307 for (auto __now = _Clock::now(); __now < __atime;
308 __now = _Clock::now())
309 {
310 if (__base_type::_M_w._M_do_wait_until(
311 __base_type::_M_addr, __val, __atime)
312 && __pred())
313 return true;
314
315 if (__base_type::_M_do_spin(__pred, __val,
316 __timed_backoff_spin_policy(__atime, __now)))
317 return true;
318 }
319 return false;
320 }
321
322 // returns true if wait ended before timeout
323 template<typename _Pred,
324 typename _Clock, typename _Dur>
325 bool
326 _M_do_wait_until(_Pred __pred,
327 const chrono::time_point<_Clock, _Dur>&
328 __atime) noexcept
329 {
330 __platform_wait_t __val;
331 if (__base_type::_M_do_spin(__pred, __val,
332 __timed_backoff_spin_policy(__atime)))
333 return true;
334 return _M_do_wait_until(__pred, __val, __atime);
335 }
336
337 template<typename _Tp, typename _ValFn,
338 typename _Rep, typename _Period>
339 bool
340 _M_do_wait_for_v(_Tp __old, _ValFn __vfn,
341 const chrono::duration<_Rep, _Period>&
342 __rtime) noexcept
343 {
344 __platform_wait_t __val;
345 if (_M_do_spin_v(__old, std::move(__vfn), __val))
346 return true;
347
348 if (!__rtime.count())
349 return false; // no rtime supplied, and spin did not acquire
350
351 auto __reltime = chrono::ceil<__wait_clock_t::duration>(__rtime);
352
353 return __base_type::_M_w._M_do_wait_until(
354 __base_type::_M_addr,
355 __val,
356 chrono::steady_clock::now() + __reltime);
357 }
358
359 template<typename _Pred,
360 typename _Rep, typename _Period>
361 bool
362 _M_do_wait_for(_Pred __pred,
363 const chrono::duration<_Rep, _Period>& __rtime) noexcept
364 {
365 __platform_wait_t __val;
366 if (__base_type::_M_do_spin(__pred, __val))
367 return true;
368
369 if (!__rtime.count())
370 return false; // no rtime supplied, and spin did not acquire
371
372 auto __reltime = chrono::ceil<__wait_clock_t::duration>(__rtime);
373
374 return _M_do_wait_until(__pred, __val,
375 chrono::steady_clock::now() + __reltime);
376 }
377 };
378
379 using __enters_timed_wait = __timed_waiter<std::true_type>;
380 using __bare_timed_wait = __timed_waiter<std::false_type>;
381 } // namespace __detail
382
383 // returns true if wait ended before timeout
384 template<typename _Tp, typename _ValFn,
385 typename _Clock, typename _Dur>
386 bool
387 __atomic_wait_address_until_v(const _Tp* __addr, _Tp&& __old, _ValFn&& __vfn,
388 const chrono::time_point<_Clock, _Dur>&
389 __atime) noexcept
390 {
391 __detail::__enters_timed_wait __w{__addr};
392 return __w._M_do_wait_until_v(__old, __vfn, __atime);
393 }
394
395 template<typename _Tp, typename _Pred,
396 typename _Clock, typename _Dur>
397 bool
398 __atomic_wait_address_until(const _Tp* __addr, _Pred __pred,
399 const chrono::time_point<_Clock, _Dur>&
400 __atime) noexcept
401 {
402 __detail::__enters_timed_wait __w{__addr};
403 return __w._M_do_wait_until(__pred, __atime);
404 }
405
406 template<typename _Pred,
407 typename _Clock, typename _Dur>
408 bool
409 __atomic_wait_address_until_bare(const __detail::__platform_wait_t* __addr,
410 _Pred __pred,
411 const chrono::time_point<_Clock, _Dur>&
412 __atime) noexcept
413 {
414 __detail::__bare_timed_wait __w{__addr};
415 return __w._M_do_wait_until(__pred, __atime);
416 }
417
418 template<typename _Tp, typename _ValFn,
419 typename _Rep, typename _Period>
420 bool
421 __atomic_wait_address_for_v(const _Tp* __addr, _Tp&& __old, _ValFn&& __vfn,
422 const chrono::duration<_Rep, _Period>& __rtime) noexcept
423 {
424 __detail::__enters_timed_wait __w{__addr};
425 return __w._M_do_wait_for_v(__old, __vfn, __rtime);
426 }
427
428 template<typename _Tp, typename _Pred,
429 typename _Rep, typename _Period>
430 bool
431 __atomic_wait_address_for(const _Tp* __addr, _Pred __pred,
432 const chrono::duration<_Rep, _Period>& __rtime) noexcept
433 {
434
435 __detail::__enters_timed_wait __w{__addr};
436 return __w._M_do_wait_for(__pred, __rtime);
437 }
438
439 template<typename _Pred,
440 typename _Rep, typename _Period>
441 bool
442 __atomic_wait_address_for_bare(const __detail::__platform_wait_t* __addr,
443 _Pred __pred,
444 const chrono::duration<_Rep, _Period>& __rtime) noexcept
445 {
446 __detail::__bare_timed_wait __w{__addr};
447 return __w._M_do_wait_for(__pred, __rtime);
448 }
449_GLIBCXX_END_NAMESPACE_VERSION
450} // namespace std
451#endif // __cpp_lib_atomic_wait
452#endif // _GLIBCXX_ATOMIC_TIMED_WAIT_H
constexpr std::remove_reference< _Tp >::type && move(_Tp &&__t) noexcept
Convert a value to an rvalue.
Definition: move.h:104
ISO C++ entities toplevel namespace is std.
void sleep_for(const chrono::duration< _Rep, _Period > &__rtime)
this_thread::sleep_for