llvm-project/compiler-rt/test/tsan/cxa_guard_acquire.cpp
Dan Blackwell 1c094a1ce2
[TSan][Test-Only] Account for race in cxa_guard_acquire.cpp test (#165853)
It is possible for "Enter potentially blocking region" to come before
"Enter constructor" in this test - if the thread that acquires the guard
fails to reach its printf before the other thread that enters the
blocking region reaches its own printf. Note that for the exit logs this
inversion is not possible.

This patch addresses this by allowing those two log lines to come in
either order.

rdar://163375661
2025-11-03 14:59:49 +00:00

104 lines
2.8 KiB
C++

// RUN: %clangxx_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
#include "test.h"
#include <assert.h>
#include <sanitizer/tsan_interface.h>
#include <stdio.h>
// We only enter a potentially blocking region on thread contention. To reliably
// trigger this, we force the initialization function to block until another
// thread has entered the potentially blocking region.
static bool init_done = false;
namespace __tsan {
#if (__APPLE__)
__attribute__((weak))
#endif
void OnPotentiallyBlockingRegionBegin() {
assert(!init_done);
printf("Enter potentially blocking region\n");
// Signal the other thread to finish initialization.
barrier_wait(&barrier);
}
#if (__APPLE__)
__attribute__((weak))
#endif
void OnPotentiallyBlockingRegionEnd() {
printf("Exit potentially blocking region\n");
}
} // namespace __tsan
struct LazyInit {
LazyInit() {
assert(!init_done);
printf("Enter constructor\n");
// Wait for the other thread to get to the blocking region.
barrier_wait(&barrier);
printf("Exit constructor\n");
}
};
const LazyInit &get_lazy_init() {
static const LazyInit lazy_init;
return lazy_init;
}
void *thread(void *arg) {
get_lazy_init();
return nullptr;
}
struct LazyInit2 {
LazyInit2() { printf("Enter constructor 2\n"); }
};
const LazyInit2 &get_lazy_init2() {
static const LazyInit2 lazy_init2;
return lazy_init2;
}
int main(int argc, char **argv) {
// CHECK: Enter main
printf("Enter main\n");
// If initialization is contended, the blocked thread should enter a
// potentially blocking region. Note that we use a DAG check because it is
// possible for Thread 1 to acquire the guard, then Thread 2 fail to acquire
// the guard then call `OnPotentiallyBlockingRegionBegin` and print "Enter
// potentially blocking region\n", before Thread 1 manages to reach "Enter
// constructor\n". This is exceptionally rare, but can be replicated by
// inserting a `sleep(1)` between `LazyInit() {` and `printf("Enter
// constructor\n");`. Due to the barrier it is not possible for the exit logs
// to be inverted.
//
// CHECK-DAG: Enter constructor
// CHECK-DAG: Enter potentially blocking region
// CHECK-NEXT: Exit constructor
// CHECK-NEXT: Exit potentially blocking region
barrier_init(&barrier, 2);
pthread_t th1, th2;
pthread_create(&th1, nullptr, thread, nullptr);
pthread_create(&th2, nullptr, thread, nullptr);
pthread_join(th1, nullptr);
pthread_join(th2, nullptr);
// Now that the value has been initialized, subsequent calls should not enter
// a potentially blocking region.
init_done = true;
get_lazy_init();
// If uncontended, there is no potentially blocking region.
//
// CHECK-NEXT: Enter constructor 2
get_lazy_init2();
get_lazy_init2();
// CHECK-NEXT: Exit main
printf("Exit main\n");
return 0;
}