Skip to content

Commit a5ee818

Browse files
author
Siva Chandra Reddy
committed
[libc][NFC] Add a platform independent thread support library.
The idea is that, other parts of the libc which require thread/lock support will be able to use this platform independent setup. With this change, only the linux implementation of a mutex type has been moved to the new library. Because of this, there is some duplication between the new library and src/threads/linux. A follow up change will move all of src/threads/linux to the new library. The duplication should be eliminated with that move. Reviewed By: lntue Differential Revision: https://reviews.llvm.org/D120795
1 parent 7e1355e commit a5ee818

File tree

17 files changed

+298
-204
lines changed

17 files changed

+298
-204
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
add_header(__bsearchcompare_t HDR __bsearchcompare_t.h)
22
add_header(__call_once_func_t HDR __call_once_func_t.h)
33
add_header(__futex_word HDR __futex_word.h)
4+
add_header(__mutex_type HDR __mutex_type.h)
45
add_header(__qsortcompare_t HDR __qsortcompare_t.h)
56
add_header(__sighandler_t HDR __sighandler_t.h)
67
add_header(cnd_t HDR cnd_t.h)
@@ -14,7 +15,7 @@ add_header(fexcept_t HDR fexcept_t.h)
1415
add_header(float_t HDR float_t.h)
1516
add_header(imaxdiv_t HDR imaxdiv_t.h)
1617
add_header(mode_t HDR mode_t.h)
17-
add_header(mtx_t HDR mtx_t.h DEPENDS .__futex_word)
18+
add_header(mtx_t HDR mtx_t.h DEPENDS .__futex_word .__mutex_type)
1819
add_header(off_t HDR off_t.h)
1920
add_header(once_flag HDR once_flag.h DEPENDS .__futex_word)
2021
add_header(size_t HDR size_t.h)
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
//===-- Definition of a common mutex 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+
#include <llvm-libc-types/__futex_word.h>
10+
11+
typedef struct {
12+
unsigned char __timed;
13+
unsigned char __recursive;
14+
unsigned char __robust;
15+
16+
void *__owner;
17+
unsigned long long __lock_count;
18+
19+
#ifdef __unix__
20+
__futex_word __ftxw;
21+
#else
22+
#error "Mutex type not defined for the target platform."
23+
#endif
24+
} __mutex_type;

libc/include/llvm-libc-types/mtx_t.h

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,8 @@
99
#ifndef __LLVM_LIBC_TYPES_MTX_T_H__
1010
#define __LLVM_LIBC_TYPES_MTX_T_H__
1111

12-
#include <llvm-libc-types/__futex_word.h>
12+
#include <llvm-libc-types/__mutex_type.h>
1313

14-
typedef struct {
15-
#ifdef __unix__
16-
__futex_word __ftxw;
17-
#else
18-
#error "mtx_t type not defined for the target platform."
19-
#endif
20-
int __mtx_type;
21-
} mtx_t;
14+
typedef __mutex_type mtx_t;
2215

2316
#endif // __LLVM_LIBC_TYPES_MTX_T_H__

libc/src/__support/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,3 +57,4 @@ add_header_library(
5757
add_subdirectory(File)
5858
add_subdirectory(FPUtil)
5959
add_subdirectory(OSUtil)
60+
add_subdirectory(threads)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/${LIBC_TARGET_OS})
2+
add_subdirectory(${LIBC_TARGET_OS})
3+
endif()
4+
5+
add_header_library(
6+
thread
7+
HDRS
8+
mutex.h
9+
DEPS
10+
.${LIBC_TARGET_OS}.thread
11+
)
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
add_header_library(
2+
thread
3+
HDRS
4+
mutex.h
5+
DEPENDS
6+
libc.include.sys_syscall
7+
libc.src.__support.CPP.atomic
8+
libc.src.__support.OSUtil.osutil
9+
)
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
#ifndef LLVM_LIBC_SRC_SUPPORT_THREAD_LINUX_MUTEX_H
2+
#define LLVM_LIBC_SRC_SUPPORT_THREAD_LINUX_MUTEX_H
3+
4+
#include "include/sys/syscall.h" // For syscall numbers.
5+
#include "src/__support/CPP/atomic.h"
6+
#include "src/__support/OSUtil/syscall.h" // For syscall functions.
7+
#include "src/__support/threads/mutex.h"
8+
9+
#include <linux/futex.h>
10+
#include <stdint.h>
11+
#include <threads.h>
12+
13+
namespace __llvm_libc {
14+
15+
struct Mutex {
16+
unsigned char timed;
17+
unsigned char recursive;
18+
unsigned char robust;
19+
20+
void *owner;
21+
unsigned long long lock_count;
22+
23+
using FutexWordType = unsigned int;
24+
25+
cpp::Atomic<FutexWordType> futex_word;
26+
27+
enum class LockState : FutexWordType {
28+
Free,
29+
Locked,
30+
Waiting,
31+
};
32+
33+
public:
34+
constexpr Mutex(bool istimed, bool isrecursive, bool isrobust)
35+
: timed(istimed), recursive(isrecursive), robust(isrobust),
36+
owner(nullptr), lock_count(0),
37+
futex_word(FutexWordType(LockState::Free)) {}
38+
39+
static MutexError init(Mutex *mutex, bool istimed, bool isrecur,
40+
bool isrobust) {
41+
mutex->timed = istimed;
42+
mutex->recursive = isrecur;
43+
mutex->robust = isrobust;
44+
mutex->owner = nullptr;
45+
mutex->lock_count = 0;
46+
mutex->futex_word.set(FutexWordType(LockState::Free));
47+
return MutexError::NONE;
48+
}
49+
50+
static MutexError init(mtx_t *m, bool istimed, bool isrecur, bool isrobust) {
51+
auto *mutex = reinterpret_cast<Mutex *>(m);
52+
return init(mutex, istimed, isrecur, isrobust);
53+
}
54+
55+
static MutexError destroy(mtx_t *) { return MutexError::NONE; }
56+
57+
MutexError reset();
58+
59+
MutexError lock() {
60+
bool was_waiting = false;
61+
while (true) {
62+
FutexWordType mutex_status = FutexWordType(LockState::Free);
63+
FutexWordType locked_status = FutexWordType(LockState::Locked);
64+
65+
if (futex_word.compare_exchange_strong(
66+
mutex_status, FutexWordType(LockState::Locked))) {
67+
if (was_waiting)
68+
futex_word = FutexWordType(LockState::Waiting);
69+
return MutexError::NONE;
70+
}
71+
72+
switch (LockState(mutex_status)) {
73+
case LockState::Waiting:
74+
// If other threads are waiting already, then join them. Note that the
75+
// futex syscall will block if the futex data is still
76+
// `LockState::Waiting` (the 4th argument to the syscall function
77+
// below.)
78+
__llvm_libc::syscall(SYS_futex, &futex_word.val, FUTEX_WAIT_PRIVATE,
79+
FutexWordType(LockState::Waiting), 0, 0, 0);
80+
was_waiting = true;
81+
// Once woken up/unblocked, try everything all over.
82+
continue;
83+
case LockState::Locked:
84+
// Mutex has been locked by another thread so set the status to
85+
// LockState::Waiting.
86+
if (futex_word.compare_exchange_strong(
87+
locked_status, FutexWordType(LockState::Waiting))) {
88+
// If we are able to set the futex data to `LockState::Waiting`, then
89+
// we will wait for the futex to be woken up. Note again that the
90+
// following syscall will block only if the futex data is still
91+
// `LockState::Waiting`.
92+
__llvm_libc::syscall(SYS_futex, &futex_word, FUTEX_WAIT_PRIVATE,
93+
FutexWordType(LockState::Waiting), 0, 0, 0);
94+
was_waiting = true;
95+
}
96+
continue;
97+
case LockState::Free:
98+
// If it was LockState::Free, we shouldn't be here at all.
99+
[[clang::fallthrough]];
100+
default:
101+
// Mutex status cannot be anything else. So control should not reach
102+
// here at all.
103+
return MutexError::BAD_LOCK_STATE;
104+
}
105+
}
106+
}
107+
108+
MutexError unlock() {
109+
while (true) {
110+
FutexWordType mutex_status = FutexWordType(LockState::Waiting);
111+
if (futex_word.compare_exchange_strong(mutex_status,
112+
FutexWordType(LockState::Free))) {
113+
// If any thread is waiting to be woken up, then do it.
114+
__llvm_libc::syscall(SYS_futex, &futex_word, FUTEX_WAKE_PRIVATE, 1, 0,
115+
0, 0);
116+
return MutexError::NONE;
117+
}
118+
119+
if (mutex_status == FutexWordType(LockState::Locked)) {
120+
// If nobody was waiting at this point, just free it.
121+
if (futex_word.compare_exchange_strong(mutex_status,
122+
FutexWordType(LockState::Free)))
123+
return MutexError::NONE;
124+
} else {
125+
// This can happen, for example if some thread tries to unlock an
126+
// already free mutex.
127+
return MutexError::UNLOCK_WITHOUT_LOCK;
128+
}
129+
}
130+
}
131+
132+
MutexError trylock();
133+
};
134+
135+
} // namespace __llvm_libc
136+
137+
#endif // LLVM_LIBC_SRC_SUPPORT_THREAD_LINUX_MUTEX_H

libc/src/__support/threads/mutex.h

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#ifndef LLVM_LIBC_SRC_SUPPORT_THREAD_MUTEX_H
2+
#define LLVM_LIBC_SRC_SUPPORT_THREAD_MUTEX_H
3+
4+
namespace __llvm_libc {
5+
6+
enum class MutexError : int {
7+
NONE,
8+
BUSY,
9+
TIMEOUT,
10+
UNLOCK_WITHOUT_LOCK,
11+
BAD_LOCK_STATE,
12+
};
13+
14+
} // namespace __llvm_libc
15+
16+
// Platform independent code will include this header file which pulls
17+
// the platfrom specific specializations using platform macros.
18+
//
19+
// The platform specific specializations should define a class by name
20+
// Mutex with non-static methods having the following signature:
21+
//
22+
// MutexError lock();
23+
// MutexError trylock();
24+
// MutexError timedlock(...);
25+
// MutexError unlock();
26+
// MutexError reset(); // Used to reset inconsistent robust mutexes.
27+
//
28+
// Apart from the above non-static methods, the specializations should
29+
// also provide few static methods with the following signature:
30+
//
31+
// static MutexError init(mtx_t *);
32+
// static MutexError destroy(mtx_t *);
33+
//
34+
// All of the static and non-static methods should ideally be implemented
35+
// as inline functions so that implementations of public functions can
36+
// call them without a function call overhead.
37+
//
38+
// Another point to keep in mind that is that the libc internally needs a
39+
// few global locks. So, to avoid static initialization order fiasco, we
40+
// want the constructors of the Mutex classes to be constexprs.
41+
42+
#ifdef __unix__
43+
#include "linux/mutex.h"
44+
#endif // __unix__
45+
46+
namespace __llvm_libc {
47+
48+
static_assert(sizeof(Mutex) <= sizeof(mtx_t),
49+
"The public mtx_t type cannot accommodate the internal mutex "
50+
"type.");
51+
52+
// An RAII class for easy locking and unlocking of mutexes.
53+
class MutexLock {
54+
Mutex *mutex;
55+
56+
public:
57+
explicit MutexLock(Mutex *m) : mutex(m) { mutex->lock(); }
58+
59+
~MutexLock() { mutex->unlock(); }
60+
};
61+
62+
} // namespace __llvm_libc
63+
64+
#endif // LLVM_LIBC_SRC_SUPPORT_THREAD_MUTEX_H

libc/src/threads/CMakeLists.txt

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,30 +25,46 @@ add_entrypoint_object(
2525

2626
add_entrypoint_object(
2727
mtx_init
28-
ALIAS
28+
SRCS
29+
mtx_init.cpp
30+
HDRS
31+
mtx_init.h
2932
DEPENDS
30-
.${LIBC_TARGET_OS}.mtx_init
33+
libc.include.threads
34+
libc.src.__support.threads.thread
3135
)
3236

3337
add_entrypoint_object(
3438
mtx_destroy
35-
ALIAS
39+
SRCS
40+
mtx_destroy.cpp
41+
HDRS
42+
mtx_destroy.h
3643
DEPENDS
37-
.${LIBC_TARGET_OS}.mtx_destroy
44+
libc.include.threads
45+
libc.src.__support.threads.thread
3846
)
3947

4048
add_entrypoint_object(
4149
mtx_lock
42-
ALIAS
50+
SRCS
51+
mtx_lock.cpp
52+
HDRS
53+
mtx_lock.h
4354
DEPENDS
44-
.${LIBC_TARGET_OS}.mtx_lock
55+
libc.include.threads
56+
libc.src.__support.threads.thread
4557
)
4658

4759
add_entrypoint_object(
4860
mtx_unlock
49-
ALIAS
61+
SRCS
62+
mtx_unlock.cpp
63+
HDRS
64+
mtx_unlock.h
5065
DEPENDS
51-
.${LIBC_TARGET_OS}.mtx_unlock
66+
libc.include.threads
67+
libc.src.__support.threads.thread
5268
)
5369

5470
add_entrypoint_object(

0 commit comments

Comments
 (0)