Skip to content

Commit 03841e7

Browse files
[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
1 parent 81d8273 commit 03841e7

23 files changed

+653
-30
lines changed

libc/config/linux/aarch64/entrypoints.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -807,6 +807,11 @@ if(LLVM_LIBC_FULL_BUILD)
807807
libc.src.pthread.pthread_rwlockattr_init
808808
libc.src.pthread.pthread_rwlockattr_setkind_np
809809
libc.src.pthread.pthread_rwlockattr_setpshared
810+
libc.src.pthread.pthread_spin_destroy
811+
libc.src.pthread.pthread_spin_init
812+
libc.src.pthread.pthread_spin_lock
813+
libc.src.pthread.pthread_spin_trylock
814+
libc.src.pthread.pthread_spin_unlock
810815
libc.src.pthread.pthread_self
811816
libc.src.pthread.pthread_setname_np
812817
libc.src.pthread.pthread_setspecific

libc/config/linux/api.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ def PThreadAPI : PublicAPI<"pthread.h"> {
143143
"pthread_once_t",
144144
"pthread_rwlockattr_t",
145145
"pthread_rwlock_t",
146+
"pthread_spinlock_t",
146147
"pthread_t",
147148
];
148149
}

libc/docs/dev/undefined_behavior.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,3 +98,11 @@ Unrecognized ``clockid_t`` values for ``pthread_rwlock_clock*`` APIs
9898
----------------------------------------------------------------------
9999
POSIX.1-2024 only demands support for ``CLOCK_REALTIME`` and ``CLOCK_MONOTONIC``. Currently,
100100
as in LLVM libc, if other clock ids are used, they will be treated as monotonic clocks.
101+
102+
PThread SpinLock Destroy
103+
------------------------
104+
POSIX.1 Issue 7 updates the spinlock destroy behavior description such that the return code for
105+
uninitialized spinlock and invalid spinlock is left undefined. We follow the recommendation as in
106+
POSIX.1-2024, where EINVAL is returned if the spinlock is invalid (here we only check for null pointers) or
107+
EBUSY is returned if the spinlock is currently locked. The lock is poisoned after a successful destroy. That is,
108+
subsequent operations on the lock object without any reinitialization will return EINVAL.

libc/include/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,7 @@ add_header_macro(
386386
.llvm-libc-types.pthread_once_t
387387
.llvm-libc-types.pthread_rwlock_t
388388
.llvm-libc-types.pthread_rwlockattr_t
389+
.llvm-libc-types.pthread_spinlock_t
389390
.llvm-libc-types.pthread_t
390391
)
391392

libc/include/llvm-libc-types/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ add_header(pthread_mutexattr_t HDR pthread_mutexattr_t.h)
5656
add_header(pthread_once_t HDR pthread_once_t.h DEPENDS .__futex_word)
5757
add_header(pthread_rwlock_t HDR pthread_rwlock_t.h DEPENDS .__futex_word .pid_t)
5858
add_header(pthread_rwlockattr_t HDR pthread_rwlockattr_t.h)
59+
add_header(pthread_spinlock_t HDR pthread_spinlock_t.h DEPENDS .pid_t)
5960
add_header(pthread_t HDR pthread_t.h DEPENDS .__thread_type)
6061
add_header(rlim_t HDR rlim_t.h)
6162
add_header(time_t HDR time_t.h)
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
//===-- Definition of pthread_spinlock_t type -----------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_TYPES_PTHREAD_SPINLOCK_T_H
10+
#define LLVM_LIBC_TYPES_PTHREAD_SPINLOCK_T_H
11+
#include "llvm-libc-types/pid_t.h"
12+
typedef struct {
13+
unsigned char __lockword;
14+
pid_t __owner;
15+
} pthread_spinlock_t;
16+
17+
#endif // LLVM_LIBC_TYPES_PTHREAD_SPINLOCK_T_H

libc/newhdrgen/yaml/pthread.yaml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ types:
1414
- type_name: __pthread_start_t
1515
- type_name: __pthread_once_func_t
1616
- type_name: __atfork_callback_t
17+
- type_name: pthread_spinlock_t
1718
enums: []
1819
functions:
1920
- name: pthread_atfork
@@ -404,3 +405,29 @@ functions:
404405
return_type: int
405406
arguments:
406407
- type: pthread_rwlock_t *
408+
- name: pthread_spin_init
409+
standards: POSIX
410+
return_type: int
411+
arguments:
412+
- type: pthread_spinlock_t *
413+
- type: int
414+
- name: pthread_spin_destroy
415+
standards: POSIX
416+
return_type: int
417+
arguments:
418+
- type: pthread_spinlock_t *
419+
- name: pthread_spin_lock
420+
standards: POSIX
421+
return_type: int
422+
arguments:
423+
- type: pthread_spinlock_t *
424+
- name: pthread_spin_trylock
425+
standards: POSIX
426+
return_type: int
427+
arguments:
428+
- type: pthread_spinlock_t *
429+
- name: pthread_spin_unlock
430+
standards: POSIX
431+
return_type: int
432+
arguments:
433+
- type: pthread_spinlock_t *

libc/spec/posix.td

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,9 @@ def POSIX : StandardSpec<"POSIX"> {
132132
PtrType PThreadRWLockTPtr = PtrType<PThreadRWLockTType>;
133133
RestrictedPtrType RestrictedPThreadRWLockTPtr = RestrictedPtrType<PThreadRWLockTType>;
134134

135+
NamedType PThreadSpinLockTType = NamedType<"pthread_spinlock_t">;
136+
PtrType PThreadSpinLockTPtr = PtrType<PThreadSpinLockTType>;
137+
135138
PtrType PThreadTPtr = PtrType<PThreadTType>;
136139
RestrictedPtrType RestrictedPThreadTPtr = RestrictedPtrType<PThreadTType>;
137140

@@ -1049,6 +1052,7 @@ def POSIX : StandardSpec<"POSIX"> {
10491052
PThreadOnceT,
10501053
PThreadRWLockAttrTType,
10511054
PThreadRWLockTType,
1055+
PThreadSpinLockTType,
10521056
PThreadStartT,
10531057
PThreadTSSDtorT,
10541058
PThreadTType,
@@ -1360,6 +1364,31 @@ def POSIX : StandardSpec<"POSIX"> {
13601364
RetValSpec<IntType>,
13611365
[ArgSpec<PThreadRWLockTPtr>]
13621366
>,
1367+
FunctionSpec<
1368+
"pthread_spin_init",
1369+
RetValSpec<IntType>,
1370+
[ArgSpec<PThreadSpinLockTPtr>, ArgSpec<IntType>]
1371+
>,
1372+
FunctionSpec<
1373+
"pthread_spin_destroy",
1374+
RetValSpec<IntType>,
1375+
[ArgSpec<PThreadSpinLockTPtr>]
1376+
>,
1377+
FunctionSpec<
1378+
"pthread_spin_lock",
1379+
RetValSpec<IntType>,
1380+
[ArgSpec<PThreadSpinLockTPtr>]
1381+
>,
1382+
FunctionSpec<
1383+
"pthread_spin_trylock",
1384+
RetValSpec<IntType>,
1385+
[ArgSpec<PThreadSpinLockTPtr>]
1386+
>,
1387+
FunctionSpec<
1388+
"pthread_spin_unlock",
1389+
RetValSpec<IntType>,
1390+
[ArgSpec<PThreadSpinLockTPtr>]
1391+
>
13631392
]
13641393
>;
13651394

libc/src/__support/threads/spin_lock.h

Lines changed: 10 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,17 @@
1111

1212
#include "src/__support/CPP/atomic.h"
1313
#include "src/__support/macros/attributes.h"
14-
#include "src/__support/macros/properties/architectures.h"
1514
#include "src/__support/threads/sleep.h"
1615

1716
namespace LIBC_NAMESPACE_DECL {
1817

19-
namespace spinlock {
20-
template <typename LockWord, typename Return>
21-
using AtomicOp = Return (cpp::Atomic<LockWord>::*)(LockWord, cpp::MemoryOrder,
22-
cpp::MemoryScope);
23-
}
24-
25-
template <typename LockWord, spinlock::AtomicOp<LockWord, LockWord> Acquire,
26-
spinlock::AtomicOp<LockWord, void> Release>
27-
class SpinLockAdaptor {
28-
cpp::Atomic<LockWord> flag;
18+
class SpinLock {
19+
cpp::Atomic<unsigned char> flag;
2920

3021
public:
31-
LIBC_INLINE constexpr SpinLockAdaptor() : flag{false} {}
22+
LIBC_INLINE constexpr SpinLock() : flag{0} {}
3223
LIBC_INLINE bool try_lock() {
33-
return !flag.*Acquire(static_cast<LockWord>(1), cpp::MemoryOrder::ACQUIRE);
24+
return !flag.exchange(1u, cpp::MemoryOrder::ACQUIRE);
3425
}
3526
LIBC_INLINE void lock() {
3627
// clang-format off
@@ -60,22 +51,15 @@ class SpinLockAdaptor {
6051
while (flag.load(cpp::MemoryOrder::RELAXED))
6152
sleep_briefly();
6253
}
63-
LIBC_INLINE void unlock() {
64-
flag.*Release(static_cast<LockWord>(0), cpp::MemoryOrder::RELEASE);
54+
LIBC_INLINE void unlock() { flag.store(0u, cpp::MemoryOrder::RELEASE); }
55+
LIBC_INLINE bool is_locked() { return flag.load(cpp::MemoryOrder::ACQUIRE); }
56+
LIBC_INLINE bool is_invalid() {
57+
return flag.load(cpp::MemoryOrder::ACQUIRE) > 1;
6558
}
59+
// poison the lock
60+
LIBC_INLINE ~SpinLock() { flag.store(0xffu, cpp::MemoryOrder::RELEASE); }
6661
};
6762

68-
// It is reported that atomic operations with higher-order semantics
69-
// lead to better performance on GPUs.
70-
#ifdef LIBC_TARGET_ARCH_IS_GPU
71-
using SpinLock =
72-
SpinLockAdaptor<unsigned int, &cpp::Atomic<unsigned int>::fetch_or,
73-
&cpp::Atomic<unsigned int>::fetch_and>;
74-
#else
75-
using SpinLock = SpinLockAdaptor<bool, &cpp::Atomic<bool>::exchange,
76-
&cpp::Atomic<bool>::store>;
77-
#endif
78-
7963
} // namespace LIBC_NAMESPACE_DECL
8064

8165
#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_SPIN_LOCK_H

libc/src/pthread/CMakeLists.txt

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -644,6 +644,71 @@ add_entrypoint_object(
644644
libc.src.__support.threads.linux.rwlock
645645
)
646646

647+
add_entrypoint_object(
648+
pthread_spin_init
649+
SRCS
650+
pthread_spin_init.cpp
651+
HDRS
652+
pthread_spin_init.h
653+
DEPENDS
654+
libc.include.pthread
655+
libc.src.__support.threads.spin_lock
656+
libc.src.__support.threads.identifier
657+
libc.hdr.errno_macros
658+
)
659+
660+
add_entrypoint_object(
661+
pthread_spin_destroy
662+
SRCS
663+
pthread_spin_destroy.cpp
664+
HDRS
665+
pthread_spin_destroy.h
666+
DEPENDS
667+
libc.include.pthread
668+
libc.src.__support.threads.spin_lock
669+
libc.src.__support.threads.identifier
670+
libc.hdr.errno_macros
671+
)
672+
673+
add_entrypoint_object(
674+
pthread_spin_lock
675+
SRCS
676+
pthread_spin_lock.cpp
677+
HDRS
678+
pthread_spin_lock.h
679+
DEPENDS
680+
libc.include.pthread
681+
libc.src.__support.threads.spin_lock
682+
libc.src.__support.threads.identifier
683+
libc.hdr.errno_macros
684+
)
685+
686+
add_entrypoint_object(
687+
pthread_spin_trylock
688+
SRCS
689+
pthread_spin_trylock.cpp
690+
HDRS
691+
pthread_spin_trylock.h
692+
DEPENDS
693+
libc.include.pthread
694+
libc.src.__support.threads.spin_lock
695+
libc.src.__support.threads.identifier
696+
libc.hdr.errno_macros
697+
)
698+
699+
add_entrypoint_object(
700+
pthread_spin_unlock
701+
SRCS
702+
pthread_spin_unlock.cpp
703+
HDRS
704+
pthread_spin_unlock.h
705+
DEPENDS
706+
libc.include.pthread
707+
libc.src.__support.threads.spin_lock
708+
libc.src.__support.threads.identifier
709+
libc.hdr.errno_macros
710+
)
711+
647712
add_entrypoint_object(
648713
pthread_once
649714
SRCS
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//===-- Implementation of pthread_spin_destroy function -------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/pthread/pthread_spin_destroy.h"
10+
#include "hdr/errno_macros.h"
11+
#include "src/__support/common.h"
12+
#include "src/__support/threads/spin_lock.h"
13+
namespace LIBC_NAMESPACE_DECL {
14+
15+
static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) &&
16+
alignof(decltype(pthread_spinlock_t::__lockword)) ==
17+
alignof(SpinLock),
18+
"pthread_spinlock_t::__lockword and SpinLock must be of the same "
19+
"size and alignment");
20+
21+
LLVM_LIBC_FUNCTION(int, pthread_spin_destroy,
22+
([[maybe_unused]] pthread_spinlock_t * lock)) {
23+
// If an implementation detects that the value specified by the lock argument
24+
// to pthread_spin_lock() or pthread_spin_trylock() does not refer to an
25+
// initialized spin lock object, it is recommended that the function should
26+
// fail and report an [EINVAL] error.
27+
if (!lock)
28+
return EINVAL;
29+
auto spin_lock = reinterpret_cast<SpinLock *>(&lock->__lockword);
30+
if (!spin_lock || spin_lock->is_invalid())
31+
return EINVAL;
32+
33+
// If an implementation detects that the value specified by the lock argument
34+
// to pthread_spin_destroy() or pthread_spin_init() refers to a locked spin
35+
// lock object, or detects that the value specified by the lock argument to
36+
// pthread_spin_init() refers to an already initialized spin lock object, it
37+
// is recommended that the function should fail and report an [EBUSY] error.
38+
if (spin_lock->is_locked())
39+
return EBUSY;
40+
41+
// poison the lock
42+
spin_lock->~SpinLock();
43+
lock->__owner = 0;
44+
return 0;
45+
}
46+
47+
} // namespace LIBC_NAMESPACE_DECL
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
//===-- Implementation header for pthread_spin_destroy function --*- C++-*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_DESTROY_H
10+
#define LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_DESTROY_H
11+
12+
#include "src/__support/macros/config.h"
13+
#include <pthread.h>
14+
15+
namespace LIBC_NAMESPACE_DECL {
16+
17+
int pthread_spin_destroy(pthread_spinlock_t *lock);
18+
19+
} // namespace LIBC_NAMESPACE_DECL
20+
21+
#endif // LLVM_LIBC_SRC_PTHREAD_PTHREAD_SPIN_DESTROY_H
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
//===-- Implementation of pthread_spin_init function ----------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "src/pthread/pthread_spin_init.h"
10+
#include "hdr/errno_macros.h"
11+
#include "src/__support/CPP/new.h"
12+
#include "src/__support/common.h"
13+
#include "src/__support/threads/spin_lock.h"
14+
#include <pthread.h> // for PTHREAD_PROCESS_SHARED, PTHREAD_PROCESS_PRIVATE
15+
16+
namespace LIBC_NAMESPACE_DECL {
17+
18+
static_assert(sizeof(pthread_spinlock_t::__lockword) == sizeof(SpinLock) &&
19+
alignof(decltype(pthread_spinlock_t::__lockword)) ==
20+
alignof(SpinLock),
21+
"pthread_spinlock_t::__lockword and SpinLock must be of the same "
22+
"size and alignment");
23+
24+
LLVM_LIBC_FUNCTION(int, pthread_spin_init,
25+
(pthread_spinlock_t * lock, [[maybe_unused]] int pshared)) {
26+
if (!lock)
27+
return EINVAL;
28+
if (pshared != PTHREAD_PROCESS_SHARED && pshared != PTHREAD_PROCESS_PRIVATE)
29+
return EINVAL;
30+
// The spin lock here is a simple atomic flag, so we don't need to do any
31+
// special handling for pshared.
32+
::new (&lock->__lockword) SpinLock();
33+
lock->__owner = 0;
34+
return 0;
35+
}
36+
37+
} // namespace LIBC_NAMESPACE_DECL

0 commit comments

Comments
 (0)