xref: /freebsd-src/contrib/llvm-project/libcxx/include/__atomic/atomic_sync.h (revision 0fca6ea1d4eea4c934cfff25ac9ee8ad6fe95583)
106c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
206c3fb27SDimitry Andric //
306c3fb27SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
406c3fb27SDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
506c3fb27SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
606c3fb27SDimitry Andric //
706c3fb27SDimitry Andric //===----------------------------------------------------------------------===//
806c3fb27SDimitry Andric 
906c3fb27SDimitry Andric #ifndef _LIBCPP___ATOMIC_ATOMIC_SYNC_H
1006c3fb27SDimitry Andric #define _LIBCPP___ATOMIC_ATOMIC_SYNC_H
1106c3fb27SDimitry Andric 
1206c3fb27SDimitry Andric #include <__atomic/contention_t.h>
1306c3fb27SDimitry Andric #include <__atomic/cxx_atomic_impl.h>
1406c3fb27SDimitry Andric #include <__atomic/memory_order.h>
15*0fca6ea1SDimitry Andric #include <__atomic/to_gcc_order.h>
1606c3fb27SDimitry Andric #include <__chrono/duration.h>
1706c3fb27SDimitry Andric #include <__config>
1806c3fb27SDimitry Andric #include <__memory/addressof.h>
1906c3fb27SDimitry Andric #include <__thread/poll_with_backoff.h>
20*0fca6ea1SDimitry Andric #include <__thread/support.h>
21*0fca6ea1SDimitry Andric #include <__type_traits/conjunction.h>
2206c3fb27SDimitry Andric #include <__type_traits/decay.h>
23*0fca6ea1SDimitry Andric #include <__type_traits/invoke.h>
24*0fca6ea1SDimitry Andric #include <__type_traits/void_t.h>
25*0fca6ea1SDimitry Andric #include <__utility/declval.h>
2606c3fb27SDimitry Andric #include <cstring>
2706c3fb27SDimitry Andric 
2806c3fb27SDimitry Andric #if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
2906c3fb27SDimitry Andric #  pragma GCC system_header
3006c3fb27SDimitry Andric #endif
3106c3fb27SDimitry Andric 
3206c3fb27SDimitry Andric _LIBCPP_BEGIN_NAMESPACE_STD
3306c3fb27SDimitry Andric 
34*0fca6ea1SDimitry Andric // The customisation points to enable the following functions:
35*0fca6ea1SDimitry Andric // - __atomic_wait
36*0fca6ea1SDimitry Andric // - __atomic_wait_unless
37*0fca6ea1SDimitry Andric // - __atomic_notify_one
38*0fca6ea1SDimitry Andric // - __atomic_notify_all
39*0fca6ea1SDimitry Andric // Note that std::atomic<T>::wait was back-ported to C++03
40*0fca6ea1SDimitry Andric // The below implementations look ugly to support C++03
41*0fca6ea1SDimitry Andric template <class _Tp, class = void>
42*0fca6ea1SDimitry Andric struct __atomic_waitable_traits {
43*0fca6ea1SDimitry Andric   template <class _AtomicWaitable>
44*0fca6ea1SDimitry Andric   static void __atomic_load(_AtomicWaitable&&, memory_order) = delete;
45*0fca6ea1SDimitry Andric 
46*0fca6ea1SDimitry Andric   template <class _AtomicWaitable>
47*0fca6ea1SDimitry Andric   static void __atomic_contention_address(_AtomicWaitable&&) = delete;
48*0fca6ea1SDimitry Andric };
49*0fca6ea1SDimitry Andric 
50*0fca6ea1SDimitry Andric template <class _Tp, class = void>
51*0fca6ea1SDimitry Andric struct __atomic_waitable : false_type {};
52*0fca6ea1SDimitry Andric 
53*0fca6ea1SDimitry Andric template <class _Tp>
54*0fca6ea1SDimitry Andric struct __atomic_waitable< _Tp,
55*0fca6ea1SDimitry Andric                           __void_t<decltype(__atomic_waitable_traits<__decay_t<_Tp> >::__atomic_load(
56*0fca6ea1SDimitry Andric                                        std::declval<const _Tp&>(), std::declval<memory_order>())),
57*0fca6ea1SDimitry Andric                                    decltype(__atomic_waitable_traits<__decay_t<_Tp> >::__atomic_contention_address(
58*0fca6ea1SDimitry Andric                                        std::declval<const _Tp&>()))> > : true_type {};
59*0fca6ea1SDimitry Andric 
60*0fca6ea1SDimitry Andric template <class _AtomicWaitable, class _Poll>
61*0fca6ea1SDimitry Andric struct __atomic_wait_poll_impl {
62*0fca6ea1SDimitry Andric   const _AtomicWaitable& __a_;
63*0fca6ea1SDimitry Andric   _Poll __poll_;
64*0fca6ea1SDimitry Andric   memory_order __order_;
65*0fca6ea1SDimitry Andric 
66*0fca6ea1SDimitry Andric   _LIBCPP_HIDE_FROM_ABI bool operator()() const {
67*0fca6ea1SDimitry Andric     auto __current_val = __atomic_waitable_traits<__decay_t<_AtomicWaitable> >::__atomic_load(__a_, __order_);
68*0fca6ea1SDimitry Andric     return __poll_(__current_val);
69*0fca6ea1SDimitry Andric   }
70*0fca6ea1SDimitry Andric };
71*0fca6ea1SDimitry Andric 
7206c3fb27SDimitry Andric #ifndef _LIBCPP_HAS_NO_THREADS
7306c3fb27SDimitry Andric 
74*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_one(void const volatile*) _NOEXCEPT;
75*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI void __cxx_atomic_notify_all(void const volatile*) _NOEXCEPT;
76cb14a3feSDimitry Andric _LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t
77*0fca6ea1SDimitry Andric __libcpp_atomic_monitor(void const volatile*) _NOEXCEPT;
78cb14a3feSDimitry Andric _LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI void
79*0fca6ea1SDimitry Andric __libcpp_atomic_wait(void const volatile*, __cxx_contention_t) _NOEXCEPT;
8006c3fb27SDimitry Andric 
81*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI void
82*0fca6ea1SDimitry Andric __cxx_atomic_notify_one(__cxx_atomic_contention_t const volatile*) _NOEXCEPT;
83*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI void
84*0fca6ea1SDimitry Andric __cxx_atomic_notify_all(__cxx_atomic_contention_t const volatile*) _NOEXCEPT;
85*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI __cxx_contention_t
86*0fca6ea1SDimitry Andric __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile*) _NOEXCEPT;
87*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI void
88*0fca6ea1SDimitry Andric __libcpp_atomic_wait(__cxx_atomic_contention_t const volatile*, __cxx_contention_t) _NOEXCEPT;
89*0fca6ea1SDimitry Andric 
90*0fca6ea1SDimitry Andric template <class _AtomicWaitable, class _Poll>
91*0fca6ea1SDimitry Andric struct __atomic_wait_backoff_impl {
92*0fca6ea1SDimitry Andric   const _AtomicWaitable& __a_;
93*0fca6ea1SDimitry Andric   _Poll __poll_;
94*0fca6ea1SDimitry Andric   memory_order __order_;
95*0fca6ea1SDimitry Andric 
96*0fca6ea1SDimitry Andric   using __waitable_traits = __atomic_waitable_traits<__decay_t<_AtomicWaitable> >;
97*0fca6ea1SDimitry Andric 
98*0fca6ea1SDimitry Andric   _LIBCPP_AVAILABILITY_SYNC
99*0fca6ea1SDimitry Andric   _LIBCPP_HIDE_FROM_ABI bool
100*0fca6ea1SDimitry Andric   __update_monitor_val_and_poll(__cxx_atomic_contention_t const volatile*, __cxx_contention_t& __monitor_val) const {
101*0fca6ea1SDimitry Andric     // In case the contention type happens to be __cxx_atomic_contention_t, i.e. __cxx_atomic_impl<int64_t>,
102*0fca6ea1SDimitry Andric     // the platform wait is directly monitoring the atomic value itself.
103*0fca6ea1SDimitry Andric     // `__poll_` takes the current value of the atomic as an in-out argument
104*0fca6ea1SDimitry Andric     // to potentially modify it. After it returns, `__monitor` has a value
105*0fca6ea1SDimitry Andric     // which can be safely waited on by `std::__libcpp_atomic_wait` without any
106*0fca6ea1SDimitry Andric     // ABA style issues.
107*0fca6ea1SDimitry Andric     __monitor_val = __waitable_traits::__atomic_load(__a_, __order_);
108*0fca6ea1SDimitry Andric     return __poll_(__monitor_val);
109*0fca6ea1SDimitry Andric   }
110*0fca6ea1SDimitry Andric 
111*0fca6ea1SDimitry Andric   _LIBCPP_AVAILABILITY_SYNC
112*0fca6ea1SDimitry Andric   _LIBCPP_HIDE_FROM_ABI bool
113*0fca6ea1SDimitry Andric   __update_monitor_val_and_poll(void const volatile* __contention_address, __cxx_contention_t& __monitor_val) const {
114*0fca6ea1SDimitry Andric     // In case the contention type is anything else, platform wait is monitoring a __cxx_atomic_contention_t
115*0fca6ea1SDimitry Andric     // from the global pool, the monitor comes from __libcpp_atomic_monitor
116*0fca6ea1SDimitry Andric     __monitor_val      = std::__libcpp_atomic_monitor(__contention_address);
117*0fca6ea1SDimitry Andric     auto __current_val = __waitable_traits::__atomic_load(__a_, __order_);
118*0fca6ea1SDimitry Andric     return __poll_(__current_val);
119*0fca6ea1SDimitry Andric   }
120*0fca6ea1SDimitry Andric 
12106c3fb27SDimitry Andric   _LIBCPP_AVAILABILITY_SYNC
122cb14a3feSDimitry Andric   _LIBCPP_HIDE_FROM_ABI bool operator()(chrono::nanoseconds __elapsed) const {
123cb14a3feSDimitry Andric     if (__elapsed > chrono::microseconds(64)) {
124*0fca6ea1SDimitry Andric       auto __contention_address = __waitable_traits::__atomic_contention_address(__a_);
125*0fca6ea1SDimitry Andric       __cxx_contention_t __monitor_val;
126*0fca6ea1SDimitry Andric       if (__update_monitor_val_and_poll(__contention_address, __monitor_val))
12706c3fb27SDimitry Andric         return true;
128*0fca6ea1SDimitry Andric       std::__libcpp_atomic_wait(__contention_address, __monitor_val);
129cb14a3feSDimitry Andric     } else if (__elapsed > chrono::microseconds(4))
13006c3fb27SDimitry Andric       __libcpp_thread_yield();
131cb14a3feSDimitry Andric     else {
132cb14a3feSDimitry Andric     } // poll
13306c3fb27SDimitry Andric     return false;
13406c3fb27SDimitry Andric   }
13506c3fb27SDimitry Andric };
13606c3fb27SDimitry Andric 
137*0fca6ea1SDimitry Andric // The semantics of this function are similar to `atomic`'s
138*0fca6ea1SDimitry Andric // `.wait(T old, std::memory_order order)`, but instead of having a hardcoded
139*0fca6ea1SDimitry Andric // predicate (is the loaded value unequal to `old`?), the predicate function is
140*0fca6ea1SDimitry Andric // specified as an argument. The loaded value is given as an in-out argument to
141*0fca6ea1SDimitry Andric // the predicate. If the predicate function returns `true`,
142*0fca6ea1SDimitry Andric // `__atomic_wait_unless` will return. If the predicate function returns
143*0fca6ea1SDimitry Andric // `false`, it must set the argument to its current understanding of the atomic
144*0fca6ea1SDimitry Andric // value. The predicate function must not return `false` spuriously.
145*0fca6ea1SDimitry Andric template <class _AtomicWaitable, class _Poll>
146*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void
147*0fca6ea1SDimitry Andric __atomic_wait_unless(const _AtomicWaitable& __a, _Poll&& __poll, memory_order __order) {
148*0fca6ea1SDimitry Andric   static_assert(__atomic_waitable<_AtomicWaitable>::value, "");
149*0fca6ea1SDimitry Andric   __atomic_wait_poll_impl<_AtomicWaitable, __decay_t<_Poll> > __poll_impl     = {__a, __poll, __order};
150*0fca6ea1SDimitry Andric   __atomic_wait_backoff_impl<_AtomicWaitable, __decay_t<_Poll> > __backoff_fn = {__a, __poll, __order};
151*0fca6ea1SDimitry Andric   std::__libcpp_thread_poll_with_backoff(__poll_impl, __backoff_fn);
152*0fca6ea1SDimitry Andric }
153*0fca6ea1SDimitry Andric 
154*0fca6ea1SDimitry Andric template <class _AtomicWaitable>
155*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void __atomic_notify_one(const _AtomicWaitable& __a) {
156*0fca6ea1SDimitry Andric   static_assert(__atomic_waitable<_AtomicWaitable>::value, "");
157*0fca6ea1SDimitry Andric   std::__cxx_atomic_notify_one(__atomic_waitable_traits<__decay_t<_AtomicWaitable> >::__atomic_contention_address(__a));
158*0fca6ea1SDimitry Andric }
159*0fca6ea1SDimitry Andric 
160*0fca6ea1SDimitry Andric template <class _AtomicWaitable>
161*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void __atomic_notify_all(const _AtomicWaitable& __a) {
162*0fca6ea1SDimitry Andric   static_assert(__atomic_waitable<_AtomicWaitable>::value, "");
163*0fca6ea1SDimitry Andric   std::__cxx_atomic_notify_all(__atomic_waitable_traits<__decay_t<_AtomicWaitable> >::__atomic_contention_address(__a));
16406c3fb27SDimitry Andric }
16506c3fb27SDimitry Andric 
16606c3fb27SDimitry Andric #else // _LIBCPP_HAS_NO_THREADS
16706c3fb27SDimitry Andric 
168*0fca6ea1SDimitry Andric template <class _AtomicWaitable, class _Poll>
169*0fca6ea1SDimitry Andric _LIBCPP_HIDE_FROM_ABI void __atomic_wait_unless(const _AtomicWaitable& __a, _Poll&& __poll, memory_order __order) {
170*0fca6ea1SDimitry Andric   __atomic_wait_poll_impl<_AtomicWaitable, __decay_t<_Poll> > __poll_fn = {__a, __poll, __order};
171*0fca6ea1SDimitry Andric   std::__libcpp_thread_poll_with_backoff(__poll_fn, __spinning_backoff_policy());
17206c3fb27SDimitry Andric }
17306c3fb27SDimitry Andric 
174*0fca6ea1SDimitry Andric template <class _AtomicWaitable>
175*0fca6ea1SDimitry Andric _LIBCPP_HIDE_FROM_ABI void __atomic_notify_one(const _AtomicWaitable&) {}
176*0fca6ea1SDimitry Andric 
177*0fca6ea1SDimitry Andric template <class _AtomicWaitable>
178*0fca6ea1SDimitry Andric _LIBCPP_HIDE_FROM_ABI void __atomic_notify_all(const _AtomicWaitable&) {}
179*0fca6ea1SDimitry Andric 
18006c3fb27SDimitry Andric #endif // _LIBCPP_HAS_NO_THREADS
18106c3fb27SDimitry Andric 
182cb14a3feSDimitry Andric template <typename _Tp>
183cb14a3feSDimitry Andric _LIBCPP_HIDE_FROM_ABI bool __cxx_nonatomic_compare_equal(_Tp const& __lhs, _Tp const& __rhs) {
18406c3fb27SDimitry Andric   return std::memcmp(std::addressof(__lhs), std::addressof(__rhs), sizeof(_Tp)) == 0;
18506c3fb27SDimitry Andric }
18606c3fb27SDimitry Andric 
187*0fca6ea1SDimitry Andric template <class _Tp>
188*0fca6ea1SDimitry Andric struct __atomic_compare_unequal_to {
189*0fca6ea1SDimitry Andric   _Tp __val_;
190*0fca6ea1SDimitry Andric   _LIBCPP_HIDE_FROM_ABI bool operator()(const _Tp& __arg) const {
191*0fca6ea1SDimitry Andric     return !std::__cxx_nonatomic_compare_equal(__arg, __val_);
19206c3fb27SDimitry Andric   }
19306c3fb27SDimitry Andric };
19406c3fb27SDimitry Andric 
195*0fca6ea1SDimitry Andric template <class _AtomicWaitable, class _Up>
196*0fca6ea1SDimitry Andric _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void
197*0fca6ea1SDimitry Andric __atomic_wait(_AtomicWaitable& __a, _Up __val, memory_order __order) {
198*0fca6ea1SDimitry Andric   static_assert(__atomic_waitable<_AtomicWaitable>::value, "");
199*0fca6ea1SDimitry Andric   __atomic_compare_unequal_to<_Up> __nonatomic_equal = {__val};
200*0fca6ea1SDimitry Andric   std::__atomic_wait_unless(__a, __nonatomic_equal, __order);
20106c3fb27SDimitry Andric }
20206c3fb27SDimitry Andric 
20306c3fb27SDimitry Andric _LIBCPP_END_NAMESPACE_STD
20406c3fb27SDimitry Andric 
20506c3fb27SDimitry Andric #endif // _LIBCPP___ATOMIC_ATOMIC_SYNC_H
206