1//===-- tsan_rtl_mutex.cpp ------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file is a part of ThreadSanitizer (TSan), a race detector.
10//
11//===----------------------------------------------------------------------===//
12
13#include <sanitizer_common/sanitizer_deadlock_detector_interface.h>
14#include <sanitizer_common/sanitizer_stackdepot.h>
15
16#include "tsan_rtl.h"
17#include "tsan_flags.h"
18#include "tsan_sync.h"
19#include "tsan_report.h"
20#include "tsan_symbolize.h"
21#include "tsan_platform.h"
22
23namespace __tsan {
24
25void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r);
26void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr,
27 FastState last_lock, StackID creation_stack_id);
28
29struct Callback final : public DDCallback {
30 ThreadState *thr;
31 uptr pc;
32
33 Callback(ThreadState *thr, uptr pc)
34 : thr(thr)
35 , pc(pc) {
36 DDCallback::pt = thr->proc()->dd_pt;
37 DDCallback::lt = thr->dd_lt;
38 }
39
40 StackID Unwind() override { return CurrentStackId(thr, pc); }
41 int UniqueTid() override { return thr->tid; }
42};
43
44void DDMutexInit(ThreadState *thr, uptr pc, SyncVar *s) {
45 Callback cb(thr, pc);
46 ctx->dd->MutexInit(cb: &cb, m: &s->dd);
47 s->dd.ctx = s->addr;
48}
49
50static void ReportMutexMisuse(ThreadState *thr, uptr pc, ReportType typ,
51 uptr addr, StackID creation_stack_id) {
52 // In Go, these misuses are either impossible, or detected by std lib,
53 // or false positives (e.g. unlock in a different thread).
54 if (SANITIZER_GO)
55 return;
56 if (!ShouldReport(thr, typ))
57 return;
58 ThreadRegistryLock l(&ctx->thread_registry);
59 ScopedReport rep(typ);
60 rep.AddMutex(addr, creation_stack_id);
61 VarSizeStackTrace trace;
62 ObtainCurrentStack(thr, toppc: pc, stack: &trace);
63 rep.AddStack(stack: trace, suppressable: true);
64 rep.AddLocation(addr, size: 1);
65 OutputReport(thr, srep: rep);
66}
67
68static void RecordMutexLock(ThreadState *thr, uptr pc, uptr addr,
69 StackID stack_id, bool write) {
70 auto typ = write ? EventType::kLock : EventType::kRLock;
71 // Note: it's important to trace before modifying mutex set
72 // because tracing can switch trace part and we write the current
73 // mutex set in the beginning of each part.
74 // If we do it in the opposite order, we will write already reduced
75 // mutex set in the beginning of the part and then trace unlock again.
76 TraceMutexLock(thr, type: typ, pc, addr, stk: stack_id);
77 thr->mset.AddAddr(addr, stack_id, write);
78}
79
80static void RecordMutexUnlock(ThreadState *thr, uptr addr) {
81 // See the comment in RecordMutexLock re order of operations.
82 TraceMutexUnlock(thr, addr);
83 thr->mset.DelAddr(addr);
84}
85
86void MutexCreate(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
87 DPrintf("#%d: MutexCreate %zx flagz=0x%x\n", thr->tid, addr, flagz);
88 if (!(flagz & MutexFlagLinkerInit) && pc && IsAppMem(mem: addr))
89 MemoryAccess(thr, pc, addr, size: 1, typ: kAccessWrite);
90 SlotLocker locker(thr);
91 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true);
92 s->SetFlags(flagz & MutexCreationFlagMask);
93 // Save stack in the case the sync object was created before as atomic.
94 if (!SANITIZER_GO && s->creation_stack_id == kInvalidStackID)
95 s->creation_stack_id = CurrentStackId(thr, pc);
96}
97
98void MutexDestroy(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
99 DPrintf("#%d: MutexDestroy %zx\n", thr->tid, addr);
100 bool unlock_locked = false;
101 StackID creation_stack_id;
102 FastState last_lock;
103 {
104 auto s = ctx->metamap.GetSyncIfExists(addr);
105 if (!s)
106 return;
107 SlotLocker locker(thr);
108 {
109 Lock lock(&s->mtx);
110 creation_stack_id = s->creation_stack_id;
111 last_lock = s->last_lock;
112 if ((flagz & MutexFlagLinkerInit) || s->IsFlagSet(f: MutexFlagLinkerInit) ||
113 ((flagz & MutexFlagNotStatic) && !s->IsFlagSet(f: MutexFlagNotStatic))) {
114 // Destroy is no-op for linker-initialized mutexes.
115 return;
116 }
117 if (common_flags()->detect_deadlocks) {
118 Callback cb(thr, pc);
119 ctx->dd->MutexDestroy(cb: &cb, m: &s->dd);
120 ctx->dd->MutexInit(cb: &cb, m: &s->dd);
121 }
122 if (flags()->report_destroy_locked && s->owner_tid != kInvalidTid &&
123 !s->IsFlagSet(f: MutexFlagBroken)) {
124 s->SetFlags(MutexFlagBroken);
125 unlock_locked = true;
126 }
127 s->Reset();
128 }
129 // Imitate a memory write to catch unlock-destroy races.
130 if (pc && IsAppMem(mem: addr))
131 MemoryAccess(thr, pc, addr, size: 1,
132 typ: kAccessWrite | kAccessFree | kAccessSlotLocked);
133 }
134 if (unlock_locked && ShouldReport(thr, typ: ReportTypeMutexDestroyLocked))
135 ReportDestroyLocked(thr, pc, addr, last_lock, creation_stack_id);
136 thr->mset.DelAddr(addr, destroy: true);
137 // s will be destroyed and freed in MetaMap::FreeBlock.
138}
139
140void MutexPreLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
141 DPrintf("#%d: MutexPreLock %zx flagz=0x%x\n", thr->tid, addr, flagz);
142 if (flagz & MutexFlagTryLock)
143 return;
144 if (!common_flags()->detect_deadlocks)
145 return;
146 Callback cb(thr, pc);
147 {
148 SlotLocker locker(thr);
149 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true);
150 ReadLock lock(&s->mtx);
151 s->UpdateFlags(flagz);
152 if (s->owner_tid != thr->tid)
153 ctx->dd->MutexBeforeLock(cb: &cb, m: &s->dd, wlock: true);
154 }
155 ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb));
156}
157
158void MutexPostLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz, int rec) {
159 DPrintf("#%d: MutexPostLock %zx flag=0x%x rec=%d\n",
160 thr->tid, addr, flagz, rec);
161 if (flagz & MutexFlagRecursiveLock)
162 CHECK_GT(rec, 0);
163 else
164 rec = 1;
165 if (pc && IsAppMem(mem: addr))
166 MemoryAccess(thr, pc, addr, size: 1, typ: kAccessRead | kAccessAtomic);
167 bool report_double_lock = false;
168 bool pre_lock = false;
169 bool first = false;
170 StackID creation_stack_id = kInvalidStackID;
171 {
172 SlotLocker locker(thr);
173 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true);
174 creation_stack_id = s->creation_stack_id;
175 RecordMutexLock(thr, pc, addr, stack_id: creation_stack_id, write: true);
176 {
177 Lock lock(&s->mtx);
178 first = s->recursion == 0;
179 s->UpdateFlags(flagz);
180 if (s->owner_tid == kInvalidTid) {
181 CHECK_EQ(s->recursion, 0);
182 s->owner_tid = thr->tid;
183 s->last_lock = thr->fast_state;
184 } else if (s->owner_tid == thr->tid) {
185 CHECK_GT(s->recursion, 0);
186 } else if (flags()->report_mutex_bugs && !s->IsFlagSet(f: MutexFlagBroken)) {
187 s->SetFlags(MutexFlagBroken);
188 report_double_lock = true;
189 }
190 s->recursion += rec;
191 if (first) {
192 if (!thr->ignore_sync) {
193 thr->clock.Acquire(src: s->clock);
194 thr->clock.Acquire(src: s->read_clock);
195 }
196 }
197 if (first && common_flags()->detect_deadlocks) {
198 pre_lock = (flagz & MutexFlagDoPreLockOnPostLock) &&
199 !(flagz & MutexFlagTryLock);
200 Callback cb(thr, pc);
201 if (pre_lock)
202 ctx->dd->MutexBeforeLock(cb: &cb, m: &s->dd, wlock: true);
203 ctx->dd->MutexAfterLock(cb: &cb, m: &s->dd, wlock: true, trylock: flagz & MutexFlagTryLock);
204 }
205 }
206 }
207 if (report_double_lock)
208 ReportMutexMisuse(thr, pc, typ: ReportTypeMutexDoubleLock, addr,
209 creation_stack_id);
210 if (first && pre_lock && common_flags()->detect_deadlocks) {
211 Callback cb(thr, pc);
212 ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb));
213 }
214}
215
216int MutexUnlock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
217 DPrintf("#%d: MutexUnlock %zx flagz=0x%x\n", thr->tid, addr, flagz);
218 if (pc && IsAppMem(mem: addr))
219 MemoryAccess(thr, pc, addr, size: 1, typ: kAccessRead | kAccessAtomic);
220 StackID creation_stack_id;
221 RecordMutexUnlock(thr, addr);
222 bool report_bad_unlock = false;
223 int rec = 0;
224 {
225 SlotLocker locker(thr);
226 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true);
227 bool released = false;
228 {
229 Lock lock(&s->mtx);
230 creation_stack_id = s->creation_stack_id;
231 if (!SANITIZER_GO && (s->recursion == 0 || s->owner_tid != thr->tid)) {
232 if (flags()->report_mutex_bugs && !s->IsFlagSet(f: MutexFlagBroken)) {
233 s->SetFlags(MutexFlagBroken);
234 report_bad_unlock = true;
235 }
236 } else {
237 rec = (flagz & MutexFlagRecursiveUnlock) ? s->recursion : 1;
238 s->recursion -= rec;
239 if (s->recursion == 0) {
240 s->owner_tid = kInvalidTid;
241 if (!thr->ignore_sync) {
242 thr->clock.ReleaseStore(dstp: &s->clock);
243 released = true;
244 }
245 }
246 }
247 if (common_flags()->detect_deadlocks && s->recursion == 0 &&
248 !report_bad_unlock) {
249 Callback cb(thr, pc);
250 ctx->dd->MutexBeforeUnlock(cb: &cb, m: &s->dd, wlock: true);
251 }
252 }
253 if (released)
254 IncrementEpoch(thr);
255 }
256 if (report_bad_unlock)
257 ReportMutexMisuse(thr, pc, typ: ReportTypeMutexBadUnlock, addr,
258 creation_stack_id);
259 if (common_flags()->detect_deadlocks && !report_bad_unlock) {
260 Callback cb(thr, pc);
261 ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb));
262 }
263 return rec;
264}
265
266void MutexPreReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
267 DPrintf("#%d: MutexPreReadLock %zx flagz=0x%x\n", thr->tid, addr, flagz);
268 if ((flagz & MutexFlagTryLock) || !common_flags()->detect_deadlocks)
269 return;
270 Callback cb(thr, pc);
271 {
272 SlotLocker locker(thr);
273 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true);
274 ReadLock lock(&s->mtx);
275 s->UpdateFlags(flagz);
276 ctx->dd->MutexBeforeLock(cb: &cb, m: &s->dd, wlock: false);
277 }
278 ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb));
279}
280
281void MutexPostReadLock(ThreadState *thr, uptr pc, uptr addr, u32 flagz) {
282 DPrintf("#%d: MutexPostReadLock %zx flagz=0x%x\n", thr->tid, addr, flagz);
283 if (pc && IsAppMem(mem: addr))
284 MemoryAccess(thr, pc, addr, size: 1, typ: kAccessRead | kAccessAtomic);
285 bool report_bad_lock = false;
286 bool pre_lock = false;
287 StackID creation_stack_id = kInvalidStackID;
288 {
289 SlotLocker locker(thr);
290 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true);
291 creation_stack_id = s->creation_stack_id;
292 RecordMutexLock(thr, pc, addr, stack_id: creation_stack_id, write: false);
293 {
294 ReadLock lock(&s->mtx);
295 s->UpdateFlags(flagz);
296 if (s->owner_tid != kInvalidTid) {
297 if (flags()->report_mutex_bugs && !s->IsFlagSet(f: MutexFlagBroken)) {
298 s->SetFlags(MutexFlagBroken);
299 report_bad_lock = true;
300 }
301 }
302 if (!thr->ignore_sync)
303 thr->clock.Acquire(src: s->clock);
304 s->last_lock = thr->fast_state;
305 if (common_flags()->detect_deadlocks) {
306 pre_lock = (flagz & MutexFlagDoPreLockOnPostLock) &&
307 !(flagz & MutexFlagTryLock);
308 Callback cb(thr, pc);
309 if (pre_lock)
310 ctx->dd->MutexBeforeLock(cb: &cb, m: &s->dd, wlock: false);
311 ctx->dd->MutexAfterLock(cb: &cb, m: &s->dd, wlock: false, trylock: flagz & MutexFlagTryLock);
312 }
313 }
314 }
315 if (report_bad_lock)
316 ReportMutexMisuse(thr, pc, typ: ReportTypeMutexBadReadLock, addr,
317 creation_stack_id);
318 if (pre_lock && common_flags()->detect_deadlocks) {
319 Callback cb(thr, pc);
320 ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb));
321 }
322}
323
324void MutexReadUnlock(ThreadState *thr, uptr pc, uptr addr) {
325 DPrintf("#%d: MutexReadUnlock %zx\n", thr->tid, addr);
326 if (pc && IsAppMem(mem: addr))
327 MemoryAccess(thr, pc, addr, size: 1, typ: kAccessRead | kAccessAtomic);
328 RecordMutexUnlock(thr, addr);
329 StackID creation_stack_id;
330 bool report_bad_unlock = false;
331 {
332 SlotLocker locker(thr);
333 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true);
334 bool released = false;
335 {
336 Lock lock(&s->mtx);
337 creation_stack_id = s->creation_stack_id;
338 if (s->owner_tid != kInvalidTid) {
339 if (flags()->report_mutex_bugs && !s->IsFlagSet(f: MutexFlagBroken)) {
340 s->SetFlags(MutexFlagBroken);
341 report_bad_unlock = true;
342 }
343 }
344 if (!thr->ignore_sync) {
345 thr->clock.Release(dstp: &s->read_clock);
346 released = true;
347 }
348 if (common_flags()->detect_deadlocks && s->recursion == 0) {
349 Callback cb(thr, pc);
350 ctx->dd->MutexBeforeUnlock(cb: &cb, m: &s->dd, wlock: false);
351 }
352 }
353 if (released)
354 IncrementEpoch(thr);
355 }
356 if (report_bad_unlock)
357 ReportMutexMisuse(thr, pc, typ: ReportTypeMutexBadReadUnlock, addr,
358 creation_stack_id);
359 if (common_flags()->detect_deadlocks) {
360 Callback cb(thr, pc);
361 ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb));
362 }
363}
364
365void MutexReadOrWriteUnlock(ThreadState *thr, uptr pc, uptr addr) {
366 DPrintf("#%d: MutexReadOrWriteUnlock %zx\n", thr->tid, addr);
367 if (pc && IsAppMem(mem: addr))
368 MemoryAccess(thr, pc, addr, size: 1, typ: kAccessRead | kAccessAtomic);
369 RecordMutexUnlock(thr, addr);
370 StackID creation_stack_id;
371 bool report_bad_unlock = false;
372 bool write = true;
373 {
374 SlotLocker locker(thr);
375 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true);
376 bool released = false;
377 {
378 Lock lock(&s->mtx);
379 creation_stack_id = s->creation_stack_id;
380 if (s->owner_tid == kInvalidTid) {
381 // Seems to be read unlock.
382 write = false;
383 if (!thr->ignore_sync) {
384 thr->clock.Release(dstp: &s->read_clock);
385 released = true;
386 }
387 } else if (s->owner_tid == thr->tid) {
388 // Seems to be write unlock.
389 CHECK_GT(s->recursion, 0);
390 s->recursion--;
391 if (s->recursion == 0) {
392 s->owner_tid = kInvalidTid;
393 if (!thr->ignore_sync) {
394 thr->clock.ReleaseStore(dstp: &s->clock);
395 released = true;
396 }
397 }
398 } else if (!s->IsFlagSet(f: MutexFlagBroken)) {
399 s->SetFlags(MutexFlagBroken);
400 report_bad_unlock = true;
401 }
402 if (common_flags()->detect_deadlocks && s->recursion == 0) {
403 Callback cb(thr, pc);
404 ctx->dd->MutexBeforeUnlock(cb: &cb, m: &s->dd, wlock: write);
405 }
406 }
407 if (released)
408 IncrementEpoch(thr);
409 }
410 if (report_bad_unlock)
411 ReportMutexMisuse(thr, pc, typ: ReportTypeMutexBadUnlock, addr,
412 creation_stack_id);
413 if (common_flags()->detect_deadlocks) {
414 Callback cb(thr, pc);
415 ReportDeadlock(thr, pc, r: ctx->dd->GetReport(cb: &cb));
416 }
417}
418
419void MutexRepair(ThreadState *thr, uptr pc, uptr addr) {
420 DPrintf("#%d: MutexRepair %zx\n", thr->tid, addr);
421 SlotLocker locker(thr);
422 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true);
423 Lock lock(&s->mtx);
424 s->owner_tid = kInvalidTid;
425 s->recursion = 0;
426}
427
428void MutexInvalidAccess(ThreadState *thr, uptr pc, uptr addr) {
429 DPrintf("#%d: MutexInvalidAccess %zx\n", thr->tid, addr);
430 StackID creation_stack_id = kInvalidStackID;
431 {
432 SlotLocker locker(thr);
433 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: true);
434 if (s)
435 creation_stack_id = s->creation_stack_id;
436 }
437 ReportMutexMisuse(thr, pc, typ: ReportTypeMutexInvalidAccess, addr,
438 creation_stack_id);
439}
440
441void Acquire(ThreadState *thr, uptr pc, uptr addr) {
442 DPrintf("#%d: Acquire %zx\n", thr->tid, addr);
443 if (thr->ignore_sync)
444 return;
445 auto s = ctx->metamap.GetSyncIfExists(addr);
446 if (!s)
447 return;
448 SlotLocker locker(thr);
449 ReadLock lock(&s->mtx);
450 if (!s->clock)
451 return;
452 thr->clock.Acquire(src: s->clock);
453}
454
455void AcquireGlobal(ThreadState *thr) {
456 DPrintf("#%d: AcquireGlobal\n", thr->tid);
457 if (thr->ignore_sync)
458 return;
459 SlotLocker locker(thr);
460 for (auto &slot : ctx->slots) thr->clock.Set(sid: slot.sid, v: slot.epoch());
461}
462
463void Release(ThreadState *thr, uptr pc, uptr addr) {
464 DPrintf("#%d: Release %zx\n", thr->tid, addr);
465 if (thr->ignore_sync)
466 return;
467 SlotLocker locker(thr);
468 {
469 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: false);
470 Lock lock(&s->mtx);
471 thr->clock.Release(dstp: &s->clock);
472 }
473 IncrementEpoch(thr);
474}
475
476void ReleaseStore(ThreadState *thr, uptr pc, uptr addr) {
477 DPrintf("#%d: ReleaseStore %zx\n", thr->tid, addr);
478 if (thr->ignore_sync)
479 return;
480 SlotLocker locker(thr);
481 {
482 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: false);
483 Lock lock(&s->mtx);
484 thr->clock.ReleaseStore(dstp: &s->clock);
485 }
486 IncrementEpoch(thr);
487}
488
489void ReleaseStoreAcquire(ThreadState *thr, uptr pc, uptr addr) {
490 DPrintf("#%d: ReleaseStoreAcquire %zx\n", thr->tid, addr);
491 if (thr->ignore_sync)
492 return;
493 SlotLocker locker(thr);
494 {
495 auto s = ctx->metamap.GetSyncOrCreate(thr, pc, addr, save_stack: false);
496 Lock lock(&s->mtx);
497 thr->clock.ReleaseStoreAcquire(dstp: &s->clock);
498 }
499 IncrementEpoch(thr);
500}
501
502void IncrementEpoch(ThreadState *thr) {
503 DCHECK(!thr->ignore_sync);
504 DCHECK(thr->slot_locked);
505 Epoch epoch = EpochInc(epoch: thr->fast_state.epoch());
506 if (!EpochOverflow(epoch)) {
507 Sid sid = thr->fast_state.sid();
508 thr->clock.Set(sid, v: epoch);
509 thr->fast_state.SetEpoch(epoch);
510 thr->slot->SetEpoch(epoch);
511 TraceTime(thr);
512 }
513}
514
515#if !SANITIZER_GO
516void AfterSleep(ThreadState *thr, uptr pc) {
517 DPrintf("#%d: AfterSleep\n", thr->tid);
518 if (thr->ignore_sync)
519 return;
520 thr->last_sleep_stack_id = CurrentStackId(thr, pc);
521 thr->last_sleep_clock.Reset();
522 SlotLocker locker(thr);
523 for (auto &slot : ctx->slots)
524 thr->last_sleep_clock.Set(sid: slot.sid, v: slot.epoch());
525}
526#endif
527
528void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) {
529 if (r == 0 || !ShouldReport(thr, typ: ReportTypeDeadlock))
530 return;
531 ThreadRegistryLock l(&ctx->thread_registry);
532 ScopedReport rep(ReportTypeDeadlock);
533 for (int i = 0; i < r->n; i++) {
534 rep.AddMutex(addr: r->loop[i].mtx_ctx0, creation_stack_id: r->loop[i].stk[0]);
535 rep.AddUniqueTid(unique_tid: (int)r->loop[i].thr_ctx);
536 rep.AddThread(tid: (int)r->loop[i].thr_ctx);
537 }
538 uptr dummy_pc = 0x42;
539 for (int i = 0; i < r->n; i++) {
540 for (int j = 0; j < (flags()->second_deadlock_stack ? 2 : 1); j++) {
541 u32 stk = r->loop[i].stk[j];
542 if (stk && stk != kInvalidStackID) {
543 rep.AddStack(stack: StackDepotGet(id: stk), suppressable: true);
544 } else {
545 // Sometimes we fail to extract the stack trace (FIXME: investigate),
546 // but we should still produce some stack trace in the report.
547 rep.AddStack(stack: StackTrace(&dummy_pc, 1), suppressable: true);
548 }
549 }
550 }
551 OutputReport(thr, srep: rep);
552}
553
554void ReportDestroyLocked(ThreadState *thr, uptr pc, uptr addr,
555 FastState last_lock, StackID creation_stack_id) {
556 // We need to lock the slot during RestoreStack because it protects
557 // the slot journal.
558 Lock slot_lock(&ctx->slots[static_cast<uptr>(last_lock.sid())].mtx);
559 ThreadRegistryLock l0(&ctx->thread_registry);
560 Lock slots_lock(&ctx->slot_mtx);
561 ScopedReport rep(ReportTypeMutexDestroyLocked);
562 rep.AddMutex(addr, creation_stack_id);
563 VarSizeStackTrace trace;
564 ObtainCurrentStack(thr, toppc: pc, stack: &trace);
565 rep.AddStack(stack: trace, suppressable: true);
566
567 Tid tid;
568 DynamicMutexSet mset;
569 uptr tag;
570 if (!RestoreStack(type: EventType::kLock, sid: last_lock.sid(), epoch: last_lock.epoch(), addr,
571 size: 0, typ: kAccessWrite, ptid: &tid, pstk: &trace, pmset: mset, ptag: &tag))
572 return;
573 rep.AddStack(stack: trace, suppressable: true);
574 rep.AddLocation(addr, size: 1);
575 OutputReport(thr, srep: rep);
576}
577
578} // namespace __tsan
579