SlotPairLocker calls SlotLock under ctx->multi_slot_mtx. SlotLock can invoke global reset DoReset if we are out of slots/epochs. But DoReset locks ctx->multi_slot_mtx as well, which leads to deadlock. Resolve the deadlock by removing SlotPairLocker/multi_slot_mtx and only lock one slot for which we will do RestoreStack. We need to lock that slot because RestoreStack accesses the slot journal. But it's unclear why we need to lock the current slot. Initially I did it just to be on the safer side (but at that time we dit not lock the second slot, so it was easy just to lock the current slot). Reviewed By: melver Differential Revision: https://reviews.llvm.org/D116040
95 lines
2.6 KiB
C++
95 lines
2.6 KiB
C++
// This run stresses global reset happenning concurrently with everything else.
|
|
// RUN: %clangxx_tsan -O1 %s -o %t && %env_tsan_opts=flush_memory_ms=1:flush_symbolizer_ms=1:memory_limit_mb=1 %run %t 2>&1 | FileCheck %s --check-prefix=CHECK-NORACE
|
|
// This run stresses race reporting happenning concurrently with everything else.
|
|
// RUN: %clangxx_tsan -O1 %s -DRACE=1 -o %t && %env_tsan_opts=suppress_equal_stacks=0:suppress_equal_addresses=0 %deflake %run %t | FileCheck %s --check-prefix=CHECK-RACE
|
|
#include "test.h"
|
|
#include <fcntl.h>
|
|
#include <string.h>
|
|
|
|
volatile long stop;
|
|
long atomic, read_only, racy;
|
|
int fds[2];
|
|
|
|
__attribute__((noinline)) void *SecondaryThread(void *x) {
|
|
__atomic_fetch_add(&atomic, 1, __ATOMIC_ACQ_REL);
|
|
return NULL;
|
|
}
|
|
|
|
void *Thread(void *x) {
|
|
const int me = (long)x;
|
|
volatile long sink = 0;
|
|
while (!stop) {
|
|
// If me == 0, we do all of the following,
|
|
// otherwise only 1 type of action.
|
|
if (me == 0 || me == 1) {
|
|
// just read the stop variable
|
|
}
|
|
if (me == 0 || me == 2) {
|
|
__atomic_store_n(&atomic, sink, __ATOMIC_RELEASE);
|
|
}
|
|
if (me == 0 || me == 3) {
|
|
sink += __atomic_fetch_add(&atomic, 1, __ATOMIC_ACQ_REL);
|
|
}
|
|
if (me == 0 || me == 4) {
|
|
SecondaryThread(NULL);
|
|
}
|
|
if (me == 0 || me == 5) {
|
|
write(fds[1], fds, 1);
|
|
}
|
|
if (me == 0 || me == 6) {
|
|
char buf[2];
|
|
read(fds[0], &buf, sizeof(buf));
|
|
}
|
|
if (me == 0 || me == 7) {
|
|
pthread_t th;
|
|
pthread_create(&th, NULL, SecondaryThread, NULL);
|
|
pthread_join(th, NULL);
|
|
}
|
|
if (me == 0 || me == 8) {
|
|
long buf;
|
|
memcpy(&buf, &read_only, sizeof(buf));
|
|
sink += buf;
|
|
}
|
|
if (me == 0 || me == 9) {
|
|
#if RACE
|
|
sink += racy++;
|
|
#else
|
|
sink += racy;
|
|
#endif
|
|
}
|
|
// If you add more actions, update kActions in main.
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int main() {
|
|
ANNOTATE_BENIGN_RACE(stop);
|
|
if (pipe(fds))
|
|
exit((perror("pipe"), 1));
|
|
if (fcntl(fds[0], F_SETFL, O_NONBLOCK))
|
|
exit((perror("fcntl"), 1));
|
|
if (fcntl(fds[1], F_SETFL, O_NONBLOCK))
|
|
exit((perror("fcntl"), 1));
|
|
const int kActions = 10;
|
|
#if RACE
|
|
const int kMultiplier = 1;
|
|
#else
|
|
const int kMultiplier = 4;
|
|
#endif
|
|
pthread_t t[kActions * kMultiplier];
|
|
for (int i = 0; i < kActions * kMultiplier; i++)
|
|
pthread_create(&t[i], NULL, Thread, (void *)(long)(i % kActions));
|
|
sleep(5);
|
|
stop = 1;
|
|
for (int i = 0; i < kActions * kMultiplier; i++)
|
|
pthread_join(t[i], NULL);
|
|
fprintf(stderr, "DONE\n");
|
|
return 0;
|
|
}
|
|
|
|
// CHECK-NORACE-NOT: ThreadSanitizer:
|
|
// CHECK-NORACE: DONE
|
|
// CHECK-NORACE-NOT: ThreadSanitizer:
|
|
// CHECK-RACE: ThreadSanitizer: data race
|
|
// CHECK-RACE: DONE
|