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
38 #include <bits/functional_hash.h>
39 #include <bits/this_thread_sleep.h>
40 #include <bits/chrono.h>
41 
42 #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
43 #include <sys/time.h>
44 #endif
45 
_GLIBCXX_VISIBILITY(default)46 namespace 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             {
249               this_thread::sleep_for(64ms);
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
453