168d75effSDimitry Andric //===-- tsan_rtl_thread.cpp -----------------------------------------------===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // This file is a part of ThreadSanitizer (TSan), a race detector. 1068d75effSDimitry Andric // 1168d75effSDimitry Andric //===----------------------------------------------------------------------===// 1268d75effSDimitry Andric 1368d75effSDimitry Andric #include "sanitizer_common/sanitizer_placement_new.h" 1468d75effSDimitry Andric #include "tsan_rtl.h" 1568d75effSDimitry Andric #include "tsan_mman.h" 1668d75effSDimitry Andric #include "tsan_platform.h" 1768d75effSDimitry Andric #include "tsan_report.h" 1868d75effSDimitry Andric #include "tsan_sync.h" 1968d75effSDimitry Andric 2068d75effSDimitry Andric namespace __tsan { 2168d75effSDimitry Andric 2268d75effSDimitry Andric // ThreadContext implementation. 2368d75effSDimitry Andric 240eae32dcSDimitry Andric ThreadContext::ThreadContext(Tid tid) : ThreadContextBase(tid), thr(), sync() {} 2568d75effSDimitry Andric 2668d75effSDimitry Andric #if !SANITIZER_GO 2768d75effSDimitry Andric ThreadContext::~ThreadContext() { 2868d75effSDimitry Andric } 2968d75effSDimitry Andric #endif 3068d75effSDimitry Andric 310eae32dcSDimitry Andric void ThreadContext::OnReset() { CHECK(!sync); } 3268d75effSDimitry Andric 3368d75effSDimitry Andric #if !SANITIZER_GO 3468d75effSDimitry Andric struct ThreadLeak { 3568d75effSDimitry Andric ThreadContext *tctx; 3668d75effSDimitry Andric int count; 3768d75effSDimitry Andric }; 3868d75effSDimitry Andric 39349cc55cSDimitry Andric static void CollectThreadLeaks(ThreadContextBase *tctx_base, void *arg) { 40349cc55cSDimitry Andric auto &leaks = *static_cast<Vector<ThreadLeak> *>(arg); 41349cc55cSDimitry Andric auto *tctx = static_cast<ThreadContext *>(tctx_base); 4268d75effSDimitry Andric if (tctx->detached || tctx->status != ThreadStatusFinished) 4368d75effSDimitry Andric return; 4468d75effSDimitry Andric for (uptr i = 0; i < leaks.Size(); i++) { 4568d75effSDimitry Andric if (leaks[i].tctx->creation_stack_id == tctx->creation_stack_id) { 4668d75effSDimitry Andric leaks[i].count++; 4768d75effSDimitry Andric return; 4868d75effSDimitry Andric } 4968d75effSDimitry Andric } 50349cc55cSDimitry Andric leaks.PushBack({tctx, 1}); 5168d75effSDimitry Andric } 5268d75effSDimitry Andric #endif 5368d75effSDimitry Andric 540eae32dcSDimitry Andric // Disabled on Mac because lldb test TestTsanBasic fails: 550eae32dcSDimitry Andric // https://reviews.llvm.org/D112603#3163158 5681ad6265SDimitry Andric #if !SANITIZER_GO && !SANITIZER_APPLE 5768d75effSDimitry Andric static void ReportIgnoresEnabled(ThreadContext *tctx, IgnoreSet *set) { 58fe6060f1SDimitry Andric if (tctx->tid == kMainTid) { 5968d75effSDimitry Andric Printf("ThreadSanitizer: main thread finished with ignores enabled\n"); 6068d75effSDimitry Andric } else { 6168d75effSDimitry Andric Printf("ThreadSanitizer: thread T%d %s finished with ignores enabled," 6268d75effSDimitry Andric " created at:\n", tctx->tid, tctx->name); 6368d75effSDimitry Andric PrintStack(SymbolizeStackId(tctx->creation_stack_id)); 6468d75effSDimitry Andric } 6568d75effSDimitry Andric Printf(" One of the following ignores was not ended" 6668d75effSDimitry Andric " (in order of probability)\n"); 6768d75effSDimitry Andric for (uptr i = 0; i < set->Size(); i++) { 6868d75effSDimitry Andric Printf(" Ignore was enabled at:\n"); 6968d75effSDimitry Andric PrintStack(SymbolizeStackId(set->At(i))); 7068d75effSDimitry Andric } 7168d75effSDimitry Andric Die(); 7268d75effSDimitry Andric } 7368d75effSDimitry Andric 7468d75effSDimitry Andric static void ThreadCheckIgnore(ThreadState *thr) { 7568d75effSDimitry Andric if (ctx->after_multithreaded_fork) 7668d75effSDimitry Andric return; 7768d75effSDimitry Andric if (thr->ignore_reads_and_writes) 7868d75effSDimitry Andric ReportIgnoresEnabled(thr->tctx, &thr->mop_ignore_set); 7968d75effSDimitry Andric if (thr->ignore_sync) 8068d75effSDimitry Andric ReportIgnoresEnabled(thr->tctx, &thr->sync_ignore_set); 8168d75effSDimitry Andric } 8268d75effSDimitry Andric #else 8368d75effSDimitry Andric static void ThreadCheckIgnore(ThreadState *thr) {} 8468d75effSDimitry Andric #endif 8568d75effSDimitry Andric 8668d75effSDimitry Andric void ThreadFinalize(ThreadState *thr) { 8768d75effSDimitry Andric ThreadCheckIgnore(thr); 8868d75effSDimitry Andric #if !SANITIZER_GO 89fe6060f1SDimitry Andric if (!ShouldReport(thr, ReportTypeThreadLeak)) 9068d75effSDimitry Andric return; 91349cc55cSDimitry Andric ThreadRegistryLock l(&ctx->thread_registry); 9268d75effSDimitry Andric Vector<ThreadLeak> leaks; 93349cc55cSDimitry Andric ctx->thread_registry.RunCallbackForEachThreadLocked(CollectThreadLeaks, 94349cc55cSDimitry Andric &leaks); 9568d75effSDimitry Andric for (uptr i = 0; i < leaks.Size(); i++) { 9668d75effSDimitry Andric ScopedReport rep(ReportTypeThreadLeak); 9768d75effSDimitry Andric rep.AddThread(leaks[i].tctx, true); 9868d75effSDimitry Andric rep.SetCount(leaks[i].count); 9968d75effSDimitry Andric OutputReport(thr, rep); 10068d75effSDimitry Andric } 10168d75effSDimitry Andric #endif 10268d75effSDimitry Andric } 10368d75effSDimitry Andric 10468d75effSDimitry Andric int ThreadCount(ThreadState *thr) { 10568d75effSDimitry Andric uptr result; 106349cc55cSDimitry Andric ctx->thread_registry.GetNumberOfThreads(0, 0, &result); 10768d75effSDimitry Andric return (int)result; 10868d75effSDimitry Andric } 10968d75effSDimitry Andric 110349cc55cSDimitry Andric struct OnCreatedArgs { 1110eae32dcSDimitry Andric VectorClock *sync; 1120eae32dcSDimitry Andric uptr sync_epoch; 1130eae32dcSDimitry Andric StackID stack; 114349cc55cSDimitry Andric }; 115349cc55cSDimitry Andric 116349cc55cSDimitry Andric Tid ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached) { 1170eae32dcSDimitry Andric // The main thread and GCD workers don't have a parent thread. 1180eae32dcSDimitry Andric Tid parent = kInvalidTid; 1190eae32dcSDimitry Andric OnCreatedArgs arg = {nullptr, 0, kInvalidStackID}; 1200eae32dcSDimitry Andric if (thr) { 1210eae32dcSDimitry Andric parent = thr->tid; 1220eae32dcSDimitry Andric arg.stack = CurrentStackId(thr, pc); 1230eae32dcSDimitry Andric if (!thr->ignore_sync) { 1240eae32dcSDimitry Andric SlotLocker locker(thr); 1250eae32dcSDimitry Andric thr->clock.ReleaseStore(&arg.sync); 1260eae32dcSDimitry Andric arg.sync_epoch = ctx->global_epoch; 1270eae32dcSDimitry Andric IncrementEpoch(thr); 1280eae32dcSDimitry Andric } 1290eae32dcSDimitry Andric } 1300eae32dcSDimitry Andric Tid tid = ctx->thread_registry.CreateThread(uid, detached, parent, &arg); 1310eae32dcSDimitry Andric DPrintf("#%d: ThreadCreate tid=%d uid=%zu\n", parent, tid, uid); 13268d75effSDimitry Andric return tid; 13368d75effSDimitry Andric } 13468d75effSDimitry Andric 135349cc55cSDimitry Andric void ThreadContext::OnCreated(void *arg) { 136349cc55cSDimitry Andric OnCreatedArgs *args = static_cast<OnCreatedArgs *>(arg); 1370eae32dcSDimitry Andric sync = args->sync; 1380eae32dcSDimitry Andric sync_epoch = args->sync_epoch; 1390eae32dcSDimitry Andric creation_stack_id = args->stack; 140349cc55cSDimitry Andric } 141349cc55cSDimitry Andric 142349cc55cSDimitry Andric extern "C" void __tsan_stack_initialization() {} 143349cc55cSDimitry Andric 144349cc55cSDimitry Andric struct OnStartedArgs { 145349cc55cSDimitry Andric ThreadState *thr; 146349cc55cSDimitry Andric uptr stk_addr; 147349cc55cSDimitry Andric uptr stk_size; 148349cc55cSDimitry Andric uptr tls_addr; 149349cc55cSDimitry Andric uptr tls_size; 150349cc55cSDimitry Andric }; 151349cc55cSDimitry Andric 152349cc55cSDimitry Andric void ThreadStart(ThreadState *thr, Tid tid, tid_t os_id, 15368d75effSDimitry Andric ThreadType thread_type) { 1540eae32dcSDimitry Andric ctx->thread_registry.StartThread(tid, os_id, thread_type, thr); 1550eae32dcSDimitry Andric if (!thr->ignore_sync) { 1560eae32dcSDimitry Andric SlotAttachAndLock(thr); 1570eae32dcSDimitry Andric if (thr->tctx->sync_epoch == ctx->global_epoch) 1580eae32dcSDimitry Andric thr->clock.Acquire(thr->tctx->sync); 1590eae32dcSDimitry Andric SlotUnlock(thr); 1600eae32dcSDimitry Andric } 1610eae32dcSDimitry Andric Free(thr->tctx->sync); 1620eae32dcSDimitry Andric 163*0fca6ea1SDimitry Andric #if !SANITIZER_GO 164*0fca6ea1SDimitry Andric thr->is_inited = true; 165*0fca6ea1SDimitry Andric #endif 166*0fca6ea1SDimitry Andric 16768d75effSDimitry Andric uptr stk_addr = 0; 16868d75effSDimitry Andric uptr stk_size = 0; 16968d75effSDimitry Andric uptr tls_addr = 0; 17068d75effSDimitry Andric uptr tls_size = 0; 17168d75effSDimitry Andric #if !SANITIZER_GO 17268d75effSDimitry Andric if (thread_type != ThreadType::Fiber) 173fe6060f1SDimitry Andric GetThreadStackAndTls(tid == kMainTid, &stk_addr, &stk_size, &tls_addr, 174fe6060f1SDimitry Andric &tls_size); 17568d75effSDimitry Andric #endif 1760eae32dcSDimitry Andric thr->stk_addr = stk_addr; 1770eae32dcSDimitry Andric thr->stk_size = stk_size; 1780eae32dcSDimitry Andric thr->tls_addr = tls_addr; 1790eae32dcSDimitry Andric thr->tls_size = tls_size; 18068d75effSDimitry Andric 18168d75effSDimitry Andric #if !SANITIZER_GO 18268d75effSDimitry Andric if (ctx->after_multithreaded_fork) { 18368d75effSDimitry Andric thr->ignore_interceptors++; 18468d75effSDimitry Andric ThreadIgnoreBegin(thr, 0); 18568d75effSDimitry Andric ThreadIgnoreSyncBegin(thr, 0); 18668d75effSDimitry Andric } 18768d75effSDimitry Andric #endif 188349cc55cSDimitry Andric 189349cc55cSDimitry Andric #if !SANITIZER_GO 190349cc55cSDimitry Andric // Don't imitate stack/TLS writes for the main thread, 191349cc55cSDimitry Andric // because its initialization is synchronized with all 192349cc55cSDimitry Andric // subsequent threads anyway. 193349cc55cSDimitry Andric if (tid != kMainTid) { 194349cc55cSDimitry Andric if (stk_addr && stk_size) { 195349cc55cSDimitry Andric const uptr pc = StackTrace::GetNextInstructionPc( 196349cc55cSDimitry Andric reinterpret_cast<uptr>(__tsan_stack_initialization)); 197349cc55cSDimitry Andric MemoryRangeImitateWrite(thr, pc, stk_addr, stk_size); 198349cc55cSDimitry Andric } 199349cc55cSDimitry Andric 200349cc55cSDimitry Andric if (tls_addr && tls_size) 201349cc55cSDimitry Andric ImitateTlsWrite(thr, tls_addr, tls_size); 202349cc55cSDimitry Andric } 203349cc55cSDimitry Andric #endif 204349cc55cSDimitry Andric } 205349cc55cSDimitry Andric 206349cc55cSDimitry Andric void ThreadContext::OnStarted(void *arg) { 2070eae32dcSDimitry Andric DPrintf("#%d: ThreadStart\n", tid); 208*0fca6ea1SDimitry Andric thr = new (arg) ThreadState(tid); 209349cc55cSDimitry Andric if (common_flags()->detect_deadlocks) 2100eae32dcSDimitry Andric thr->dd_lt = ctx->dd->CreateLogicalThread(tid); 211349cc55cSDimitry Andric thr->tctx = this; 21268d75effSDimitry Andric } 21368d75effSDimitry Andric 21468d75effSDimitry Andric void ThreadFinish(ThreadState *thr) { 2150eae32dcSDimitry Andric DPrintf("#%d: ThreadFinish\n", thr->tid); 21668d75effSDimitry Andric ThreadCheckIgnore(thr); 21768d75effSDimitry Andric if (thr->stk_addr && thr->stk_size) 21868d75effSDimitry Andric DontNeedShadowFor(thr->stk_addr, thr->stk_size); 21968d75effSDimitry Andric if (thr->tls_addr && thr->tls_size) 22068d75effSDimitry Andric DontNeedShadowFor(thr->tls_addr, thr->tls_size); 22168d75effSDimitry Andric thr->is_dead = true; 222349cc55cSDimitry Andric #if !SANITIZER_GO 2230eae32dcSDimitry Andric thr->is_inited = false; 224349cc55cSDimitry Andric thr->ignore_interceptors++; 2250eae32dcSDimitry Andric PlatformCleanUpThreadState(thr); 226349cc55cSDimitry Andric #endif 2270eae32dcSDimitry Andric if (!thr->ignore_sync) { 2280eae32dcSDimitry Andric SlotLocker locker(thr); 2290eae32dcSDimitry Andric ThreadRegistryLock lock(&ctx->thread_registry); 2300eae32dcSDimitry Andric // Note: detached is protected by the thread registry mutex, 2310eae32dcSDimitry Andric // the thread may be detaching concurrently in another thread. 2320eae32dcSDimitry Andric if (!thr->tctx->detached) { 2330eae32dcSDimitry Andric thr->clock.ReleaseStore(&thr->tctx->sync); 2340eae32dcSDimitry Andric thr->tctx->sync_epoch = ctx->global_epoch; 2350eae32dcSDimitry Andric IncrementEpoch(thr); 236349cc55cSDimitry Andric } 237349cc55cSDimitry Andric } 238349cc55cSDimitry Andric #if !SANITIZER_GO 239349cc55cSDimitry Andric UnmapOrDie(thr->shadow_stack, kShadowStackSize * sizeof(uptr)); 240349cc55cSDimitry Andric #else 241349cc55cSDimitry Andric Free(thr->shadow_stack); 242349cc55cSDimitry Andric #endif 243349cc55cSDimitry Andric thr->shadow_stack = nullptr; 244349cc55cSDimitry Andric thr->shadow_stack_pos = nullptr; 245349cc55cSDimitry Andric thr->shadow_stack_end = nullptr; 246349cc55cSDimitry Andric if (common_flags()->detect_deadlocks) 247349cc55cSDimitry Andric ctx->dd->DestroyLogicalThread(thr->dd_lt); 2480eae32dcSDimitry Andric SlotDetach(thr); 2490eae32dcSDimitry Andric ctx->thread_registry.FinishThread(thr->tid); 250349cc55cSDimitry Andric thr->~ThreadState(); 2510eae32dcSDimitry Andric } 2520eae32dcSDimitry Andric 2530eae32dcSDimitry Andric void ThreadContext::OnFinished() { 2540eae32dcSDimitry Andric Lock lock(&ctx->slot_mtx); 2550eae32dcSDimitry Andric Lock lock1(&trace.mtx); 2560eae32dcSDimitry Andric // Queue all trace parts into the global recycle queue. 2570eae32dcSDimitry Andric auto parts = &trace.parts; 2580eae32dcSDimitry Andric while (trace.local_head) { 2590eae32dcSDimitry Andric CHECK(parts->Queued(trace.local_head)); 2600eae32dcSDimitry Andric ctx->trace_part_recycle.PushBack(trace.local_head); 2610eae32dcSDimitry Andric trace.local_head = parts->Next(trace.local_head); 2620eae32dcSDimitry Andric } 2630eae32dcSDimitry Andric ctx->trace_part_recycle_finished += parts->Size(); 2640eae32dcSDimitry Andric if (ctx->trace_part_recycle_finished > Trace::kFinishedThreadHi) { 2650eae32dcSDimitry Andric ctx->trace_part_finished_excess += parts->Size(); 2660eae32dcSDimitry Andric trace.parts_allocated = 0; 2670eae32dcSDimitry Andric } else if (ctx->trace_part_recycle_finished > Trace::kFinishedThreadLo && 2680eae32dcSDimitry Andric parts->Size() > 1) { 2690eae32dcSDimitry Andric ctx->trace_part_finished_excess += parts->Size() - 1; 2700eae32dcSDimitry Andric trace.parts_allocated = 1; 2710eae32dcSDimitry Andric } 2720eae32dcSDimitry Andric // From now on replay will use trace->final_pos. 2730eae32dcSDimitry Andric trace.final_pos = (Event *)atomic_load_relaxed(&thr->trace_pos); 2740eae32dcSDimitry Andric atomic_store_relaxed(&thr->trace_pos, 0); 2750eae32dcSDimitry Andric thr->tctx = nullptr; 2760eae32dcSDimitry Andric thr = nullptr; 27768d75effSDimitry Andric } 27868d75effSDimitry Andric 2795ffd83dbSDimitry Andric struct ConsumeThreadContext { 2805ffd83dbSDimitry Andric uptr uid; 2815ffd83dbSDimitry Andric ThreadContextBase *tctx; 2825ffd83dbSDimitry Andric }; 2835ffd83dbSDimitry Andric 284349cc55cSDimitry Andric Tid ThreadConsumeTid(ThreadState *thr, uptr pc, uptr uid) { 285349cc55cSDimitry Andric return ctx->thread_registry.ConsumeThreadUserId(uid); 28668d75effSDimitry Andric } 28768d75effSDimitry Andric 2880eae32dcSDimitry Andric struct JoinArg { 2890eae32dcSDimitry Andric VectorClock *sync; 2900eae32dcSDimitry Andric uptr sync_epoch; 2910eae32dcSDimitry Andric }; 2920eae32dcSDimitry Andric 293349cc55cSDimitry Andric void ThreadJoin(ThreadState *thr, uptr pc, Tid tid) { 29468d75effSDimitry Andric CHECK_GT(tid, 0); 29568d75effSDimitry Andric DPrintf("#%d: ThreadJoin tid=%d\n", thr->tid, tid); 2960eae32dcSDimitry Andric JoinArg arg = {}; 2970eae32dcSDimitry Andric ctx->thread_registry.JoinThread(tid, &arg); 2980eae32dcSDimitry Andric if (!thr->ignore_sync) { 2990eae32dcSDimitry Andric SlotLocker locker(thr); 3000eae32dcSDimitry Andric if (arg.sync_epoch == ctx->global_epoch) 3010eae32dcSDimitry Andric thr->clock.Acquire(arg.sync); 3020eae32dcSDimitry Andric } 3030eae32dcSDimitry Andric Free(arg.sync); 30468d75effSDimitry Andric } 30568d75effSDimitry Andric 3060eae32dcSDimitry Andric void ThreadContext::OnJoined(void *ptr) { 3070eae32dcSDimitry Andric auto arg = static_cast<JoinArg *>(ptr); 3080eae32dcSDimitry Andric arg->sync = sync; 3090eae32dcSDimitry Andric arg->sync_epoch = sync_epoch; 3100eae32dcSDimitry Andric sync = nullptr; 3110eae32dcSDimitry Andric sync_epoch = 0; 31268d75effSDimitry Andric } 31368d75effSDimitry Andric 3140eae32dcSDimitry Andric void ThreadContext::OnDead() { CHECK_EQ(sync, nullptr); } 315349cc55cSDimitry Andric 316349cc55cSDimitry Andric void ThreadDetach(ThreadState *thr, uptr pc, Tid tid) { 31768d75effSDimitry Andric CHECK_GT(tid, 0); 318349cc55cSDimitry Andric ctx->thread_registry.DetachThread(tid, thr); 319349cc55cSDimitry Andric } 320349cc55cSDimitry Andric 3210eae32dcSDimitry Andric void ThreadContext::OnDetached(void *arg) { Free(sync); } 322349cc55cSDimitry Andric 323349cc55cSDimitry Andric void ThreadNotJoined(ThreadState *thr, uptr pc, Tid tid, uptr uid) { 324349cc55cSDimitry Andric CHECK_GT(tid, 0); 325349cc55cSDimitry Andric ctx->thread_registry.SetThreadUserId(tid, uid); 32668d75effSDimitry Andric } 32768d75effSDimitry Andric 32868d75effSDimitry Andric void ThreadSetName(ThreadState *thr, const char *name) { 329349cc55cSDimitry Andric ctx->thread_registry.SetThreadName(thr->tid, name); 33068d75effSDimitry Andric } 33168d75effSDimitry Andric 33268d75effSDimitry Andric #if !SANITIZER_GO 33368d75effSDimitry Andric void FiberSwitchImpl(ThreadState *from, ThreadState *to) { 33468d75effSDimitry Andric Processor *proc = from->proc(); 33568d75effSDimitry Andric ProcUnwire(proc, from); 33668d75effSDimitry Andric ProcWire(proc, to); 33768d75effSDimitry Andric set_cur_thread(to); 33868d75effSDimitry Andric } 33968d75effSDimitry Andric 34068d75effSDimitry Andric ThreadState *FiberCreate(ThreadState *thr, uptr pc, unsigned flags) { 341349cc55cSDimitry Andric void *mem = Alloc(sizeof(ThreadState)); 34268d75effSDimitry Andric ThreadState *fiber = static_cast<ThreadState *>(mem); 34368d75effSDimitry Andric internal_memset(fiber, 0, sizeof(*fiber)); 344349cc55cSDimitry Andric Tid tid = ThreadCreate(thr, pc, 0, true); 34568d75effSDimitry Andric FiberSwitchImpl(thr, fiber); 34668d75effSDimitry Andric ThreadStart(fiber, tid, 0, ThreadType::Fiber); 34768d75effSDimitry Andric FiberSwitchImpl(fiber, thr); 34868d75effSDimitry Andric return fiber; 34968d75effSDimitry Andric } 35068d75effSDimitry Andric 35168d75effSDimitry Andric void FiberDestroy(ThreadState *thr, uptr pc, ThreadState *fiber) { 35268d75effSDimitry Andric FiberSwitchImpl(thr, fiber); 35368d75effSDimitry Andric ThreadFinish(fiber); 35468d75effSDimitry Andric FiberSwitchImpl(fiber, thr); 355349cc55cSDimitry Andric Free(fiber); 35668d75effSDimitry Andric } 35768d75effSDimitry Andric 35868d75effSDimitry Andric void FiberSwitch(ThreadState *thr, uptr pc, 35968d75effSDimitry Andric ThreadState *fiber, unsigned flags) { 36068d75effSDimitry Andric if (!(flags & FiberSwitchFlagNoSync)) 36168d75effSDimitry Andric Release(thr, pc, (uptr)fiber); 36268d75effSDimitry Andric FiberSwitchImpl(thr, fiber); 36368d75effSDimitry Andric if (!(flags & FiberSwitchFlagNoSync)) 36468d75effSDimitry Andric Acquire(fiber, pc, (uptr)fiber); 36568d75effSDimitry Andric } 36668d75effSDimitry Andric #endif 36768d75effSDimitry Andric 36868d75effSDimitry Andric } // namespace __tsan 369