Skip to content

Commit 0d13cdf

Browse files
[libc] rework mutex
1 parent cd9bab2 commit 0d13cdf

File tree

18 files changed

+253
-94
lines changed

18 files changed

+253
-94
lines changed

libc/config/config.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,15 @@
4040
"value": true,
4141
"doc": "Enable -fstack-protector-strong to defend against stack smashing attack."
4242
}
43+
},
44+
"pthread": {
45+
"LIBC_CONF_TIMEOUT_ENSURE_MONOTONICITY": {
46+
"value": true,
47+
"doc": "Automatically adjust timeout to CLOCK_MONOTONIC."
48+
},
49+
"LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT": {
50+
"value": 100,
51+
"doc": "Default number of spins before blocking if a mutex is contented."
52+
}
4353
}
4454
}

libc/docs/configure.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@ to learn about the defaults for your platform and target.
3434
- ``LIBC_CONF_PRINTF_DISABLE_INDEX_MODE``: Disable index mode in the printf format string.
3535
- ``LIBC_CONF_PRINTF_DISABLE_WRITE_INT``: Disable handling of %n in printf format string.
3636
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE``: Use large table for better printf long double performance.
37+
* **"pthread" options**
38+
- ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is contented.
39+
- ``LIBC_CONF_TIMEOUT_ENSURE_MONOTONICITY``: Automatically adjust timeout to CLOCK_MONOTONIC.
3740
* **"string" options**
3841
- ``LIBC_CONF_MEMSET_X86_USE_SOFTWARE_PREFETCHING``: Inserts prefetch for write instructions (PREFETCHW) for memset on x86 to recover performance when hardware prefetcher is disabled.
3942
- ``LIBC_CONF_STRING_UNSAFE_WIDE_READ``: Read more than a byte at a time to perform byte-string operations like strlen.

libc/hdr/types/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,3 +108,12 @@ add_proxy_header_library(
108108
libc.include.llvm-libc-types.struct_timeval
109109
libc.include.sys_time
110110
)
111+
112+
add_proxy_header_library(
113+
pid_t
114+
HDRS
115+
pid_t.h
116+
FULL_BUILD_DEPENDS
117+
libc.include.llvm-libc-types.pid_t
118+
libc.include.sys_types
119+
)

libc/hdr/types/pid_t.h

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
//===-- Proxy for pid_t ---------------------------------------------------===//
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_HDR_TYPES_PID_T_H
10+
#define LLVM_LIBC_HDR_TYPES_PID_T_H
11+
12+
#ifdef LIBC_FULL_BUILD
13+
14+
#include "include/llvm-libc-types/pid_t.h"
15+
16+
#else // Overlay mode
17+
18+
#include <sys/types.h>
19+
20+
#endif // LLVM_LIBC_FULL_BUILD
21+
22+
#endif // LLVM_LIBC_HDR_TYPES_PID_T_H

libc/src/__support/File/dir.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class Dir {
5454
Dir(const Dir &) = delete;
5555

5656
explicit Dir(int fdesc)
57-
: fd(fdesc), readptr(0), fillsize(0), mutex(false, false, false) {}
57+
: fd(fdesc), readptr(0), fillsize(0), mutex(false, false, false, false) {}
5858
~Dir() = default;
5959

6060
Dir &operator=(const Dir &) = delete;

libc/src/__support/File/file.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ class File {
154154
uint8_t *buffer, size_t buffer_size, int buffer_mode,
155155
bool owned, ModeFlags modeflags)
156156
: platform_write(wf), platform_read(rf), platform_seek(sf),
157-
platform_close(cf), mutex(false, false, false), ungetc_buf(0),
157+
platform_close(cf), mutex(false, false, false, false), ungetc_buf(0),
158158
buf(buffer), bufsize(buffer_size), bufmode(buffer_mode), own_buf(owned),
159159
mode(modeflags), pos(0), prev_op(FileOp::NONE), read_limit(0),
160160
eof(false), err(false) {

libc/src/__support/threads/fork_callbacks.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ class AtForkCallbackManager {
3333
size_t next_index;
3434

3535
public:
36-
constexpr AtForkCallbackManager() : mtx(false, false, false), next_index(0) {}
36+
constexpr AtForkCallbackManager()
37+
: mtx(false, false, false, false), next_index(0) {}
3738

3839
bool register_triple(const ForkCallbackTriple &triple) {
3940
cpp::lock_guard lock(mtx);

libc/src/__support/threads/gpu/mutex.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace LIBC_NAMESPACE {
1919
/// define the Mutex interface and require that only a single thread executes
2020
/// code requiring a mutex lock.
2121
struct Mutex {
22-
LIBC_INLINE constexpr Mutex(bool, bool, bool) {}
22+
LIBC_INLINE constexpr Mutex(bool, bool, bool, bool) {}
2323

2424
LIBC_INLINE MutexError lock() { return MutexError::NONE; }
2525
LIBC_INLINE MutexError unlock() { return MutexError::NONE; }

libc/src/__support/threads/linux/CMakeLists.txt

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,35 @@ add_header_library(
2222
libc.src.__support.time.linux.abs_timeout
2323
)
2424

25+
set(raw_mutex_additional_flags)
26+
if (LIBC_CONF_TIMEOUT_ENSURE_MONOTONICITY)
27+
set(raw_mutex_additional_flags -DLIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY)
28+
endif()
29+
30+
add_header_library(
31+
raw_mutex
32+
HDRS
33+
mutex.h
34+
DEPENDS
35+
.futex_utils
36+
libc.src.__support.threads.sleep
37+
libc.src.__support.time.linux.abs_timeout
38+
libc.src.__support.time.linux.monotonicity
39+
libc.src.__support.CPP.optional
40+
libc.hdr.types.pid_t
41+
COMPILE_OPTIONS
42+
-DLIBC_COPT_RAW_MUTEX_DEFAULT_SPIN_COUNT=${LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT}
43+
${raw_mutex_additional_flags}
44+
45+
)
46+
2547
add_header_library(
2648
mutex
2749
HDRS
2850
mutex.h
2951
DEPENDS
3052
.futex_utils
53+
.raw_mutex
3154
libc.src.__support.threads.mutex_common
3255
)
3356

libc/src/__support/threads/linux/mutex.h

Lines changed: 50 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -9,114 +9,81 @@
99
#ifndef LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_MUTEX_H
1010
#define LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_MUTEX_H
1111

12+
#include "hdr/types/pid_t.h"
13+
#include "src/__support/CPP/optional.h"
1214
#include "src/__support/threads/linux/futex_utils.h"
15+
#include "src/__support/threads/linux/raw_mutex.h"
1316
#include "src/__support/threads/mutex_common.h"
1417

1518
namespace LIBC_NAMESPACE {
16-
struct Mutex {
17-
unsigned char timed;
19+
20+
// TODO: support shared/recursive/robust mutexes.
21+
class Mutex final : private internal::RawMutex {
22+
// reserved timed, may be useful when combined with other flags.
23+
[[maybe_unused]] unsigned char timed;
1824
unsigned char recursive;
1925
unsigned char robust;
26+
unsigned char pshared;
2027

21-
void *owner;
28+
// TLS address may not work across forked processes. Use thead id instead.
29+
pid_t owner;
2230
unsigned long long lock_count;
2331

24-
Futex futex_word;
25-
2632
enum class LockState : FutexWordType {
27-
Free,
28-
Locked,
29-
Waiting,
33+
Free = UNLOCKED,
34+
Locked = LOCKED,
35+
Waiting = CONTENTED,
3036
};
3137

3238
public:
33-
constexpr Mutex(bool istimed, bool isrecursive, bool isrobust)
34-
: timed(istimed), recursive(isrecursive), robust(isrobust),
35-
owner(nullptr), lock_count(0),
36-
futex_word(FutexWordType(LockState::Free)) {}
37-
38-
static MutexError init(Mutex *mutex, bool istimed, bool isrecur,
39-
bool isrobust) {
40-
mutex->timed = istimed;
39+
LIBC_INLINE constexpr Mutex(bool is_timed, bool is_recursive, bool is_robust,
40+
bool is_pshared)
41+
: internal::RawMutex(), timed(is_timed), recursive(is_recursive),
42+
robust(is_robust), pshared(is_pshared), owner(0), lock_count(0) {}
43+
44+
LIBC_INLINE static MutexError init(Mutex *mutex, bool is_timed, bool isrecur,
45+
bool isrobust, bool is_pshared) {
46+
internal::RawMutex::init(mutex);
47+
mutex->timed = is_timed;
4148
mutex->recursive = isrecur;
4249
mutex->robust = isrobust;
43-
mutex->owner = nullptr;
50+
mutex->pshared = is_pshared;
51+
mutex->owner = 0;
4452
mutex->lock_count = 0;
45-
mutex->futex_word.set(FutexWordType(LockState::Free));
4653
return MutexError::NONE;
4754
}
4855

49-
static MutexError destroy(Mutex *) { return MutexError::NONE; }
50-
51-
MutexError reset();
52-
53-
MutexError lock() {
54-
bool was_waiting = false;
55-
while (true) {
56-
FutexWordType mutex_status = FutexWordType(LockState::Free);
57-
FutexWordType locked_status = FutexWordType(LockState::Locked);
56+
LIBC_INLINE static MutexError destroy(Mutex *) { return MutexError::NONE; }
5857

59-
if (futex_word.compare_exchange_strong(
60-
mutex_status, FutexWordType(LockState::Locked))) {
61-
if (was_waiting)
62-
futex_word = FutexWordType(LockState::Waiting);
63-
return MutexError::NONE;
64-
}
65-
66-
switch (LockState(mutex_status)) {
67-
case LockState::Waiting:
68-
// If other threads are waiting already, then join them. Note that the
69-
// futex syscall will block if the futex data is still
70-
// `LockState::Waiting` (the 4th argument to the syscall function
71-
// below.)
72-
futex_word.wait(FutexWordType(LockState::Waiting));
73-
was_waiting = true;
74-
// Once woken up/unblocked, try everything all over.
75-
continue;
76-
case LockState::Locked:
77-
// Mutex has been locked by another thread so set the status to
78-
// LockState::Waiting.
79-
if (futex_word.compare_exchange_strong(
80-
locked_status, FutexWordType(LockState::Waiting))) {
81-
// If we are able to set the futex data to `LockState::Waiting`, then
82-
// we will wait for the futex to be woken up. Note again that the
83-
// following syscall will block only if the futex data is still
84-
// `LockState::Waiting`.
85-
futex_word.wait(FutexWordType(LockState::Waiting));
86-
was_waiting = true;
87-
}
88-
continue;
89-
case LockState::Free:
90-
// If it was LockState::Free, we shouldn't be here at all.
91-
return MutexError::BAD_LOCK_STATE;
92-
}
93-
}
58+
LIBC_INLINE MutexError lock() {
59+
this->internal::RawMutex::lock(
60+
/* timeout */ cpp::nullopt,
61+
/* is_pshared */ this->pshared,
62+
/* spin_count */ LIBC_COPT_RAW_MUTEX_DEFAULT_SPIN_COUNT);
63+
// TODO: record owner and lock count.
64+
return MutexError::NONE;
9465
}
9566

96-
MutexError unlock() {
97-
while (true) {
98-
FutexWordType mutex_status = FutexWordType(LockState::Waiting);
99-
if (futex_word.compare_exchange_strong(mutex_status,
100-
FutexWordType(LockState::Free))) {
101-
// If any thread is waiting to be woken up, then do it.
102-
futex_word.notify_one();
103-
return MutexError::NONE;
104-
}
67+
LIBC_INLINE MutexError timed_lock(internal::AbsTimeout abs_time) {
68+
if (this->internal::RawMutex::lock(
69+
abs_time, /* is_shared */ this->pshared,
70+
/* spin_count */ LIBC_COPT_RAW_MUTEX_DEFAULT_SPIN_COUNT))
71+
return MutexError::NONE;
72+
return MutexError::TIMEOUT;
73+
}
10574

106-
if (mutex_status == FutexWordType(LockState::Locked)) {
107-
// If nobody was waiting at this point, just free it.
108-
if (futex_word.compare_exchange_strong(mutex_status,
109-
FutexWordType(LockState::Free)))
110-
return MutexError::NONE;
111-
} else {
112-
// This can happen, for example if some thread tries to unlock an
113-
// already free mutex.
114-
return MutexError::UNLOCK_WITHOUT_LOCK;
115-
}
116-
}
75+
LIBC_INLINE MutexError unlock() {
76+
if (this->internal::RawMutex::unlock(/* is_shared */ this->pshared))
77+
return MutexError::NONE;
78+
return MutexError::UNLOCK_WITHOUT_LOCK;
11779
}
11880

119-
MutexError trylock();
81+
LIBC_INLINE MutexError try_lock() {
82+
if (this->internal::RawMutex::try_lock())
83+
return MutexError::NONE;
84+
return MutexError::BUSY;
85+
}
86+
friend struct CndVar;
12087
};
12188

12289
} // namespace LIBC_NAMESPACE

0 commit comments

Comments
 (0)