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