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