[libcxx-commits] [libcxx] [libc++] fix condition_variable_any hangs on stop_request (PR #77127)

Louis Dionne via libcxx-commits libcxx-commits at lists.llvm.org
Fri Jan 12 09:22:33 PST 2024


================
@@ -256,23 +262,60 @@ condition_variable_any::wait_for(_Lock& __lock, const chrono::duration<_Rep, _Pe
 #  if _LIBCPP_STD_VER >= 20 && !defined(_LIBCPP_HAS_NO_EXPERIMENTAL_STOP_TOKEN)
 
 template <class _Lock, class _Predicate>
-bool condition_variable_any::wait(_Lock& __lock, stop_token __stoken, _Predicate __pred) {
-  while (!__stoken.stop_requested()) {
+bool condition_variable_any::wait(_Lock& __user_lock, stop_token __stoken, _Predicate __pred) {
+  // It is not safe to call the destructor on the other thread while waiting,
+  // even if we copy the shared_ptr<mutex> inside the function,
+  // because the stop_callback needs the internal cv.
+  // In order to make it work, we need to change the ABI to make both
+  // mutex and internal cv to be hold inside the shared_ptr, and
+  // make a local copy inside this function
+
+  if (__stoken.stop_requested())
+    return __pred();
+  std::stop_callback __cb(__stoken, [this] { notify_all(); });
+
+  while (true) {
     if (__pred())
       return true;
-    wait(__lock);
-  }
+
+    // We need to take the internal lock before checking stop_requested,
+    // so that the notification cannot come in between the stop_requested
+    // check and entering the wait.
+    // Note that the stop_callback takes the same internal lock before notifying
+    unique_lock<mutex> __internal_lock(*__mut_);
+    if (__stoken.stop_requested())
+      break;
+
+    __unlock_guard<_Lock> __unlock(__user_lock);
+    unique_lock<mutex> __internal_lock2(std::move(__internal_lock));
----------------
ldionne wrote:

```suggestion
    unique_lock<mutex> __internal_lock2(std::move(__internal_lock)); // switch unlock order between __mut_ and __user_lock
```

https://github.com/llvm/llvm-project/pull/77127


More information about the libcxx-commits mailing list