xref: /netbsd-src/external/apache2/llvm/dist/libcxx/src/atomic.cpp (revision 4d6fc14bc9b0c5bf3e30be318c143ee82cadd108)
1*4d6fc14bSjoerg //===------------------------- atomic.cpp ---------------------------------===//
2*4d6fc14bSjoerg //
3*4d6fc14bSjoerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*4d6fc14bSjoerg // See https://llvm.org/LICENSE.txt for license information.
5*4d6fc14bSjoerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*4d6fc14bSjoerg //
7*4d6fc14bSjoerg //===----------------------------------------------------------------------===//
8*4d6fc14bSjoerg 
9*4d6fc14bSjoerg #include <__config>
10*4d6fc14bSjoerg #ifndef _LIBCPP_HAS_NO_THREADS
11*4d6fc14bSjoerg 
12*4d6fc14bSjoerg #include <climits>
13*4d6fc14bSjoerg #include <atomic>
14*4d6fc14bSjoerg #include <functional>
15*4d6fc14bSjoerg 
16*4d6fc14bSjoerg #ifdef __linux__
17*4d6fc14bSjoerg 
18*4d6fc14bSjoerg #include <unistd.h>
19*4d6fc14bSjoerg #include <linux/futex.h>
20*4d6fc14bSjoerg #include <sys/syscall.h>
21*4d6fc14bSjoerg 
22*4d6fc14bSjoerg // libc++ uses SYS_futex as a universal syscall name. However, on 32 bit architectures
23*4d6fc14bSjoerg // with a 64 bit time_t, we need to specify SYS_futex_time64.
24*4d6fc14bSjoerg #if !defined(SYS_futex) && defined(SYS_futex_time64)
25*4d6fc14bSjoerg # define SYS_futex SYS_futex_time64
26*4d6fc14bSjoerg #endif
27*4d6fc14bSjoerg 
28*4d6fc14bSjoerg #else // <- Add other operating systems here
29*4d6fc14bSjoerg 
30*4d6fc14bSjoerg // Baseline needs no new headers
31*4d6fc14bSjoerg 
32*4d6fc14bSjoerg #endif
33*4d6fc14bSjoerg 
34*4d6fc14bSjoerg _LIBCPP_BEGIN_NAMESPACE_STD
35*4d6fc14bSjoerg 
36*4d6fc14bSjoerg #ifdef __linux__
37*4d6fc14bSjoerg 
__libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile * __ptr,__cxx_contention_t __val)38*4d6fc14bSjoerg static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
39*4d6fc14bSjoerg                                               __cxx_contention_t __val)
40*4d6fc14bSjoerg {
41*4d6fc14bSjoerg     static constexpr timespec __timeout = { 2, 0 };
42*4d6fc14bSjoerg     syscall(SYS_futex, __ptr, FUTEX_WAIT_PRIVATE, __val, &__timeout, 0, 0);
43*4d6fc14bSjoerg }
44*4d6fc14bSjoerg 
__libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile * __ptr,bool __notify_one)45*4d6fc14bSjoerg static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr,
46*4d6fc14bSjoerg                                               bool __notify_one)
47*4d6fc14bSjoerg {
48*4d6fc14bSjoerg     syscall(SYS_futex, __ptr, FUTEX_WAKE_PRIVATE, __notify_one ? 1 : INT_MAX, 0, 0, 0);
49*4d6fc14bSjoerg }
50*4d6fc14bSjoerg 
51*4d6fc14bSjoerg #elif defined(__APPLE__) && defined(_LIBCPP_USE_ULOCK)
52*4d6fc14bSjoerg 
53*4d6fc14bSjoerg extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value,
54*4d6fc14bSjoerg 		uint32_t timeout); /* timeout is specified in microseconds */
55*4d6fc14bSjoerg extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
56*4d6fc14bSjoerg 
57*4d6fc14bSjoerg #define UL_COMPARE_AND_WAIT				1
58*4d6fc14bSjoerg #define ULF_WAKE_ALL					0x00000100
59*4d6fc14bSjoerg 
60*4d6fc14bSjoerg static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
61*4d6fc14bSjoerg                                               __cxx_contention_t __val)
62*4d6fc14bSjoerg {
63*4d6fc14bSjoerg     __ulock_wait(UL_COMPARE_AND_WAIT,
64*4d6fc14bSjoerg                  const_cast<__cxx_atomic_contention_t*>(__ptr), __val, 0);
65*4d6fc14bSjoerg }
66*4d6fc14bSjoerg 
67*4d6fc14bSjoerg static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr,
68*4d6fc14bSjoerg                                               bool __notify_one)
69*4d6fc14bSjoerg {
70*4d6fc14bSjoerg     __ulock_wake(UL_COMPARE_AND_WAIT | (__notify_one ? 0 : ULF_WAKE_ALL),
71*4d6fc14bSjoerg                  const_cast<__cxx_atomic_contention_t*>(__ptr), 0);
72*4d6fc14bSjoerg }
73*4d6fc14bSjoerg 
74*4d6fc14bSjoerg #else // <- Add other operating systems here
75*4d6fc14bSjoerg 
76*4d6fc14bSjoerg // Baseline is just a timed backoff
77*4d6fc14bSjoerg 
78*4d6fc14bSjoerg static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
79*4d6fc14bSjoerg                                               __cxx_contention_t __val)
80*4d6fc14bSjoerg {
81*4d6fc14bSjoerg     __libcpp_thread_poll_with_backoff([=]() -> bool {
82*4d6fc14bSjoerg         return !__cxx_nonatomic_compare_equal(__cxx_atomic_load(__ptr, memory_order_relaxed), __val);
83*4d6fc14bSjoerg     }, __libcpp_timed_backoff_policy());
84*4d6fc14bSjoerg }
85*4d6fc14bSjoerg 
86*4d6fc14bSjoerg static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile*, bool) { }
87*4d6fc14bSjoerg 
88*4d6fc14bSjoerg #endif // __linux__
89*4d6fc14bSjoerg 
90*4d6fc14bSjoerg static constexpr size_t __libcpp_contention_table_size = (1 << 8);  /* < there's no magic in this number */
91*4d6fc14bSjoerg 
92*4d6fc14bSjoerg struct alignas(64) /*  aim to avoid false sharing */ __libcpp_contention_table_entry
93*4d6fc14bSjoerg {
94*4d6fc14bSjoerg     __cxx_atomic_contention_t __contention_state;
95*4d6fc14bSjoerg     __cxx_atomic_contention_t __platform_state;
__libcpp_contention_table_entry__libcpp_contention_table_entry96*4d6fc14bSjoerg     inline constexpr __libcpp_contention_table_entry() :
97*4d6fc14bSjoerg         __contention_state(0), __platform_state(0) { }
98*4d6fc14bSjoerg };
99*4d6fc14bSjoerg 
100*4d6fc14bSjoerg static __libcpp_contention_table_entry __libcpp_contention_table[ __libcpp_contention_table_size ];
101*4d6fc14bSjoerg 
102*4d6fc14bSjoerg static hash<void const volatile*> __libcpp_contention_hasher;
103*4d6fc14bSjoerg 
__libcpp_contention_state(void const volatile * p)104*4d6fc14bSjoerg static __libcpp_contention_table_entry* __libcpp_contention_state(void const volatile * p)
105*4d6fc14bSjoerg {
106*4d6fc14bSjoerg     return &__libcpp_contention_table[__libcpp_contention_hasher(p) & (__libcpp_contention_table_size - 1)];
107*4d6fc14bSjoerg }
108*4d6fc14bSjoerg 
109*4d6fc14bSjoerg /* Given an atomic to track contention and an atomic to actually wait on, which may be
110*4d6fc14bSjoerg    the same atomic, we try to detect contention to avoid spuriously calling the platform. */
111*4d6fc14bSjoerg 
__libcpp_contention_notify(__cxx_atomic_contention_t volatile * __contention_state,__cxx_atomic_contention_t const volatile * __platform_state,bool __notify_one)112*4d6fc14bSjoerg static void __libcpp_contention_notify(__cxx_atomic_contention_t volatile* __contention_state,
113*4d6fc14bSjoerg                                        __cxx_atomic_contention_t const volatile* __platform_state,
114*4d6fc14bSjoerg                                        bool __notify_one)
115*4d6fc14bSjoerg {
116*4d6fc14bSjoerg     if(0 != __cxx_atomic_load(__contention_state, memory_order_seq_cst))
117*4d6fc14bSjoerg         // We only call 'wake' if we consumed a contention bit here.
118*4d6fc14bSjoerg         __libcpp_platform_wake_by_address(__platform_state, __notify_one);
119*4d6fc14bSjoerg }
__libcpp_contention_monitor_for_wait(__cxx_atomic_contention_t volatile * __contention_state,__cxx_atomic_contention_t const volatile * __platform_state)120*4d6fc14bSjoerg static __cxx_contention_t __libcpp_contention_monitor_for_wait(__cxx_atomic_contention_t volatile* __contention_state,
121*4d6fc14bSjoerg                                                                __cxx_atomic_contention_t const volatile* __platform_state)
122*4d6fc14bSjoerg {
123*4d6fc14bSjoerg     // We will monitor this value.
124*4d6fc14bSjoerg     return __cxx_atomic_load(__platform_state, memory_order_acquire);
125*4d6fc14bSjoerg }
__libcpp_contention_wait(__cxx_atomic_contention_t volatile * __contention_state,__cxx_atomic_contention_t const volatile * __platform_state,__cxx_contention_t __old_value)126*4d6fc14bSjoerg static void __libcpp_contention_wait(__cxx_atomic_contention_t volatile* __contention_state,
127*4d6fc14bSjoerg                                      __cxx_atomic_contention_t const volatile* __platform_state,
128*4d6fc14bSjoerg                                      __cxx_contention_t __old_value)
129*4d6fc14bSjoerg {
130*4d6fc14bSjoerg     __cxx_atomic_fetch_add(__contention_state, __cxx_contention_t(1), memory_order_seq_cst);
131*4d6fc14bSjoerg     // We sleep as long as the monitored value hasn't changed.
132*4d6fc14bSjoerg     __libcpp_platform_wait_on_address(__platform_state, __old_value);
133*4d6fc14bSjoerg     __cxx_atomic_fetch_sub(__contention_state, __cxx_contention_t(1), memory_order_release);
134*4d6fc14bSjoerg }
135*4d6fc14bSjoerg 
136*4d6fc14bSjoerg /* When the incoming atomic is the wrong size for the platform wait size, need to
137*4d6fc14bSjoerg    launder the value sequence through an atomic from our table. */
138*4d6fc14bSjoerg 
__libcpp_atomic_notify(void const volatile * __location)139*4d6fc14bSjoerg static void __libcpp_atomic_notify(void const volatile* __location)
140*4d6fc14bSjoerg {
141*4d6fc14bSjoerg     auto const __entry = __libcpp_contention_state(__location);
142*4d6fc14bSjoerg     // The value sequence laundering happens on the next line below.
143*4d6fc14bSjoerg     __cxx_atomic_fetch_add(&__entry->__platform_state, __cxx_contention_t(1), memory_order_release);
144*4d6fc14bSjoerg     __libcpp_contention_notify(&__entry->__contention_state,
145*4d6fc14bSjoerg                                &__entry->__platform_state,
146*4d6fc14bSjoerg                                false /* when laundering, we can't handle notify_one */);
147*4d6fc14bSjoerg }
148*4d6fc14bSjoerg _LIBCPP_EXPORTED_FROM_ABI
__cxx_atomic_notify_one(void const volatile * __location)149*4d6fc14bSjoerg void __cxx_atomic_notify_one(void const volatile* __location)
150*4d6fc14bSjoerg     { __libcpp_atomic_notify(__location); }
151*4d6fc14bSjoerg _LIBCPP_EXPORTED_FROM_ABI
__cxx_atomic_notify_all(void const volatile * __location)152*4d6fc14bSjoerg void __cxx_atomic_notify_all(void const volatile* __location)
153*4d6fc14bSjoerg     { __libcpp_atomic_notify(__location); }
154*4d6fc14bSjoerg _LIBCPP_EXPORTED_FROM_ABI
__libcpp_atomic_monitor(void const volatile * __location)155*4d6fc14bSjoerg __cxx_contention_t __libcpp_atomic_monitor(void const volatile* __location)
156*4d6fc14bSjoerg {
157*4d6fc14bSjoerg     auto const __entry = __libcpp_contention_state(__location);
158*4d6fc14bSjoerg     return __libcpp_contention_monitor_for_wait(&__entry->__contention_state, &__entry->__platform_state);
159*4d6fc14bSjoerg }
160*4d6fc14bSjoerg _LIBCPP_EXPORTED_FROM_ABI
__libcpp_atomic_wait(void const volatile * __location,__cxx_contention_t __old_value)161*4d6fc14bSjoerg void __libcpp_atomic_wait(void const volatile* __location, __cxx_contention_t __old_value)
162*4d6fc14bSjoerg {
163*4d6fc14bSjoerg     auto const __entry = __libcpp_contention_state(__location);
164*4d6fc14bSjoerg     __libcpp_contention_wait(&__entry->__contention_state, &__entry->__platform_state, __old_value);
165*4d6fc14bSjoerg }
166*4d6fc14bSjoerg 
167*4d6fc14bSjoerg /* When the incoming atomic happens to be the platform wait size, we still need to use the
168*4d6fc14bSjoerg    table for the contention detection, but we can use the atomic directly for the wait. */
169*4d6fc14bSjoerg 
170*4d6fc14bSjoerg _LIBCPP_EXPORTED_FROM_ABI
__cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile * __location)171*4d6fc14bSjoerg void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location)
172*4d6fc14bSjoerg {
173*4d6fc14bSjoerg     __libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, true);
174*4d6fc14bSjoerg }
175*4d6fc14bSjoerg _LIBCPP_EXPORTED_FROM_ABI
__cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile * __location)176*4d6fc14bSjoerg void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile* __location)
177*4d6fc14bSjoerg {
178*4d6fc14bSjoerg     __libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, false);
179*4d6fc14bSjoerg }
180*4d6fc14bSjoerg _LIBCPP_EXPORTED_FROM_ABI
__libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile * __location)181*4d6fc14bSjoerg __cxx_contention_t __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile* __location)
182*4d6fc14bSjoerg {
183*4d6fc14bSjoerg     return __libcpp_contention_monitor_for_wait(&__libcpp_contention_state(__location)->__contention_state, __location);
184*4d6fc14bSjoerg }
185*4d6fc14bSjoerg _LIBCPP_EXPORTED_FROM_ABI
__libcpp_atomic_wait(__cxx_atomic_contention_t const volatile * __location,__cxx_contention_t __old_value)186*4d6fc14bSjoerg void __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile* __location, __cxx_contention_t __old_value)
187*4d6fc14bSjoerg {
188*4d6fc14bSjoerg     __libcpp_contention_wait(&__libcpp_contention_state(__location)->__contention_state, __location, __old_value);
189*4d6fc14bSjoerg }
190*4d6fc14bSjoerg 
191*4d6fc14bSjoerg _LIBCPP_END_NAMESPACE_STD
192*4d6fc14bSjoerg 
193*4d6fc14bSjoerg #endif //_LIBCPP_HAS_NO_THREADS
194