18
18
#include < __memory/addressof.h>
19
19
#include < __thread/poll_with_backoff.h>
20
20
#include < __thread/support.h>
21
+ #include < __type_traits/conjunction.h>
21
22
#include < __type_traits/decay.h>
23
+ #include < __type_traits/invoke.h>
24
+ #include < __type_traits/void_t.h>
25
+ #include < __utility/declval.h>
22
26
#include < cstring>
23
27
24
28
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
27
31
28
32
_LIBCPP_BEGIN_NAMESPACE_STD
29
33
30
- template <class _Atp , class _Poll >
31
- struct __libcpp_atomic_wait_poll_impl {
32
- _Atp* __a_;
34
+ // The customisation points to enable the following functions:
35
+ // - __atomic_wait
36
+ // - __atomic_wait_unless
37
+ // - __atomic_notify_one
38
+ // - __atomic_notify_all
39
+ // Note that std::atomic<T>::wait was back-ported to C++03
40
+ // The below implementations look ugly to support C++03
41
+ template <class _Tp , class = void >
42
+ struct __atomic_waitable_traits {
43
+ template <class _AtomicWaitable >
44
+ static void __atomic_load (_AtomicWaitable&&, memory_order) = delete;
45
+
46
+ template <class _AtomicWaitable >
47
+ static void __atomic_contention_address (_AtomicWaitable&&) = delete;
48
+ };
49
+
50
+ template <class _Tp >
51
+ struct __atomic_waitable_traits <_Tp, __enable_if_t <!__is_same(_Tp, __decay_t <_Tp>)> >
52
+ : __atomic_waitable_traits<__decay_t <_Tp> > {};
53
+
54
+ template <class _Tp , class = void >
55
+ struct __atomic_waitable : false_type {};
56
+
57
+ template <class _Tp >
58
+ struct __atomic_waitable <
59
+ _Tp,
60
+ __void_t <decltype(__atomic_waitable_traits<_Tp>::__atomic_load(
61
+ std::declval<const _Tp&>(), std::declval<memory_order>())),
62
+ decltype (__atomic_waitable_traits<_Tp>::__atomic_contention_address(std::declval<const _Tp&>()))> >
63
+ : true_type {};
64
+
65
+ template <class _AtomicWaitable , class _Poll >
66
+ struct __atomic_wait_poll_impl {
67
+ const _AtomicWaitable& __a_;
33
68
_Poll __poll_;
34
69
memory_order __order_;
35
70
36
- _LIBCPP_AVAILABILITY_SYNC
37
71
_LIBCPP_HIDE_FROM_ABI bool operator ()() const {
38
- auto __current_val = std::__cxx_atomic_load (__a_, __order_);
72
+ auto __current_val = __atomic_waitable_traits<_AtomicWaitable>:: __atomic_load (__a_, __order_);
39
73
return __poll_ (__current_val);
40
74
}
41
75
};
@@ -56,42 +90,39 @@ __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile*);
56
90
_LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI void
57
91
__libcpp_atomic_wait (__cxx_atomic_contention_t const volatile *, __cxx_contention_t );
58
92
59
- template <class _Atp , class _Poll >
60
- struct __libcpp_atomic_wait_backoff_impl {
61
- _Atp* __a_;
93
+ template <class _AtomicWaitable , class _Poll >
94
+ struct __atomic_wait_backoff_impl {
95
+ const _AtomicWaitable& __a_;
62
96
_Poll __poll_;
63
97
memory_order __order_;
64
98
65
99
_LIBCPP_AVAILABILITY_SYNC
66
100
_LIBCPP_HIDE_FROM_ABI bool
67
- __poll_or_get_monitor (__cxx_atomic_contention_t const volatile *, __cxx_contention_t & __monitor) const {
68
- // In case the atomic can be waited on directly, the monitor value is just
69
- // the value of the atomic.
70
- // `__poll_` takes the current value of the atomic as an in-out argument
71
- // to potentially modify it. After it returns, `__monitor` has a value
72
- // which can be safely waited on by `std::__libcpp_atomic_wait` without any
73
- // ABA style issues.
74
- __monitor = std::__cxx_atomic_load (__a_, __order_);
75
- return __poll_ (__monitor);
101
+ __update_monitor_val_and_poll (__cxx_atomic_contention_t const volatile *, __cxx_contention_t & __monitor_val) const {
102
+ // In case the contention type happens to be __cxx_atomic_contention_t, i.e. __cxx_atomic_impl<int64_t>,
103
+ // the platform wait is directly monitoring the atomic value itself.
104
+ __monitor_val = __atomic_waitable_traits<_AtomicWaitable>::__atomic_load (__a_, __order_);
105
+ return __poll_ (__monitor_val);
76
106
}
77
107
78
108
_LIBCPP_AVAILABILITY_SYNC
79
- _LIBCPP_HIDE_FROM_ABI bool __poll_or_get_monitor ( void const volatile *, __cxx_contention_t & __monitor) const {
80
- // In case we must wait on an atomic from the pool, the monitor comes from
81
- // `std::__libcpp_atomic_monitor`.
82
- // Only then we may read from `__a_`. This is the "event count" pattern.
83
- __monitor = std::__libcpp_atomic_monitor (__a_ );
84
- auto __current_val = std::__cxx_atomic_load (__a_, __order_);
109
+ _LIBCPP_HIDE_FROM_ABI bool
110
+ __update_monitor_val_and_poll ( void const volatile * __contention_address, __cxx_contention_t & __monitor_val) const {
111
+ // In case the contention type is anything else, platform wait is monitoring a __cxx_atomic_contention_t
112
+ // from the global pool, the monitor comes from __libcpp_atomic_monitor
113
+ __monitor_val = std::__libcpp_atomic_monitor (__contention_address );
114
+ auto __current_val = __atomic_waitable_traits<_AtomicWaitable>:: __atomic_load (__a_, __order_);
85
115
return __poll_ (__current_val);
86
116
}
87
117
88
118
_LIBCPP_AVAILABILITY_SYNC
89
119
_LIBCPP_HIDE_FROM_ABI bool operator ()(chrono::nanoseconds __elapsed) const {
90
120
if (__elapsed > chrono::microseconds (64 )) {
91
- __cxx_contention_t __monitor;
92
- if (__poll_or_get_monitor (__a_, __monitor))
121
+ auto __contention_address = __atomic_waitable_traits<_AtomicWaitable>::__atomic_contention_address (__a_);
122
+ __cxx_contention_t __monitor_val;
123
+ if (__update_monitor_val_and_poll (__contention_address, __monitor_val))
93
124
return true ;
94
- std::__libcpp_atomic_wait (__a_, __monitor );
125
+ std::__libcpp_atomic_wait (__contention_address, __monitor_val );
95
126
} else if (__elapsed > chrono::microseconds (4 ))
96
127
__libcpp_thread_yield ();
97
128
else {
@@ -100,34 +131,41 @@ struct __libcpp_atomic_wait_backoff_impl {
100
131
}
101
132
};
102
133
103
- // The semantics of this function are similar to `atomic`'s
104
- // `.wait(T old, std::memory_order order)`, but instead of having a hardcoded
105
- // predicate (is the loaded value unequal to `old`?), the predicate function is
106
- // specified as an argument. The loaded value is given as an in-out argument to
107
- // the predicate. If the predicate function returns `true`,
108
- // `_cxx_atomic_wait_unless` will return. If the predicate function returns
109
- // `false`, it must set the argument to its current understanding of the atomic
110
- // value. The predicate function must not return `false` spuriously.
111
- template <class _Atp , class _Poll >
134
+ template <class _AtomicWaitable , class _Poll >
112
135
_LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void
113
- __cxx_atomic_wait_unless (_Atp* __a, _Poll&& __poll, memory_order __order) {
114
- __libcpp_atomic_wait_poll_impl<_Atp, __decay_t <_Poll> > __poll_fn = {__a, __poll, __order};
115
- __libcpp_atomic_wait_backoff_impl<_Atp, __decay_t <_Poll> > __backoff_fn = {__a, __poll, __order};
116
- (void )std::__libcpp_thread_poll_with_backoff (__poll_fn, __backoff_fn);
136
+ __atomic_wait_unless (const _AtomicWaitable& __a, _Poll&& __poll, memory_order __order) {
137
+ static_assert (__atomic_waitable<_AtomicWaitable>::value, " " );
138
+ __atomic_wait_poll_impl<_AtomicWaitable, __decay_t <_Poll> > __poll_impl = {__a, __poll, __order};
139
+ __atomic_wait_backoff_impl<_AtomicWaitable, __decay_t <_Poll> > __backoff_fn = {__a, __poll, __order};
140
+ std::__libcpp_thread_poll_with_backoff (__poll_impl, __backoff_fn);
141
+ }
142
+
143
+ template <class _AtomicWaitable >
144
+ _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void __atomic_notify_one (const _AtomicWaitable& __a) {
145
+ static_assert (__atomic_waitable<_AtomicWaitable>::value, " " );
146
+ std::__cxx_atomic_notify_one (__atomic_waitable_traits<_AtomicWaitable>::__atomic_contention_address (__a));
147
+ }
148
+
149
+ template <class _AtomicWaitable >
150
+ _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void __atomic_notify_all (const _AtomicWaitable& __a) {
151
+ static_assert (__atomic_waitable<_AtomicWaitable>::value, " " );
152
+ std::__cxx_atomic_notify_all (__atomic_waitable_traits<_AtomicWaitable>::__atomic_contention_address (__a));
117
153
}
118
154
119
155
#else // _LIBCPP_HAS_NO_THREADS
120
156
121
- template <class _Tp >
122
- _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_notify_all (__cxx_atomic_impl<_Tp> const volatile *) {}
123
- template <class _Tp >
124
- _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_notify_one (__cxx_atomic_impl<_Tp> const volatile *) {}
125
- template <class _Atp , class _Poll >
126
- _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_wait_unless (_Atp* __a, _Poll&& __poll, memory_order __order) {
127
- __libcpp_atomic_wait_poll_impl<_Atp, __decay_t <_Poll> > __poll_fn = {__a, __poll, __order};
128
- (void )std::__libcpp_thread_poll_with_backoff (__poll_fn, __spinning_backoff_policy ());
157
+ template <class _AtomicWaitable , class _Poll >
158
+ _LIBCPP_HIDE_FROM_ABI void __atomic_wait_unless (const _AtomicWaitable& __a, _Poll&& __poll, memory_order __order) {
159
+ __atomic_wait_poll_impl<_AtomicWaitable, __decay_t <_Poll> > __poll_fn = {__a, __poll, __order};
160
+ std::__libcpp_thread_poll_with_backoff (__poll_fn, __spinning_backoff_policy ());
129
161
}
130
162
163
+ template <class _AtomicWaitable >
164
+ _LIBCPP_HIDE_FROM_ABI void __atomic_notify_one (const _AtomicWaitable&) {}
165
+
166
+ template <class _AtomicWaitable >
167
+ _LIBCPP_HIDE_FROM_ABI void __atomic_notify_all (const _AtomicWaitable&) {}
168
+
131
169
#endif // _LIBCPP_HAS_NO_THREADS
132
170
133
171
template <typename _Tp>
@@ -143,11 +181,12 @@ struct __atomic_compare_unequal_to {
143
181
}
144
182
};
145
183
146
- template <class _Atp , class _Tp >
184
+ template <class _AtomicWaitable , class _Up >
147
185
_LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void
148
- __cxx_atomic_wait (_Atp* __a, _Tp const __val, memory_order __order) {
149
- __atomic_compare_unequal_to<_Tp> __poll_fn = {__val};
150
- std::__cxx_atomic_wait_unless (__a, __poll_fn, __order);
186
+ __atomic_wait (_AtomicWaitable& __a, _Up __val, memory_order __order) {
187
+ static_assert (__atomic_waitable<_AtomicWaitable>::value, " " );
188
+ __atomic_compare_unequal_to<_Up> __nonatomic_equal = {__val};
189
+ std::__atomic_wait_unless (__a, __nonatomic_equal, __order);
151
190
}
152
191
153
192
_LIBCPP_END_NAMESPACE_STD
0 commit comments