xref: /freebsd/contrib/llvm-project/libcxx/src/atomic.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
1349cc55cSDimitry Andric //===----------------------------------------------------------------------===//
25ffd83dbSDimitry Andric //
35ffd83dbSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
45ffd83dbSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
55ffd83dbSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
65ffd83dbSDimitry Andric //
75ffd83dbSDimitry Andric //===----------------------------------------------------------------------===//
85ffd83dbSDimitry Andric 
95ffd83dbSDimitry Andric #include <__config>
105ffd83dbSDimitry Andric #ifndef _LIBCPP_HAS_NO_THREADS
115ffd83dbSDimitry Andric 
12*06c3fb27SDimitry Andric #include <__thread/timed_backoff_policy.h>
135ffd83dbSDimitry Andric #include <atomic>
1404eeddc0SDimitry Andric #include <climits>
155ffd83dbSDimitry Andric #include <functional>
1604eeddc0SDimitry Andric #include <thread>
175ffd83dbSDimitry Andric 
18*06c3fb27SDimitry Andric #include "include/apple_availability.h"
19*06c3fb27SDimitry Andric 
205ffd83dbSDimitry Andric #ifdef __linux__
215ffd83dbSDimitry Andric 
225ffd83dbSDimitry Andric #include <unistd.h>
235ffd83dbSDimitry Andric #include <linux/futex.h>
245ffd83dbSDimitry Andric #include <sys/syscall.h>
255ffd83dbSDimitry Andric 
26d409305fSDimitry Andric // libc++ uses SYS_futex as a universal syscall name. However, on 32 bit architectures
27d409305fSDimitry Andric // with a 64 bit time_t, we need to specify SYS_futex_time64.
28d409305fSDimitry Andric #if !defined(SYS_futex) && defined(SYS_futex_time64)
29d409305fSDimitry Andric # define SYS_futex SYS_futex_time64
30d409305fSDimitry Andric #endif
31d409305fSDimitry Andric 
329c996882SKonstantin Belousov #elif defined(__FreeBSD__)
339c996882SKonstantin Belousov 
349c996882SKonstantin Belousov #include <sys/types.h>
359c996882SKonstantin Belousov #include <sys/umtx.h>
369c996882SKonstantin Belousov 
375ffd83dbSDimitry Andric #else // <- Add other operating systems here
385ffd83dbSDimitry Andric 
395ffd83dbSDimitry Andric // Baseline needs no new headers
405ffd83dbSDimitry Andric 
415ffd83dbSDimitry Andric #endif
425ffd83dbSDimitry Andric 
435ffd83dbSDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD
445ffd83dbSDimitry Andric 
455ffd83dbSDimitry Andric #ifdef __linux__
465ffd83dbSDimitry Andric 
475ffd83dbSDimitry Andric static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
485ffd83dbSDimitry Andric                                               __cxx_contention_t __val)
495ffd83dbSDimitry Andric {
505ffd83dbSDimitry Andric     static constexpr timespec __timeout = { 2, 0 };
515ffd83dbSDimitry Andric     syscall(SYS_futex, __ptr, FUTEX_WAIT_PRIVATE, __val, &__timeout, 0, 0);
525ffd83dbSDimitry Andric }
535ffd83dbSDimitry Andric 
545ffd83dbSDimitry Andric static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr,
555ffd83dbSDimitry Andric                                               bool __notify_one)
565ffd83dbSDimitry Andric {
575ffd83dbSDimitry Andric     syscall(SYS_futex, __ptr, FUTEX_WAKE_PRIVATE, __notify_one ? 1 : INT_MAX, 0, 0, 0);
585ffd83dbSDimitry Andric }
595ffd83dbSDimitry Andric 
605ffd83dbSDimitry Andric #elif defined(__APPLE__) && defined(_LIBCPP_USE_ULOCK)
615ffd83dbSDimitry Andric 
625ffd83dbSDimitry Andric extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value,
635ffd83dbSDimitry Andric                             uint32_t timeout); /* timeout is specified in microseconds */
645ffd83dbSDimitry Andric extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value);
655ffd83dbSDimitry Andric 
665ffd83dbSDimitry Andric #define UL_COMPARE_AND_WAIT 1
675ffd83dbSDimitry Andric #define ULF_WAKE_ALL        0x00000100
685ffd83dbSDimitry Andric 
695ffd83dbSDimitry Andric static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
705ffd83dbSDimitry Andric                                               __cxx_contention_t __val)
715ffd83dbSDimitry Andric {
725ffd83dbSDimitry Andric     __ulock_wait(UL_COMPARE_AND_WAIT,
735ffd83dbSDimitry Andric                  const_cast<__cxx_atomic_contention_t*>(__ptr), __val, 0);
745ffd83dbSDimitry Andric }
755ffd83dbSDimitry Andric 
765ffd83dbSDimitry Andric static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr,
775ffd83dbSDimitry Andric                                               bool __notify_one)
785ffd83dbSDimitry Andric {
795ffd83dbSDimitry Andric     __ulock_wake(UL_COMPARE_AND_WAIT | (__notify_one ? 0 : ULF_WAKE_ALL),
805ffd83dbSDimitry Andric                  const_cast<__cxx_atomic_contention_t*>(__ptr), 0);
815ffd83dbSDimitry Andric }
825ffd83dbSDimitry Andric 
8325b18d89SKonstantin Belousov #elif defined(__FreeBSD__) && __SIZEOF_LONG__ == 8
844c4a2926SKonstantin Belousov /*
854c4a2926SKonstantin Belousov  * Since __cxx_contention_t is int64_t even on 32bit FreeBSD
864c4a2926SKonstantin Belousov  * platforms, we have to use umtx ops that work on the long type, and
874c4a2926SKonstantin Belousov  * limit its use to architectures where long and int64_t are synonyms.
884c4a2926SKonstantin Belousov  */
899c996882SKonstantin Belousov 
909c996882SKonstantin Belousov static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
919c996882SKonstantin Belousov                                               __cxx_contention_t __val)
929c996882SKonstantin Belousov {
939c996882SKonstantin Belousov     _umtx_op(const_cast<__cxx_atomic_contention_t*>(__ptr),
949c996882SKonstantin Belousov              UMTX_OP_WAIT, __val, NULL, NULL);
959c996882SKonstantin Belousov }
969c996882SKonstantin Belousov 
979c996882SKonstantin Belousov static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr,
989c996882SKonstantin Belousov                                               bool __notify_one)
999c996882SKonstantin Belousov {
1009c996882SKonstantin Belousov     _umtx_op(const_cast<__cxx_atomic_contention_t*>(__ptr),
1019c996882SKonstantin Belousov              UMTX_OP_WAKE, __notify_one ? 1 : INT_MAX, NULL, NULL);
1029c996882SKonstantin Belousov }
1039c996882SKonstantin Belousov 
1045ffd83dbSDimitry Andric #else // <- Add other operating systems here
1055ffd83dbSDimitry Andric 
1065ffd83dbSDimitry Andric // Baseline is just a timed backoff
1075ffd83dbSDimitry Andric 
1085ffd83dbSDimitry Andric static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr,
1095ffd83dbSDimitry Andric                                               __cxx_contention_t __val)
1105ffd83dbSDimitry Andric {
1115ffd83dbSDimitry Andric     __libcpp_thread_poll_with_backoff([=]() -> bool {
1125ffd83dbSDimitry Andric         return !__cxx_nonatomic_compare_equal(__cxx_atomic_load(__ptr, memory_order_relaxed), __val);
1135ffd83dbSDimitry Andric     }, __libcpp_timed_backoff_policy());
1145ffd83dbSDimitry Andric }
1155ffd83dbSDimitry Andric 
1165ffd83dbSDimitry Andric static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile*, bool) { }
1175ffd83dbSDimitry Andric 
1185ffd83dbSDimitry Andric #endif // __linux__
1195ffd83dbSDimitry Andric 
1205ffd83dbSDimitry Andric static constexpr size_t __libcpp_contention_table_size = (1 << 8);  /* < there's no magic in this number */
1215ffd83dbSDimitry Andric 
1225ffd83dbSDimitry Andric struct alignas(64) /*  aim to avoid false sharing */ __libcpp_contention_table_entry
1235ffd83dbSDimitry Andric {
1245ffd83dbSDimitry Andric     __cxx_atomic_contention_t __contention_state;
1255ffd83dbSDimitry Andric     __cxx_atomic_contention_t __platform_state;
1265ffd83dbSDimitry Andric     inline constexpr __libcpp_contention_table_entry() :
1275ffd83dbSDimitry Andric         __contention_state(0), __platform_state(0) { }
1285ffd83dbSDimitry Andric };
1295ffd83dbSDimitry Andric 
1305ffd83dbSDimitry Andric static __libcpp_contention_table_entry __libcpp_contention_table[ __libcpp_contention_table_size ];
1315ffd83dbSDimitry Andric 
1325ffd83dbSDimitry Andric static hash<void const volatile*> __libcpp_contention_hasher;
1335ffd83dbSDimitry Andric 
1345ffd83dbSDimitry Andric static __libcpp_contention_table_entry* __libcpp_contention_state(void const volatile * p)
1355ffd83dbSDimitry Andric {
1365ffd83dbSDimitry Andric     return &__libcpp_contention_table[__libcpp_contention_hasher(p) & (__libcpp_contention_table_size - 1)];
1375ffd83dbSDimitry Andric }
1385ffd83dbSDimitry Andric 
1395ffd83dbSDimitry Andric /* Given an atomic to track contention and an atomic to actually wait on, which may be
1405ffd83dbSDimitry Andric    the same atomic, we try to detect contention to avoid spuriously calling the platform. */
1415ffd83dbSDimitry Andric 
1425ffd83dbSDimitry Andric static void __libcpp_contention_notify(__cxx_atomic_contention_t volatile* __contention_state,
1435ffd83dbSDimitry Andric                                        __cxx_atomic_contention_t const volatile* __platform_state,
1445ffd83dbSDimitry Andric                                        bool __notify_one)
1455ffd83dbSDimitry Andric {
1465ffd83dbSDimitry Andric     if(0 != __cxx_atomic_load(__contention_state, memory_order_seq_cst))
1475ffd83dbSDimitry Andric         // We only call 'wake' if we consumed a contention bit here.
1485ffd83dbSDimitry Andric         __libcpp_platform_wake_by_address(__platform_state, __notify_one);
1495ffd83dbSDimitry Andric }
1505ffd83dbSDimitry Andric static __cxx_contention_t __libcpp_contention_monitor_for_wait(__cxx_atomic_contention_t volatile* __contention_state,
1515ffd83dbSDimitry Andric                                                                __cxx_atomic_contention_t const volatile* __platform_state)
1525ffd83dbSDimitry Andric {
1535ffd83dbSDimitry Andric     // We will monitor this value.
1545ffd83dbSDimitry Andric     return __cxx_atomic_load(__platform_state, memory_order_acquire);
1555ffd83dbSDimitry Andric }
1565ffd83dbSDimitry Andric static void __libcpp_contention_wait(__cxx_atomic_contention_t volatile* __contention_state,
1575ffd83dbSDimitry Andric                                      __cxx_atomic_contention_t const volatile* __platform_state,
1585ffd83dbSDimitry Andric                                      __cxx_contention_t __old_value)
1595ffd83dbSDimitry Andric {
1605ffd83dbSDimitry Andric     __cxx_atomic_fetch_add(__contention_state, __cxx_contention_t(1), memory_order_seq_cst);
1615ffd83dbSDimitry Andric     // We sleep as long as the monitored value hasn't changed.
1625ffd83dbSDimitry Andric     __libcpp_platform_wait_on_address(__platform_state, __old_value);
1635ffd83dbSDimitry Andric     __cxx_atomic_fetch_sub(__contention_state, __cxx_contention_t(1), memory_order_release);
1645ffd83dbSDimitry Andric }
1655ffd83dbSDimitry Andric 
1665ffd83dbSDimitry Andric /* When the incoming atomic is the wrong size for the platform wait size, need to
1675ffd83dbSDimitry Andric    launder the value sequence through an atomic from our table. */
1685ffd83dbSDimitry Andric 
1695ffd83dbSDimitry Andric static void __libcpp_atomic_notify(void const volatile* __location)
1705ffd83dbSDimitry Andric {
1715ffd83dbSDimitry Andric     auto const __entry = __libcpp_contention_state(__location);
1725ffd83dbSDimitry Andric     // The value sequence laundering happens on the next line below.
1735ffd83dbSDimitry Andric     __cxx_atomic_fetch_add(&__entry->__platform_state, __cxx_contention_t(1), memory_order_release);
1745ffd83dbSDimitry Andric     __libcpp_contention_notify(&__entry->__contention_state,
1755ffd83dbSDimitry Andric                                &__entry->__platform_state,
1765ffd83dbSDimitry Andric                                false /* when laundering, we can't handle notify_one */);
1775ffd83dbSDimitry Andric }
1785ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI
1795ffd83dbSDimitry Andric void __cxx_atomic_notify_one(void const volatile* __location)
1805ffd83dbSDimitry Andric     { __libcpp_atomic_notify(__location); }
1815ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI
1825ffd83dbSDimitry Andric void __cxx_atomic_notify_all(void const volatile* __location)
1835ffd83dbSDimitry Andric     { __libcpp_atomic_notify(__location); }
1845ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI
1855ffd83dbSDimitry Andric __cxx_contention_t __libcpp_atomic_monitor(void const volatile* __location)
1865ffd83dbSDimitry Andric {
1875ffd83dbSDimitry Andric     auto const __entry = __libcpp_contention_state(__location);
1885ffd83dbSDimitry Andric     return __libcpp_contention_monitor_for_wait(&__entry->__contention_state, &__entry->__platform_state);
1895ffd83dbSDimitry Andric }
1905ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI
1915ffd83dbSDimitry Andric void __libcpp_atomic_wait(void const volatile* __location, __cxx_contention_t __old_value)
1925ffd83dbSDimitry Andric {
1935ffd83dbSDimitry Andric     auto const __entry = __libcpp_contention_state(__location);
1945ffd83dbSDimitry Andric     __libcpp_contention_wait(&__entry->__contention_state, &__entry->__platform_state, __old_value);
1955ffd83dbSDimitry Andric }
1965ffd83dbSDimitry Andric 
1975ffd83dbSDimitry Andric /* When the incoming atomic happens to be the platform wait size, we still need to use the
1985ffd83dbSDimitry Andric    table for the contention detection, but we can use the atomic directly for the wait. */
1995ffd83dbSDimitry Andric 
2005ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI
2015ffd83dbSDimitry Andric void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location)
2025ffd83dbSDimitry Andric {
2035ffd83dbSDimitry Andric     __libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, true);
2045ffd83dbSDimitry Andric }
2055ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI
2065ffd83dbSDimitry Andric void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile* __location)
2075ffd83dbSDimitry Andric {
2085ffd83dbSDimitry Andric     __libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, false);
2095ffd83dbSDimitry Andric }
2105ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI
2115ffd83dbSDimitry Andric __cxx_contention_t __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile* __location)
2125ffd83dbSDimitry Andric {
2135ffd83dbSDimitry Andric     return __libcpp_contention_monitor_for_wait(&__libcpp_contention_state(__location)->__contention_state, __location);
2145ffd83dbSDimitry Andric }
2155ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI
2165ffd83dbSDimitry Andric void __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile* __location, __cxx_contention_t __old_value)
2175ffd83dbSDimitry Andric {
2185ffd83dbSDimitry Andric     __libcpp_contention_wait(&__libcpp_contention_state(__location)->__contention_state, __location, __old_value);
2195ffd83dbSDimitry Andric }
2205ffd83dbSDimitry Andric 
2215ffd83dbSDimitry Andric _LIBCPP_END_NAMESPACE_STD
2225ffd83dbSDimitry Andric 
2235ffd83dbSDimitry Andric #endif //_LIBCPP_HAS_NO_THREADS
224