summaryrefslogtreecommitdiff
path: root/test/tsan/real_deadlock_detector_stress_test.cc
diff options
context:
space:
mode:
authorDmitry Vyukov <dvyukov@google.com>2014-12-18 10:32:48 +0000
committerDmitry Vyukov <dvyukov@google.com>2014-12-18 10:32:48 +0000
commit8119ff6c27eae24b8d2dec89bdec237f48b1a15b (patch)
treef92c92957562b1b1dde834ad0229774c1c85152c /test/tsan/real_deadlock_detector_stress_test.cc
parentc4c3dfde97c7e76d978babced98688848da01c35 (diff)
tsan: add a stress test for deadlock detector
currently disabled because fails git-svn-id: https://llvm.org/svn/llvm-project/compiler-rt/trunk@224509 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/tsan/real_deadlock_detector_stress_test.cc')
-rw-r--r--test/tsan/real_deadlock_detector_stress_test.cc188
1 files changed, 188 insertions, 0 deletions
diff --git a/test/tsan/real_deadlock_detector_stress_test.cc b/test/tsan/real_deadlock_detector_stress_test.cc
new file mode 100644
index 000000000..29b70fcf2
--- /dev/null
+++ b/test/tsan/real_deadlock_detector_stress_test.cc
@@ -0,0 +1,188 @@
+// RUN: %clang_tsan -O1 %s -o %t && %run %t 2>&1 | FileCheck %s
+#include <pthread.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <time.h>
+#include <errno.h>
+#include <vector>
+#include <algorithm>
+
+const int kThreads = 32;
+const int kMutexes = 32 << 10;
+const int kIters = 100 << 10;
+const int kMaxPerThread = 10;
+
+const int kStateInited = 0;
+const int kStateNotInited = -1;
+const int kStateLocked = -2;
+
+struct Mutex {
+ int state;
+ pthread_rwlock_t m;
+};
+
+Mutex mtx[kMutexes];
+
+void check(int res) {
+ if (res != 0) {
+ printf("SOMETHING HAS FAILED\n");
+ exit(1);
+ }
+}
+
+bool cas(int *a, int oldval, int newval) {
+ return __atomic_compare_exchange_n(a, &oldval, newval, false,
+ __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
+}
+
+void *Thread(void *seed) {
+ unsigned rnd = (unsigned)(unsigned long)seed;
+ int err;
+ std::vector<int> locked;
+ for (int i = 0; i < kIters; i++) {
+ int what = rand_r(&rnd) % 10;
+ if (what < 4 && locked.size() < kMaxPerThread) {
+ // lock
+ int max_locked = -1;
+ if (!locked.empty()) {
+ max_locked = *std::max_element(locked.begin(), locked.end());
+ if (max_locked == kMutexes - 1) {
+ i--;
+ continue;
+ }
+ }
+ int id = (rand_r(&rnd) % (kMutexes - max_locked - 1)) + max_locked + 1;
+ Mutex *m = &mtx[id];
+ // init the mutex if necessary or acquire a reference
+ for (;;) {
+ int old = __atomic_load_n(&m->state, __ATOMIC_RELAXED);
+ if (old == kStateLocked) {
+ pthread_yield();
+ continue;
+ }
+ int newv = old + 1;
+ if (old == kStateNotInited)
+ newv = kStateLocked;
+ if (cas(&m->state, old, newv)) {
+ if (old == kStateNotInited) {
+ if ((err = pthread_rwlock_init(&m->m, 0))) {
+ fprintf(stderr, "pthread_rwlock_init failed with %d\n", err);
+ exit(1);
+ }
+ if (!cas(&m->state, kStateLocked, 1)) {
+ fprintf(stderr, "init commit failed\n");
+ exit(1);
+ }
+ }
+ break;
+ }
+ }
+ // now we have an inited and referenced mutex, choose what to do
+ bool failed = false;
+ switch (rand_r(&rnd) % 4) {
+ case 0:
+ if ((err = pthread_rwlock_wrlock(&m->m))) {
+ fprintf(stderr, "pthread_rwlock_wrlock failed with %d\n", err);
+ exit(1);
+ }
+ break;
+ case 1:
+ if ((err = pthread_rwlock_rdlock(&m->m))) {
+ fprintf(stderr, "pthread_rwlock_rdlock failed with %d\n", err);
+ exit(1);
+ }
+ break;
+ case 2:
+ err = pthread_rwlock_trywrlock(&m->m);
+ if (err != 0 && err != EBUSY) {
+ fprintf(stderr, "pthread_rwlock_trywrlock failed with %d\n", err);
+ exit(1);
+ }
+ failed = err == EBUSY;
+ break;
+ case 3:
+ err = pthread_rwlock_tryrdlock(&m->m);
+ if (err != 0 && err != EBUSY) {
+ fprintf(stderr, "pthread_rwlock_tryrdlock failed with %d\n", err);
+ exit(1);
+ }
+ failed = err == EBUSY;
+ break;
+ }
+ if (failed) {
+ if (__atomic_fetch_sub(&m->state, 1, __ATOMIC_ACQ_REL) <= 0) {
+ fprintf(stderr, "failed to unref after failed trylock\n");
+ exit(1);
+ }
+ continue;
+ }
+ locked.push_back(id);
+ } else if (what < 9 && !locked.empty()) {
+ // unlock
+ int pos = rand_r(&rnd) % locked.size();
+ int id = locked[pos];
+ locked[pos] = locked[locked.size() - 1];
+ locked.pop_back();
+ Mutex *m = &mtx[id];
+ if ((err = pthread_rwlock_unlock(&m->m))) {
+ fprintf(stderr, "pthread_rwlock_unlock failed with %d\n", err);
+ exit(1);
+ }
+ if (__atomic_fetch_sub(&m->state, 1, __ATOMIC_ACQ_REL) <= 0) {
+ fprintf(stderr, "failed to unref after unlock\n");
+ exit(1);
+ }
+ } else {
+ // Destroy a random mutex.
+ int id = rand_r(&rnd) % kMutexes;
+ Mutex *m = &mtx[id];
+ if (!cas(&m->state, kStateInited, kStateLocked)) {
+ i--;
+ continue;
+ }
+ if ((err = pthread_rwlock_destroy(&m->m))) {
+ fprintf(stderr, "pthread_rwlock_destroy failed with %d\n", err);
+ exit(1);
+ }
+ if (!cas(&m->state, kStateLocked, kStateNotInited)) {
+ fprintf(stderr, "destroy commit failed\n");
+ exit(1);
+ }
+ }
+ }
+ // Unlock all previously locked mutexes, otherwise other threads can deadlock.
+ for (int i = 0; i < locked.size(); i++) {
+ int id = locked[i];
+ Mutex *m = &mtx[id];
+ if ((err = pthread_rwlock_unlock(&m->m))) {
+ fprintf(stderr, "pthread_rwlock_unlock failed with %d\n", err);
+ exit(1);
+ }
+ }
+ return 0;
+}
+
+int main() {
+ // Currently fails with:
+ // CHECK failed: bv_.clearBit(lock_id)
+ fprintf(stderr, "DONE\n");
+ return 0;
+
+ int s = time(0);
+ fprintf(stderr, "seed %d\n", s);
+ srand(s);
+ for (int i = 0; i < kMutexes; i++)
+ mtx[i].state = kStateNotInited;
+ pthread_t t[kThreads];
+ for (int i = 0; i < kThreads; i++)
+ pthread_create(&t[i], 0, Thread, (void*)(unsigned long)rand());
+ for (int i = 0; i < kThreads; i++)
+ pthread_join(t[i], 0);
+ fprintf(stderr, "DONE\n");
+ return 0;
+}
+
+// CHECK-NOT: WARNING: ThreadSanitizer
+// CHECK: DONE
+