Index: lib/tsan/dd/dd_rtl.cc =================================================================== --- lib/tsan/dd/dd_rtl.cc +++ lib/tsan/dd/dd_rtl.cc @@ -37,7 +37,7 @@ } static void ReportDeadlock(Thread *thr, DDReport *rep) { - if (rep == 0) + if (rep == 0 || thr->ignore_reads_writes_and_reports) return; BlockingMutexLock lock(&ctx->report_mutex); Printf("==============================\n"); Index: lib/tsan/rtl/tsan_interceptors.cc =================================================================== --- lib/tsan/rtl/tsan_interceptors.cc +++ lib/tsan/rtl/tsan_interceptors.cc @@ -692,7 +692,7 @@ if (fd > 0) FdAccess(thr, pc, fd); - if (thr->ignore_reads_and_writes == 0) + if (thr->ignore_reads_writes_and_reports == 0) MemoryRangeImitateWrite(thr, pc, (uptr)res, sz); else MemoryResetRange(thr, pc, (uptr)res, sz); @@ -711,7 +711,7 @@ if (fd > 0) FdAccess(thr, pc, fd); - if (thr->ignore_reads_and_writes == 0) + if (thr->ignore_reads_writes_and_reports == 0) MemoryRangeImitateWrite(thr, pc, (uptr)res, sz); else MemoryResetRange(thr, pc, (uptr)res, sz); @@ -1796,11 +1796,11 @@ // ignores are enabled we should disable ignores. This is critical for sync // and interceptors, because otherwise we can miss syncronization and report // false races. - int ignore_reads_and_writes = thr->ignore_reads_and_writes; + int ignore_reads_writes_and_reports = thr->ignore_reads_writes_and_reports; int ignore_interceptors = thr->ignore_interceptors; int ignore_sync = thr->ignore_sync; if (!ctx->after_multithreaded_fork) { - thr->ignore_reads_and_writes = 0; + thr->ignore_reads_writes_and_reports = 0; thr->fast_state.ClearIgnoreBit(); thr->ignore_interceptors = 0; thr->ignore_sync = 0; @@ -1821,8 +1821,8 @@ ((sighandler_t)pc)(sig); } if (!ctx->after_multithreaded_fork) { - thr->ignore_reads_and_writes = ignore_reads_and_writes; - if (ignore_reads_and_writes) + thr->ignore_reads_writes_and_reports = ignore_reads_writes_and_reports; + if (ignore_reads_writes_and_reports) thr->fast_state.SetIgnoreBit(); thr->ignore_interceptors = ignore_interceptors; thr->ignore_sync = ignore_sync; Index: lib/tsan/rtl/tsan_mman.cc =================================================================== --- lib/tsan/rtl/tsan_mman.cc +++ lib/tsan/rtl/tsan_mman.cc @@ -183,7 +183,7 @@ void OnUserAlloc(ThreadState *thr, uptr pc, uptr p, uptr sz, bool write) { DPrintf("#%d: alloc(%zu) = %p\n", thr->tid, sz, p); ctx->metamap.AllocBlock(thr, pc, p, sz); - if (write && thr->ignore_reads_and_writes == 0) + if (write && thr->ignore_reads_writes_and_reports == 0) MemoryRangeImitateWrite(thr, pc, (uptr)p, sz); else MemoryResetRange(thr, pc, (uptr)p, sz); @@ -193,7 +193,7 @@ CHECK_NE(p, (void*)0); uptr sz = ctx->metamap.FreeBlock(thr->proc(), p); DPrintf("#%d: free(%p, %zu)\n", thr->tid, p, sz); - if (write && thr->ignore_reads_and_writes == 0) + if (write && thr->ignore_reads_writes_and_reports == 0) MemoryRangeFreed(thr, pc, (uptr)p, sz); } Index: lib/tsan/rtl/tsan_rtl.h =================================================================== --- lib/tsan/rtl/tsan_rtl.h +++ lib/tsan/rtl/tsan_rtl.h @@ -379,7 +379,7 @@ // This is a slow path flag. On fast path, fast_state.GetIgnoreBit() is read. // We do not distinguish beteween ignoring reads and writes // for better performance. - int ignore_reads_and_writes; + int ignore_reads_writes_and_reports; int ignore_sync; // Go does not support ignores. #if !SANITIZER_GO Index: lib/tsan/rtl/tsan_rtl.cc =================================================================== --- lib/tsan/rtl/tsan_rtl.cc +++ lib/tsan/rtl/tsan_rtl.cc @@ -115,7 +115,7 @@ : fast_state(tid, epoch) // Do not touch these, rely on zero initialization, // they may be accessed before the ctor. - // , ignore_reads_and_writes() + // , ignore_reads_writes_and_reports() // , ignore_interceptors() , clock(tid, reuse_count) #if !SANITIZER_GO @@ -982,8 +982,8 @@ void ThreadIgnoreBegin(ThreadState *thr, uptr pc, bool save_stack) { DPrintf("#%d: ThreadIgnoreBegin\n", thr->tid); - thr->ignore_reads_and_writes++; - CHECK_GT(thr->ignore_reads_and_writes, 0); + thr->ignore_reads_writes_and_reports++; + CHECK_GT(thr->ignore_reads_writes_and_reports, 0); thr->fast_state.SetIgnoreBit(); #if !SANITIZER_GO if (save_stack && !ctx->after_multithreaded_fork) @@ -993,9 +993,9 @@ void ThreadIgnoreEnd(ThreadState *thr, uptr pc) { DPrintf("#%d: ThreadIgnoreEnd\n", thr->tid); - CHECK_GT(thr->ignore_reads_and_writes, 0); - thr->ignore_reads_and_writes--; - if (thr->ignore_reads_and_writes == 0) { + CHECK_GT(thr->ignore_reads_writes_and_reports, 0); + thr->ignore_reads_writes_and_reports--; + if (thr->ignore_reads_writes_and_reports == 0) { thr->fast_state.ClearIgnoreBit(); #if !SANITIZER_GO thr->mop_ignore_set.Reset(); Index: lib/tsan/rtl/tsan_rtl_mutex.cc =================================================================== --- lib/tsan/rtl/tsan_rtl_mutex.cc +++ lib/tsan/rtl/tsan_rtl_mutex.cc @@ -510,7 +510,7 @@ } void ReportDeadlock(ThreadState *thr, uptr pc, DDReport *r) { - if (r == 0) + if (r == 0 || thr->ignore_reads_writes_and_reports) return; ThreadRegistryLock l(ctx->thread_registry); ScopedReport rep(ReportTypeDeadlock); Index: lib/tsan/rtl/tsan_rtl_report.cc =================================================================== --- lib/tsan/rtl/tsan_rtl_report.cc +++ lib/tsan/rtl/tsan_rtl_report.cc @@ -40,7 +40,7 @@ ScopedIgnoreInterceptors ignore; #if !SANITIZER_GO cur_thread()->ignore_sync++; - cur_thread()->ignore_reads_and_writes++; + cur_thread()->ignore_reads_writes_and_reports++; #endif Printf("FATAL: ThreadSanitizer CHECK failed: " "%s:%d \"%s\" (0x%zx, 0x%zx)\n", Index: lib/tsan/rtl/tsan_rtl_thread.cc =================================================================== --- lib/tsan/rtl/tsan_rtl_thread.cc +++ lib/tsan/rtl/tsan_rtl_thread.cc @@ -192,7 +192,7 @@ static void ThreadCheckIgnore(ThreadState *thr) { if (ctx->after_multithreaded_fork) return; - if (thr->ignore_reads_and_writes) + if (thr->ignore_reads_writes_and_reports) ReportIgnoresEnabled(thr->tctx, &thr->mop_ignore_set); if (thr->ignore_sync) ReportIgnoresEnabled(thr->tctx, &thr->sync_ignore_set); Index: test/tsan/Darwin/deadlock.mm =================================================================== --- test/tsan/Darwin/deadlock.mm +++ test/tsan/Darwin/deadlock.mm @@ -0,0 +1,47 @@ +// RUN: %clang_tsan %s -o %t -framework Foundation +// RUN: %deflake %run %t 2>&1 | FileCheck %s + +#import + +#import "../test.h" + +pthread_mutex_t m1; +pthread_mutex_t m2; + +int main(int argc, const char *argv[]) { + barrier_init(&barrier, 2); + fprintf(stderr, "Hello world.\n"); + + pthread_mutex_init(&m1, NULL); + pthread_mutex_init(&m2, NULL); + + [NSThread detachNewThreadWithBlock:^{ + pthread_mutex_lock(&m1); + pthread_mutex_lock(&m2); + pthread_mutex_unlock(&m2); + pthread_mutex_unlock(&m1); + + barrier_wait(&barrier); + }]; + [NSThread detachNewThreadWithBlock:^{ + barrier_wait(&barrier); + + pthread_mutex_lock(&m2); + pthread_mutex_lock(&m1); + pthread_mutex_unlock(&m1); + pthread_mutex_unlock(&m2); + + dispatch_sync(dispatch_get_main_queue(), ^{ + CFRunLoopStop(CFRunLoopGetCurrent()); + }); + }]; + + CFRunLoopRun(); + + fprintf(stderr, "Done.\n"); + return 0; +} + +// CHECK: Hello world. +// CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock) +// CHECK: Done.