Index: lib/tsan/rtl/tsan.syms.extra =================================================================== --- lib/tsan/rtl/tsan.syms.extra +++ lib/tsan/rtl/tsan.syms.extra @@ -8,6 +8,8 @@ __tsan_unaligned* __tsan_release __tsan_acquire +__tsan_invisible_barrier_init +__tsan_invisible_barrier_wait __ubsan_* Annotate* WTFAnnotate* Index: lib/tsan/rtl/tsan_interface.h =================================================================== --- lib/tsan/rtl/tsan_interface.h +++ lib/tsan/rtl/tsan_interface.h @@ -75,6 +75,12 @@ SANITIZER_INTERFACE_ATTRIBUTE void __tsan_write_range(void *addr, unsigned long size); // NOLINT +// For testing purposes only. +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_invisible_barrier_init(void *barrier, unsigned count); +SANITIZER_INTERFACE_ATTRIBUTE +void __tsan_invisible_barrier_wait(void *barrier); + #ifdef __cplusplus } // extern "C" #endif Index: lib/tsan/rtl/tsan_platform_linux.cc =================================================================== --- lib/tsan/rtl/tsan_platform_linux.cc +++ lib/tsan/rtl/tsan_platform_linux.cc @@ -16,12 +16,14 @@ #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_LINUX || SANITIZER_FREEBSD +#include "interception/interception.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_posix.h" #include "sanitizer_common/sanitizer_procmaps.h" #include "sanitizer_common/sanitizer_stoptheworld.h" #include "sanitizer_common/sanitizer_stackdepot.h" +#include "tsan_interface.h" #include "tsan_platform.h" #include "tsan_rtl.h" #include "tsan_flags.h" @@ -337,4 +339,20 @@ } // namespace __tsan +typedef pthread_barrier_t __tsan_invisible_barrier_t; + +DECLARE_REAL(int, pthread_barrier_init, void *b, void *a, unsigned count); +DECLARE_REAL(int, pthread_barrier_wait, void *b); + +extern "C" +void __tsan_invisible_barrier_init(void *barrier, unsigned count) { + CHECK_EQ(REAL(pthread_barrier_init)(barrier, 0, count), 0); +} + +extern "C" +void __tsan_invisible_barrier_wait(void *barrier) { + int res = REAL(pthread_barrier_wait)(barrier); + CHECK(res == PTHREAD_BARRIER_SERIAL_THREAD || res == 0); +} + #endif // SANITIZER_LINUX || SANITIZER_FREEBSD Index: lib/tsan/rtl/tsan_platform_mac.cc =================================================================== --- lib/tsan/rtl/tsan_platform_mac.cc +++ lib/tsan/rtl/tsan_platform_mac.cc @@ -15,11 +15,13 @@ #include "sanitizer_common/sanitizer_platform.h" #if SANITIZER_MAC +#include "interception/interception.h" #include "sanitizer_common/sanitizer_atomic.h" #include "sanitizer_common/sanitizer_common.h" #include "sanitizer_common/sanitizer_libc.h" #include "sanitizer_common/sanitizer_posix.h" #include "sanitizer_common/sanitizer_procmaps.h" +#include "tsan_interface.h" #include "tsan_platform.h" #include "tsan_rtl.h" #include "tsan_flags.h" @@ -184,4 +186,36 @@ } // namespace __tsan +typedef struct { + pthread_mutex_t mutex; + pthread_cond_t cond; + int current_count; + int max_count; +} __tsan_invisible_barrier_t; + +extern "C" +void __tsan_invisible_barrier_init(void *barrier, unsigned max_count) { + __tsan_invisible_barrier_t *b = (__tsan_invisible_barrier_t *)barrier; + CHECK_GT(max_count, 0); + CHECK_EQ(REAL(pthread_mutex_init)(&b->mutex, 0), 0); + CHECK_EQ(REAL(pthread_cond_init)(&b->cond, 0), 0); + b->max_count = max_count; + b->current_count = 0; +} + +extern "C" +void __tsan_invisible_barrier_wait(void *barrier) { + __tsan_invisible_barrier_t *b = (__tsan_invisible_barrier_t *)barrier; + REAL(pthread_mutex_lock)(&b->mutex); + b->current_count += 1; + if (b->current_count >= b->max_count) { + b->current_count = 0; + REAL(pthread_cond_broadcast)(&b->cond); + REAL(pthread_mutex_unlock)(&b->mutex); + } else { + REAL(pthread_cond_wait)(&b->cond, &b->mutex); + REAL(pthread_mutex_unlock)(&b->mutex); + } +} + #endif // SANITIZER_MAC Index: test/tsan/test.h =================================================================== --- test/tsan/test.h +++ test/tsan/test.h @@ -5,35 +5,25 @@ #include #include -// 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). -__typeof(pthread_barrier_wait) *barrier_wait; - -void barrier_init(pthread_barrier_t *barrier, unsigned count) { -#if defined(__FreeBSD__) - static const char libpthread_name[] = "libpthread.so"; -#else - static const char libpthread_name[] = "libpthread.so.0"; -#endif +typedef struct { + char __opaque[128]; +} __tsan_invisible_barrier_t; - if (barrier_wait == 0) { - void *h = dlopen(libpthread_name, RTLD_LAZY); - if (h == 0) { - fprintf(stderr, "failed to dlopen %s, exiting\n", libpthread_name); - exit(1); - } - barrier_wait = (__typeof(barrier_wait))dlsym(h, "pthread_barrier_wait"); - if (barrier_wait == 0) { - fprintf(stderr, "failed to resolve pthread_barrier_wait, exiting\n"); - exit(1); - } - } - pthread_barrier_init(barrier, 0, count); -} +#ifdef __cplusplus +extern "C" { +#endif +void __tsan_invisible_barrier_init(__tsan_invisible_barrier_t *barrier, + unsigned count); +void __tsan_invisible_barrier_wait(__tsan_invisible_barrier_t *barrier); +#ifdef __cplusplus +} // extern "C" +#endif // Default instance of the barrier, but a test can declare more manually. -pthread_barrier_t barrier; +__tsan_invisible_barrier_t barrier; + +#define barrier_init __tsan_invisible_barrier_init +#define barrier_wait __tsan_invisible_barrier_wait void print_address(void *address) { // On FreeBSD, the %p conversion specifier works as 0x%x and thus does not match