Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 793bdd8

Browse files
[libc][CndVar] reimplmement conditional variable with FIFO ordering (#192748)
This PR reimplements conditional variable with two different variants: - futex-based shared condvar with atomic counter for waiters - queue-based private condvar Notice that thread-local queue node cannot be reliably accessed in shared processes, so we cannot use a unified implementation in this case. POSIX.1-2024 (Issue 8) added atomicity conditions to conditional variable: - The `pthread_cond_broadcast()` function shall, **as a single atomic operation**, determine which threads, if any, are blocked on the specified condition variable cond and unblock all of these threads. - The `pthread_cond_signal()` function shall, as a **single atomic operation**, determine which threads, if any, are blocked on the specified condition variable cond and unblock at least one of these threads. This means that threads parked after a condvar signal event shall not steal signals before it. From my read, both implementation fulfills the requirement but glibc claims that single futex does not provide the stronger ordering needed by its users hence switched to a rotational queue, to fulfill the requirements mentioned in https://sourceware.org/bugzilla/show_bug.cgi?id=13165. As in a single futex, the lock acquisition do not happen in order and hence when a spurious wakeup happen, later waiters may "steal" the signal. However musl's shared implementation and bionic's whole implementation and Rust's std condvar still stick to a signal-futex+accounting data style. Musl's private condvar and this implementation are even stronger in the sense that not only the queue is decided as an atomic event, the mutex acquisition also happens in baton-passing style. Our implementation is different from musl in the sense that we have done some spin attempts instead of always do futex syscall to requeue threads (with a new requeued state added). This gives a chance for the signal/waiter to stay in fast path if the queue is really small. Based on the microbenchmark, this implementation is generally more performant. We also abuse the `RawMutex`'s LOCKED state as "waiting for its turn". One caveat we can see from the benchmark (https://github.com/SchrodingerZhu/condvar-benchmark) is that strict FIFO condvar is not that good if users are maintain the order on themselves as in `turn_ring`, because many threads may just wake up first only see it is not its turn while blocking the real thread in turn. However, a semaphore or other synchronization primitives would be more suitable in those cases. Even though in benchmark like `turn_ring/broadcast_stress` made this code perform badly (but still similar to musl anyway), they are not really the correct usage. In other cases, we are always close to the fastest while providing a stronger FIFO semantic. TODO in future commit: - support pthread cancellation. Ideally we should block cancellation when signal is consumed and add cancellation callback for checking illegibility. - think about stronger ordering semantic in shared case (?)
1 parent ccc608f commit 793bdd8

19 files changed

Lines changed: 523 additions & 189 deletions

File tree

libc/cmake/modules/LLVMLibCCompileOptionRules.cmake

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,10 @@ function(_get_compile_options_from_config output_var)
154154
libc_add_definition(config_options "LIBC_THREAD_MODE=${LIBC_CONF_THREAD_MODE}")
155155
endif()
156156

157+
if(LIBC_CONF_TIMEOUT_ENSURE_MONOTONICITY)
158+
libc_add_definition(config_options "LIBC_COPT_TIMEOUT_ENSURE_MONOTONICITY")
159+
endif()
160+
157161
if(LIBC_CONF_TRAP_ON_RAISE_FP_EXCEPT)
158162
libc_add_definition(config_options "LIBC_TRAP_ON_RAISE_FP_EXCEPT")
159163
endif()

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ typedef struct {
1515
void *__qfront;
1616
void *__qback;
1717
__futex_word __qmtx;
18+
char __padding[4];
1819
} cnd_t;
1920

2021
#endif // LLVM_LIBC_TYPES_CND_T_H

libc/src/__support/threads/CMakeLists.txt

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,6 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.futex_utils)
3838
raw_mutex
3939
HDRS
4040
raw_mutex.h
41-
COMPILE_OPTIONS
42-
${monotonicity_flags}
4341
DEPENDS
4442
.futex_utils
4543
libc.src.__support.threads.sleep
@@ -55,7 +53,6 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.futex_utils)
5553
raw_rwlock.h
5654
COMPILE_OPTIONS
5755
${rwlock_default_spin_count}
58-
${monotonicity_flags}
5956
DEPENDS
6057
.raw_mutex
6158
libc.src.__support.common
@@ -147,12 +144,24 @@ if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.callonce)
147144
)
148145
endif()
149146

150-
if(TARGET libc.src.__support.threads.${LIBC_TARGET_OS}.CndVar)
151-
add_object_library(
147+
if(TARGET libc.src.__support.threads.futex_utils)
148+
add_header_library(
152149
CndVar
153-
ALIAS
150+
HDRS
151+
CndVar.h
154152
DEPENDS
155-
.${LIBC_TARGET_OS}.CndVar
153+
.futex_utils
154+
.mutex
155+
.mutex_common
156+
.raw_mutex
157+
libc.hdr.stdint_proxy
158+
libc.src.__support.CPP.expected
159+
libc.src.__support.CPP.limits
160+
libc.src.__support.CPP.mutex
161+
libc.src.__support.CPP.new
162+
libc.src.__support.threads.sleep
163+
libc.src.__support.time.monotonicity
164+
libc.src.__support.time.abs_timeout
156165
)
157166
endif()
158167

0 commit comments

Comments
 (0)