// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadMutex // RUN: %env_tsan_opts=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-NOT-SECOND // RUN: %env_tsan_opts=detect_deadlocks=1:second_deadlock_stack=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-SECOND // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadSpinLock // RUN: %env_tsan_opts=detect_deadlocks=1 %deflake %run %t | FileCheck %s // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRWLock // RUN: %env_tsan_opts=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-RD // RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRecursiveMutex // RUN: %env_tsan_opts=detect_deadlocks=1 %deflake %run %t | FileCheck %s --check-prefix=CHECK --check-prefix=CHECK-REC #include "test.h" #undef NDEBUG #include #include #ifndef LockType #define LockType PthreadMutex #endif // You can optionally pass [test_number [iter_count]] on command line. static int test_number = -1; static int iter_count = 100000; class PthreadMutex { public: explicit PthreadMutex(bool recursive = false) { if (recursive) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); assert(0 == pthread_mutex_init(&mu_, &attr)); } else { assert(0 == pthread_mutex_init(&mu_, 0)); } } ~PthreadMutex() { assert(0 == pthread_mutex_destroy(&mu_)); (void)padding_; } static bool supports_read_lock() { return false; } static bool supports_recursive_lock() { return false; } void lock() { assert(0 == pthread_mutex_lock(&mu_)); } void unlock() { assert(0 == pthread_mutex_unlock(&mu_)); } bool try_lock() { return 0 == pthread_mutex_trylock(&mu_); } void rdlock() { assert(0); } void rdunlock() { assert(0); } bool try_rdlock() { assert(0); } private: pthread_mutex_t mu_; char padding_[64 - sizeof(pthread_mutex_t)]; }; class PthreadRecursiveMutex : public PthreadMutex { public: PthreadRecursiveMutex() : PthreadMutex(true) { } static bool supports_recursive_lock() { return true; } }; #ifndef __APPLE__ class PthreadSpinLock { public: PthreadSpinLock() { assert(0 == pthread_spin_init(&mu_, 0)); } ~PthreadSpinLock() { assert(0 == pthread_spin_destroy(&mu_)); (void)padding_; } static bool supports_read_lock() { return false; } static bool supports_recursive_lock() { return false; } void lock() { assert(0 == pthread_spin_lock(&mu_)); } void unlock() { assert(0 == pthread_spin_unlock(&mu_)); } bool try_lock() { return 0 == pthread_spin_trylock(&mu_); } void rdlock() { assert(0); } void rdunlock() { assert(0); } bool try_rdlock() { assert(0); } private: pthread_spinlock_t mu_; char padding_[64 - sizeof(pthread_spinlock_t)]; }; #else class PthreadSpinLock : public PthreadMutex { }; #endif class PthreadRWLock { public: PthreadRWLock() { assert(0 == pthread_rwlock_init(&mu_, 0)); } ~PthreadRWLock() { assert(0 == pthread_rwlock_destroy(&mu_)); (void)padding_; } static bool supports_read_lock() { return true; } static bool supports_recursive_lock() { return false; } void lock() { assert(0 == pthread_rwlock_wrlock(&mu_)); } void unlock() { assert(0 == pthread_rwlock_unlock(&mu_)); } bool try_lock() { return 0 == pthread_rwlock_trywrlock(&mu_); } void rdlock() { assert(0 == pthread_rwlock_rdlock(&mu_)); } void rdunlock() { assert(0 == pthread_rwlock_unlock(&mu_)); } bool try_rdlock() { return 0 == pthread_rwlock_tryrdlock(&mu_); } private: pthread_rwlock_t mu_; char padding_[256 - sizeof(pthread_rwlock_t)]; }; class LockTest { public: LockTest() : n_(), locks_() {} void Init(size_t n) { n_ = n; locks_ = new LockType*[n_]; for (size_t i = 0; i < n_; i++) locks_[i] = new LockType; } ~LockTest() { for (size_t i = 0; i < n_; i++) delete locks_[i]; delete [] locks_; } void L(size_t i) { assert(i < n_); locks_[i]->lock(); } void U(size_t i) { assert(i < n_); locks_[i]->unlock(); } void RL(size_t i) { assert(i < n_); locks_[i]->rdlock(); } void RU(size_t i) { assert(i < n_); locks_[i]->rdunlock(); } void *A(size_t i) { assert(i < n_); return locks_[i]; } bool T(size_t i) { assert(i < n_); return locks_[i]->try_lock(); } // Simple lock order onversion. void Test1() { if (test_number > 0 && test_number != 1) return; fprintf(stderr, "Starting Test1\n"); // CHECK: Starting Test1 Init(5); print_address("Expecting lock inversion: ", 2, A(0), A(1)); // CHECK: Expecting lock inversion: [[A1:0x[a-f0-9]*]] [[A2:0x[a-f0-9]*]] Lock_0_1(); Lock_1_0(); // CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock) // CHECK: Cycle in lock order graph: [[M1:M[0-9]+]] ([[A1]]) => [[M2:M[0-9]+]] ([[A2]]) => [[M1]] // CHECK: Mutex [[M2]] acquired here while holding mutex [[M1]] // CHECK: #0 pthread_ // CHECK-SECOND: Mutex [[M1]] previously acquired by the same thread here: // CHECK-SECOND: #0 pthread_ // CHECK-NOT-SECOND: second_deadlock_stack=1 to get more informative warning message // CHECK-NOT-SECOND-NOT: #0 pthread_ // CHECK: Mutex [[M1]] acquired here while holding mutex [[M2]] // CHECK: #0 pthread_ // CHECK-SECOND: Mutex [[M2]] previously acquired by the same thread here: // CHECK-SECOND: #0 pthread_ // CHECK-NOT-SECOND-NOT: #0 pthread_ // CHECK-NOT: WARNING: ThreadSanitizer: } // Simple lock order inversion with 3 locks. void Test2() { if (test_number > 0 && test_number != 2) return; fprintf(stderr, "Starting Test2\n"); // CHECK: Starting Test2 Init(5); print_address("Expecting lock inversion: ", 3, A(0), A(1), A(2)); // CHECK: Expecting lock inversion: [[A1:0x[a-f0-9]*]] [[A2:0x[a-f0-9]*]] [[A3:0x[a-f0-9]*]] Lock2(0, 1); Lock2(1, 2); Lock2(2, 0); // CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock) // CHECK: Cycle in lock order graph: [[M1:M[0-9]+]] ([[A1]]) => [[M2:M[0-9]+]] ([[A2]]) => [[M3:M[0-9]+]] ([[A3]]) => [[M1]] // CHECK-NOT: WARNING: ThreadSanitizer: } // Lock order inversion with lots of new locks created (but not used) // between. Since the new locks are not used we should still detect the // deadlock. void Test3() { if (test_number > 0 && test_number != 3) return; fprintf(stderr, "Starting Test3\n"); // CHECK: Starting Test3 Init(5); Lock_0_1(); L(2); CreateAndDestroyManyLocks(); U(2); Lock_1_0(); // CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock) // CHECK-NOT: WARNING: ThreadSanitizer: } // lock l0=>l1; then create and use lots of locks; then lock l1=>l0. // The deadlock epoch should have changed and we should not report anything. void Test4() { if (test_number > 0 && test_number != 4) return; fprintf(stderr, "Starting Test4\n"); // CHECK: Starting Test4 Init(5); Lock_0_1(); L(2); CreateLockUnlockAndDestroyManyLocks(); U(2); Lock_1_0(); // CHECK-NOT: WARNING: ThreadSanitizer: } void Test5() { if (test_number > 0 && test_number != 5) return; fprintf(stderr, "Starting Test5\n"); // CHECK: Starting Test5 Init(5); RunThreads(&LockTest::Lock_0_1, &LockTest::Lock_1_0); // CHECK: WARNING: ThreadSanitizer: lock-order-inversion // CHECK: Cycle in lock order graph: [[M1:M[0-9]+]] ({{.*}}) => [[M2:M[0-9]+]] ({{.*}}) => [[M1]] // CHECK: Mutex [[M2]] acquired here while holding mutex [[M1]] in thread [[T1:T[0-9]+]] // CHECK: Mutex [[M1]] acquired here while holding mutex [[M2]] in thread [[T2:T[0-9]+]] // CHECK: Thread [[T1]] {{.*}} created by main thread // CHECK: Thread [[T2]] {{.*}} created by main thread // CHECK-NOT: WARNING: ThreadSanitizer: } void Test6() { if (test_number > 0 && test_number != 6) return; fprintf(stderr, "Starting Test6: 3 threads lock/unlock private mutexes\n"); // CHECK: Starting Test6 Init(100); // CHECK-NOT: WARNING: ThreadSanitizer: RunThreads(&LockTest::Lock1_Loop_0, &LockTest::Lock1_Loop_1, &LockTest::Lock1_Loop_2); } void Test7() { if (test_number > 0 && test_number != 7) return; fprintf(stderr, "Starting Test7\n"); // CHECK: Starting Test7 Init(10); L(0); T(1); U(1); U(0); T(1); L(0); U(1); U(0); // CHECK-NOT: WARNING: ThreadSanitizer: fprintf(stderr, "No cycle: 0=>1\n"); // CHECK: No cycle: 0=>1 T(2); L(3); U(3); U(2); L(3); T(2); U(3); U(2); // CHECK-NOT: WARNING: ThreadSanitizer: fprintf(stderr, "No cycle: 2=>3\n"); // CHECK: No cycle: 2=>3 T(4); L(5); U(4); U(5); L(5); L(4); U(4); U(5); // CHECK: WARNING: ThreadSanitizer: lock-order-inversion fprintf(stderr, "Have cycle: 4=>5\n"); // CHECK: Have cycle: 4=>5 L(7); L(6); U(6); U(7); T(6); L(7); U(6); U(7); // CHECK: WARNING: ThreadSanitizer: lock-order-inversion fprintf(stderr, "Have cycle: 6=>7\n"); // CHECK: Have cycle: 6=>7 } void Test8() { if (test_number > 0 && test_number != 8) return; if (!LockType::supports_read_lock()) return; fprintf(stderr, "Starting Test8\n"); Init(5); // CHECK-RD: Starting Test8 RL(0); L(1); RU(0); U(1); L(1); RL(0); RU(0); U(1); // CHECK-RD: WARNING: ThreadSanitizer: lock-order-inversion fprintf(stderr, "Have cycle: 0=>1\n"); // CHECK-RD: Have cycle: 0=>1 RL(2); RL(3); RU(2); RU(3); RL(3); RL(2); RU(2); RU(3); // CHECK-RD: WARNING: ThreadSanitizer: lock-order-inversion fprintf(stderr, "Have cycle: 2=>3\n"); // CHECK-RD: Have cycle: 2=>3 } void Test9() { if (test_number > 0 && test_number != 9) return; if (!LockType::supports_recursive_lock()) return; fprintf(stderr, "Starting Test9\n"); // CHECK-REC: Starting Test9 Init(5); L(0); L(0); L(0); L(1); U(1); U(0); U(0); U(0); L(1); L(1); L(1); L(0); U(0); U(1); U(1); U(1); // CHECK-REC: WARNING: ThreadSanitizer: lock-order-inversion } void Test10() { if (test_number > 0 && test_number != 10) return; fprintf(stderr, "Starting Test10: 4 threads lock/unlock 4 private mutexes, one under another\n"); // CHECK: Starting Test10 Init(100); // CHECK-NOT: WARNING: ThreadSanitizer: RunThreads(&LockTest::Test10_Thread1, &LockTest::Test10_Thread2, &LockTest::Test10_Thread3, &LockTest::Test10_Thread4); } void Test10_Thread1() { Test10_Thread(0); } void Test10_Thread2() { Test10_Thread(10); } void Test10_Thread3() { Test10_Thread(20); } void Test10_Thread4() { Test10_Thread(30); } void Test10_Thread(size_t m) { for (int i = 0; i < iter_count; i++) { L(m + 0); L(m + 1); L(m + 2); L(m + 3); U(m + 3); U(m + 2); U(m + 1); U(m + 0); } } void Test11() { if (test_number > 0 && test_number != 11) return; fprintf(stderr, "Starting Test11: 4 threads lock/unlock 4 private mutexes, all under another private mutex\n"); // CHECK: Starting Test11 Init(500); // CHECK-NOT: WARNING: ThreadSanitizer: RunThreads(&LockTest::Test11_Thread1, &LockTest::Test11_Thread2, &LockTest::Test11_Thread3, &LockTest::Test11_Thread4); } void Test11_Thread1() { Test10_Thread(0); } void Test11_Thread2() { Test10_Thread(10); } void Test11_Thread3() { Test10_Thread(20); } void Test11_Thread4() { Test10_Thread(30); } void Test11_Thread(size_t m) { for (int i = 0; i < iter_count; i++) { L(m); L(m + 100); U(m + 100); L(m + 200); U(m + 200); L(m + 300); U(m + 300); L(m + 400); U(m + 500); U(m); } } void Test12() { if (test_number > 0 && test_number != 12) return; if (!LockType::supports_read_lock()) return; fprintf(stderr, "Starting Test12: 4 threads read lock/unlock 4 shared mutexes, one under another\n"); // CHECK-RD: Starting Test12 Init(500); // CHECK-RD-NOT: WARNING: ThreadSanitizer: RunThreads(&LockTest::Test12_Thread, &LockTest::Test12_Thread, &LockTest::Test12_Thread, &LockTest::Test12_Thread); } void Test12_Thread() { for (int i = 0; i < iter_count; i++) { RL(000); RL(100); RL(200); RL(300); RU(300); RU(200); RU(100); RU(000); } } void Test13() { if (test_number > 0 && test_number != 13) return; if (!LockType::supports_read_lock()) return; fprintf(stderr, "Starting Test13: 4 threads read lock/unlock 4 shared mutexes, all under another shared mutex\n"); // CHECK-RD: Starting Test13 Init(500); // CHECK-RD-NOT: WARNING: ThreadSanitizer: RunThreads(&LockTest::Test13_Thread, &LockTest::Test13_Thread, &LockTest::Test13_Thread, &LockTest::Test13_Thread); } void Test13_Thread() { for (int i = 0; i < iter_count; i++) { RL(0); RL(100); RU(100); RL(200); RU(200); RL(300); RU(300); RL(400); RU(400); RU(0); } } void Test14() { if (test_number > 0 && test_number != 14) return; fprintf(stderr, "Starting Test14: create lots of locks in 4 threads\n"); Init(10); // CHECK-RD: Starting Test14 RunThreads(&LockTest::CreateAndDestroyLocksLoop, &LockTest::CreateAndDestroyLocksLoop, &LockTest::CreateAndDestroyLocksLoop, &LockTest::CreateAndDestroyLocksLoop); } void Test15() { if (test_number > 0 && test_number != 15) return; if (!LockType::supports_read_lock()) return; fprintf(stderr, "Starting Test15: recursive rlock\n"); // DISABLEDCHECK-RD: Starting Test15 Init(5); RL(0); RL(0); RU(0); RU(0); // Recusrive reader lock. RL(0); RL(0); RL(0); RU(0); RU(0); RU(0); // Recusrive reader lock. } // More detailed output test. void Test16() { if (test_number > 0 && test_number != 16) return; fprintf(stderr, "Starting Test16: detailed output test with two locks\n"); // CHECK: Starting Test16 // CHECK: WARNING: ThreadSanitizer: lock-order-inversion // CHECK: acquired here while holding mutex // CHECK: LockTest::Acquire1 // CHECK-NEXT: LockTest::Acquire_0_then_1 // CHECK-SECOND: previously acquired by the same thread here // CHECK-SECOND: LockTest::Acquire0 // CHECK-SECOND-NEXT: LockTest::Acquire_0_then_1 // CHECK: acquired here while holding mutex // CHECK: LockTest::Acquire0 // CHECK-NEXT: LockTest::Acquire_1_then_0 // CHECK-SECOND: previously acquired by the same thread here // CHECK-SECOND: LockTest::Acquire1 // CHECK-SECOND-NEXT: LockTest::Acquire_1_then_0 Init(5); Acquire_0_then_1(); U(0); U(1); Acquire_1_then_0(); U(0); U(1); } // More detailed output test. void Test17() { if (test_number > 0 && test_number != 17) return; fprintf(stderr, "Starting Test17: detailed output test with three locks\n"); // CHECK: Starting Test17 // CHECK: WARNING: ThreadSanitizer: lock-order-inversion // CHECK: LockTest::Acquire1 // CHECK-NEXT: LockTest::Acquire_0_then_1 // CHECK: LockTest::Acquire2 // CHECK-NEXT: LockTest::Acquire_1_then_2 // CHECK: LockTest::Acquire0 // CHECK-NEXT: LockTest::Acquire_2_then_0 Init(5); Acquire_0_then_1(); U(0); U(1); Acquire_1_then_2(); U(1); U(2); Acquire_2_then_0(); U(0); U(2); } __attribute__((noinline)) void Acquire2() { L(2); } __attribute__((noinline)) void Acquire1() { L(1); } __attribute__((noinline)) void Acquire0() { L(0); } __attribute__((noinline)) void Acquire_1_then_0() { Acquire1(); Acquire0(); } __attribute__((noinline)) void Acquire_0_then_1() { Acquire0(); Acquire1(); } __attribute__((noinline)) void Acquire_1_then_2() { Acquire1(); Acquire2(); } __attribute__((noinline)) void Acquire_2_then_0() { Acquire2(); Acquire0(); } // This test creates, locks, unlocks and destroys lots of mutexes. void Test18() { if (test_number > 0 && test_number != 18) return; fprintf(stderr, "Starting Test18: create, lock and destroy 4 locks; all in " "4 threads in a loop\n"); RunThreads(&LockTest::Test18_Thread, &LockTest::Test18_Thread, &LockTest::Test18_Thread, &LockTest::Test18_Thread); } void Test18_Thread() { LockType *l = new LockType[4]; for (size_t i = 0; i < iter_count / 100; i++) { for (int i = 0; i < 4; i++) l[i].lock(); for (int i = 0; i < 4; i++) l[i].unlock(); for (int i = 0; i < 4; i++) l[i].~LockType(); for (int i = 0; i < 4; i++) new ((void*)&l[i]) LockType(); } delete [] l; } void Test19() { if (test_number > 0 && test_number != 19) return; fprintf(stderr, "Starting Test19: lots of lock inversions\n"); const int kNumLocks = 45; Init(kNumLocks); for (int i = 0; i < kNumLocks; i++) { for (int j = 0; j < kNumLocks; j++) L((i + j) % kNumLocks); for (int j = 0; j < kNumLocks; j++) U((i + j) % kNumLocks); } } private: void Lock2(size_t l1, size_t l2) { L(l1); L(l2); U(l2); U(l1); } template void Lock_0_1() { Lock2(0, 1); if (wait) barrier_wait(&barrier); } template void Lock_1_0() { if (wait) barrier_wait(&barrier); Lock2(1, 0); } void Lock1_Loop(size_t i, size_t n_iter) { for (size_t it = 0; it < n_iter; it++) { // if ((it & (it - 1)) == 0) fprintf(stderr, "%zd", i); L(i); U(i); } // fprintf(stderr, "\n"); } void Lock1_Loop_0() { Lock1_Loop(0, iter_count); } void Lock1_Loop_1() { Lock1_Loop(10, iter_count); } void Lock1_Loop_2() { Lock1_Loop(20, iter_count); } void CreateAndDestroyManyLocks() { LockType *create_many_locks_but_never_acquire = new LockType[kDeadlockGraphSize]; (void)create_many_locks_but_never_acquire; delete [] create_many_locks_but_never_acquire; } void CreateAndDestroyLocksLoop() { for (size_t it = 0; it <= iter_count; it++) { LockType some_locks[10]; (void)some_locks; } } void CreateLockUnlockAndDestroyManyLocks() { LockType many_locks[kDeadlockGraphSize]; for (size_t i = 0; i < kDeadlockGraphSize; i++) { many_locks[i].lock(); many_locks[i].unlock(); } } // LockTest Member function callback. struct CB { void (LockTest::*f)(); LockTest *lt; }; // Thread function with CB. static void *Thread(void *param) { CB *cb = (CB*)param; (cb->lt->*cb->f)(); return NULL; } void RunThreads(void (LockTest::*f1)(), void (LockTest::*f2)(), void (LockTest::*f3)() = 0, void (LockTest::*f4)() = 0) { const int kNumThreads = 4; pthread_t t[kNumThreads]; CB cb[kNumThreads] = {{f1, this}, {f2, this}, {f3, this}, {f4, this}}; for (int i = 0; i < kNumThreads && cb[i].f; i++) pthread_create(&t[i], 0, Thread, &cb[i]); for (int i = 0; i < kNumThreads && cb[i].f; i++) pthread_join(t[i], 0); } static const size_t kDeadlockGraphSize = 4096; size_t n_; LockType **locks_; }; int main(int argc, char **argv) { barrier_init(&barrier, 2); if (argc > 1) test_number = atoi(argv[1]); if (argc > 2) iter_count = atoi(argv[2]); LockTest().Test1(); LockTest().Test2(); LockTest().Test3(); LockTest().Test4(); LockTest().Test5(); LockTest().Test6(); LockTest().Test7(); LockTest().Test8(); LockTest().Test9(); LockTest().Test10(); LockTest().Test11(); LockTest().Test12(); LockTest().Test13(); LockTest().Test14(); LockTest().Test15(); LockTest().Test16(); LockTest().Test17(); LockTest().Test18(); LockTest().Test19(); fprintf(stderr, "ALL-DONE\n"); // CHECK: ALL-DONE }