3 // Copyright (C) 2020 Free Software Foundation, Inc.
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)
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.
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.
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/>.
25 /** @file bits/atomic_wait.h
26 * This is an internal header file, included by other library headers.
27 * Do not attempt to use it directly. @headername{atomic}
30 #ifndef _GLIBCXX_ATOMIC_WAIT_H
31 #define _GLIBCXX_ATOMIC_WAIT_H 1
33 #pragma GCC system_header
35 #include <bits/c++config.h>
36 #if defined _GLIBCXX_HAS_GTHREADS || _GLIBCXX_HAVE_LINUX_FUTEX
37 #include <bits/functional_hash.h>
38 #include <bits/gthr.h>
39 #include <bits/std_mutex.h>
40 #include <bits/unique_lock.h>
41 #include <ext/numeric_traits.h>
43 #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
50 // TODO get this from Autoconf
51 #define _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE 1
53 namespace std
_GLIBCXX_VISIBILITY(default)
55 _GLIBCXX_BEGIN_NAMESPACE_VERSION
58 using __platform_wait_t
= int;
60 constexpr auto __atomic_spin_count_1
= 16;
61 constexpr auto __atomic_spin_count_2
= 12;
64 auto __platform_wait_max_value
=
65 __gnu_cxx::__int_traits
<__platform_wait_t
>::__max
;
67 template<typename _Tp
>
68 inline constexpr bool __platform_wait_uses_type
69 #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
70 = is_same_v
<remove_cv_t
<_Tp
>, __platform_wait_t
>;
75 #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
76 enum class __futex_wait_flags
: int
78 #ifdef _GLIBCXX_HAVE_LINUX_FUTEX_PRIVATE
87 __wait_private
= __wait
| __private_flag
,
88 __wake_private
= __wake
| __private_flag
,
89 __wait_bitset_private
= __wait_bitset
| __private_flag
,
90 __wake_bitset_private
= __wake_bitset
| __private_flag
,
91 __bitset_match_any
= -1
94 template<typename _Tp
>
96 __platform_wait(const _Tp
* __addr
, __platform_wait_t __val
) noexcept
100 auto __e
= syscall (SYS_futex
, static_cast<const void*>(__addr
),
101 static_cast<int>(__futex_wait_flags::__wait_private
),
105 else if (!(errno
== EINTR
|| errno
== EAGAIN
))
106 __throw_system_error(__e
);
110 template<typename _Tp
>
112 __platform_notify(const _Tp
* __addr
, bool __all
) noexcept
114 syscall (SYS_futex
, static_cast<const void*>(__addr
),
115 static_cast<int>(__futex_wait_flags::__wake_private
),
116 __all
? INT_MAX
: 1);
122 alignas(64) __platform_wait_t _M_ver
= 0;
123 alignas(64) __platform_wait_t _M_wait
= 0;
125 #ifndef _GLIBCXX_HAVE_LINUX_FUTEX
126 using __lock_t
= std::unique_lock
<std::mutex
>;
127 mutable __lock_t::mutex_type _M_mtx
;
129 # ifdef __GTHREAD_COND_INIT
130 mutable __gthread_cond_t _M_cv
= __GTHREAD_COND_INIT
;
131 __waiters() noexcept
= default;
133 mutable __gthread_cond_t _M_cv
;
136 __GTHREAD_COND_INIT_FUNCTION(&_M_cond
);
142 _M_enter_wait() noexcept
144 __platform_wait_t __res
;
145 __atomic_load(&_M_ver
, &__res
, __ATOMIC_ACQUIRE
);
146 __atomic_fetch_add(&_M_wait
, 1, __ATOMIC_ACQ_REL
);
151 _M_leave_wait() noexcept
153 __atomic_fetch_sub(&_M_wait
, 1, __ATOMIC_ACQ_REL
);
157 _M_do_wait(__platform_wait_t __version
) noexcept
159 #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
160 __platform_wait(&_M_ver
, __version
);
162 __platform_wait_t __cur
= 0;
163 while (__cur
<= __version
)
165 __waiters::__lock_t
__l(_M_mtx
);
166 auto __e
= __gthread_cond_wait(&_M_cv
, __l
.mutex()->native_handle());
168 __throw_system_error(__e
);
169 __platform_wait_t __last
= __cur
;
170 __atomic_load(&_M_ver
, &__cur
, __ATOMIC_ACQUIRE
);
172 break; // break the loop if version overflows
178 _M_waiting() const noexcept
180 __platform_wait_t __res
;
181 __atomic_load(&_M_wait
, &__res
, __ATOMIC_ACQUIRE
);
186 _M_notify(bool __all
) noexcept
188 __atomic_fetch_add(&_M_ver
, 1, __ATOMIC_ACQ_REL
);
189 #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
190 __platform_notify(&_M_ver
, __all
);
192 auto __e
= __gthread_cond_broadcast(&_M_cv
);
194 __throw_system_error(__e
);
199 _S_for(const void* __t
)
201 const unsigned char __mask
= 0xf;
202 static __waiters __w
[__mask
+ 1];
204 auto __key
= _Hash_impl::hash(__t
) & __mask
;
212 __platform_wait_t _M_version
;
214 template<typename _Tp
>
215 __waiter(const _Tp
* __addr
) noexcept
216 : _M_w(__waiters::_S_for(static_cast<const void*>(__addr
)))
217 , _M_version(_M_w
._M_enter_wait())
221 { _M_w
._M_leave_wait(); }
223 void _M_do_wait() noexcept
224 { _M_w
._M_do_wait(_M_version
); }
228 __thread_relax() noexcept
230 #if defined __i386__ || defined __x86_64__
231 __builtin_ia32_pause();
232 #elif defined _GLIBCXX_USE_SCHED_YIELD
238 __thread_yield() noexcept
240 #if defined _GLIBCXX_USE_SCHED_YIELD
245 } // namespace __detail
247 template<typename _Pred
>
249 __atomic_spin(_Pred
& __pred
) noexcept
251 for (auto __i
= 0; __i
< __detail::__atomic_spin_count_1
; ++__i
)
256 if (__i
< __detail::__atomic_spin_count_2
)
257 __detail::__thread_relax();
259 __detail::__thread_yield();
264 template<typename _Tp
, typename _Pred
>
266 __atomic_wait(const _Tp
* __addr
, _Tp __old
, _Pred __pred
) noexcept
268 using namespace __detail
;
269 if (std::__atomic_spin(__pred
))
272 __waiter
__w(__addr
);
275 if constexpr (__platform_wait_uses_type
<_Tp
>)
277 __platform_wait(__addr
, __old
);
281 // TODO support timed backoff when this can be moved into the lib
287 template<typename _Tp
>
289 __atomic_notify(const _Tp
* __addr
, bool __all
) noexcept
291 using namespace __detail
;
292 auto& __w
= __waiters::_S_for((void*)__addr
);
293 if (!__w
._M_waiting())
296 #ifdef _GLIBCXX_HAVE_LINUX_FUTEX
297 if constexpr (__platform_wait_uses_type
<_Tp
>)
299 __platform_notify((__platform_wait_t
*)(void*) __addr
, __all
);
304 __w
._M_notify(__all
);
307 _GLIBCXX_END_NAMESPACE_VERSION
309 #endif // GTHREADS || LINUX_FUTEX
310 #endif // _GLIBCXX_ATOMIC_WAIT_H