Index: compiler-rt/trunk/lib/asan/asan_mac.cc =================================================================== --- compiler-rt/trunk/lib/asan/asan_mac.cc +++ compiler-rt/trunk/lib/asan/asan_mac.cc @@ -138,7 +138,8 @@ t = AsanThread::Create(/* start_routine */ nullptr, /* arg */ nullptr, parent_tid, stack, /* detached */ true); t->Init(); - asanThreadRegistry().StartThread(t->tid(), 0, 0); + asanThreadRegistry().StartThread(t->tid(), GetTid(), + /* workerthread */ true, 0); SetCurrentThread(t); } } Index: compiler-rt/trunk/lib/asan/asan_thread.cc =================================================================== --- compiler-rt/trunk/lib/asan/asan_thread.cc +++ compiler-rt/trunk/lib/asan/asan_thread.cc @@ -239,7 +239,8 @@ thread_return_t AsanThread::ThreadStart( uptr os_id, atomic_uintptr_t *signal_thread_is_registered) { Init(); - asanThreadRegistry().StartThread(tid(), os_id, nullptr); + asanThreadRegistry().StartThread(tid(), os_id, /*workerthread*/ false, + nullptr); if (signal_thread_is_registered) atomic_store(signal_thread_is_registered, 1, memory_order_release); Index: compiler-rt/trunk/lib/lsan/lsan_thread.cc =================================================================== --- compiler-rt/trunk/lib/lsan/lsan_thread.cc +++ compiler-rt/trunk/lib/lsan/lsan_thread.cc @@ -97,7 +97,7 @@ args.tls_end = args.tls_begin + tls_size; GetAllocatorCacheRange(&args.cache_begin, &args.cache_end); args.dtls = DTLS_Get(); - thread_registry->StartThread(tid, os_id, &args); + thread_registry->StartThread(tid, os_id, /*workerthread*/ false, &args); } void ThreadFinish() { Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.h =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.h +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.h @@ -45,6 +45,7 @@ ThreadStatus status; bool detached; + bool workerthread; u32 parent_tid; ThreadContextBase *next; // For storing thread contexts in a list. @@ -54,7 +55,7 @@ void SetDead(); void SetJoined(void *arg); void SetFinished(); - void SetStarted(uptr _os_id, void *arg); + void SetStarted(uptr _os_id, bool _workerthread, void *arg); void SetCreated(uptr _user_id, u64 _unique_id, bool _detached, u32 _parent_tid, void *arg); void Reset(); @@ -115,7 +116,7 @@ void DetachThread(u32 tid, void *arg); void JoinThread(u32 tid, void *arg); void FinishThread(u32 tid); - void StartThread(u32 tid, uptr os_id, void *arg); + void StartThread(u32 tid, uptr os_id, bool workerthread, void *arg); private: const ThreadContextFactory context_factory_; Index: compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.cc =================================================================== --- compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.cc +++ compiler-rt/trunk/lib/sanitizer_common/sanitizer_thread_registry.cc @@ -19,7 +19,7 @@ ThreadContextBase::ThreadContextBase(u32 tid) : tid(tid), unique_id(0), reuse_count(), os_id(0), user_id(0), status(ThreadStatusInvalid), - detached(false), parent_tid(0), next(0) { + detached(false), workerthread(false), parent_tid(0), next(0) { name[0] = '\0'; } @@ -59,9 +59,10 @@ OnFinished(); } -void ThreadContextBase::SetStarted(uptr _os_id, void *arg) { +void ThreadContextBase::SetStarted(uptr _os_id, bool _workerthread, void *arg) { status = ThreadStatusRunning; os_id = _os_id; + workerthread = _workerthread; OnStarted(arg); } @@ -266,14 +267,15 @@ } } -void ThreadRegistry::StartThread(u32 tid, uptr os_id, void *arg) { +void ThreadRegistry::StartThread(u32 tid, uptr os_id, bool workerthread, + void *arg) { BlockingMutexLock l(&mtx_); running_threads_++; CHECK_LT(tid, n_contexts_); ThreadContextBase *tctx = threads_[tid]; CHECK_NE(tctx, 0); CHECK_EQ(ThreadStatusCreated, tctx->status); - tctx->SetStarted(os_id, arg); + tctx->SetStarted(os_id, workerthread, arg); } void ThreadRegistry::QuarantinePush(ThreadContextBase *tctx) { Index: compiler-rt/trunk/lib/tsan/go/tsan_go.cc =================================================================== --- compiler-rt/trunk/lib/tsan/go/tsan_go.cc +++ compiler-rt/trunk/lib/tsan/go/tsan_go.cc @@ -214,7 +214,7 @@ ThreadState *thr = AllocGoroutine(); *pthr = thr; int goid = ThreadCreate(parent, (uptr)pc, 0, true); - ThreadStart(thr, goid, 0); + ThreadStart(thr, goid, 0, /*workerthread*/ false); } void __tsan_go_end(ThreadState *thr) { Index: compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc +++ compiler-rt/trunk/lib/tsan/rtl/tsan_interceptors.cc @@ -881,7 +881,7 @@ internal_sched_yield(); Processor *proc = ProcCreate(); ProcWire(proc, thr); - ThreadStart(thr, tid, GetTid()); + ThreadStart(thr, tid, GetTid(), /*workerthread*/ false); atomic_store(&p->tid, 0, memory_order_release); } void *res = callback(param); Index: compiler-rt/trunk/lib/tsan/rtl/tsan_platform_mac.cc =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_platform_mac.cc +++ compiler-rt/trunk/lib/tsan/rtl/tsan_platform_mac.cc @@ -207,7 +207,7 @@ ThreadState *parent_thread_state = nullptr; // No parent. int tid = ThreadCreate(parent_thread_state, 0, (uptr)thread, true); CHECK_NE(tid, 0); - ThreadStart(thr, tid, GetTid()); + ThreadStart(thr, tid, GetTid(), /*workerthread*/ true); } } else if (event == PTHREAD_INTROSPECTION_THREAD_TERMINATE) { if (thread == pthread_self()) { Index: compiler-rt/trunk/lib/tsan/rtl/tsan_report.h =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_report.h +++ compiler-rt/trunk/lib/tsan/rtl/tsan_report.h @@ -89,8 +89,9 @@ int id; uptr os_id; bool running; + bool workerthread; char *name; - int parent_tid; + u32 parent_tid; ReportStack *stack; }; Index: compiler-rt/trunk/lib/tsan/rtl/tsan_report.cc =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_report.cc +++ compiler-rt/trunk/lib/tsan/rtl/tsan_report.cc @@ -235,9 +235,15 @@ if (rt->name && rt->name[0] != '\0') Printf(" '%s'", rt->name); char thrbuf[kThreadBufSize]; - Printf(" (tid=%zu, %s) created by %s", - rt->os_id, rt->running ? "running" : "finished", - thread_name(thrbuf, rt->parent_tid)); + const char *thread_status = rt->running ? "running" : "finished"; + if (rt->workerthread) { + Printf(" (tid=%zu, %s) is a GCD worker thread\n", rt->os_id, thread_status); + Printf("\n"); + Printf("%s", d.EndThreadDescription()); + return; + } + Printf(" (tid=%zu, %s) created by %s", rt->os_id, thread_status, + thread_name(thrbuf, rt->parent_tid)); if (rt->stack) Printf(" at:"); Printf("\n"); Index: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h +++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.h @@ -713,7 +713,7 @@ void FuncExit(ThreadState *thr); int ThreadCreate(ThreadState *thr, uptr pc, uptr uid, bool detached); -void ThreadStart(ThreadState *thr, int tid, uptr os_id); +void ThreadStart(ThreadState *thr, int tid, uptr os_id, bool workerthread); void ThreadFinish(ThreadState *thr); int ThreadTid(ThreadState *thr, uptr pc, uptr uid); void ThreadJoin(ThreadState *thr, uptr pc, int tid); Index: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.cc =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.cc +++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl.cc @@ -381,7 +381,7 @@ // Initialize thread 0. int tid = ThreadCreate(thr, 0, 0, true); CHECK_EQ(tid, 0); - ThreadStart(thr, tid, GetTid()); + ThreadStart(thr, tid, GetTid(), /*workerthread*/ false); #if TSAN_CONTAINS_UBSAN __ubsan::InitAsPlugin(); #endif Index: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc +++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_report.cc @@ -202,6 +202,7 @@ rt->running = (tctx->status == ThreadStatusRunning); rt->name = internal_strdup(tctx->name); rt->parent_tid = tctx->parent_tid; + rt->workerthread = tctx->workerthread; rt->stack = 0; rt->stack = SymbolizeStackId(tctx->creation_stack_id); if (rt->stack) Index: compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_thread.cc =================================================================== --- compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_thread.cc +++ compiler-rt/trunk/lib/tsan/rtl/tsan_rtl_thread.cc @@ -236,7 +236,7 @@ return tid; } -void ThreadStart(ThreadState *thr, int tid, uptr os_id) { +void ThreadStart(ThreadState *thr, int tid, uptr os_id, bool workerthread) { uptr stk_addr = 0; uptr stk_size = 0; uptr tls_addr = 0; @@ -266,7 +266,7 @@ ThreadRegistry *tr = ctx->thread_registry; OnStartedArgs args = { thr, stk_addr, stk_size, tls_addr, tls_size }; - tr->StartThread(tid, os_id, &args); + tr->StartThread(tid, os_id, workerthread, &args); tr->Lock(); thr->tctx = (ThreadContext*)tr->GetThreadLocked(tid); Index: compiler-rt/trunk/test/tsan/Darwin/workerthreads.mm =================================================================== --- compiler-rt/trunk/test/tsan/Darwin/workerthreads.mm +++ compiler-rt/trunk/test/tsan/Darwin/workerthreads.mm @@ -0,0 +1,43 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %deflake %run %t 2>&1 | FileCheck %s + +#import + +#import "../test.h" + +long global; + +int main() { + fprintf(stderr, "Hello world.\n"); + print_address("addr=", 1, &global); + barrier_init(&barrier, 2); + + global = 42; + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + global = 43; + barrier_wait(&barrier); + }); + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ + barrier_wait(&barrier); + global = 44; + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }); + + CFRunLoopRun(); + fprintf(stderr, "Done.\n"); +} + +// CHECK: Hello world. +// CHECK: WARNING: ThreadSanitizer: data race +// CHECK: Write of size 8 +// CHECK: Previous write of size 8 +// CHECK: Location is global +// CHECK: Thread {{.*}} is a GCD worker thread +// CHECK-NOT: failed to restore the stack +// CHECK: Thread {{.*}} is a GCD worker thread +// CHECK-NOT: failed to restore the stack +// CHECK: Done.