1 //===-- tsan_rtl_mutex.cc -------------------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is a part of ThreadSanitizer (TSan), a race detector.
10 //===----------------------------------------------------------------------===//
13 #include "tsan_flags.h"
14 #include "tsan_sync.h"
15 #include "tsan_report.h"
16 #include "tsan_symbolize.h"
17 #include "tsan_platform.h"
21 void MutexCreate(ThreadState
*thr
, uptr pc
, uptr addr
,
22 bool rw
, bool recursive
, bool linker_init
) {
24 CHECK_GT(thr
->in_rtl
, 0);
25 DPrintf("#%d: MutexCreate %zx\n", thr
->tid
, addr
);
26 StatInc(thr
, StatMutexCreate
);
27 if (!linker_init
&& IsAppMem(addr
))
28 MemoryWrite1Byte(thr
, pc
, addr
);
29 SyncVar
*s
= ctx
->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
31 s
->is_recursive
= recursive
;
32 s
->is_linker_init
= linker_init
;
36 void MutexDestroy(ThreadState
*thr
, uptr pc
, uptr addr
) {
38 CHECK_GT(thr
->in_rtl
, 0);
39 DPrintf("#%d: MutexDestroy %zx\n", thr
->tid
, addr
);
40 StatInc(thr
, StatMutexDestroy
);
42 // Global mutexes not marked as LINKER_INITIALIZED
43 // cause tons of not interesting reports, so just ignore it.
44 if (IsGlobalVar(addr
))
47 SyncVar
*s
= ctx
->synctab
.GetAndRemove(thr
, pc
, addr
);
51 MemoryWrite1Byte(thr
, pc
, addr
);
52 if (flags()->report_destroy_locked
53 && s
->owner_tid
!= SyncVar::kInvalidTid
56 ScopedReport
rep(ReportTypeMutexDestroyLocked
);
59 trace
.ObtainCurrent(thr
, pc
);
61 FastState
last(s
->last_lock
);
62 RestoreStack(last
.tid(), last
.epoch(), &trace
, 0);
64 rep
.AddLocation(s
->addr
, 1);
65 OutputReport(ctx
, rep
);
67 thr
->mset
.Remove(s
->GetId());
71 void MutexLock(ThreadState
*thr
, uptr pc
, uptr addr
) {
72 CHECK_GT(thr
->in_rtl
, 0);
73 DPrintf("#%d: MutexLock %zx\n", thr
->tid
, addr
);
75 MemoryRead1Byte(thr
, pc
, addr
);
76 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
77 thr
->fast_state
.IncrementEpoch();
78 TraceAddEvent(thr
, thr
->fast_state
, EventTypeLock
, s
->GetId());
79 if (s
->owner_tid
== SyncVar::kInvalidTid
) {
80 CHECK_EQ(s
->recursion
, 0);
81 s
->owner_tid
= thr
->tid
;
82 s
->last_lock
= thr
->fast_state
.raw();
83 } else if (s
->owner_tid
== thr
->tid
) {
84 CHECK_GT(s
->recursion
, 0);
86 Printf("ThreadSanitizer WARNING: double lock\n");
87 PrintCurrentStack(thr
, pc
);
89 if (s
->recursion
== 0) {
90 StatInc(thr
, StatMutexLock
);
91 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
92 thr
->clock
.acquire(&s
->clock
);
93 StatInc(thr
, StatSyncAcquire
);
94 thr
->clock
.acquire(&s
->read_clock
);
95 StatInc(thr
, StatSyncAcquire
);
96 } else if (!s
->is_recursive
) {
97 StatInc(thr
, StatMutexRecLock
);
100 thr
->mset
.Add(s
->GetId(), true, thr
->fast_state
.epoch());
104 void MutexUnlock(ThreadState
*thr
, uptr pc
, uptr addr
) {
105 CHECK_GT(thr
->in_rtl
, 0);
106 DPrintf("#%d: MutexUnlock %zx\n", thr
->tid
, addr
);
108 MemoryRead1Byte(thr
, pc
, addr
);
109 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
110 thr
->fast_state
.IncrementEpoch();
111 TraceAddEvent(thr
, thr
->fast_state
, EventTypeUnlock
, s
->GetId());
112 if (s
->recursion
== 0) {
115 Printf("ThreadSanitizer WARNING: unlock of unlocked mutex\n");
116 PrintCurrentStack(thr
, pc
);
118 } else if (s
->owner_tid
!= thr
->tid
) {
121 Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
122 PrintCurrentStack(thr
, pc
);
126 if (s
->recursion
== 0) {
127 StatInc(thr
, StatMutexUnlock
);
128 s
->owner_tid
= SyncVar::kInvalidTid
;
129 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
130 thr
->fast_synch_epoch
= thr
->fast_state
.epoch();
131 thr
->clock
.ReleaseStore(&s
->clock
);
132 StatInc(thr
, StatSyncRelease
);
134 StatInc(thr
, StatMutexRecUnlock
);
137 thr
->mset
.Del(s
->GetId(), true);
141 void MutexReadLock(ThreadState
*thr
, uptr pc
, uptr addr
) {
142 CHECK_GT(thr
->in_rtl
, 0);
143 DPrintf("#%d: MutexReadLock %zx\n", thr
->tid
, addr
);
144 StatInc(thr
, StatMutexReadLock
);
146 MemoryRead1Byte(thr
, pc
, addr
);
147 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, false);
148 thr
->fast_state
.IncrementEpoch();
149 TraceAddEvent(thr
, thr
->fast_state
, EventTypeRLock
, s
->GetId());
150 if (s
->owner_tid
!= SyncVar::kInvalidTid
) {
151 Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n");
152 PrintCurrentStack(thr
, pc
);
154 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
155 thr
->clock
.acquire(&s
->clock
);
156 s
->last_lock
= thr
->fast_state
.raw();
157 StatInc(thr
, StatSyncAcquire
);
158 thr
->mset
.Add(s
->GetId(), false, thr
->fast_state
.epoch());
162 void MutexReadUnlock(ThreadState
*thr
, uptr pc
, uptr addr
) {
163 CHECK_GT(thr
->in_rtl
, 0);
164 DPrintf("#%d: MutexReadUnlock %zx\n", thr
->tid
, addr
);
165 StatInc(thr
, StatMutexReadUnlock
);
167 MemoryRead1Byte(thr
, pc
, addr
);
168 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
169 thr
->fast_state
.IncrementEpoch();
170 TraceAddEvent(thr
, thr
->fast_state
, EventTypeRUnlock
, s
->GetId());
171 if (s
->owner_tid
!= SyncVar::kInvalidTid
) {
172 Printf("ThreadSanitizer WARNING: read unlock of a write "
174 PrintCurrentStack(thr
, pc
);
176 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
177 thr
->fast_synch_epoch
= thr
->fast_state
.epoch();
178 thr
->clock
.release(&s
->read_clock
);
179 StatInc(thr
, StatSyncRelease
);
181 thr
->mset
.Del(s
->GetId(), false);
184 void MutexReadOrWriteUnlock(ThreadState
*thr
, uptr pc
, uptr addr
) {
185 CHECK_GT(thr
->in_rtl
, 0);
186 DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr
->tid
, addr
);
188 MemoryRead1Byte(thr
, pc
, addr
);
189 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
191 if (s
->owner_tid
== SyncVar::kInvalidTid
) {
192 // Seems to be read unlock.
194 StatInc(thr
, StatMutexReadUnlock
);
195 thr
->fast_state
.IncrementEpoch();
196 TraceAddEvent(thr
, thr
->fast_state
, EventTypeRUnlock
, s
->GetId());
197 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
198 thr
->fast_synch_epoch
= thr
->fast_state
.epoch();
199 thr
->clock
.release(&s
->read_clock
);
200 StatInc(thr
, StatSyncRelease
);
201 } else if (s
->owner_tid
== thr
->tid
) {
202 // Seems to be write unlock.
203 thr
->fast_state
.IncrementEpoch();
204 TraceAddEvent(thr
, thr
->fast_state
, EventTypeUnlock
, s
->GetId());
205 CHECK_GT(s
->recursion
, 0);
207 if (s
->recursion
== 0) {
208 StatInc(thr
, StatMutexUnlock
);
209 s
->owner_tid
= SyncVar::kInvalidTid
;
210 // FIXME: Refactor me, plz.
211 // The sequence of events is quite tricky and doubled in several places.
212 // First, it's a bug to increment the epoch w/o writing to the trace.
213 // Then, the acquire/release logic can be factored out as well.
214 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
215 thr
->fast_synch_epoch
= thr
->fast_state
.epoch();
216 thr
->clock
.ReleaseStore(&s
->clock
);
217 StatInc(thr
, StatSyncRelease
);
219 StatInc(thr
, StatMutexRecUnlock
);
221 } else if (!s
->is_broken
) {
223 Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
224 PrintCurrentStack(thr
, pc
);
226 thr
->mset
.Del(s
->GetId(), write
);
230 void Acquire(ThreadState
*thr
, uptr pc
, uptr addr
) {
231 CHECK_GT(thr
->in_rtl
, 0);
232 DPrintf("#%d: Acquire %zx\n", thr
->tid
, addr
);
233 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, false);
234 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
235 thr
->clock
.acquire(&s
->clock
);
236 StatInc(thr
, StatSyncAcquire
);
240 void AcquireGlobal(ThreadState
*thr
, uptr pc
) {
241 Context
*ctx
= CTX();
242 Lock
l(&ctx
->thread_mtx
);
243 for (unsigned i
= 0; i
< kMaxTid
; i
++) {
244 ThreadContext
*tctx
= ctx
->threads
[i
];
247 if (tctx
->status
== ThreadStatusRunning
)
248 thr
->clock
.set(i
, tctx
->thr
->fast_state
.epoch());
250 thr
->clock
.set(i
, tctx
->epoch1
);
254 void Release(ThreadState
*thr
, uptr pc
, uptr addr
) {
255 CHECK_GT(thr
->in_rtl
, 0);
256 DPrintf("#%d: Release %zx\n", thr
->tid
, addr
);
257 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
258 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
259 thr
->clock
.release(&s
->clock
);
260 StatInc(thr
, StatSyncRelease
);
264 void ReleaseStore(ThreadState
*thr
, uptr pc
, uptr addr
) {
265 CHECK_GT(thr
->in_rtl
, 0);
266 DPrintf("#%d: ReleaseStore %zx\n", thr
->tid
, addr
);
267 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
268 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
269 thr
->clock
.ReleaseStore(&s
->clock
);
270 StatInc(thr
, StatSyncRelease
);
275 void AfterSleep(ThreadState
*thr
, uptr pc
) {
276 Context
*ctx
= CTX();
277 thr
->last_sleep_stack_id
= CurrentStackId(thr
, pc
);
278 Lock
l(&ctx
->thread_mtx
);
279 for (unsigned i
= 0; i
< kMaxTid
; i
++) {
280 ThreadContext
*tctx
= ctx
->threads
[i
];
283 if (tctx
->status
== ThreadStatusRunning
)
284 thr
->last_sleep_clock
.set(i
, tctx
->thr
->fast_state
.epoch());
286 thr
->last_sleep_clock
.set(i
, tctx
->epoch1
);
291 } // namespace __tsan