Skip to content

[libc] add spin lock family functions #100509

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Aug 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions libc/config/linux/aarch64/entrypoints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -734,6 +734,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
Expand Down
1 change: 1 addition & 0 deletions libc/config/linux/api.td
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ def PThreadAPI : PublicAPI<"pthread.h"> {
"pthread_once_t",
"pthread_rwlockattr_t",
"pthread_rwlock_t",
"pthread_spinlock_t",
"pthread_t",
];
}
Expand Down
8 changes: 8 additions & 0 deletions libc/docs/dev/undefined_behavior.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
1 change: 1 addition & 0 deletions libc/include/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
)

Expand Down
1 change: 1 addition & 0 deletions libc/include/llvm-libc-types/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
17 changes: 17 additions & 0 deletions libc/include/llvm-libc-types/pthread_spinlock_t.h
Original file line number Diff line number Diff line change
@@ -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
27 changes: 27 additions & 0 deletions libc/newhdrgen/yaml/pthread.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 *
29 changes: 29 additions & 0 deletions libc/spec/posix.td
Original file line number Diff line number Diff line change
Expand Up @@ -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>;

Expand Down Expand Up @@ -1049,6 +1052,7 @@ def POSIX : StandardSpec<"POSIX"> {
PThreadOnceT,
PThreadRWLockAttrTType,
PThreadRWLockTType,
PThreadSpinLockTType,
PThreadStartT,
PThreadTSSDtorT,
PThreadTType,
Expand Down Expand Up @@ -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>]
>
]
>;

Expand Down
36 changes: 10 additions & 26 deletions libc/src/__support/threads/spin_lock.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
65 changes: 65 additions & 0 deletions libc/src/pthread/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
47 changes: 47 additions & 0 deletions libc/src/pthread/pthread_spin_destroy.cpp
Original file line number Diff line number Diff line change
@@ -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
21 changes: 21 additions & 0 deletions libc/src/pthread/pthread_spin_destroy.h
Original file line number Diff line number Diff line change
@@ -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>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we have any issues created for pthread's proxy headers yet?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess no. Such routines are hardly used in overlay mode. That is being said, I think we should open one as many implementations do support overlay mode.


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
37 changes: 37 additions & 0 deletions libc/src/pthread/pthread_spin_init.cpp
Original file line number Diff line number Diff line change
@@ -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
Loading
Loading