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 125ffd83dbSDimitry Andric #include <atomic> 13*04eeddc0SDimitry Andric #include <climits> 145ffd83dbSDimitry Andric #include <functional> 15*04eeddc0SDimitry Andric #include <thread> 165ffd83dbSDimitry Andric 175ffd83dbSDimitry Andric #ifdef __linux__ 185ffd83dbSDimitry Andric 195ffd83dbSDimitry Andric #include <unistd.h> 205ffd83dbSDimitry Andric #include <linux/futex.h> 215ffd83dbSDimitry Andric #include <sys/syscall.h> 225ffd83dbSDimitry Andric 23d409305fSDimitry Andric // libc++ uses SYS_futex as a universal syscall name. However, on 32 bit architectures 24d409305fSDimitry Andric // with a 64 bit time_t, we need to specify SYS_futex_time64. 25d409305fSDimitry Andric #if !defined(SYS_futex) && defined(SYS_futex_time64) 26d409305fSDimitry Andric # define SYS_futex SYS_futex_time64 27d409305fSDimitry Andric #endif 28d409305fSDimitry Andric 295ffd83dbSDimitry Andric #else // <- Add other operating systems here 305ffd83dbSDimitry Andric 315ffd83dbSDimitry Andric // Baseline needs no new headers 325ffd83dbSDimitry Andric 335ffd83dbSDimitry Andric #endif 345ffd83dbSDimitry Andric 355ffd83dbSDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD 365ffd83dbSDimitry Andric 375ffd83dbSDimitry Andric #ifdef __linux__ 385ffd83dbSDimitry Andric 395ffd83dbSDimitry Andric static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr, 405ffd83dbSDimitry Andric __cxx_contention_t __val) 415ffd83dbSDimitry Andric { 425ffd83dbSDimitry Andric static constexpr timespec __timeout = { 2, 0 }; 435ffd83dbSDimitry Andric syscall(SYS_futex, __ptr, FUTEX_WAIT_PRIVATE, __val, &__timeout, 0, 0); 445ffd83dbSDimitry Andric } 455ffd83dbSDimitry Andric 465ffd83dbSDimitry Andric static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr, 475ffd83dbSDimitry Andric bool __notify_one) 485ffd83dbSDimitry Andric { 495ffd83dbSDimitry Andric syscall(SYS_futex, __ptr, FUTEX_WAKE_PRIVATE, __notify_one ? 1 : INT_MAX, 0, 0, 0); 505ffd83dbSDimitry Andric } 515ffd83dbSDimitry Andric 525ffd83dbSDimitry Andric #elif defined(__APPLE__) && defined(_LIBCPP_USE_ULOCK) 535ffd83dbSDimitry Andric 545ffd83dbSDimitry Andric extern "C" int __ulock_wait(uint32_t operation, void *addr, uint64_t value, 555ffd83dbSDimitry Andric uint32_t timeout); /* timeout is specified in microseconds */ 565ffd83dbSDimitry Andric extern "C" int __ulock_wake(uint32_t operation, void *addr, uint64_t wake_value); 575ffd83dbSDimitry Andric 585ffd83dbSDimitry Andric #define UL_COMPARE_AND_WAIT 1 595ffd83dbSDimitry Andric #define ULF_WAKE_ALL 0x00000100 605ffd83dbSDimitry Andric 615ffd83dbSDimitry Andric static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr, 625ffd83dbSDimitry Andric __cxx_contention_t __val) 635ffd83dbSDimitry Andric { 645ffd83dbSDimitry Andric __ulock_wait(UL_COMPARE_AND_WAIT, 655ffd83dbSDimitry Andric const_cast<__cxx_atomic_contention_t*>(__ptr), __val, 0); 665ffd83dbSDimitry Andric } 675ffd83dbSDimitry Andric 685ffd83dbSDimitry Andric static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile* __ptr, 695ffd83dbSDimitry Andric bool __notify_one) 705ffd83dbSDimitry Andric { 715ffd83dbSDimitry Andric __ulock_wake(UL_COMPARE_AND_WAIT | (__notify_one ? 0 : ULF_WAKE_ALL), 725ffd83dbSDimitry Andric const_cast<__cxx_atomic_contention_t*>(__ptr), 0); 735ffd83dbSDimitry Andric } 745ffd83dbSDimitry Andric 755ffd83dbSDimitry Andric #else // <- Add other operating systems here 765ffd83dbSDimitry Andric 775ffd83dbSDimitry Andric // Baseline is just a timed backoff 785ffd83dbSDimitry Andric 795ffd83dbSDimitry Andric static void __libcpp_platform_wait_on_address(__cxx_atomic_contention_t const volatile* __ptr, 805ffd83dbSDimitry Andric __cxx_contention_t __val) 815ffd83dbSDimitry Andric { 825ffd83dbSDimitry Andric __libcpp_thread_poll_with_backoff([=]() -> bool { 835ffd83dbSDimitry Andric return !__cxx_nonatomic_compare_equal(__cxx_atomic_load(__ptr, memory_order_relaxed), __val); 845ffd83dbSDimitry Andric }, __libcpp_timed_backoff_policy()); 855ffd83dbSDimitry Andric } 865ffd83dbSDimitry Andric 875ffd83dbSDimitry Andric static void __libcpp_platform_wake_by_address(__cxx_atomic_contention_t const volatile*, bool) { } 885ffd83dbSDimitry Andric 895ffd83dbSDimitry Andric #endif // __linux__ 905ffd83dbSDimitry Andric 915ffd83dbSDimitry Andric static constexpr size_t __libcpp_contention_table_size = (1 << 8); /* < there's no magic in this number */ 925ffd83dbSDimitry Andric 935ffd83dbSDimitry Andric struct alignas(64) /* aim to avoid false sharing */ __libcpp_contention_table_entry 945ffd83dbSDimitry Andric { 955ffd83dbSDimitry Andric __cxx_atomic_contention_t __contention_state; 965ffd83dbSDimitry Andric __cxx_atomic_contention_t __platform_state; 975ffd83dbSDimitry Andric inline constexpr __libcpp_contention_table_entry() : 985ffd83dbSDimitry Andric __contention_state(0), __platform_state(0) { } 995ffd83dbSDimitry Andric }; 1005ffd83dbSDimitry Andric 1015ffd83dbSDimitry Andric static __libcpp_contention_table_entry __libcpp_contention_table[ __libcpp_contention_table_size ]; 1025ffd83dbSDimitry Andric 1035ffd83dbSDimitry Andric static hash<void const volatile*> __libcpp_contention_hasher; 1045ffd83dbSDimitry Andric 1055ffd83dbSDimitry Andric static __libcpp_contention_table_entry* __libcpp_contention_state(void const volatile * p) 1065ffd83dbSDimitry Andric { 1075ffd83dbSDimitry Andric return &__libcpp_contention_table[__libcpp_contention_hasher(p) & (__libcpp_contention_table_size - 1)]; 1085ffd83dbSDimitry Andric } 1095ffd83dbSDimitry Andric 1105ffd83dbSDimitry Andric /* Given an atomic to track contention and an atomic to actually wait on, which may be 1115ffd83dbSDimitry Andric the same atomic, we try to detect contention to avoid spuriously calling the platform. */ 1125ffd83dbSDimitry Andric 1135ffd83dbSDimitry Andric static void __libcpp_contention_notify(__cxx_atomic_contention_t volatile* __contention_state, 1145ffd83dbSDimitry Andric __cxx_atomic_contention_t const volatile* __platform_state, 1155ffd83dbSDimitry Andric bool __notify_one) 1165ffd83dbSDimitry Andric { 1175ffd83dbSDimitry Andric if(0 != __cxx_atomic_load(__contention_state, memory_order_seq_cst)) 1185ffd83dbSDimitry Andric // We only call 'wake' if we consumed a contention bit here. 1195ffd83dbSDimitry Andric __libcpp_platform_wake_by_address(__platform_state, __notify_one); 1205ffd83dbSDimitry Andric } 1215ffd83dbSDimitry Andric static __cxx_contention_t __libcpp_contention_monitor_for_wait(__cxx_atomic_contention_t volatile* __contention_state, 1225ffd83dbSDimitry Andric __cxx_atomic_contention_t const volatile* __platform_state) 1235ffd83dbSDimitry Andric { 1245ffd83dbSDimitry Andric // We will monitor this value. 1255ffd83dbSDimitry Andric return __cxx_atomic_load(__platform_state, memory_order_acquire); 1265ffd83dbSDimitry Andric } 1275ffd83dbSDimitry Andric static void __libcpp_contention_wait(__cxx_atomic_contention_t volatile* __contention_state, 1285ffd83dbSDimitry Andric __cxx_atomic_contention_t const volatile* __platform_state, 1295ffd83dbSDimitry Andric __cxx_contention_t __old_value) 1305ffd83dbSDimitry Andric { 1315ffd83dbSDimitry Andric __cxx_atomic_fetch_add(__contention_state, __cxx_contention_t(1), memory_order_seq_cst); 1325ffd83dbSDimitry Andric // We sleep as long as the monitored value hasn't changed. 1335ffd83dbSDimitry Andric __libcpp_platform_wait_on_address(__platform_state, __old_value); 1345ffd83dbSDimitry Andric __cxx_atomic_fetch_sub(__contention_state, __cxx_contention_t(1), memory_order_release); 1355ffd83dbSDimitry Andric } 1365ffd83dbSDimitry Andric 1375ffd83dbSDimitry Andric /* When the incoming atomic is the wrong size for the platform wait size, need to 1385ffd83dbSDimitry Andric launder the value sequence through an atomic from our table. */ 1395ffd83dbSDimitry Andric 1405ffd83dbSDimitry Andric static void __libcpp_atomic_notify(void const volatile* __location) 1415ffd83dbSDimitry Andric { 1425ffd83dbSDimitry Andric auto const __entry = __libcpp_contention_state(__location); 1435ffd83dbSDimitry Andric // The value sequence laundering happens on the next line below. 1445ffd83dbSDimitry Andric __cxx_atomic_fetch_add(&__entry->__platform_state, __cxx_contention_t(1), memory_order_release); 1455ffd83dbSDimitry Andric __libcpp_contention_notify(&__entry->__contention_state, 1465ffd83dbSDimitry Andric &__entry->__platform_state, 1475ffd83dbSDimitry Andric false /* when laundering, we can't handle notify_one */); 1485ffd83dbSDimitry Andric } 1495ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI 1505ffd83dbSDimitry Andric void __cxx_atomic_notify_one(void const volatile* __location) 1515ffd83dbSDimitry Andric { __libcpp_atomic_notify(__location); } 1525ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI 1535ffd83dbSDimitry Andric void __cxx_atomic_notify_all(void const volatile* __location) 1545ffd83dbSDimitry Andric { __libcpp_atomic_notify(__location); } 1555ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI 1565ffd83dbSDimitry Andric __cxx_contention_t __libcpp_atomic_monitor(void const volatile* __location) 1575ffd83dbSDimitry Andric { 1585ffd83dbSDimitry Andric auto const __entry = __libcpp_contention_state(__location); 1595ffd83dbSDimitry Andric return __libcpp_contention_monitor_for_wait(&__entry->__contention_state, &__entry->__platform_state); 1605ffd83dbSDimitry Andric } 1615ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI 1625ffd83dbSDimitry Andric void __libcpp_atomic_wait(void const volatile* __location, __cxx_contention_t __old_value) 1635ffd83dbSDimitry Andric { 1645ffd83dbSDimitry Andric auto const __entry = __libcpp_contention_state(__location); 1655ffd83dbSDimitry Andric __libcpp_contention_wait(&__entry->__contention_state, &__entry->__platform_state, __old_value); 1665ffd83dbSDimitry Andric } 1675ffd83dbSDimitry Andric 1685ffd83dbSDimitry Andric /* When the incoming atomic happens to be the platform wait size, we still need to use the 1695ffd83dbSDimitry Andric table for the contention detection, but we can use the atomic directly for the wait. */ 1705ffd83dbSDimitry Andric 1715ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI 1725ffd83dbSDimitry Andric void __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile* __location) 1735ffd83dbSDimitry Andric { 1745ffd83dbSDimitry Andric __libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, true); 1755ffd83dbSDimitry Andric } 1765ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI 1775ffd83dbSDimitry Andric void __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile* __location) 1785ffd83dbSDimitry Andric { 1795ffd83dbSDimitry Andric __libcpp_contention_notify(&__libcpp_contention_state(__location)->__contention_state, __location, false); 1805ffd83dbSDimitry Andric } 1815ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI 1825ffd83dbSDimitry Andric __cxx_contention_t __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile* __location) 1835ffd83dbSDimitry Andric { 1845ffd83dbSDimitry Andric return __libcpp_contention_monitor_for_wait(&__libcpp_contention_state(__location)->__contention_state, __location); 1855ffd83dbSDimitry Andric } 1865ffd83dbSDimitry Andric _LIBCPP_EXPORTED_FROM_ABI 1875ffd83dbSDimitry Andric void __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile* __location, __cxx_contention_t __old_value) 1885ffd83dbSDimitry Andric { 1895ffd83dbSDimitry Andric __libcpp_contention_wait(&__libcpp_contention_state(__location)->__contention_state, __location, __old_value); 1905ffd83dbSDimitry Andric } 1915ffd83dbSDimitry Andric 1925ffd83dbSDimitry Andric _LIBCPP_END_NAMESPACE_STD 1935ffd83dbSDimitry Andric 1945ffd83dbSDimitry Andric #endif //_LIBCPP_HAS_NO_THREADS 195