[libc-commits] [libc] [libc] add spin lock family functions (PR #100509)

Schrodinger ZHU Yifan via libc-commits libc-commits at lists.llvm.org
Sat Jul 27 20:33:12 PDT 2024


https://github.com/SchrodingerZhu updated https://github.com/llvm/llvm-project/pull/100509

>From e22776c4461cf01150bb34ae4963771f2ff352e5 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Wed, 24 Jul 2024 22:33:15 -0700
Subject: [PATCH 1/5] [libc] add spin lock family functions

---
 libc/config/linux/aarch64/entrypoints.txt     |  5 ++
 libc/config/linux/api.td                      |  1 +
 libc/docs/dev/undefined_behavior.rst          |  9 +++
 libc/include/CMakeLists.txt                   |  1 +
 libc/include/llvm-libc-types/CMakeLists.txt   |  1 +
 .../llvm-libc-types/pthread_spinlock_t.h      | 17 +++++
 libc/spec/posix.td                            | 29 +++++++++
 libc/src/__support/threads/spin_lock.h        | 36 +++-------
 libc/src/pthread/CMakeLists.txt               | 65 +++++++++++++++++++
 libc/src/pthread/pthread_spin_destroy.cpp     | 46 +++++++++++++
 libc/src/pthread/pthread_spin_destroy.h       | 22 +++++++
 libc/src/pthread/pthread_spin_init.cpp        | 33 ++++++++++
 libc/src/pthread/pthread_spin_init.h          | 21 ++++++
 libc/src/pthread/pthread_spin_lock.cpp        | 45 +++++++++++++
 libc/src/pthread/pthread_spin_lock.h          | 21 ++++++
 libc/src/pthread/pthread_spin_trylock.cpp     | 39 +++++++++++
 libc/src/pthread/pthread_spin_trylock.h       | 22 +++++++
 libc/src/pthread/pthread_spin_unlock.cpp      | 42 ++++++++++++
 libc/src/pthread/pthread_spin_unlock.h        | 22 +++++++
 19 files changed, 451 insertions(+), 26 deletions(-)
 create mode 100644 libc/include/llvm-libc-types/pthread_spinlock_t.h
 create mode 100644 libc/src/pthread/pthread_spin_destroy.cpp
 create mode 100644 libc/src/pthread/pthread_spin_destroy.h
 create mode 100644 libc/src/pthread/pthread_spin_init.cpp
 create mode 100644 libc/src/pthread/pthread_spin_init.h
 create mode 100644 libc/src/pthread/pthread_spin_lock.cpp
 create mode 100644 libc/src/pthread/pthread_spin_lock.h
 create mode 100644 libc/src/pthread/pthread_spin_trylock.cpp
 create mode 100644 libc/src/pthread/pthread_spin_trylock.h
 create mode 100644 libc/src/pthread/pthread_spin_unlock.cpp
 create mode 100644 libc/src/pthread/pthread_spin_unlock.h

diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index 2334fed773702..bf3a31fc5afb9 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -707,6 +707,11 @@ if(LLVM_LIBC_FULL_BUILD)
     libc.src.pthread.pthread_rwlockattr_init
     libc.src.pthread.pthread_rwlockattr_setkind_np
     libc.src.pthread.pthread_rwlockattr_setpshared
+    libc.src.pthread.pthread_spin_destroy
+    libc.src.pthread.pthread_spin_init
+    libc.src.pthread.pthread_spin_lock
+    libc.src.pthread.pthread_spin_trylock
+    libc.src.pthread.pthread_spin_unlock
     libc.src.pthread.pthread_self
     libc.src.pthread.pthread_setname_np
     libc.src.pthread.pthread_setspecific
diff --git a/libc/config/linux/api.td b/libc/config/linux/api.td
index 320f3e92183bf..6a7c64296bf92 100644
--- a/libc/config/linux/api.td
+++ b/libc/config/linux/api.td
@@ -143,6 +143,7 @@ def PThreadAPI : PublicAPI<"pthread.h"> {
       "pthread_once_t",
       "pthread_rwlockattr_t",
       "pthread_rwlock_t",
+      "pthread_spinlock_t",
       "pthread_t",
   ];
 }
diff --git a/libc/docs/dev/undefined_behavior.rst b/libc/docs/dev/undefined_behavior.rst
index b712780222aa3..49574bc32d2cd 100644
--- a/libc/docs/dev/undefined_behavior.rst
+++ b/libc/docs/dev/undefined_behavior.rst
@@ -116,3 +116,12 @@ inherited from parent process triggered inside the instruction window between ``
 and ``exec*``. As libc failed to maintain its internal states correctly, even though the
 functions used inside the signal handlers are marked as ``async-signal-safe`` (such as
 ``getpid``), they will still return wrong values or lead to other even worse situations.
+
+PThread SpinLock Destroy
+------------------------
+POSIX.1 Issue 7 updates the spinlock destroy behavior description such that the return code for
+uninitialized spinlock and invalid spinlock is left undefined. We follow the recommendation as in
+POSIX.1-2024, where EINVAL is returned if the spinlock is invalid (here we only check for null pointers) or
+EBUSY is returned if the spinlock is currently locked. The lock is poisoned after a successful destroy. That is,
+subsequent operations on the lock object without any reinitialization will return EINVAL.
+
diff --git a/libc/include/CMakeLists.txt b/libc/include/CMakeLists.txt
index 37cae19123318..cbde24e17619f 100644
--- a/libc/include/CMakeLists.txt
+++ b/libc/include/CMakeLists.txt
@@ -386,6 +386,7 @@ add_header_macro(
     .llvm-libc-types.pthread_once_t
     .llvm-libc-types.pthread_rwlock_t
     .llvm-libc-types.pthread_rwlockattr_t
+    .llvm-libc-types.pthread_spinlock_t
     .llvm-libc-types.pthread_t
 )
 
diff --git a/libc/include/llvm-libc-types/CMakeLists.txt b/libc/include/llvm-libc-types/CMakeLists.txt
index d8b975572e0dd..9e77ab226ce6c 100644
--- a/libc/include/llvm-libc-types/CMakeLists.txt
+++ b/libc/include/llvm-libc-types/CMakeLists.txt
@@ -56,6 +56,7 @@ add_header(pthread_mutexattr_t HDR pthread_mutexattr_t.h)
 add_header(pthread_once_t HDR pthread_once_t.h DEPENDS .__futex_word)
 add_header(pthread_rwlock_t HDR pthread_rwlock_t.h DEPENDS .__futex_word .pid_t)
 add_header(pthread_rwlockattr_t HDR pthread_rwlockattr_t.h)
+add_header(pthread_spinlock_t HDR pthread_spinlock_t.h DEPENDS .pid_t)
 add_header(pthread_t HDR pthread_t.h DEPENDS .__thread_type)
 add_header(rlim_t HDR rlim_t.h)
 add_header(time_t HDR time_t.h)
diff --git a/libc/include/llvm-libc-types/pthread_spinlock_t.h b/libc/include/llvm-libc-types/pthread_spinlock_t.h
new file mode 100644
index 0000000000000..03eb02dded9ef
--- /dev/null
+++ b/libc/include/llvm-libc-types/pthread_spinlock_t.h
@@ -0,0 +1,17 @@
+//===-- Definition of pthread_spinlock_t type -----------------------------===//
+//
+// 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_TYPES_PTHREAD_SPINLOCK_T_H
+#define LLVM_LIBC_TYPES_PTHREAD_SPINLOCK_T_H
+#include "llvm-libc-types/pid_t.h"
+typedef struct {
+  unsigned char __lockword;
+  pid_t __owner;
+} pthread_spinlock_t;
+
+#endif // LLVM_LIBC_TYPES_PTHREAD_SPINLOCK_T_H
diff --git a/libc/spec/posix.td b/libc/spec/posix.td
index 48f743dff4e6f..2da004b1eae40 100644
--- a/libc/spec/posix.td
+++ b/libc/spec/posix.td
@@ -132,6 +132,9 @@ def POSIX : StandardSpec<"POSIX"> {
   PtrType PThreadRWLockTPtr = PtrType<PThreadRWLockTType>;
   RestrictedPtrType RestrictedPThreadRWLockTPtr = RestrictedPtrType<PThreadRWLockTType>;
 
+  NamedType PThreadSpinLockTType = NamedType<"pthread_spinlock_t">;
+  PtrType PThreadSpinLockTPtr = PtrType<PThreadSpinLockTType>;
+
   PtrType PThreadTPtr = PtrType<PThreadTType>;
   RestrictedPtrType RestrictedPThreadTPtr = RestrictedPtrType<PThreadTType>;
 
@@ -1039,6 +1042,7 @@ def POSIX : StandardSpec<"POSIX"> {
         PThreadOnceT,
         PThreadRWLockAttrTType,
         PThreadRWLockTType,
+        PThreadSpinLockTType,
         PThreadStartT,
         PThreadTSSDtorT,
         PThreadTType,
@@ -1340,6 +1344,31 @@ def POSIX : StandardSpec<"POSIX"> {
         RetValSpec<IntType>,
         [ArgSpec<PThreadRWLockTPtr>]
       >,
+      FunctionSpec<
+        "pthread_spin_init",
+        RetValSpec<IntType>,
+        [ArgSpec<PThreadSpinLockTPtr>, ArgSpec<IntType>]
+      >,
+      FunctionSpec<
+        "pthread_spin_destroy",
+        RetValSpec<IntType>,
+        [ArgSpec<PThreadSpinLockTPtr>]
+      >,
+      FunctionSpec<
+        "pthread_spin_lock",
+        RetValSpec<IntType>,
+        [ArgSpec<PThreadSpinLockTPtr>]
+      >,
+      FunctionSpec<
+        "pthread_spin_trylock",
+        RetValSpec<IntType>,
+        [ArgSpec<PThreadSpinLockTPtr>]
+      >,
+      FunctionSpec<
+        "pthread_spin_unlock",
+        RetValSpec<IntType>,
+        [ArgSpec<PThreadSpinLockTPtr>]
+      >
     ]
   >;
 
diff --git a/libc/src/__support/threads/spin_lock.h b/libc/src/__support/threads/spin_lock.h
index 8a36550568464..e176ad9eeac2a 100644
--- a/libc/src/__support/threads/spin_lock.h
+++ b/libc/src/__support/threads/spin_lock.h
@@ -11,26 +11,17 @@
 
 #include "src/__support/CPP/atomic.h"
 #include "src/__support/macros/attributes.h"
-#include "src/__support/macros/properties/architectures.h"
 #include "src/__support/threads/sleep.h"
 
 namespace LIBC_NAMESPACE_DECL {
 
-namespace spinlock {
-template <typename LockWord, typename Return>
-using AtomicOp = Return (cpp::Atomic<LockWord>::*)(LockWord, cpp::MemoryOrder,
-                                                   cpp::MemoryScope);
-}
-
-template <typename LockWord, spinlock::AtomicOp<LockWord, LockWord> Acquire,
-          spinlock::AtomicOp<LockWord, void> Release>
-class SpinLockAdaptor {
-  cpp::Atomic<LockWord> flag;
+class SpinLock {
+  cpp::Atomic<unsigned char> flag;
 
 public:
-  LIBC_INLINE constexpr SpinLockAdaptor() : flag{false} {}
+  LIBC_INLINE constexpr SpinLock() : flag{0} {}
   LIBC_INLINE bool try_lock() {
-    return !flag.*Acquire(static_cast<LockWord>(1), cpp::MemoryOrder::ACQUIRE);
+    return !flag.exchange(1u, cpp::MemoryOrder::ACQUIRE);
   }
   LIBC_INLINE void lock() {
     // clang-format off
@@ -60,22 +51,15 @@ class SpinLockAdaptor {
       while (flag.load(cpp::MemoryOrder::RELAXED))
         sleep_briefly();
   }
-  LIBC_INLINE void unlock() {
-    flag.*Release(static_cast<LockWord>(0), cpp::MemoryOrder::RELEASE);
+  LIBC_INLINE void unlock() { flag.store(0u, cpp::MemoryOrder::RELEASE); }
+  LIBC_INLINE bool is_locked() { return flag.load(cpp::MemoryOrder::ACQUIRE); }
+  LIBC_INLINE bool is_invalid() {
+    return flag.load(cpp::MemoryOrder::ACQUIRE) > 1;
   }
+  // poison the lock
+  LIBC_INLINE ~SpinLock() { flag.store(0xffu, cpp::MemoryOrder::RELEASE); }
 };
 
-// It is reported that atomic operations with higher-order semantics
-// lead to better performance on GPUs.
-#ifdef LIBC_TARGET_ARCH_IS_GPU
-using SpinLock =
-    SpinLockAdaptor<unsigned int, &cpp::Atomic<unsigned int>::fetch_or,
-                    &cpp::Atomic<unsigned int>::fetch_and>;
-#else
-using SpinLock = SpinLockAdaptor<bool, &cpp::Atomic<bool>::exchange,
-                                 &cpp::Atomic<bool>::store>;
-#endif
-
 } // namespace LIBC_NAMESPACE_DECL
 
 #endif // LLVM_LIBC_SRC___SUPPORT_THREADS_SPIN_LOCK_H
diff --git a/libc/src/pthread/CMakeLists.txt b/libc/src/pthread/CMakeLists.txt
index dc748b22e0378..45dcd5ff6fd3f 100644
--- a/libc/src/pthread/CMakeLists.txt
+++ b/libc/src/pthread/CMakeLists.txt
@@ -622,6 +622,71 @@ add_entrypoint_object(
     libc.src.__support.threads.linux.rwlock
 )
 
+add_entrypoint_object(
+  pthread_spin_init
+  SRCS
+    pthread_spin_init.cpp
+  HDRS
+    pthread_spin_init.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.spin_lock
+    libc.src.__support.threads.tid
+    libc.hdr.errno_macros
+)
+
+add_entrypoint_object(
+  pthread_spin_destroy
+  SRCS
+    pthread_spin_destroy.cpp
+  HDRS
+    pthread_spin_destroy.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.spin_lock
+    libc.src.__support.threads.tid
+    libc.hdr.errno_macros
+)
+
+add_entrypoint_object(
+  pthread_spin_lock
+  SRCS
+    pthread_spin_lock.cpp
+  HDRS
+    pthread_spin_lock.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.spin_lock
+    libc.src.__support.threads.tid
+    libc.hdr.errno_macros
+)
+
+add_entrypoint_object(
+  pthread_spin_trylock
+  SRCS
+    pthread_spin_trylock.cpp
+  HDRS
+    pthread_spin_trylock.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.spin_lock
+    libc.src.__support.threads.tid
+    libc.hdr.errno_macros
+)
+
+add_entrypoint_object(
+  pthread_spin_unlock
+  SRCS
+    pthread_spin_unlock.cpp
+  HDRS
+    pthread_spin_unlock.h
+  DEPENDS
+    libc.include.pthread
+    libc.src.__support.threads.spin_lock
+    libc.src.__support.threads.tid
+    libc.hdr.errno_macros
+)
+
 add_entrypoint_object(
   pthread_once
   SRCS
diff --git a/libc/src/pthread/pthread_spin_destroy.cpp b/libc/src/pthread/pthread_spin_destroy.cpp
new file mode 100644
index 0000000000000..229dcb47c03ba
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_destroy.cpp
@@ -0,0 +1,46 @@
+//===-- Implementation of pthread_spin_destroy function ------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/pthread/pthread_spin_destroy.h"
+#include "hdr/errno_macros.h"
+#include "src/__support/common.h"
+#include "src/__support/threads/spin_lock.h"
+namespace LIBC_NAMESPACE_DECL {
+
+static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) &&
+                  alignof(decltype(pthread_spinlock_t::__lockword)) ==
+                      alignof(SpinLock),
+              "pthread_spinlock_t::__lockword and SpinLock must be of the same "
+              "size and alignment");
+
+LLVM_LIBC_FUNCTION(int, pthread_spin_destroy,
+                   ([[maybe_unused]] pthread_spinlock_t * lock)) {
+  auto *spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
+  // Accroding to POSIX.1-2024:
+  // If an implementation detects that the value specified by the lock argument
+  // to pthread_spin_destroy() does not refer to an initialized spin lock
+  // object, it is recommended that the function should fail and report an
+  // [EINVAL] error.
+  if (!spin_lock || spin_lock->is_invalid())
+    return EINVAL;
+
+  // If an implementation detects that the value specified by the lock argument
+  // to pthread_spin_destroy() or pthread_spin_init() refers to a locked spin
+  // lock object, or detects that the value specified by the lock argument to
+  // pthread_spin_init() refers to an already initialized spin lock object, it
+  // is recommended that the function should fail and report an [EBUSY] error.
+  if (spin_lock->is_locked())
+    return EBUSY;
+
+  // poison the lock
+  spin_lock->~SpinLock();
+  lock->__owner = 0;
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pthread/pthread_spin_destroy.h b/libc/src/pthread/pthread_spin_destroy.h
new file mode 100644
index 0000000000000..a6f38aac8da33
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_destroy.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for pthread_spin_destroy function ---*- 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_PTHREAD_PTHREAD_SPIN_DESTROY_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_DESTROY_H
+
+#include "src/__support/macros/config.h"
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pthread_spin_destroy(pthread_spinlock_t *lock);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_DESTROY_H
diff --git a/libc/src/pthread/pthread_spin_init.cpp b/libc/src/pthread/pthread_spin_init.cpp
new file mode 100644
index 0000000000000..e399a20507e3b
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_init.cpp
@@ -0,0 +1,33 @@
+//===-- Implementation of pthread_spin_init function ----------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/pthread/pthread_spin_init.h"
+#include "src/__support/CPP/new.h"
+#include "src/__support/common.h"
+#include "src/__support/threads/spin_lock.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) &&
+                  alignof(decltype(pthread_spinlock_t::__lockword)) ==
+                      alignof(SpinLock),
+              "pthread_spinlock_t::__lockword and SpinLock must be of the same "
+              "size and alignment");
+
+LLVM_LIBC_FUNCTION(int, pthread_spin_init,
+                   (pthread_spinlock_t * lock, [[maybe_unused]] int pshared)) {
+  // The spin lock here is a simple atomic flag, so we don't need to do any
+  // special handling for pshared.
+  ::new (&lock->__lockword) SpinLock();
+  lock->__owner = 0;
+  // No memory allocation needed, hence always succeeds. POSIX.1-2024 does not
+  // specify checking on invalid lock or pshared values.
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pthread/pthread_spin_init.h b/libc/src/pthread/pthread_spin_init.h
new file mode 100644
index 0000000000000..89f7e3adc82e9
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_init.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for pthread_spin_init function ---*- 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_PTHREAD_PTHREAD_SPIN_INIT_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_INIT_H
+
+#include "src/__support/macros/config.h"
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pthread_spin_init(pthread_spinlock_t *lock, int pshared);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_INIT_H
diff --git a/libc/src/pthread/pthread_spin_lock.cpp b/libc/src/pthread/pthread_spin_lock.cpp
new file mode 100644
index 0000000000000..d155eda620131
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_lock.cpp
@@ -0,0 +1,45 @@
+//===-- Implementation of pthread_spin_lock function ----------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/pthread/pthread_spin_lock.h"
+#include "hdr/errno_macros.h"
+#include "src/__support/common.h"
+#include "src/__support/threads/spin_lock.h"
+#include "src/__support/threads/tid.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) &&
+                  alignof(decltype(pthread_spinlock_t::__lockword)) ==
+                      alignof(SpinLock),
+              "pthread_spinlock_t::__lockword and SpinLock must be of the same "
+              "size and alignment");
+
+LLVM_LIBC_FUNCTION(int, pthread_spin_lock, (pthread_spinlock_t * lock)) {
+  auto spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
+  // If an implementation detects that the value specified by the lock argument
+  // to pthread_spin_lock() or pthread_spin_trylock() does not refer to an
+  // initialized spin lock object, it is recommended that the function should
+  // fail and report an [EINVAL] error.
+  if (!spin_lock || spin_lock->is_invalid())
+    return EINVAL;
+
+  pid_t self_tid = gettid_inline();
+  // If an implementation detects that the value specified by the lock argument
+  // to pthread_spin_lock() refers to a spin lock object for which the calling
+  // thread already holds the lock, it is recommended that the function should
+  // fail and report an [EDEADLK] error.
+  if (lock->__owner == self_tid)
+    return EDEADLK;
+
+  spin_lock->lock();
+  lock->__owner = self_tid;
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pthread/pthread_spin_lock.h b/libc/src/pthread/pthread_spin_lock.h
new file mode 100644
index 0000000000000..835aa858b25f8
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_lock.h
@@ -0,0 +1,21 @@
+//===-- Implementation header for pthread_spin_lock function ---*- 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_PTHREAD_PTHREAD_SPIN_LOCK_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_LOCK_H
+
+#include "src/__support/macros/config.h"
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pthread_spin_lock(pthread_spinlock_t *lock);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_LOCK_H
diff --git a/libc/src/pthread/pthread_spin_trylock.cpp b/libc/src/pthread/pthread_spin_trylock.cpp
new file mode 100644
index 0000000000000..87ce870d2a367
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_trylock.cpp
@@ -0,0 +1,39 @@
+//===-- Implementation of pthread_spin_trylock function -------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/pthread/pthread_spin_trylock.h"
+#include "hdr/errno_macros.h"
+#include "src/__support/common.h"
+#include "src/__support/threads/spin_lock.h"
+#include "src/__support/threads/tid.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) &&
+                  alignof(decltype(pthread_spinlock_t::__lockword)) ==
+                      alignof(SpinLock),
+              "pthread_spinlock_t::__lockword and SpinLock must be of the same "
+              "size and alignment");
+
+LLVM_LIBC_FUNCTION(int, pthread_spin_trylock, (pthread_spinlock_t * lock)) {
+  auto spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
+  // If an implementation detects that the value specified by the lock argument
+  // to pthread_spin_lock() or pthread_spin_trylock() does not refer to an
+  // initialized spin lock object, it is recommended that the function should
+  // fail and report an [EINVAL] error.
+  if (!spin_lock || spin_lock->is_invalid())
+    return EINVAL;
+  // Try to acquire the lock without blocking.
+  if (!spin_lock->try_lock())
+    return EBUSY;
+  // We have acquired the lock. Update the owner field.
+  lock->__owner = gettid_inline();
+  return 0;
+}
+
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pthread/pthread_spin_trylock.h b/libc/src/pthread/pthread_spin_trylock.h
new file mode 100644
index 0000000000000..e175ab8d7cfbe
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_trylock.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for pthread_spin_trylock function ---*- 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_PTHREAD_PTHREAD_SPIN_TRYLOCK_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_TRYLOCK_H
+
+#include "src/__support/macros/config.h"
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pthread_spin_trylock(pthread_spinlock_t *lock);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_TRYLOCK_H
diff --git a/libc/src/pthread/pthread_spin_unlock.cpp b/libc/src/pthread/pthread_spin_unlock.cpp
new file mode 100644
index 0000000000000..4f9ff0863ac06
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_unlock.cpp
@@ -0,0 +1,42 @@
+//===-- Implementation of pthread_spin_unlock function --------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/pthread/pthread_spin_unlock.h"
+#include "hdr/errno_macros.h"
+#include "src/__support/common.h"
+#include "src/__support/threads/spin_lock.h"
+#include "src/__support/threads/tid.h"
+
+namespace LIBC_NAMESPACE_DECL {
+
+static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) &&
+                  alignof(decltype(pthread_spinlock_t::__lockword)) ==
+                      alignof(SpinLock),
+              "pthread_spinlock_t::__lockword and SpinLock must be of the same "
+              "size and alignment");
+
+LLVM_LIBC_FUNCTION(int, pthread_spin_unlock, (pthread_spinlock_t * lock)) {
+  auto *spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
+  // If an implementation detects that the value specified by the lock argument
+  // to pthread_spin_unlock() does not refer to an initialized spin lock object,
+  // it is recommended that the function should fail and report an [EINVAL]
+  // error.
+  if (!spin_lock || spin_lock->is_invalid())
+    return EINVAL;
+  // If an implementation detects that the value specified by the lock argument
+  // to pthread_spin_unlock() refers to a spin lock object for which the current
+  // thread does not hold the lock, it is recommended that the function should
+  // fail and report an [EPERM] error.
+  if (lock->__owner != gettid_inline())
+    return EPERM;
+  // Release the lock.
+  lock->__owner = 0;
+  spin_lock->unlock();
+  return 0;
+}
+} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/pthread/pthread_spin_unlock.h b/libc/src/pthread/pthread_spin_unlock.h
new file mode 100644
index 0000000000000..e72a1aa9300ba
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_unlock.h
@@ -0,0 +1,22 @@
+//===-- Implementation header for pthread_spin_unlock function ---*- 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_PTHREAD_PTHREAD_SPIN_UNLOCK_H
+#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_UNLOCK_H
+
+#include "src/__support/macros/config.h"
+#include <pthread.h>
+
+namespace LIBC_NAMESPACE_DECL {
+
+int pthread_spin_unlock(pthread_spinlock_t *lock);
+
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_UNLOCK_H

>From dbfb3594ac546bb85d2fd0e22f1e780664710f63 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Fri, 26 Jul 2024 21:04:36 -0700
Subject: [PATCH 2/5] [libc] add function spec to new header gen

---
 libc/newhdrgen/yaml/pthread.yaml | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)

diff --git a/libc/newhdrgen/yaml/pthread.yaml b/libc/newhdrgen/yaml/pthread.yaml
index 292d91751e406..5cc8e0642444d 100644
--- a/libc/newhdrgen/yaml/pthread.yaml
+++ b/libc/newhdrgen/yaml/pthread.yaml
@@ -390,3 +390,29 @@ functions:
     return_type: int
     arguments:
       - type: pthread_rwlock_t *
+  - name: pthread_spin_init
+    standards: POSIX
+    return_type: int
+    arguments:
+      - type: pthread_spinlock_t *
+      - type: int
+  - name: pthread_spin_destroy
+    standards: POSIX
+    return_type: int
+    arguments:
+      - type: pthread_spinlock_t *
+  - name: pthread_spin_lock
+    standards: POSIX
+    return_type: int
+    arguments:
+      - type: pthread_spinlock_t *
+  - name: pthread_spin_trylock
+    standards: POSIX
+    return_type: int
+    arguments:
+      - type: pthread_spinlock_t *
+  - name: pthread_spin_unlock
+    standards: POSIX
+    return_type: int
+    arguments:
+      - type: pthread_spinlock_t *

>From cb84b2acb1f141121f9b34d93822f990489a53f0 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Fri, 26 Jul 2024 21:38:44 -0700
Subject: [PATCH 3/5] [libc] adjust license headers

---
 libc/src/pthread/pthread_spin_destroy.cpp | 2 +-
 libc/src/pthread/pthread_spin_destroy.h   | 3 +--
 libc/src/pthread/pthread_spin_unlock.h    | 3 +--
 3 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/libc/src/pthread/pthread_spin_destroy.cpp b/libc/src/pthread/pthread_spin_destroy.cpp
index 229dcb47c03ba..4e89bf219355a 100644
--- a/libc/src/pthread/pthread_spin_destroy.cpp
+++ b/libc/src/pthread/pthread_spin_destroy.cpp
@@ -1,4 +1,4 @@
-//===-- Implementation of pthread_spin_destroy function ------------------===//
+//===-- Implementation of pthread_spin_destroy function -------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
diff --git a/libc/src/pthread/pthread_spin_destroy.h b/libc/src/pthread/pthread_spin_destroy.h
index a6f38aac8da33..2581e9e92f70d 100644
--- a/libc/src/pthread/pthread_spin_destroy.h
+++ b/libc/src/pthread/pthread_spin_destroy.h
@@ -1,5 +1,4 @@
-//===-- Implementation header for pthread_spin_destroy function ---*- C++
-//-*-===//
+//===-- Implementation header for pthread_spin_destroy function --*- C++-*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
diff --git a/libc/src/pthread/pthread_spin_unlock.h b/libc/src/pthread/pthread_spin_unlock.h
index e72a1aa9300ba..4918613f2e6c6 100644
--- a/libc/src/pthread/pthread_spin_unlock.h
+++ b/libc/src/pthread/pthread_spin_unlock.h
@@ -1,5 +1,4 @@
-//===-- Implementation header for pthread_spin_unlock function ---*- C++
-//-*-===//
+//===-- Implementation header for pthread_spin_unlock function ---*- C++-*-===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.

>From c8787e7096e75c50de7d098b1d2417618c56a200 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Fri, 26 Jul 2024 22:02:04 -0700
Subject: [PATCH 4/5] [libc] add integration tests

---
 libc/src/__support/threads/CMakeLists.txt     |   2 +-
 .../integration/src/pthread/CMakeLists.txt    |  18 +++
 .../src/pthread/pthread_spinlock_test.cpp     | 116 ++++++++++++++++++
 3 files changed, 135 insertions(+), 1 deletion(-)
 create mode 100644 libc/test/integration/src/pthread/pthread_spinlock_test.cpp

diff --git a/libc/src/__support/threads/CMakeLists.txt b/libc/src/__support/threads/CMakeLists.txt
index f1a2f162acfc2..ab474b23bcbfb 100644
--- a/libc/src/__support/threads/CMakeLists.txt
+++ b/libc/src/__support/threads/CMakeLists.txt
@@ -101,7 +101,7 @@ endif()
 
 set(tid_dep)
 if (LLVM_LIBC_FULL_BUILD)
-  list(APPEND tid_dep libc.src.__support.thread)
+  list(APPEND tid_dep libc.src.__support.threads.thread)
 else()
   list(APPEND tid_dep libc.src.__support.OSUtil.osutil)
   list(APPEND tid_dep libc.include.sys_syscall)
diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt
index fa5fd3ad55d5f..c1d9c70666152 100644
--- a/libc/test/integration/src/pthread/CMakeLists.txt
+++ b/libc/test/integration/src/pthread/CMakeLists.txt
@@ -57,6 +57,24 @@ add_integration_test(
     libc.src.__support.threads.sleep
 )
 
+add_integration_test(
+  pthread_spinlock_test
+  SUITE
+    libc-pthread-integration-tests
+  SRCS
+    pthread_spinlock_test.cpp
+  DEPENDS
+    libc.include.pthread
+    libc.include.errno
+    libc.src.pthread.pthread_spin_init
+    libc.src.pthread.pthread_spin_destroy
+    libc.src.pthread.pthread_spin_lock
+    libc.src.pthread.pthread_spin_trylock
+    libc.src.pthread.pthread_spin_unlock
+    libc.src.pthread.pthread_create
+    libc.src.pthread.pthread_join
+)
+
 add_integration_test(
   pthread_test
   SUITE
diff --git a/libc/test/integration/src/pthread/pthread_spinlock_test.cpp b/libc/test/integration/src/pthread/pthread_spinlock_test.cpp
new file mode 100644
index 0000000000000..6bcd611a6a2f8
--- /dev/null
+++ b/libc/test/integration/src/pthread/pthread_spinlock_test.cpp
@@ -0,0 +1,116 @@
+//===-- Tests for pthread_spinlock ----------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "src/pthread/pthread_create.h"
+#include "src/pthread/pthread_join.h"
+#include "src/pthread/pthread_spin_destroy.h"
+#include "src/pthread/pthread_spin_init.h"
+#include "src/pthread/pthread_spin_lock.h"
+#include "src/pthread/pthread_spin_trylock.h"
+#include "src/pthread/pthread_spin_unlock.h"
+#include "test/IntegrationTest/test.h"
+#include <errno.h>
+#include <pthread.h>
+
+namespace {
+void smoke_test() {
+  pthread_spinlock_t lock;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
+}
+
+void trylock_test() {
+  pthread_spinlock_t lock;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), EBUSY);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
+}
+
+void destroy_held_lock_test() {
+  pthread_spinlock_t lock;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), EBUSY);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
+}
+
+void use_after_destroy_test() {
+  pthread_spinlock_t lock;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), EINVAL);
+}
+
+void unlock_without_holding_test() {
+  pthread_spinlock_t lock;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), EPERM);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
+}
+
+void deadlock_test() {
+  pthread_spinlock_t lock;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), EDEADLK);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
+}
+
+void multi_thread_test() {
+  struct shared_data {
+    pthread_spinlock_t lock;
+    int count = 0;
+  } shared;
+  pthread_t thread[10];
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&shared.lock, 0), /*pshared=*/0);
+  for (int i = 0; i < 10; ++i) {
+    ASSERT_EQ(
+        LIBC_NAMESPACE::pthread_create(
+            &thread[i], nullptr,
+            [](void *arg) -> void * {
+              auto *data = static_cast<shared_data *>(arg);
+              for (int j = 0; j < 1000; ++j) {
+                ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&data->lock), 0);
+                data->count += j;
+                ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&data->lock), 0);
+              }
+              return nullptr;
+            },
+            &shared),
+        0);
+  }
+  for (int i = 0; i < 10; ++i) {
+    ASSERT_EQ(LIBC_NAMESPACE::pthread_join(thread[i], nullptr), 0);
+  }
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&shared.lock), 0);
+  ASSERT_EQ(shared.count, 1000 * 999 * 5);
+}
+
+} // namespace
+
+TEST_MAIN() {
+  smoke_test();
+  trylock_test();
+  destroy_held_lock_test();
+  use_after_destroy_test();
+  unlock_without_holding_test();
+  deadlock_test();
+  multi_thread_test();
+  return 0;
+}

>From 7083aac164f103c131d42694f5ad64b5a4b5c455 Mon Sep 17 00:00:00 2001
From: Schrodinger ZHU Yifan <i at zhuyi.fan>
Date: Sat, 27 Jul 2024 20:32:45 -0700
Subject: [PATCH 5/5] [libc] address code reviews

---
 libc/src/pthread/pthread_spin_destroy.cpp     | 11 ++---
 libc/src/pthread/pthread_spin_init.cpp        |  8 +++-
 libc/src/pthread/pthread_spin_lock.cpp        |  6 ++-
 libc/src/pthread/pthread_spin_trylock.cpp     |  4 +-
 libc/src/pthread/pthread_spin_unlock.cpp      | 10 +++--
 .../src/pthread/pthread_spinlock_test.cpp     | 43 ++++++++++++++++---
 6 files changed, 61 insertions(+), 21 deletions(-)

diff --git a/libc/src/pthread/pthread_spin_destroy.cpp b/libc/src/pthread/pthread_spin_destroy.cpp
index 4e89bf219355a..7d93dd967afed 100644
--- a/libc/src/pthread/pthread_spin_destroy.cpp
+++ b/libc/src/pthread/pthread_spin_destroy.cpp
@@ -20,12 +20,13 @@ static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) &&
 
 LLVM_LIBC_FUNCTION(int, pthread_spin_destroy,
                    ([[maybe_unused]] pthread_spinlock_t * lock)) {
-  auto *spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
-  // Accroding to POSIX.1-2024:
   // If an implementation detects that the value specified by the lock argument
-  // to pthread_spin_destroy() does not refer to an initialized spin lock
-  // object, it is recommended that the function should fail and report an
-  // [EINVAL] error.
+  // to pthread_spin_lock() or pthread_spin_trylock() does not refer to an
+  // initialized spin lock object, it is recommended that the function should
+  // fail and report an [EINVAL] error.
+  if (!lock)
+    return EINVAL;
+  auto spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
   if (!spin_lock || spin_lock->is_invalid())
     return EINVAL;
 
diff --git a/libc/src/pthread/pthread_spin_init.cpp b/libc/src/pthread/pthread_spin_init.cpp
index e399a20507e3b..54972475288bc 100644
--- a/libc/src/pthread/pthread_spin_init.cpp
+++ b/libc/src/pthread/pthread_spin_init.cpp
@@ -7,9 +7,11 @@
 //===----------------------------------------------------------------------===//
 
 #include "src/pthread/pthread_spin_init.h"
+#include "hdr/errno_macros.h"
 #include "src/__support/CPP/new.h"
 #include "src/__support/common.h"
 #include "src/__support/threads/spin_lock.h"
+#include <pthread.h> // for PTHREAD_PROCESS_SHARED, PTHREAD_PROCESS_PRIVATE
 
 namespace LIBC_NAMESPACE_DECL {
 
@@ -21,12 +23,14 @@ static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) &&
 
 LLVM_LIBC_FUNCTION(int, pthread_spin_init,
                    (pthread_spinlock_t * lock, [[maybe_unused]] int pshared)) {
+  if (!lock)
+    return EINVAL;
+  if (pshared != PTHREAD_PROCESS_SHARED && pshared != PTHREAD_PROCESS_PRIVATE)
+    return EINVAL;
   // The spin lock here is a simple atomic flag, so we don't need to do any
   // special handling for pshared.
   ::new (&lock->__lockword) SpinLock();
   lock->__owner = 0;
-  // No memory allocation needed, hence always succeeds. POSIX.1-2024 does not
-  // specify checking on invalid lock or pshared values.
   return 0;
 }
 
diff --git a/libc/src/pthread/pthread_spin_lock.cpp b/libc/src/pthread/pthread_spin_lock.cpp
index d155eda620131..7e828e27da190 100644
--- a/libc/src/pthread/pthread_spin_lock.cpp
+++ b/libc/src/pthread/pthread_spin_lock.cpp
@@ -21,12 +21,14 @@ static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) &&
               "size and alignment");
 
 LLVM_LIBC_FUNCTION(int, pthread_spin_lock, (pthread_spinlock_t * lock)) {
-  auto spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
   // If an implementation detects that the value specified by the lock argument
   // to pthread_spin_lock() or pthread_spin_trylock() does not refer to an
   // initialized spin lock object, it is recommended that the function should
   // fail and report an [EINVAL] error.
-  if (!spin_lock || spin_lock->is_invalid())
+  if (!lock)
+    return EINVAL;
+  auto spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
+  if (spin_lock->is_invalid())
     return EINVAL;
 
   pid_t self_tid = gettid_inline();
diff --git a/libc/src/pthread/pthread_spin_trylock.cpp b/libc/src/pthread/pthread_spin_trylock.cpp
index 87ce870d2a367..97825f8e9aa6f 100644
--- a/libc/src/pthread/pthread_spin_trylock.cpp
+++ b/libc/src/pthread/pthread_spin_trylock.cpp
@@ -21,11 +21,13 @@ static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) &&
               "size and alignment");
 
 LLVM_LIBC_FUNCTION(int, pthread_spin_trylock, (pthread_spinlock_t * lock)) {
-  auto spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
   // If an implementation detects that the value specified by the lock argument
   // to pthread_spin_lock() or pthread_spin_trylock() does not refer to an
   // initialized spin lock object, it is recommended that the function should
   // fail and report an [EINVAL] error.
+  if (!lock)
+    return EINVAL;
+  auto spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
   if (!spin_lock || spin_lock->is_invalid())
     return EINVAL;
   // Try to acquire the lock without blocking.
diff --git a/libc/src/pthread/pthread_spin_unlock.cpp b/libc/src/pthread/pthread_spin_unlock.cpp
index 4f9ff0863ac06..204dc73eeb6d3 100644
--- a/libc/src/pthread/pthread_spin_unlock.cpp
+++ b/libc/src/pthread/pthread_spin_unlock.cpp
@@ -21,11 +21,13 @@ static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) &&
               "size and alignment");
 
 LLVM_LIBC_FUNCTION(int, pthread_spin_unlock, (pthread_spinlock_t * lock)) {
-  auto *spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
   // If an implementation detects that the value specified by the lock argument
-  // to pthread_spin_unlock() does not refer to an initialized spin lock object,
-  // it is recommended that the function should fail and report an [EINVAL]
-  // error.
+  // to pthread_spin_lock() or pthread_spin_trylock() does not refer to an
+  // initialized spin lock object, it is recommended that the function should
+  // fail and report an [EINVAL] error.
+  if (!lock)
+    return EINVAL;
+  auto spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
   if (!spin_lock || spin_lock->is_invalid())
     return EINVAL;
   // If an implementation detects that the value specified by the lock argument
diff --git a/libc/test/integration/src/pthread/pthread_spinlock_test.cpp b/libc/test/integration/src/pthread/pthread_spinlock_test.cpp
index 6bcd611a6a2f8..edef9d26a6dbd 100644
--- a/libc/test/integration/src/pthread/pthread_spinlock_test.cpp
+++ b/libc/test/integration/src/pthread/pthread_spinlock_test.cpp
@@ -20,7 +20,8 @@
 namespace {
 void smoke_test() {
   pthread_spinlock_t lock;
-  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
+            0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
@@ -28,7 +29,8 @@ void smoke_test() {
 
 void trylock_test() {
   pthread_spinlock_t lock;
-  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
+            0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(&lock), EBUSY);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
@@ -39,7 +41,8 @@ void trylock_test() {
 
 void destroy_held_lock_test() {
   pthread_spinlock_t lock;
-  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
+            0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), EBUSY);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
@@ -48,7 +51,8 @@ void destroy_held_lock_test() {
 
 void use_after_destroy_test() {
   pthread_spinlock_t lock;
-  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
+            0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), EINVAL);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), EINVAL);
@@ -58,27 +62,50 @@ void use_after_destroy_test() {
 
 void unlock_without_holding_test() {
   pthread_spinlock_t lock;
-  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
+            0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), EPERM);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
 }
 
 void deadlock_test() {
   pthread_spinlock_t lock;
-  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
+            0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(&lock), EDEADLK);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(&lock), 0);
   ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
 }
 
+void null_lock_test() {
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(nullptr, 0), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_lock(nullptr), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_trylock(nullptr), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_unlock(nullptr), EINVAL);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(nullptr), EINVAL);
+}
+
+void pshared_attribute_test() {
+  pthread_spinlock_t lock;
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_SHARED),
+            0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
+
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, PTHREAD_PROCESS_PRIVATE),
+            0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_destroy(&lock), 0);
+
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&lock, -1), EINVAL);
+}
+
 void multi_thread_test() {
   struct shared_data {
     pthread_spinlock_t lock;
     int count = 0;
   } shared;
   pthread_t thread[10];
-  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&shared.lock, 0), /*pshared=*/0);
+  ASSERT_EQ(LIBC_NAMESPACE::pthread_spin_init(&shared.lock, 0), 0);
   for (int i = 0; i < 10; ++i) {
     ASSERT_EQ(
         LIBC_NAMESPACE::pthread_create(
@@ -112,5 +139,7 @@ TEST_MAIN() {
   unlock_without_holding_test();
   deadlock_test();
   multi_thread_test();
+  null_lock_test();
+  pshared_attribute_test();
   return 0;
 }



More information about the libc-commits mailing list