@@ -126,11 +126,11 @@ public:
126
126
#include < __condition_variable/condition_variable.h>
127
127
#include < __config>
128
128
#include < __memory/shared_ptr.h>
129
- #include < __memory/unique_ptr.h>
130
129
#include < __mutex/lock_guard.h>
131
130
#include < __mutex/mutex.h>
132
131
#include < __mutex/tag_types.h>
133
132
#include < __mutex/unique_lock.h>
133
+ #include < __stop_token/stop_callback.h>
134
134
#include < __stop_token/stop_token.h>
135
135
#include < __utility/move.h>
136
136
#include < version>
@@ -200,19 +200,26 @@ inline void condition_variable_any::notify_all() _NOEXCEPT {
200
200
__cv_.notify_all ();
201
201
}
202
202
203
- struct __lock_external {
204
- template <class _Lock >
205
- _LIBCPP_HIDE_FROM_ABI void operator ()(_Lock* __m) {
206
- __m->lock ();
203
+ template <class _Lock >
204
+ struct __unlock_guard {
205
+ _Lock& __lock_;
206
+
207
+ _LIBCPP_HIDE_FROM_ABI __unlock_guard (_Lock& __lock) : __lock_(__lock) { __lock_.unlock (); }
208
+
209
+ _LIBCPP_HIDE_FROM_ABI ~__unlock_guard () _NOEXCEPT // turns exception to std::terminate
210
+ {
211
+ __lock_.lock ();
207
212
}
213
+
214
+ __unlock_guard (const __unlock_guard&) = delete ;
215
+ __unlock_guard& operator =(const __unlock_guard&) = delete ;
208
216
};
209
217
210
218
template <class _Lock >
211
219
void condition_variable_any::wait (_Lock& __lock) {
212
220
shared_ptr<mutex> __mut = __mut_;
213
221
unique_lock<mutex> __lk (*__mut);
214
- __lock.unlock ();
215
- unique_ptr<_Lock, __lock_external> __lxx (&__lock);
222
+ __unlock_guard<_Lock> __unlock (__lock);
216
223
lock_guard<unique_lock<mutex> > __lx (__lk, adopt_lock_t ());
217
224
__cv_.wait (__lk);
218
225
} // __mut_.unlock(), __lock.lock()
@@ -227,8 +234,7 @@ template <class _Lock, class _Clock, class _Duration>
227
234
cv_status condition_variable_any::wait_until (_Lock& __lock, const chrono::time_point<_Clock, _Duration>& __t ) {
228
235
shared_ptr<mutex> __mut = __mut_;
229
236
unique_lock<mutex> __lk (*__mut);
230
- __lock.unlock ();
231
- unique_ptr<_Lock, __lock_external> __lxx (&__lock);
237
+ __unlock_guard<_Lock> __unlock (__lock);
232
238
lock_guard<unique_lock<mutex> > __lx (__lk, adopt_lock_t ());
233
239
return __cv_.wait_until (__lk, __t );
234
240
} // __mut_.unlock(), __lock.lock()
@@ -256,24 +262,75 @@ condition_variable_any::wait_for(_Lock& __lock, const chrono::duration<_Rep, _Pe
256
262
# if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
257
263
258
264
template <class _Lock , class _Predicate >
259
- bool condition_variable_any::wait (_Lock& __lock, stop_token __stoken, _Predicate __pred) {
260
- while (!__stoken.stop_requested ()) {
265
+ bool condition_variable_any::wait (_Lock& __user_lock, stop_token __stoken, _Predicate __pred) {
266
+ if (__stoken.stop_requested ())
267
+ return __pred ();
268
+
269
+ // Per https://eel.is/c++draft/thread.condition.condvarany#general-note-2,
270
+ // we do need to take a copy of the shared pointer __mut_
271
+ // This ensures that a thread can call the destructor immediately after calling
272
+ // notify_all, without waiting all the wait calls.
273
+ // A thread can also safely call the destructor immediately after calling
274
+ // request_stop, as the call to request_stop would evaluate the callback,
275
+ // which accesses the internal condition variable, immediately on the same thread.
276
+ // In this situation, it is OK even without copying a shared ownership the internal
277
+ // condition variable. However, this needs the evaluation of stop_callback to
278
+ // happen-before the destruction.
279
+ // The spec only says "Only the notification to unblock the wait needs to happen
280
+ // before destruction". To make this work, we need to copy the shared ownership of
281
+ // the internal condition variable inside this function, which is not possible
282
+ // with the current ABI.
283
+ shared_ptr<mutex> __mut = __mut_;
284
+
285
+ stop_callback __cb (__stoken, [this ] { notify_all (); });
286
+
287
+ while (true ) {
261
288
if (__pred ())
262
289
return true ;
263
- wait (__lock);
264
- }
290
+
291
+ // We need to take the internal lock before checking stop_requested,
292
+ // so that the notification cannot come in between the stop_requested
293
+ // check and entering the wait.
294
+ // Note that the stop_callback takes the same internal lock before notifying
295
+ unique_lock<mutex> __internal_lock (*__mut);
296
+ if (__stoken.stop_requested ())
297
+ break ;
298
+
299
+ __unlock_guard<_Lock> __unlock (__user_lock);
300
+ unique_lock<mutex> __internal_lock2 (
301
+ std::move (__internal_lock)); // switch unlock order between __internal_lock and __user_lock
302
+ __cv_.wait (__internal_lock2);
303
+ } // __internal_lock2.unlock(), __user_lock.lock()
265
304
return __pred ();
266
305
}
267
306
268
307
template <class _Lock , class _Clock , class _Duration , class _Predicate >
269
308
bool condition_variable_any::wait_until (
270
- _Lock& __lock, stop_token __stoken, const chrono::time_point<_Clock, _Duration>& __abs_time, _Predicate __pred) {
271
- while (!__stoken.stop_requested ()) {
309
+ _Lock& __user_lock,
310
+ stop_token __stoken,
311
+ const chrono::time_point<_Clock, _Duration>& __abs_time,
312
+ _Predicate __pred) {
313
+ if (__stoken.stop_requested ())
314
+ return __pred ();
315
+
316
+ shared_ptr<mutex> __mut = __mut_;
317
+ stop_callback __cb (__stoken, [this ] { notify_all (); });
318
+
319
+ while (true ) {
272
320
if (__pred ())
273
321
return true ;
274
- if (wait_until (__lock, __abs_time) == cv_status::timeout)
275
- return __pred ();
276
- }
322
+
323
+ unique_lock<mutex> __internal_lock (*__mut);
324
+ if (__stoken.stop_requested ())
325
+ break ;
326
+
327
+ __unlock_guard<_Lock> __unlock (__user_lock);
328
+ unique_lock<mutex> __internal_lock2 (
329
+ std::move (__internal_lock)); // switch unlock order between __internal_lock and __user_lock
330
+
331
+ if (__cv_.wait_until (__internal_lock2, __abs_time) == cv_status::timeout)
332
+ break ;
333
+ } // __internal_lock2.unlock(), __user_lock.lock()
277
334
return __pred ();
278
335
}
279
336
0 commit comments