Skip to content

Commit 5442e15

Browse files
[libc][__support] move CndVar to __support (llvm#89329)
We should be able to reuse this between the implementation of C11 cnd_t condition variables and POSIX pthread_cond_t condition variables. The current implementation is hyper linux specific, making use of Futex. That obviously wont work outside of linux, so split the OS specific functions off into their own source outside of the header. Modifies the return values of the to-be-shared impl to return 0 on success and -1 on error. This pattern was shamelessly stolen from Bionic's [__bionic_thrd_error](https://android.googlesource.com/platform/bionic/+/refs/heads/main/libc/include/bits/threads_inlines.h#41). Fixes: llvm#88580 Link: llvm#88583
1 parent b3e71ec commit 5442e15

File tree

11 files changed

+215
-169
lines changed

11 files changed

+215
-169
lines changed

libc/src/__support/threads/CMakeLists.txt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,3 +71,12 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.callonce)
7171
.${LIBC_TARGET_OS}.callonce
7272
)
7373
endif()
74+
75+
if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.CndVar)
76+
add_object_library(
77+
CndVar
78+
ALIAS
79+
DEPENDS
80+
.${LIBC_TARGET_OS}.CndVar
81+
)
82+
endif()

libc/src/__support/threads/CndVar.h

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
//===-- A platform independent abstraction layer for cond vars --*- 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___SUPPORT_SRC_THREADS_LINUX_CNDVAR_H
10+
#define LLVM_LIBC___SUPPORT_SRC_THREADS_LINUX_CNDVAR_H
11+
12+
#include "src/__support/threads/linux/futex_utils.h" // Futex
13+
#include "src/__support/threads/mutex.h" // Mutex
14+
15+
#include <stdint.h> // uint32_t
16+
17+
namespace LIBC_NAMESPACE {
18+
19+
struct CndVar {
20+
enum CndWaiterStatus : uint32_t {
21+
WS_Waiting = 0xE,
22+
WS_Signalled = 0x5,
23+
};
24+
25+
struct CndWaiter {
26+
Futex futex_word = WS_Waiting;
27+
CndWaiter *next = nullptr;
28+
};
29+
30+
CndWaiter *waitq_front;
31+
CndWaiter *waitq_back;
32+
Mutex qmtx;
33+
34+
static int init(CndVar *cv) {
35+
cv->waitq_front = cv->waitq_back = nullptr;
36+
auto err = Mutex::init(&cv->qmtx, false, false, false);
37+
return err == MutexError::NONE ? 0 : -1;
38+
}
39+
40+
static void destroy(CndVar *cv) {
41+
cv->waitq_front = cv->waitq_back = nullptr;
42+
}
43+
44+
// Returns 0 on success, -1 on error.
45+
int wait(Mutex *m);
46+
void notify_one();
47+
void broadcast();
48+
};
49+
50+
} // namespace LIBC_NAMESPACE
51+
52+
#endif // LLVM_LIBC_SRC___SUPPORT_THREADS_LINUX_CNDVAR_H

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,3 +63,16 @@ add_object_library(
6363
DEPENDS
6464
.futex_utils
6565
)
66+
67+
add_object_library(
68+
CndVar
69+
SRCS
70+
CndVar.cpp
71+
HDRS
72+
../CndVar.h
73+
DEPENDS
74+
libc.include.sys_syscall
75+
libc.src.__support.OSUtil.osutil
76+
libc.src.__support.threads.linux.futex_word_type
77+
libc.src.__support.threads.mutex
78+
)
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
//===-- Utility condition variable class ------------------------*- 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+
#include "src/__support/threads/CndVar.h"
10+
#include "src/__support/OSUtil/syscall.h" // syscall_impl
11+
#include "src/__support/threads/linux/futex_word.h" // FutexWordType
12+
#include "src/__support/threads/mutex.h" // Mutex, MutexLock
13+
14+
#include <sys/syscall.h> // For syscall numbers.
15+
16+
namespace LIBC_NAMESPACE {
17+
18+
int CndVar::wait(Mutex *m) {
19+
// The goal is to perform "unlock |m| and wait" in an
20+
// atomic operation. However, it is not possible to do it
21+
// in the true sense so we do it in spirit. Before unlocking
22+
// |m|, a new waiter object is added to the waiter queue with
23+
// the waiter queue locked. Iff a signalling thread signals
24+
// the waiter before the waiter actually starts waiting, the
25+
// wait operation will not begin at all and the waiter immediately
26+
// returns.
27+
28+
CndWaiter waiter;
29+
{
30+
MutexLock ml(&qmtx);
31+
CndWaiter *old_back = nullptr;
32+
if (waitq_front == nullptr) {
33+
waitq_front = waitq_back = &waiter;
34+
} else {
35+
old_back = waitq_back;
36+
waitq_back->next = &waiter;
37+
waitq_back = &waiter;
38+
}
39+
40+
if (m->unlock() != MutexError::NONE) {
41+
// If we do not remove the queued up waiter before returning,
42+
// then another thread can potentially signal a non-existing
43+
// waiter. Note also that we do this with |qmtx| locked. This
44+
// ensures that another thread will not signal the withdrawing
45+
// waiter.
46+
waitq_back = old_back;
47+
if (waitq_back == nullptr)
48+
waitq_front = nullptr;
49+
else
50+
waitq_back->next = nullptr;
51+
52+
return -1;
53+
}
54+
}
55+
56+
waiter.futex_word.wait(WS_Waiting, cpp::nullopt, true);
57+
58+
// At this point, if locking |m| fails, we can simply return as the
59+
// queued up waiter would have been removed from the queue.
60+
auto err = m->lock();
61+
return err == MutexError::NONE ? 0 : -1;
62+
}
63+
64+
void CndVar::notify_one() {
65+
// We don't use an RAII locker in this method as we want to unlock
66+
// |qmtx| and signal the waiter using a single FUTEX_WAKE_OP signal.
67+
qmtx.lock();
68+
if (waitq_front == nullptr)
69+
qmtx.unlock();
70+
71+
CndWaiter *first = waitq_front;
72+
waitq_front = waitq_front->next;
73+
if (waitq_front == nullptr)
74+
waitq_back = nullptr;
75+
76+
qmtx.futex_word = FutexWordType(Mutex::LockState::Free);
77+
78+
// this is a special WAKE_OP, so we use syscall directly
79+
LIBC_NAMESPACE::syscall_impl<long>(
80+
FUTEX_SYSCALL_ID, &qmtx.futex_word.val, FUTEX_WAKE_OP, 1, 1,
81+
&first->futex_word.val,
82+
FUTEX_OP(FUTEX_OP_SET, WS_Signalled, FUTEX_OP_CMP_EQ, WS_Waiting));
83+
}
84+
85+
void CndVar::broadcast() {
86+
MutexLock ml(&qmtx);
87+
uint32_t dummy_futex_word;
88+
CndWaiter *waiter = waitq_front;
89+
waitq_front = waitq_back = nullptr;
90+
while (waiter != nullptr) {
91+
// FUTEX_WAKE_OP is used instead of just FUTEX_WAKE as it allows us to
92+
// atomically update the waiter status to WS_Signalled before waking
93+
// up the waiter. A dummy location is used for the other futex of
94+
// FUTEX_WAKE_OP.
95+
LIBC_NAMESPACE::syscall_impl<long>(
96+
FUTEX_SYSCALL_ID, &dummy_futex_word, FUTEX_WAKE_OP, 1, 1,
97+
&waiter->futex_word.val,
98+
FUTEX_OP(FUTEX_OP_SET, WS_Signalled, FUTEX_OP_CMP_EQ, WS_Waiting));
99+
waiter = waiter->next;
100+
}
101+
}
102+
103+
} // namespace LIBC_NAMESPACE

libc/src/threads/linux/CMakeLists.txt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
add_header_library(
22
threads_utils
33
HDRS
4-
CndVar.h
54
Futex.h
65
DEPENDS
76
libc.include.sys_syscall
@@ -20,8 +19,8 @@ add_entrypoint_object(
2019
HDRS
2120
../cnd_init.h
2221
DEPENDS
23-
.threads_utils
2422
libc.include.threads
23+
libc.src.__support.threads.CndVar
2524
)
2625

2726
add_entrypoint_object(
@@ -31,8 +30,8 @@ add_entrypoint_object(
3130
HDRS
3231
../cnd_destroy.h
3332
DEPENDS
34-
.threads_utils
3533
libc.include.threads
34+
libc.src.__support.threads.CndVar
3635
)
3736

3837
add_entrypoint_object(
@@ -42,9 +41,9 @@ add_entrypoint_object(
4241
HDRS
4342
../cnd_wait.h
4443
DEPENDS
45-
.threads_utils
4644
libc.include.threads
4745
libc.src.__support.threads.mutex
46+
libc.src.__support.threads.CndVar
4847
)
4948

5049
add_entrypoint_object(
@@ -54,8 +53,8 @@ add_entrypoint_object(
5453
HDRS
5554
../cnd_signal.h
5655
DEPENDS
57-
.threads_utils
5856
libc.include.threads
57+
libc.src.__support.threads.CndVar
5958
)
6059

6160
add_entrypoint_object(
@@ -65,6 +64,6 @@ add_entrypoint_object(
6564
HDRS
6665
../cnd_broadcast.h
6766
DEPENDS
68-
.threads_utils
6967
libc.include.threads
68+
libc.src.__support.threads.CndVar
7069
)

libc/src/threads/linux/CndVar.h

Lines changed: 0 additions & 148 deletions
This file was deleted.

0 commit comments

Comments
 (0)