mirror of
https://github.com/capstone-engine/llvm-capstone.git
synced 2025-01-24 01:58:21 +00:00
tsan: fix test invisible barrier
Another attempt at fixing tsan_invisible_barrier. Current implementation causes: https://llvm.org/bugs/show_bug.cgi?id=25643 There were several unsuccessful iterations for this functionality: Initially it was implemented in user code using REAL(pthread_barrier_wait). But pthread_barrier_wait is not supported on MacOS. Futexes are linux-specific for this matter. Then we switched to atomics+usleep(10). But usleep produced parasitic "as-if synchronized via sleep" messages in reports which failed some output tests. Then we switched to atomics+sched_yield. But this produced tons of tsan- visible events, which lead to "failed to restore stack trace" failures. Move implementation into runtime and use internal_sched_yield in the wait loop. This way tsan should see no events from the barrier, so not trace overflows and no "as-if synchronized via sleep" messages. llvm-svn: 255030
This commit is contained in:
parent
d0edbdfad1
commit
eee690b29a
@ -2683,3 +2683,39 @@ void InitializeInterceptors() {
|
||||
}
|
||||
|
||||
} // namespace __tsan
|
||||
|
||||
// Invisible barrier for tests.
|
||||
// There were several unsuccessful iterations for this functionality:
|
||||
// 1. Initially it was implemented in user code using
|
||||
// REAL(pthread_barrier_wait). But pthread_barrier_wait is not supported on
|
||||
// MacOS. Futexes are linux-specific for this matter.
|
||||
// 2. Then we switched to atomics+usleep(10). But usleep produced parasitic
|
||||
// "as-if synchronized via sleep" messages in reports which failed some
|
||||
// output tests.
|
||||
// 3. Then we switched to atomics+sched_yield. But this produced tons of tsan-
|
||||
// visible events, which lead to "failed to restore stack trace" failures.
|
||||
// Note that no_sanitize_thread attribute does not turn off atomic interception
|
||||
// so attaching it to the function defined in user code does not help.
|
||||
// That's why we now have what we have.
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __tsan_testonly_barrier_init(u64 *barrier, u32 count) {
|
||||
if (count >= (1 << 8)) {
|
||||
Printf("barrier_init: count is too large (%d)\n", count);
|
||||
Die();
|
||||
}
|
||||
// 8 lsb is thread count, the remaining are count of entered threads.
|
||||
*barrier = count;
|
||||
}
|
||||
|
||||
extern "C" SANITIZER_INTERFACE_ATTRIBUTE
|
||||
void __tsan_testonly_barrier_wait(u64 *barrier) {
|
||||
unsigned old = __atomic_fetch_add(barrier, 1 << 8, __ATOMIC_RELAXED);
|
||||
unsigned old_epoch = (old >> 8) / (old & 0xff);
|
||||
for (;;) {
|
||||
unsigned cur = __atomic_load_n(barrier, __ATOMIC_RELAXED);
|
||||
unsigned cur_epoch = (cur >> 8) / (cur & 0xff);
|
||||
if (cur_epoch != old_epoch)
|
||||
return;
|
||||
internal_sched_yield();
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
// symbolized correctly.
|
||||
|
||||
// RUN: %clangxx_tsan -O1 %s -DBUILD_SO -fPIC -shared -o %t-so.so
|
||||
// RUN: %clangxx_tsan -O1 %s -o %t && %deflake %run %t | FileCheck %s
|
||||
// RUN: %clangxx_tsan -O1 %s -o %t -rdynamic && %deflake %run %t | FileCheck %s
|
||||
|
||||
#ifdef BUILD_SO
|
||||
|
||||
|
@ -14,27 +14,24 @@
|
||||
// TSan-invisible barrier.
|
||||
// Tests use it to establish necessary execution order in a way that does not
|
||||
// interfere with tsan (does not establish synchronization between threads).
|
||||
// 8 lsb is thread count, the remaining are count of entered threads.
|
||||
typedef unsigned long long invisible_barrier_t;
|
||||
|
||||
void barrier_init(invisible_barrier_t *barrier, unsigned count) {
|
||||
if (count >= (1 << 8))
|
||||
exit(fprintf(stderr, "barrier_init: count is too large (%d)\n", count));
|
||||
*barrier = count;
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
void __tsan_testonly_barrier_init(invisible_barrier_t *barrier,
|
||||
unsigned count);
|
||||
void __tsan_testonly_barrier_wait(invisible_barrier_t *barrier);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
static inline void barrier_init(invisible_barrier_t *barrier, unsigned count) {
|
||||
__tsan_testonly_barrier_init(barrier, count);
|
||||
}
|
||||
|
||||
void barrier_wait(invisible_barrier_t *barrier) {
|
||||
unsigned old = __atomic_fetch_add(barrier, 1 << 8, __ATOMIC_RELAXED);
|
||||
unsigned old_epoch = (old >> 8) / (old & 0xff);
|
||||
for (;;) {
|
||||
unsigned cur = __atomic_load_n(barrier, __ATOMIC_RELAXED);
|
||||
unsigned cur_epoch = (cur >> 8) / (cur & 0xff);
|
||||
if (cur_epoch != old_epoch)
|
||||
return;
|
||||
// Can't use usleep, because it leads to spurious "As if synchronized via
|
||||
// sleep" messages which fail some output tests.
|
||||
sched_yield();
|
||||
}
|
||||
static inline void barrier_wait(invisible_barrier_t *barrier) {
|
||||
__tsan_testonly_barrier_wait(barrier);
|
||||
}
|
||||
|
||||
// Default instance of the barrier, but a test can declare more manually.
|
||||
|
Loading…
x
Reference in New Issue
Block a user