Skip to content

Commit 97795be

Browse files
committed
tsan: optimize test-only barrier
The updated lots_of_threads.c test with 300 threads started running for too long on machines with low hardware parallelism (e.g. taskset -c 0-1). On lots of CPUs it finishes in ~2 secs. But with taskset -c 0-1 it runs for hundreds of seconds effectively spinning in the barrier in the sleep loop. We now have the handy futex API in sanitizer_common. Use it instead of the passive spin loop. It makes the test run only faster with taskset -c 0-1, it runs for ~1.5 secs, while with full parallelism it still runs for ~2 secs (but consumes less CPU time). Depends on D107131. Reviewed By: vitalybuka Differential Revision: https://reviews.llvm.org/D107132
1 parent dbe36e4 commit 97795be

File tree

2 files changed

+24
-18
lines changed

2 files changed

+24
-18
lines changed

compiler-rt/lib/tsan/rtl/tsan_interceptors_posix.cpp

+23-17
Original file line numberDiff line numberDiff line change
@@ -2916,30 +2916,36 @@ void InitializeInterceptors() {
29162916
// Note that no_sanitize_thread attribute does not turn off atomic interception
29172917
// so attaching it to the function defined in user code does not help.
29182918
// That's why we now have what we have.
2919-
constexpr uptr kBarrierThreadBits = 10;
2920-
constexpr uptr kBarrierThreads = 1 << kBarrierThreadBits;
2919+
constexpr u32 kBarrierThreadBits = 10;
2920+
constexpr u32 kBarrierThreads = 1 << kBarrierThreadBits;
29212921

2922-
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
2923-
void __tsan_testonly_barrier_init(u64 *barrier, u32 count) {
2924-
if (count >= kBarrierThreads) {
2925-
Printf("barrier_init: count is too large (%d)\n", count);
2922+
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __tsan_testonly_barrier_init(
2923+
atomic_uint32_t *barrier, u32 num_threads) {
2924+
if (num_threads >= kBarrierThreads) {
2925+
Printf("barrier_init: count is too large (%d)\n", num_threads);
29262926
Die();
29272927
}
29282928
// kBarrierThreadBits lsb is thread count,
29292929
// the remaining are count of entered threads.
2930-
*barrier = count;
2930+
atomic_store(barrier, num_threads, memory_order_relaxed);
29312931
}
29322932

2933-
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
2934-
void __tsan_testonly_barrier_wait(u64 *barrier) {
2935-
constexpr uptr kThreadMask = kBarrierThreads - 1;
2936-
unsigned old = __atomic_fetch_add(barrier, kBarrierThreads, __ATOMIC_RELAXED);
2937-
unsigned old_epoch = (old >> kBarrierThreadBits) / (old & kThreadMask);
2933+
static u32 barrier_epoch(u32 value) {
2934+
return (value >> kBarrierThreadBits) / (value & (kBarrierThreads - 1));
2935+
}
2936+
2937+
extern "C" SANITIZER_INTERFACE_ATTRIBUTE void __tsan_testonly_barrier_wait(
2938+
atomic_uint32_t *barrier) {
2939+
u32 old = atomic_fetch_add(barrier, kBarrierThreads, memory_order_relaxed);
2940+
u32 old_epoch = barrier_epoch(old);
2941+
if (barrier_epoch(old + kBarrierThreads) != old_epoch) {
2942+
FutexWake(barrier, (1 << 30));
2943+
return;
2944+
}
29382945
for (;;) {
2939-
unsigned cur = __atomic_load_n(barrier, __ATOMIC_RELAXED);
2940-
unsigned cur_epoch = (cur >> kBarrierThreadBits) / (cur & kThreadMask);
2941-
if (cur_epoch != old_epoch)
2942-
break;
2943-
internal_usleep(100);
2946+
u32 cur = atomic_load(barrier, memory_order_relaxed);
2947+
if (barrier_epoch(cur) != old_epoch)
2948+
return;
2949+
FutexWait(barrier, cur);
29442950
}
29452951
}

compiler-rt/test/tsan/test.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
// TSan-invisible barrier.
1818
// Tests use it to establish necessary execution order in a way that does not
1919
// interfere with tsan (does not establish synchronization between threads).
20-
typedef unsigned long long invisible_barrier_t;
20+
typedef unsigned invisible_barrier_t;
2121

2222
#ifdef __cplusplus
2323
extern "C" {

0 commit comments

Comments
 (0)