xref: /llvm-project/compiler-rt/test/tsan/real_deadlock_detector_stress_test.cpp (revision bcaeed49cb063de9fe504aa29e1cadff8a7be710)
1*bcaeed49SFangrui Song // RUN: %clangxx_tsan -O1 %s %link_libcxx_tsan -o %t && %run %t 2>&1 | FileCheck %s
2*bcaeed49SFangrui Song 
3*bcaeed49SFangrui Song #include <pthread.h>
4*bcaeed49SFangrui Song #include <stdlib.h>
5*bcaeed49SFangrui Song #include <stdio.h>
6*bcaeed49SFangrui Song #include <unistd.h>
7*bcaeed49SFangrui Song #include <time.h>
8*bcaeed49SFangrui Song #include <errno.h>
9*bcaeed49SFangrui Song #include <vector>
10*bcaeed49SFangrui Song #include <algorithm>
11*bcaeed49SFangrui Song #include <sys/time.h>
12*bcaeed49SFangrui Song 
13*bcaeed49SFangrui Song const int kThreads = 4;
14*bcaeed49SFangrui Song const int kMutexes = 16 << 10;
15*bcaeed49SFangrui Song const int kIters = 400 << 10;
16*bcaeed49SFangrui Song const int kMaxPerThread = 10;
17*bcaeed49SFangrui Song 
18*bcaeed49SFangrui Song const int kStateInited = 0;
19*bcaeed49SFangrui Song const int kStateNotInited = -1;
20*bcaeed49SFangrui Song const int kStateLocked = -2;
21*bcaeed49SFangrui Song 
22*bcaeed49SFangrui Song struct Mutex {
23*bcaeed49SFangrui Song   int state;
24*bcaeed49SFangrui Song   pthread_rwlock_t m;
25*bcaeed49SFangrui Song };
26*bcaeed49SFangrui Song 
27*bcaeed49SFangrui Song Mutex mtx[kMutexes];
28*bcaeed49SFangrui Song 
check(int res)29*bcaeed49SFangrui Song void check(int res) {
30*bcaeed49SFangrui Song   if (res != 0) {
31*bcaeed49SFangrui Song     printf("SOMETHING HAS FAILED\n");
32*bcaeed49SFangrui Song     exit(1);
33*bcaeed49SFangrui Song   }
34*bcaeed49SFangrui Song }
35*bcaeed49SFangrui Song 
cas(int * a,int oldval,int newval)36*bcaeed49SFangrui Song bool cas(int *a, int oldval, int newval) {
37*bcaeed49SFangrui Song   return __atomic_compare_exchange_n(a, &oldval, newval, false,
38*bcaeed49SFangrui Song       __ATOMIC_ACQ_REL, __ATOMIC_RELAXED);
39*bcaeed49SFangrui Song }
40*bcaeed49SFangrui Song 
Thread(void * seed)41*bcaeed49SFangrui Song void *Thread(void *seed) {
42*bcaeed49SFangrui Song   unsigned rnd = (unsigned)(unsigned long)seed;
43*bcaeed49SFangrui Song   int err;
44*bcaeed49SFangrui Song   std::vector<int> locked;
45*bcaeed49SFangrui Song   for (int i = 0; i < kIters; i++) {
46*bcaeed49SFangrui Song     int what = rand_r(&rnd) % 10;
47*bcaeed49SFangrui Song     if (what < 4 && locked.size() < kMaxPerThread) {
48*bcaeed49SFangrui Song       // lock
49*bcaeed49SFangrui Song       int max_locked = -1;
50*bcaeed49SFangrui Song       if (!locked.empty()) {
51*bcaeed49SFangrui Song         max_locked = *std::max_element(locked.begin(), locked.end());
52*bcaeed49SFangrui Song         if (max_locked == kMutexes - 1) {
53*bcaeed49SFangrui Song           i--;
54*bcaeed49SFangrui Song           continue;
55*bcaeed49SFangrui Song         }
56*bcaeed49SFangrui Song       }
57*bcaeed49SFangrui Song       int id = (rand_r(&rnd) % (kMutexes - max_locked - 1)) + max_locked + 1;
58*bcaeed49SFangrui Song       Mutex *m = &mtx[id];
59*bcaeed49SFangrui Song       // init the mutex if necessary or acquire a reference
60*bcaeed49SFangrui Song       for (;;) {
61*bcaeed49SFangrui Song         int old = __atomic_load_n(&m->state, __ATOMIC_RELAXED);
62*bcaeed49SFangrui Song         if (old == kStateLocked) {
63*bcaeed49SFangrui Song           sched_yield();
64*bcaeed49SFangrui Song           continue;
65*bcaeed49SFangrui Song         }
66*bcaeed49SFangrui Song         int newv = old + 1;
67*bcaeed49SFangrui Song         if (old == kStateNotInited)
68*bcaeed49SFangrui Song           newv = kStateLocked;
69*bcaeed49SFangrui Song         if (cas(&m->state, old, newv)) {
70*bcaeed49SFangrui Song           if (old == kStateNotInited) {
71*bcaeed49SFangrui Song             if ((err = pthread_rwlock_init(&m->m, 0))) {
72*bcaeed49SFangrui Song               fprintf(stderr, "pthread_rwlock_init failed with %d\n", err);
73*bcaeed49SFangrui Song               exit(1);
74*bcaeed49SFangrui Song             }
75*bcaeed49SFangrui Song             if (!cas(&m->state, kStateLocked, 1)) {
76*bcaeed49SFangrui Song               fprintf(stderr, "init commit failed\n");
77*bcaeed49SFangrui Song               exit(1);
78*bcaeed49SFangrui Song             }
79*bcaeed49SFangrui Song           }
80*bcaeed49SFangrui Song           break;
81*bcaeed49SFangrui Song         }
82*bcaeed49SFangrui Song       }
83*bcaeed49SFangrui Song       // now we have an inited and referenced mutex, choose what to do
84*bcaeed49SFangrui Song       bool failed = false;
85*bcaeed49SFangrui Song       switch (rand_r(&rnd) % 4) {
86*bcaeed49SFangrui Song       case 0:
87*bcaeed49SFangrui Song         if ((err = pthread_rwlock_wrlock(&m->m))) {
88*bcaeed49SFangrui Song           fprintf(stderr, "pthread_rwlock_wrlock failed with %d\n", err);
89*bcaeed49SFangrui Song           exit(1);
90*bcaeed49SFangrui Song         }
91*bcaeed49SFangrui Song         break;
92*bcaeed49SFangrui Song       case 1:
93*bcaeed49SFangrui Song         if ((err = pthread_rwlock_rdlock(&m->m))) {
94*bcaeed49SFangrui Song           fprintf(stderr, "pthread_rwlock_rdlock failed with %d\n", err);
95*bcaeed49SFangrui Song           exit(1);
96*bcaeed49SFangrui Song         }
97*bcaeed49SFangrui Song         break;
98*bcaeed49SFangrui Song       case 2:
99*bcaeed49SFangrui Song         err = pthread_rwlock_trywrlock(&m->m);
100*bcaeed49SFangrui Song         if (err != 0 && err != EBUSY) {
101*bcaeed49SFangrui Song           fprintf(stderr, "pthread_rwlock_trywrlock failed with %d\n", err);
102*bcaeed49SFangrui Song           exit(1);
103*bcaeed49SFangrui Song         }
104*bcaeed49SFangrui Song         failed = err == EBUSY;
105*bcaeed49SFangrui Song         break;
106*bcaeed49SFangrui Song       case 3:
107*bcaeed49SFangrui Song         err = pthread_rwlock_tryrdlock(&m->m);
108*bcaeed49SFangrui Song         if (err != 0 && err != EBUSY) {
109*bcaeed49SFangrui Song           fprintf(stderr, "pthread_rwlock_tryrdlock failed with %d\n", err);
110*bcaeed49SFangrui Song           exit(1);
111*bcaeed49SFangrui Song         }
112*bcaeed49SFangrui Song         failed = err == EBUSY;
113*bcaeed49SFangrui Song         break;
114*bcaeed49SFangrui Song       }
115*bcaeed49SFangrui Song       if (failed) {
116*bcaeed49SFangrui Song         if (__atomic_fetch_sub(&m->state, 1, __ATOMIC_ACQ_REL) <= 0) {
117*bcaeed49SFangrui Song           fprintf(stderr, "failed to unref after failed trylock\n");
118*bcaeed49SFangrui Song           exit(1);
119*bcaeed49SFangrui Song         }
120*bcaeed49SFangrui Song         continue;
121*bcaeed49SFangrui Song       }
122*bcaeed49SFangrui Song       locked.push_back(id);
123*bcaeed49SFangrui Song     } else if (what < 9 && !locked.empty()) {
124*bcaeed49SFangrui Song       // unlock
125*bcaeed49SFangrui Song       int pos = rand_r(&rnd) % locked.size();
126*bcaeed49SFangrui Song       int id = locked[pos];
127*bcaeed49SFangrui Song       locked[pos] = locked[locked.size() - 1];
128*bcaeed49SFangrui Song       locked.pop_back();
129*bcaeed49SFangrui Song       Mutex *m = &mtx[id];
130*bcaeed49SFangrui Song       if ((err = pthread_rwlock_unlock(&m->m))) {
131*bcaeed49SFangrui Song         fprintf(stderr, "pthread_rwlock_unlock failed with %d\n", err);
132*bcaeed49SFangrui Song         exit(1);
133*bcaeed49SFangrui Song       }
134*bcaeed49SFangrui Song       if (__atomic_fetch_sub(&m->state, 1, __ATOMIC_ACQ_REL) <= 0) {
135*bcaeed49SFangrui Song         fprintf(stderr, "failed to unref after unlock\n");
136*bcaeed49SFangrui Song         exit(1);
137*bcaeed49SFangrui Song       }
138*bcaeed49SFangrui Song     } else {
139*bcaeed49SFangrui Song       // Destroy a random mutex.
140*bcaeed49SFangrui Song       int id = rand_r(&rnd) % kMutexes;
141*bcaeed49SFangrui Song       Mutex *m = &mtx[id];
142*bcaeed49SFangrui Song       if (!cas(&m->state, kStateInited, kStateLocked)) {
143*bcaeed49SFangrui Song         i--;
144*bcaeed49SFangrui Song         continue;
145*bcaeed49SFangrui Song       }
146*bcaeed49SFangrui Song       if ((err = pthread_rwlock_destroy(&m->m))) {
147*bcaeed49SFangrui Song         fprintf(stderr, "pthread_rwlock_destroy failed with %d\n", err);
148*bcaeed49SFangrui Song         exit(1);
149*bcaeed49SFangrui Song       }
150*bcaeed49SFangrui Song       if (!cas(&m->state, kStateLocked, kStateNotInited)) {
151*bcaeed49SFangrui Song         fprintf(stderr, "destroy commit failed\n");
152*bcaeed49SFangrui Song         exit(1);
153*bcaeed49SFangrui Song       }
154*bcaeed49SFangrui Song     }
155*bcaeed49SFangrui Song   }
156*bcaeed49SFangrui Song   // Unlock all previously locked mutexes, otherwise other threads can deadlock.
157*bcaeed49SFangrui Song   for (int i = 0; i < locked.size(); i++) {
158*bcaeed49SFangrui Song     int id = locked[i];
159*bcaeed49SFangrui Song     Mutex *m = &mtx[id];
160*bcaeed49SFangrui Song     if ((err = pthread_rwlock_unlock(&m->m))) {
161*bcaeed49SFangrui Song       fprintf(stderr, "pthread_rwlock_unlock failed with %d\n", err);
162*bcaeed49SFangrui Song       exit(1);
163*bcaeed49SFangrui Song     }
164*bcaeed49SFangrui Song   }
165*bcaeed49SFangrui Song   return 0;
166*bcaeed49SFangrui Song }
167*bcaeed49SFangrui Song 
main()168*bcaeed49SFangrui Song int main() {
169*bcaeed49SFangrui Song   struct timeval tv;
170*bcaeed49SFangrui Song   gettimeofday(&tv, NULL);
171*bcaeed49SFangrui Song   unsigned s = tv.tv_sec + tv.tv_usec;
172*bcaeed49SFangrui Song   fprintf(stderr, "seed %d\n", s);
173*bcaeed49SFangrui Song   srand(s);
174*bcaeed49SFangrui Song   for (int i = 0; i < kMutexes; i++)
175*bcaeed49SFangrui Song     mtx[i].state = kStateNotInited;
176*bcaeed49SFangrui Song   pthread_t t[kThreads];
177*bcaeed49SFangrui Song   for (int i = 0; i < kThreads; i++)
178*bcaeed49SFangrui Song     pthread_create(&t[i], 0, Thread, (void*)(unsigned long)rand());
179*bcaeed49SFangrui Song   for (int i = 0; i < kThreads; i++)
180*bcaeed49SFangrui Song     pthread_join(t[i], 0);
181*bcaeed49SFangrui Song   fprintf(stderr, "DONE\n");
182*bcaeed49SFangrui Song   return 0;
183*bcaeed49SFangrui Song }
184*bcaeed49SFangrui Song 
185*bcaeed49SFangrui Song // CHECK-NOT: WARNING: ThreadSanitizer
186*bcaeed49SFangrui Song // CHECK: DONE
187*bcaeed49SFangrui Song 
188