[libc-commits] [libc] [libc] rework mutex (PR #92168)

Schrodinger ZHU Yifan via libc-commits libc-commits at lists.llvm.org
Wed May 15 08:44:39 PDT 2024


================
@@ -0,0 +1,116 @@
+//===--- Implementation of a Linux RawMutex class ---------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_RAW_MUTEX_H
+#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_RAW_MUTEX_H
+
+#include "futex_word.h"
+#include "src/__support/CPP/optional.h"
+#include "src/__support/common.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/threads/linux/futex_utils.h"
+#include "src/__support/threads/sleep.h"
+#include "src/__support/time/linux/abs_timeout.h"
+#ifdef LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY
+#include "src/__support/time/linux/monotonicity.h"
+#endif
+
+#ifndef LIBC_COPT_RAW_MUTEX_DEFAULT_SPIN_COUNT
+#define LIBC_COPT_RAW_MUTEX_DEFAULT_SPIN_COUNT 100
+#endif
+
+namespace LIBC_NAMESPACE {
+namespace internal {
+// Lock is a simple timable lock for internal usage.
+// This is separated from Mutex because this one does not need to consider
+// robustness and reentrancy. Also, this one has spin optimization for shorter
+// critical sections.
+class RawMutex {
+protected:
+  Futex futex;
+  LIBC_INLINE_VAR static constexpr FutexWordType UNLOCKED = 0b00;
+  LIBC_INLINE_VAR static constexpr FutexWordType LOCKED = 0b01;
+  LIBC_INLINE_VAR static constexpr FutexWordType CONTENTED = 0b10;
+
+  LIBC_INLINE FutexWordType spin(uint_fast32_t spin_count) {
+    FutexWordType result;
+    for (;;) {
+      result = futex.load(cpp::MemoryOrder::RELAXED);
+      // spin until one of the following conditions is met:
+      // - the mutex is unlocked
+      // - the mutex is contented
+      // - the spin count reaches 0
+      if (result != LOCKED || spin_count == 0)
+        return result;
+      // Pause the pipeline to avoid extraneous memory operations due to
+      // speculation.
+      sleep_briefly();
----------------
SchrodingerZhu wrote:

Notice that the spin loops continues only if the lock is hold by someone and contention is not heavy. In that case, the spinning thread knows that there is someone making progress in their critical section and the spinning one will likely obtain the lock once that critical section is over. Therefore, if the critical section is short, considerable syscalls can be avoided.

Notice that `CndVar`, `RwLock` exhibits waiting queues protected by mutexes. The critical section concerning the update of the queue is actually small. Hence, I think it may be a good choice to spin several rounds before going to sleep (futex_wait). 

I currently choose `100` as the default value because it aligns with the choice of Rust's standard library. 

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


More information about the libc-commits mailing list