@@ -43,15 +43,53 @@ __libcpp_atomic_monitor(__cxx_atomic_contention_t const volatile*);
43
43
_LIBCPP_AVAILABILITY_SYNC _LIBCPP_EXPORTED_FROM_ABI void
44
44
__libcpp_atomic_wait (__cxx_atomic_contention_t const volatile *, __cxx_contention_t );
45
45
46
- template <class _Atp , class _BackoffTest >
46
+ template <class _Atp , class _Poll >
47
+ struct __libcpp_atomic_wait_poll_impl {
48
+ _Atp* __a_;
49
+ _Poll __poll_;
50
+ memory_order __order_;
51
+
52
+ _LIBCPP_AVAILABILITY_SYNC
53
+ _LIBCPP_HIDE_FROM_ABI bool operator ()() const {
54
+ auto __current_val = __cxx_atomic_load (__a_, __order_);
55
+ return __poll_ (__current_val);
56
+ }
57
+ };
58
+
59
+ template <class _Atp , class _Poll >
47
60
struct __libcpp_atomic_wait_backoff_impl {
48
61
_Atp* __a_;
49
- _BackoffTest __backoff_test_;
62
+ _Poll __poll_;
63
+ memory_order __order_;
64
+
65
+ _LIBCPP_AVAILABILITY_SYNC
66
+ _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 = __cxx_atomic_load (__a_, __order_);
75
+ return __poll_ (__monitor);
76
+ }
77
+
78
+ _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 = __cxx_atomic_load (__a_, __order_);
85
+ return __poll_ (__current_val);
86
+ }
87
+
50
88
_LIBCPP_AVAILABILITY_SYNC
51
89
_LIBCPP_HIDE_FROM_ABI bool operator ()(chrono::nanoseconds __elapsed) const {
52
90
if (__elapsed > chrono::microseconds (64 )) {
53
- auto __monitor = std::__libcpp_atomic_monitor (__a_) ;
54
- if (__backoff_test_ ( __monitor))
91
+ __cxx_contention_t __monitor;
92
+ if (__poll_or_get_monitor (__a_, __monitor))
55
93
return true ;
56
94
std::__libcpp_atomic_wait (__a_, __monitor);
57
95
} else if (__elapsed > chrono::microseconds (4 ))
@@ -62,26 +100,20 @@ struct __libcpp_atomic_wait_backoff_impl {
62
100
}
63
101
};
64
102
65
- template <class _Atp , class _Poll , class _BackoffTest >
66
- _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool
67
- __cxx_atomic_wait (_Atp* __a, _Poll&& __poll, _BackoffTest&& __backoff_test) {
68
- __libcpp_atomic_wait_backoff_impl<_Atp, __decay_t <_BackoffTest> > __backoff_fn = {__a, __backoff_test};
69
- return std::__libcpp_thread_poll_with_backoff (__poll, __backoff_fn);
70
- }
71
-
72
- template <class _Poll >
73
- struct __libcpp_atomic_wait_poll_as_backoff_test {
74
- _Poll __poll_;
75
-
76
- _LIBCPP_AVAILABILITY_SYNC
77
- _LIBCPP_HIDE_FROM_ABI bool operator ()(__cxx_contention_t &) const { return __poll_ (); }
78
- };
79
-
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_fn` 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.
80
111
template <class _Atp , class _Poll >
81
- _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_wait (_Atp* __a, _Poll&& __poll) {
82
- __libcpp_atomic_wait_backoff_impl<_Atp, __libcpp_atomic_wait_poll_as_backoff_test<_Poll&> > __backoff_fn = {
83
- __a, {__poll}};
84
- return std::__libcpp_thread_poll_with_backoff (__poll, __backoff_fn);
112
+ _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void
113
+ __cxx_atomic_wait_fn (_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);
85
117
}
86
118
87
119
#else // _LIBCPP_HAS_NO_THREADS
@@ -90,9 +122,10 @@ template <class _Tp>
90
122
_LIBCPP_HIDE_FROM_ABI void __cxx_atomic_notify_all (__cxx_atomic_impl<_Tp> const volatile *) {}
91
123
template <class _Tp >
92
124
_LIBCPP_HIDE_FROM_ABI void __cxx_atomic_notify_one (__cxx_atomic_impl<_Tp> const volatile *) {}
93
- template <class _Atp , class _Fn >
94
- _LIBCPP_HIDE_FROM_ABI bool __cxx_atomic_wait (_Atp*, _Fn&& __test_fn) {
95
- return std::__libcpp_thread_poll_with_backoff (__test_fn, __spinning_backoff_policy ());
125
+ template <class _Atp , class _Poll >
126
+ _LIBCPP_HIDE_FROM_ABI void __cxx_atomic_wait_fn (_Atp*, _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 ());
96
129
}
97
130
98
131
#endif // _LIBCPP_HAS_NO_THREADS
@@ -102,21 +135,19 @@ _LIBCPP_HIDE_FROM_ABI bool __cxx_nonatomic_compare_equal(_Tp const& __lhs, _Tp c
102
135
return std::memcmp (std::addressof (__lhs), std::addressof (__rhs), sizeof (_Tp)) == 0 ;
103
136
}
104
137
105
- template <class _Atp , class _Tp >
106
- struct __cxx_atomic_wait_test_fn_impl {
107
- _Atp* __a;
138
+ template <class _Tp >
139
+ struct __cxx_atomic_wait_poll_fn_impl {
108
140
_Tp __val;
109
- memory_order __order;
110
- _LIBCPP_HIDE_FROM_ABI bool operator ()() const {
111
- return !std::__cxx_nonatomic_compare_equal (std::__cxx_atomic_load (__a, __order), __val);
141
+ _LIBCPP_HIDE_FROM_ABI bool operator ()(_Tp& __current_val) const {
142
+ return !std::__cxx_nonatomic_compare_equal (__current_val, __val);
112
143
}
113
144
};
114
145
115
146
template <class _Atp , class _Tp >
116
- _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI bool
147
+ _LIBCPP_AVAILABILITY_SYNC _LIBCPP_HIDE_FROM_ABI void
117
148
__cxx_atomic_wait (_Atp* __a, _Tp const __val, memory_order __order) {
118
- __cxx_atomic_wait_test_fn_impl<_Atp, _Tp> __test_fn = {__a, __val, __order };
119
- return std::__cxx_atomic_wait (__a, __test_fn );
149
+ __cxx_atomic_wait_poll_fn_impl< _Tp> __poll_fn = {__val};
150
+ std::__cxx_atomic_wait_fn (__a, __poll_fn, __order );
120
151
}
121
152
122
153
_LIBCPP_END_NAMESPACE_STD
0 commit comments