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