[libc-commits] [libc] 03841e7 - [libc] add spin lock family functions (#100509)
via libc-commits
libc-commits at lists.llvm.org
Tue Aug 6 21:02:58 PDT 2024
Author: Schrodinger ZHU Yifan
Date: 2024-08-06T21:02:54-07:00
New Revision: 03841e7ab847b279d65be707a8e0f2799fd69f50
URL: https://github.com/llvm/llvm-project/commit/03841e7ab847b279d65be707a8e0f2799fd69f50
DIFF: https://github.com/llvm/llvm-project/commit/03841e7ab847b279d65be707a8e0f2799fd69f50.diff
LOG: [libc] add spin lock family functions (#100509)
This PR:
- add entrypoints for `pthread_spin_*`
- additionally, the fixes a typo that has been disabling lock related
tests
Added:
libc/include/llvm-libc-types/pthread_spinlock_t.h
libc/src/pthread/pthread_spin_destroy.cpp
libc/src/pthread/pthread_spin_destroy.h
libc/src/pthread/pthread_spin_init.cpp
libc/src/pthread/pthread_spin_init.h
libc/src/pthread/pthread_spin_lock.cpp
libc/src/pthread/pthread_spin_lock.h
libc/src/pthread/pthread_spin_trylock.cpp
libc/src/pthread/pthread_spin_trylock.h
libc/src/pthread/pthread_spin_unlock.cpp
libc/src/pthread/pthread_spin_unlock.h
libc/test/integration/src/pthread/pthread_spinlock_test.cpp
Modified:
libc/config/linux/aarch64/entrypoints.txt
libc/config/linux/api.td
libc/docs/dev/undefined_behavior.rst
libc/include/CMakeLists.txt
libc/include/llvm-libc-types/CMakeLists.txt
libc/newhdrgen/yaml/pthread.yaml
libc/spec/posix.td
libc/src/__support/threads/spin_lock.h
libc/src/pthread/CMakeLists.txt
libc/test/integration/src/pthread/CMakeLists.txt
libc/test/integration/src/pthread/pthread_rwlock_test.cpp
Removed:
################################################################################
diff --git a/libc/config/linux/aarch64/entrypoints.txt b/libc/config/linux/aarch64/entrypoints.txt
index e211e9a3d557c..7677770c49573 100644
--- a/libc/config/linux/aarch64/entrypoints.txt
+++ b/libc/config/linux/aarch64/entrypoints.txt
@@ -807,6 +807,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 9f50545d745d6..d0d882b7010e3 100644
--- a/libc/docs/dev/undefined_behavior.rst
+++ b/libc/docs/dev/undefined_behavior.rst
@@ -98,3 +98,11 @@ Unrecognized ``clockid_t`` values for ``pthread_rwlock_clock*`` APIs
----------------------------------------------------------------------
POSIX.1-2024 only demands support for ``CLOCK_REALTIME`` and ``CLOCK_MONOTONIC``. Currently,
as in LLVM libc, if other clock ids are used, they will be treated as monotonic clocks.
+
+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/newhdrgen/yaml/pthread.yaml b/libc/newhdrgen/yaml/pthread.yaml
index b6934e3092597..f3c59ee44943b 100644
--- a/libc/newhdrgen/yaml/pthread.yaml
+++ b/libc/newhdrgen/yaml/pthread.yaml
@@ -14,6 +14,7 @@ types:
- type_name: __pthread_start_t
- type_name: __pthread_once_func_t
- type_name: __atfork_callback_t
+ - type_name: pthread_spinlock_t
enums: []
functions:
- name: pthread_atfork
@@ -404,3 +405,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 *
diff --git a/libc/spec/posix.td b/libc/spec/posix.td
index 0edf9080c712e..085f2ec34ab34 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>;
@@ -1049,6 +1052,7 @@ def POSIX : StandardSpec<"POSIX"> {
PThreadOnceT,
PThreadRWLockAttrTType,
PThreadRWLockTType,
+ PThreadSpinLockTType,
PThreadStartT,
PThreadTSSDtorT,
PThreadTType,
@@ -1360,6 +1364,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 70d10e6c4e3f8..e7e92e5b60dc2 100644
--- a/libc/src/pthread/CMakeLists.txt
+++ b/libc/src/pthread/CMakeLists.txt
@@ -644,6 +644,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.identifier
+ 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.identifier
+ 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.identifier
+ 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.identifier
+ 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.identifier
+ 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..7d93dd967afed
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_destroy.cpp
@@ -0,0 +1,47 @@
+//===-- 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)) {
+ // 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;
+
+ // 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..2581e9e92f70d
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_destroy.h
@@ -0,0 +1,21 @@
+//===-- 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..54972475288bc
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_init.cpp
@@ -0,0 +1,37 @@
+//===-- 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 "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 {
+
+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)) {
+ 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;
+ 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..61c8db14e4873
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_lock.cpp
@@ -0,0 +1,47 @@
+//===-- 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/identifier.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_lock, (pthread_spinlock_t * lock)) {
+ // 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->is_invalid())
+ return EINVAL;
+
+ pid_t self_tid = internal::gettid();
+ // 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..99b0eac413554
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_trylock.cpp
@@ -0,0 +1,41 @@
+//===-- 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/identifier.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_trylock, (pthread_spinlock_t * lock)) {
+ // 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.
+ if (!spin_lock->try_lock())
+ return EBUSY;
+ // We have acquired the lock. Update the owner field.
+ lock->__owner = internal::gettid();
+ 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..a02f2b3604587
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_unlock.cpp
@@ -0,0 +1,44 @@
+//===-- 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/identifier.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_unlock, (pthread_spinlock_t * lock)) {
+ // 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;
+ // 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 != internal::gettid())
+ 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..4918613f2e6c6
--- /dev/null
+++ b/libc/src/pthread/pthread_spin_unlock.h
@@ -0,0 +1,21 @@
+//===-- 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
diff --git a/libc/test/integration/src/pthread/CMakeLists.txt b/libc/test/integration/src/pthread/CMakeLists.txt
index eb26822597c2f..48d4368df156d 100644
--- a/libc/test/integration/src/pthread/CMakeLists.txt
+++ b/libc/test/integration/src/pthread/CMakeLists.txt
@@ -24,9 +24,9 @@ add_integration_test(
SRCS
pthread_rwlock_test.cpp
DEPENDS
+ libc.hdr.time_macros
+ libc.hdr.errno_macros
libc.include.pthread
- libc.include.time
- libc.include.errno
libc.src.pthread.pthread_rwlock_destroy
libc.src.pthread.pthread_rwlock_init
libc.src.pthread.pthread_rwlock_rdlock
@@ -59,6 +59,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.hdr.errno_macros
+ libc.include.pthread
+ 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_rwlock_test.cpp b/libc/test/integration/src/pthread/pthread_rwlock_test.cpp
index 9f5fba187713e..4cd4255a333e6 100644
--- a/libc/test/integration/src/pthread/pthread_rwlock_test.cpp
+++ b/libc/test/integration/src/pthread/pthread_rwlock_test.cpp
@@ -6,6 +6,8 @@
//
//===----------------------------------------------------------------------===//
+#include "hdr/errno_macros.h"
+#include "hdr/time_macros.h"
#include "src/__support/CPP/atomic.h"
#include "src/__support/CPP/new.h"
#include "src/__support/OSUtil/syscall.h"
@@ -40,9 +42,7 @@
#include "src/time/clock_gettime.h"
#include "src/unistd/fork.h"
#include "test/IntegrationTest/test.h"
-#include <errno.h>
#include <pthread.h>
-#include <time.h>
namespace LIBC_NAMESPACE_DECL {
namespace rwlock {
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..233daf8332fb0
--- /dev/null
+++ b/libc/test/integration/src/pthread/pthread_spinlock_test.cpp
@@ -0,0 +1,145 @@
+//===-- 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 "hdr/errno_macros.h"
+#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 <pthread.h>
+
+namespace {
+void smoke_test() {
+ pthread_spinlock_t lock;
+ 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);
+}
+
+void trylock_test() {
+ pthread_spinlock_t lock;
+ 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);
+ 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, 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);
+ 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, 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);
+ 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, 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, 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), 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();
+ null_lock_test();
+ pshared_attribute_test();
+ return 0;
+}
More information about the libc-commits
mailing list