xref: /netbsd-src/external/gpl3/gcc.old/dist/libsanitizer/tsan/tsan_mutex.cc (revision 9573673d78c64ea1eac42d7f2e9521be89932ae5)
1 //===-- tsan_mutex.cc -----------------------------------------------------===//
2 //
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
5 //
6 //===----------------------------------------------------------------------===//
7 //
8 // This file is a part of ThreadSanitizer (TSan), a race detector.
9 //
10 //===----------------------------------------------------------------------===//
11 #include "sanitizer_common/sanitizer_libc.h"
12 #include "tsan_mutex.h"
13 #include "tsan_platform.h"
14 #include "tsan_rtl.h"
15 
16 namespace __tsan {
17 
18 // Simple reader-writer spin-mutex. Optimized for not-so-contended case.
19 // Readers have preference, can possibly starvate writers.
20 
21 // The table fixes what mutexes can be locked under what mutexes.
22 // E.g. if the row for MutexTypeThreads contains MutexTypeReport,
23 // then Report mutex can be locked while under Threads mutex.
24 // The leaf mutexes can be locked under any other mutexes.
25 // Recursive locking is not supported.
26 #if TSAN_DEBUG && !TSAN_GO
27 const MutexType MutexTypeLeaf = (MutexType)-1;
28 static MutexType CanLockTab[MutexTypeCount][MutexTypeCount] = {
29   /*0  MutexTypeInvalid*/     {},
30   /*1  MutexTypeTrace*/       {MutexTypeLeaf},
31   /*2  MutexTypeThreads*/     {MutexTypeReport},
32   /*3  MutexTypeReport*/      {MutexTypeSyncTab, MutexTypeMBlock,
33                                MutexTypeJavaMBlock},
34   /*4  MutexTypeSyncVar*/     {},
35   /*5  MutexTypeSyncTab*/     {MutexTypeSyncVar},
36   /*6  MutexTypeSlab*/        {MutexTypeLeaf},
37   /*7  MutexTypeAnnotations*/ {},
38   /*8  MutexTypeAtExit*/      {MutexTypeSyncTab},
39   /*9  MutexTypeMBlock*/      {MutexTypeSyncVar},
40   /*10 MutexTypeJavaMBlock*/  {MutexTypeSyncVar},
41 };
42 
43 static bool CanLockAdj[MutexTypeCount][MutexTypeCount];
44 #endif
45 
46 void InitializeMutex() {
47 #if TSAN_DEBUG && !TSAN_GO
48   // Build the "can lock" adjacency matrix.
49   // If [i][j]==true, then one can lock mutex j while under mutex i.
50   const int N = MutexTypeCount;
51   int cnt[N] = {};
52   bool leaf[N] = {};
53   for (int i = 1; i < N; i++) {
54     for (int j = 0; j < N; j++) {
55       MutexType z = CanLockTab[i][j];
56       if (z == MutexTypeInvalid)
57         continue;
58       if (z == MutexTypeLeaf) {
59         CHECK(!leaf[i]);
60         leaf[i] = true;
61         continue;
62       }
63       CHECK(!CanLockAdj[i][(int)z]);
64       CanLockAdj[i][(int)z] = true;
65       cnt[i]++;
66     }
67   }
68   for (int i = 0; i < N; i++) {
69     CHECK(!leaf[i] || cnt[i] == 0);
70   }
71   // Add leaf mutexes.
72   for (int i = 0; i < N; i++) {
73     if (!leaf[i])
74       continue;
75     for (int j = 0; j < N; j++) {
76       if (i == j || leaf[j] || j == MutexTypeInvalid)
77         continue;
78       CHECK(!CanLockAdj[j][i]);
79       CanLockAdj[j][i] = true;
80     }
81   }
82   // Build the transitive closure.
83   bool CanLockAdj2[MutexTypeCount][MutexTypeCount];
84   for (int i = 0; i < N; i++) {
85     for (int j = 0; j < N; j++) {
86       CanLockAdj2[i][j] = CanLockAdj[i][j];
87     }
88   }
89   for (int k = 0; k < N; k++) {
90     for (int i = 0; i < N; i++) {
91       for (int j = 0; j < N; j++) {
92         if (CanLockAdj2[i][k] && CanLockAdj2[k][j]) {
93           CanLockAdj2[i][j] = true;
94         }
95       }
96     }
97   }
98 #if 0
99   Printf("Can lock graph:\n");
100   for (int i = 0; i < N; i++) {
101     for (int j = 0; j < N; j++) {
102       Printf("%d ", CanLockAdj[i][j]);
103     }
104     Printf("\n");
105   }
106   Printf("Can lock graph closure:\n");
107   for (int i = 0; i < N; i++) {
108     for (int j = 0; j < N; j++) {
109       Printf("%d ", CanLockAdj2[i][j]);
110     }
111     Printf("\n");
112   }
113 #endif
114   // Verify that the graph is acyclic.
115   for (int i = 0; i < N; i++) {
116     if (CanLockAdj2[i][i]) {
117       Printf("Mutex %d participates in a cycle\n", i);
118       Die();
119     }
120   }
121 #endif
122 }
123 
124 DeadlockDetector::DeadlockDetector() {
125   // Rely on zero initialization because some mutexes can be locked before ctor.
126 }
127 
128 #if TSAN_DEBUG && !TSAN_GO
129 void DeadlockDetector::Lock(MutexType t) {
130   // Printf("LOCK %d @%zu\n", t, seq_ + 1);
131   CHECK_GT(t, MutexTypeInvalid);
132   CHECK_LT(t, MutexTypeCount);
133   u64 max_seq = 0;
134   u64 max_idx = MutexTypeInvalid;
135   for (int i = 0; i != MutexTypeCount; i++) {
136     if (locked_[i] == 0)
137       continue;
138     CHECK_NE(locked_[i], max_seq);
139     if (max_seq < locked_[i]) {
140       max_seq = locked_[i];
141       max_idx = i;
142     }
143   }
144   locked_[t] = ++seq_;
145   if (max_idx == MutexTypeInvalid)
146     return;
147   // Printf("  last %d @%zu\n", max_idx, max_seq);
148   if (!CanLockAdj[max_idx][t]) {
149     Printf("ThreadSanitizer: internal deadlock detected\n");
150     Printf("ThreadSanitizer: can't lock %d while under %zu\n",
151                t, (uptr)max_idx);
152     CHECK(0);
153   }
154 }
155 
156 void DeadlockDetector::Unlock(MutexType t) {
157   // Printf("UNLO %d @%zu #%zu\n", t, seq_, locked_[t]);
158   CHECK(locked_[t]);
159   locked_[t] = 0;
160 }
161 #endif
162 
163 const uptr kUnlocked = 0;
164 const uptr kWriteLock = 1;
165 const uptr kReadLock = 2;
166 
167 class Backoff {
168  public:
169   Backoff()
170     : iter_() {
171   }
172 
173   bool Do() {
174     if (iter_++ < kActiveSpinIters)
175       proc_yield(kActiveSpinCnt);
176     else
177       internal_sched_yield();
178     return true;
179   }
180 
181   u64 Contention() const {
182     u64 active = iter_ % kActiveSpinIters;
183     u64 passive = iter_ - active;
184     return active + 10 * passive;
185   }
186 
187  private:
188   int iter_;
189   static const int kActiveSpinIters = 10;
190   static const int kActiveSpinCnt = 20;
191 };
192 
193 Mutex::Mutex(MutexType type, StatType stat_type) {
194   CHECK_GT(type, MutexTypeInvalid);
195   CHECK_LT(type, MutexTypeCount);
196 #if TSAN_DEBUG
197   type_ = type;
198 #endif
199 #if TSAN_COLLECT_STATS
200   stat_type_ = stat_type;
201 #endif
202   atomic_store(&state_, kUnlocked, memory_order_relaxed);
203 }
204 
205 Mutex::~Mutex() {
206   CHECK_EQ(atomic_load(&state_, memory_order_relaxed), kUnlocked);
207 }
208 
209 void Mutex::Lock() {
210 #if TSAN_DEBUG && !TSAN_GO
211   cur_thread()->deadlock_detector.Lock(type_);
212 #endif
213   uptr cmp = kUnlocked;
214   if (atomic_compare_exchange_strong(&state_, &cmp, kWriteLock,
215                                      memory_order_acquire))
216     return;
217   for (Backoff backoff; backoff.Do();) {
218     if (atomic_load(&state_, memory_order_relaxed) == kUnlocked) {
219       cmp = kUnlocked;
220       if (atomic_compare_exchange_weak(&state_, &cmp, kWriteLock,
221                                        memory_order_acquire)) {
222 #if TSAN_COLLECT_STATS
223         StatInc(cur_thread(), stat_type_, backoff.Contention());
224 #endif
225         return;
226       }
227     }
228   }
229 }
230 
231 void Mutex::Unlock() {
232   uptr prev = atomic_fetch_sub(&state_, kWriteLock, memory_order_release);
233   (void)prev;
234   DCHECK_NE(prev & kWriteLock, 0);
235 #if TSAN_DEBUG && !TSAN_GO
236   cur_thread()->deadlock_detector.Unlock(type_);
237 #endif
238 }
239 
240 void Mutex::ReadLock() {
241 #if TSAN_DEBUG && !TSAN_GO
242   cur_thread()->deadlock_detector.Lock(type_);
243 #endif
244   uptr prev = atomic_fetch_add(&state_, kReadLock, memory_order_acquire);
245   if ((prev & kWriteLock) == 0)
246     return;
247   for (Backoff backoff; backoff.Do();) {
248     prev = atomic_load(&state_, memory_order_acquire);
249     if ((prev & kWriteLock) == 0) {
250 #if TSAN_COLLECT_STATS
251       StatInc(cur_thread(), stat_type_, backoff.Contention());
252 #endif
253       return;
254     }
255   }
256 }
257 
258 void Mutex::ReadUnlock() {
259   uptr prev = atomic_fetch_sub(&state_, kReadLock, memory_order_release);
260   (void)prev;
261   DCHECK_EQ(prev & kWriteLock, 0);
262   DCHECK_GT(prev & ~kWriteLock, 0);
263 #if TSAN_DEBUG && !TSAN_GO
264   cur_thread()->deadlock_detector.Unlock(type_);
265 #endif
266 }
267 
268 void Mutex::CheckLocked() {
269   CHECK_NE(atomic_load(&state_, memory_order_relaxed), 0);
270 }
271 
272 }  // namespace __tsan
273