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