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 Lock
l(&ctx
->thread_mtx
);
57 ScopedReport
rep(ReportTypeMutexDestroyLocked
);
60 trace
.ObtainCurrent(thr
, pc
);
62 FastState
last(s
->last_lock
);
63 RestoreStack(last
.tid(), last
.epoch(), &trace
, 0);
65 rep
.AddLocation(s
->addr
, 1);
66 OutputReport(ctx
, rep
);
68 thr
->mset
.Remove(s
->GetId());
72 void MutexLock(ThreadState
*thr
, uptr pc
, uptr addr
) {
73 CHECK_GT(thr
->in_rtl
, 0);
74 DPrintf("#%d: MutexLock %zx\n", thr
->tid
, addr
);
76 MemoryRead1Byte(thr
, pc
, addr
);
77 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
78 thr
->fast_state
.IncrementEpoch();
79 TraceAddEvent(thr
, thr
->fast_state
, EventTypeLock
, s
->GetId());
80 if (s
->owner_tid
== SyncVar::kInvalidTid
) {
81 CHECK_EQ(s
->recursion
, 0);
82 s
->owner_tid
= thr
->tid
;
83 s
->last_lock
= thr
->fast_state
.raw();
84 } else if (s
->owner_tid
== thr
->tid
) {
85 CHECK_GT(s
->recursion
, 0);
87 Printf("ThreadSanitizer WARNING: double lock\n");
88 PrintCurrentStack(thr
, pc
);
90 if (s
->recursion
== 0) {
91 StatInc(thr
, StatMutexLock
);
92 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
93 thr
->clock
.acquire(&s
->clock
);
94 StatInc(thr
, StatSyncAcquire
);
95 thr
->clock
.acquire(&s
->read_clock
);
96 StatInc(thr
, StatSyncAcquire
);
97 } else if (!s
->is_recursive
) {
98 StatInc(thr
, StatMutexRecLock
);
101 thr
->mset
.Add(s
->GetId(), true, thr
->fast_state
.epoch());
105 void MutexUnlock(ThreadState
*thr
, uptr pc
, uptr addr
) {
106 CHECK_GT(thr
->in_rtl
, 0);
107 DPrintf("#%d: MutexUnlock %zx\n", thr
->tid
, addr
);
109 MemoryRead1Byte(thr
, pc
, addr
);
110 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
111 thr
->fast_state
.IncrementEpoch();
112 TraceAddEvent(thr
, thr
->fast_state
, EventTypeUnlock
, s
->GetId());
113 if (s
->recursion
== 0) {
116 Printf("ThreadSanitizer WARNING: unlock of unlocked mutex\n");
117 PrintCurrentStack(thr
, pc
);
119 } else if (s
->owner_tid
!= thr
->tid
) {
122 Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
123 PrintCurrentStack(thr
, pc
);
127 if (s
->recursion
== 0) {
128 StatInc(thr
, StatMutexUnlock
);
129 s
->owner_tid
= SyncVar::kInvalidTid
;
130 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
131 thr
->fast_synch_epoch
= thr
->fast_state
.epoch();
132 thr
->clock
.ReleaseStore(&s
->clock
);
133 StatInc(thr
, StatSyncRelease
);
135 StatInc(thr
, StatMutexRecUnlock
);
138 thr
->mset
.Del(s
->GetId(), true);
142 void MutexReadLock(ThreadState
*thr
, uptr pc
, uptr addr
) {
143 CHECK_GT(thr
->in_rtl
, 0);
144 DPrintf("#%d: MutexReadLock %zx\n", thr
->tid
, addr
);
145 StatInc(thr
, StatMutexReadLock
);
147 MemoryRead1Byte(thr
, pc
, addr
);
148 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, false);
149 thr
->fast_state
.IncrementEpoch();
150 TraceAddEvent(thr
, thr
->fast_state
, EventTypeRLock
, s
->GetId());
151 if (s
->owner_tid
!= SyncVar::kInvalidTid
) {
152 Printf("ThreadSanitizer WARNING: read lock of a write locked mutex\n");
153 PrintCurrentStack(thr
, pc
);
155 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
156 thr
->clock
.acquire(&s
->clock
);
157 s
->last_lock
= thr
->fast_state
.raw();
158 StatInc(thr
, StatSyncAcquire
);
159 thr
->mset
.Add(s
->GetId(), false, thr
->fast_state
.epoch());
163 void MutexReadUnlock(ThreadState
*thr
, uptr pc
, uptr addr
) {
164 CHECK_GT(thr
->in_rtl
, 0);
165 DPrintf("#%d: MutexReadUnlock %zx\n", thr
->tid
, addr
);
166 StatInc(thr
, StatMutexReadUnlock
);
168 MemoryRead1Byte(thr
, pc
, addr
);
169 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
170 thr
->fast_state
.IncrementEpoch();
171 TraceAddEvent(thr
, thr
->fast_state
, EventTypeRUnlock
, s
->GetId());
172 if (s
->owner_tid
!= SyncVar::kInvalidTid
) {
173 Printf("ThreadSanitizer WARNING: read unlock of a write "
175 PrintCurrentStack(thr
, pc
);
177 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
178 thr
->fast_synch_epoch
= thr
->fast_state
.epoch();
179 thr
->clock
.release(&s
->read_clock
);
180 StatInc(thr
, StatSyncRelease
);
182 thr
->mset
.Del(s
->GetId(), false);
185 void MutexReadOrWriteUnlock(ThreadState
*thr
, uptr pc
, uptr addr
) {
186 CHECK_GT(thr
->in_rtl
, 0);
187 DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr
->tid
, addr
);
189 MemoryRead1Byte(thr
, pc
, addr
);
190 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
192 if (s
->owner_tid
== SyncVar::kInvalidTid
) {
193 // Seems to be read unlock.
195 StatInc(thr
, StatMutexReadUnlock
);
196 thr
->fast_state
.IncrementEpoch();
197 TraceAddEvent(thr
, thr
->fast_state
, EventTypeRUnlock
, s
->GetId());
198 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
199 thr
->fast_synch_epoch
= thr
->fast_state
.epoch();
200 thr
->clock
.release(&s
->read_clock
);
201 StatInc(thr
, StatSyncRelease
);
202 } else if (s
->owner_tid
== thr
->tid
) {
203 // Seems to be write unlock.
204 thr
->fast_state
.IncrementEpoch();
205 TraceAddEvent(thr
, thr
->fast_state
, EventTypeUnlock
, s
->GetId());
206 CHECK_GT(s
->recursion
, 0);
208 if (s
->recursion
== 0) {
209 StatInc(thr
, StatMutexUnlock
);
210 s
->owner_tid
= SyncVar::kInvalidTid
;
211 // FIXME: Refactor me, plz.
212 // The sequence of events is quite tricky and doubled in several places.
213 // First, it's a bug to increment the epoch w/o writing to the trace.
214 // Then, the acquire/release logic can be factored out as well.
215 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
216 thr
->fast_synch_epoch
= thr
->fast_state
.epoch();
217 thr
->clock
.ReleaseStore(&s
->clock
);
218 StatInc(thr
, StatSyncRelease
);
220 StatInc(thr
, StatMutexRecUnlock
);
222 } else if (!s
->is_broken
) {
224 Printf("ThreadSanitizer WARNING: mutex unlock by another thread\n");
225 PrintCurrentStack(thr
, pc
);
227 thr
->mset
.Del(s
->GetId(), write
);
231 void Acquire(ThreadState
*thr
, uptr pc
, uptr addr
) {
232 CHECK_GT(thr
->in_rtl
, 0);
233 DPrintf("#%d: Acquire %zx\n", thr
->tid
, addr
);
234 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, false);
235 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
236 thr
->clock
.acquire(&s
->clock
);
237 StatInc(thr
, StatSyncAcquire
);
241 void AcquireGlobal(ThreadState
*thr
, uptr pc
) {
242 Context
*ctx
= CTX();
243 Lock
l(&ctx
->thread_mtx
);
244 for (unsigned i
= 0; i
< kMaxTid
; i
++) {
245 ThreadContext
*tctx
= ctx
->threads
[i
];
248 if (tctx
->status
== ThreadStatusRunning
)
249 thr
->clock
.set(i
, tctx
->thr
->fast_state
.epoch());
251 thr
->clock
.set(i
, tctx
->epoch1
);
255 void Release(ThreadState
*thr
, uptr pc
, uptr addr
) {
256 CHECK_GT(thr
->in_rtl
, 0);
257 DPrintf("#%d: Release %zx\n", thr
->tid
, addr
);
258 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
259 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
260 thr
->clock
.release(&s
->clock
);
261 StatInc(thr
, StatSyncRelease
);
265 void ReleaseStore(ThreadState
*thr
, uptr pc
, uptr addr
) {
266 CHECK_GT(thr
->in_rtl
, 0);
267 DPrintf("#%d: ReleaseStore %zx\n", thr
->tid
, addr
);
268 SyncVar
*s
= CTX()->synctab
.GetOrCreateAndLock(thr
, pc
, addr
, true);
269 thr
->clock
.set(thr
->tid
, thr
->fast_state
.epoch());
270 thr
->clock
.ReleaseStore(&s
->clock
);
271 StatInc(thr
, StatSyncRelease
);
276 void AfterSleep(ThreadState
*thr
, uptr pc
) {
277 Context
*ctx
= CTX();
278 thr
->last_sleep_stack_id
= CurrentStackId(thr
, pc
);
279 Lock
l(&ctx
->thread_mtx
);
280 for (unsigned i
= 0; i
< kMaxTid
; i
++) {
281 ThreadContext
*tctx
= ctx
->threads
[i
];
284 if (tctx
->status
== ThreadStatusRunning
)
285 thr
->last_sleep_clock
.set(i
, tctx
->thr
->fast_state
.epoch());
287 thr
->last_sleep_clock
.set(i
, tctx
->epoch1
);
292 } // namespace __tsan