[libc-commits] [PATCH] D80398: [libc] Fix mtx_unlock to handle multiple waiters on a single mutex.

Siva Chandra via Phabricator via libc-commits libc-commits at lists.llvm.org
Thu May 21 12:27:25 PDT 2020


sivachandra created this revision.
sivachandra added a reviewer: phosek.
Herald added subscribers: libc-commits, ecnelises, tschuett.
Herald added a project: libc-project.

Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D80398

Files:
  libc/src/threads/linux/mtx_unlock.cpp
  libc/test/src/threads/mtx_test.cpp


Index: libc/test/src/threads/mtx_test.cpp
===================================================================
--- libc/test/src/threads/mtx_test.cpp
+++ libc/test/src/threads/mtx_test.cpp
@@ -127,3 +127,56 @@
   __llvm_libc::thrd_join(&thread, &retval);
   ASSERT_EQ(retval, 0);
 }
+
+static constexpr int THREAD_COUNT = 10;
+static mtx_t multiple_waiter_lock;
+static mtx_t counter_lock;
+static int wait_count = 0;
+
+int waiter_func(void *) {
+  __llvm_libc::mtx_lock(&counter_lock);
+  ++wait_count;
+  __llvm_libc::mtx_unlock(&counter_lock);
+
+  // Block on the waiter lock until the main
+  // thread unblocks.
+  __llvm_libc::mtx_lock(&multiple_waiter_lock);
+  __llvm_libc::mtx_unlock(&multiple_waiter_lock);
+
+  __llvm_libc::mtx_lock(&counter_lock);
+  --wait_count;
+  __llvm_libc::mtx_unlock(&counter_lock);
+
+  return 0;
+}
+
+TEST(MutexTest, MultipleWaiters) {
+  __llvm_libc::mtx_init(&multiple_waiter_lock, mtx_plain);
+  __llvm_libc::mtx_init(&counter_lock, mtx_plain);
+
+  __llvm_libc::mtx_lock(&multiple_waiter_lock);
+  thrd_t waiters[THREAD_COUNT];
+  for (int i = 0; i < THREAD_COUNT; ++i) {
+    __llvm_libc::thrd_create(waiters + i, waiter_func, nullptr);
+  }
+
+  // Spin until the counter is incremented to the desired
+  // value.
+  while (true) {
+    __llvm_libc::mtx_lock(&counter_lock);
+    if (wait_count == THREAD_COUNT) {
+      __llvm_libc::mtx_unlock(&counter_lock);
+      break;
+    }
+    __llvm_libc::mtx_unlock(&counter_lock);
+  }
+
+  __llvm_libc::mtx_unlock(&multiple_waiter_lock);
+
+  int retval;
+  for (int i = 0; i < THREAD_COUNT; ++i) {
+    __llvm_libc::thrd_join(waiters + i, &retval);
+  }
+
+  ASSERT_EQ(wait_count, 0);
+}
Index: libc/src/threads/linux/mtx_unlock.cpp
===================================================================
--- libc/src/threads/linux/mtx_unlock.cpp
+++ libc/src/threads/linux/mtx_unlock.cpp
@@ -12,6 +12,7 @@
 #include "src/__support/common.h"
 #include "src/threads/linux/thread_utils.h"
 
+#include <limits.h>
 #include <linux/futex.h> // For futex operations.
 #include <stdatomic.h>   // for atomic_compare_exchange_strong.
 
@@ -23,9 +24,14 @@
   while (true) {
     uint32_t mutex_status = MS_Waiting;
     if (atomic_compare_exchange_strong(futex_word, &mutex_status, MS_Free)) {
-      // If any thread is waiting to be woken up, then do it.
-      __llvm_libc::syscall(SYS_futex, futex_word, FUTEX_WAKE_PRIVATE, 1, 0, 0,
-                           0);
+      // If any thread is waiting to be woken up, then do it. We wake up all
+      // waiters as this will ensure that the correct mutex status gets set.
+      // If we wake up only one waiter, then that waiter will lock the mutex
+      // with status MS_Locked and never realize that there are other waiters.
+      // But waking up all waiters, we make sure that the waiters which do not
+      // get the lock set the status back to MS_Waiting.
+      __llvm_libc::syscall(SYS_futex, futex_word, FUTEX_WAKE_PRIVATE, INT_MAX,
+                           0, 0, 0);
       return thrd_success;
     }
 


-------------- next part --------------
A non-text attachment was scrubbed...
Name: D80398.265574.patch
Type: text/x-patch
Size: 3059 bytes
Desc: not available
URL: <http://lists.llvm.org/pipermail/libc-commits/attachments/20200521/8e190422/attachment.bin>


More information about the libc-commits mailing list