2014-02-25 17:33:10 +08:00
|
|
|
// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadMutex
|
|
|
|
// RUN: TSAN_OPTIONS=detect_deadlocks=1 not %t 2>&1 | FileCheck %s
|
|
|
|
// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadSpinLock
|
|
|
|
// RUN: TSAN_OPTIONS=detect_deadlocks=1 not %t 2>&1 | FileCheck %s
|
|
|
|
// RUN: %clangxx_tsan %s -o %t -DLockType=PthreadRWLock
|
2014-02-19 22:17:25 +08:00
|
|
|
// RUN: TSAN_OPTIONS=detect_deadlocks=1 not %t 2>&1 | FileCheck %s
|
2014-02-18 17:08:03 +08:00
|
|
|
#include <pthread.h>
|
|
|
|
#undef NDEBUG
|
|
|
|
#include <assert.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
|
2014-02-25 16:42:34 +08:00
|
|
|
class PthreadMutex {
|
2014-02-18 17:08:03 +08:00
|
|
|
public:
|
2014-02-25 16:42:34 +08:00
|
|
|
PthreadMutex() { assert(0 == pthread_mutex_init(&mu_, 0)); }
|
|
|
|
~PthreadMutex() {
|
2014-02-18 17:08:03 +08:00
|
|
|
assert(0 == pthread_mutex_destroy(&mu_));
|
|
|
|
(void)padding_;
|
|
|
|
}
|
|
|
|
void lock() { assert(0 == pthread_mutex_lock(&mu_)); }
|
|
|
|
void unlock() { assert(0 == pthread_mutex_unlock(&mu_)); }
|
2014-02-25 16:24:15 +08:00
|
|
|
bool try_lock() { return 0 == pthread_mutex_trylock(&mu_); }
|
2014-02-18 17:08:03 +08:00
|
|
|
|
|
|
|
private:
|
|
|
|
pthread_mutex_t mu_;
|
|
|
|
char padding_[64 - sizeof(pthread_mutex_t)];
|
|
|
|
};
|
|
|
|
|
2014-02-25 16:42:34 +08:00
|
|
|
class PthreadSpinLock {
|
|
|
|
public:
|
|
|
|
PthreadSpinLock() { assert(0 == pthread_spin_init(&mu_, 0)); }
|
|
|
|
~PthreadSpinLock() {
|
|
|
|
assert(0 == pthread_spin_destroy(&mu_));
|
|
|
|
(void)padding_;
|
|
|
|
}
|
|
|
|
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_); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
pthread_spinlock_t mu_;
|
|
|
|
char padding_[64 - sizeof(pthread_spinlock_t)];
|
|
|
|
};
|
|
|
|
|
2014-02-25 17:33:10 +08:00
|
|
|
class PthreadRWLock {
|
|
|
|
public:
|
|
|
|
PthreadRWLock() { assert(0 == pthread_rwlock_init(&mu_, 0)); }
|
|
|
|
~PthreadRWLock() {
|
|
|
|
assert(0 == pthread_rwlock_destroy(&mu_));
|
|
|
|
(void)padding_;
|
|
|
|
}
|
|
|
|
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_); }
|
|
|
|
|
|
|
|
private:
|
|
|
|
pthread_rwlock_t mu_;
|
|
|
|
char padding_[64 - sizeof(pthread_rwlock_t)];
|
|
|
|
};
|
2014-02-25 16:42:34 +08:00
|
|
|
|
2014-02-18 17:08:03 +08:00
|
|
|
class LockTest {
|
|
|
|
public:
|
2014-02-25 16:42:34 +08:00
|
|
|
LockTest(size_t n) : n_(n), locks_(new LockType[n]) { }
|
2014-02-18 17:08:03 +08:00
|
|
|
~LockTest() { delete [] locks_; }
|
|
|
|
void L(size_t i) {
|
|
|
|
assert(i < n_);
|
|
|
|
locks_[i].lock();
|
|
|
|
}
|
|
|
|
void U(size_t i) {
|
|
|
|
assert(i < n_);
|
|
|
|
locks_[i].unlock();
|
|
|
|
}
|
|
|
|
|
2014-02-18 22:56:19 +08:00
|
|
|
void *A(size_t i) {
|
|
|
|
assert(i < n_);
|
|
|
|
return &locks_[i];
|
|
|
|
}
|
|
|
|
|
2014-02-25 16:24:15 +08:00
|
|
|
bool T(size_t i) {
|
|
|
|
assert(i < n_);
|
|
|
|
return locks_[i].try_lock();
|
|
|
|
}
|
|
|
|
|
2014-02-18 20:50:31 +08:00
|
|
|
// Simple lock order onversion.
|
2014-02-18 17:08:03 +08:00
|
|
|
void Test1() {
|
|
|
|
fprintf(stderr, "Starting Test1\n");
|
|
|
|
// CHECK: Starting Test1
|
2014-02-18 22:56:19 +08:00
|
|
|
fprintf(stderr, "Expecting lock inversion: %p %p\n", A(0), A(1));
|
|
|
|
// CHECK: Expecting lock inversion: [[A1:0x[a-f0-9]*]] [[A2:0x[a-f0-9]*]]
|
2014-02-21 23:07:18 +08:00
|
|
|
Lock_0_1();
|
|
|
|
Lock_1_0();
|
2014-02-19 22:17:25 +08:00
|
|
|
// CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock)
|
|
|
|
// CHECK: path: [[M1:M[0-9]+]] => [[M2:M[0-9]+]] => [[M1]]
|
|
|
|
// CHECK: Mutex [[M1]] ([[A1]]) created at:
|
|
|
|
// CHECK: Mutex [[M2]] ([[A2]]) created at:
|
|
|
|
// CHECK-NOT: WARNING: ThreadSanitizer:
|
2014-02-18 17:08:03 +08:00
|
|
|
}
|
|
|
|
|
2014-02-18 20:50:31 +08:00
|
|
|
// Simple lock order inversion with 3 locks.
|
2014-02-18 17:08:03 +08:00
|
|
|
void Test2() {
|
|
|
|
fprintf(stderr, "Starting Test2\n");
|
|
|
|
// CHECK: Starting Test2
|
2014-02-19 22:24:31 +08:00
|
|
|
fprintf(stderr, "Expecting lock inversion: %p %p %p\n", 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]*]]
|
2014-02-21 23:07:18 +08:00
|
|
|
Lock2(0, 1);
|
|
|
|
Lock2(1, 2);
|
|
|
|
Lock2(2, 0);
|
2014-02-19 22:17:25 +08:00
|
|
|
// CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock)
|
2014-02-19 22:24:31 +08:00
|
|
|
// CHECK: path: [[M1:M[0-9]+]] => [[M2:M[0-9]+]] => [[M3:M[0-9]+]] => [[M1]]
|
|
|
|
// CHECK: Mutex [[M1]] ([[A1]]) created at:
|
|
|
|
// CHECK: Mutex [[M2]] ([[A2]]) created at:
|
|
|
|
// CHECK: Mutex [[M3]] ([[A3]]) created at:
|
2014-02-19 22:17:25 +08:00
|
|
|
// CHECK-NOT: WARNING: ThreadSanitizer:
|
2014-02-18 17:08:03 +08:00
|
|
|
}
|
|
|
|
|
2014-02-18 20:50:31 +08:00
|
|
|
// 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() {
|
|
|
|
fprintf(stderr, "Starting Test3\n");
|
|
|
|
// CHECK: Starting Test3
|
2014-02-21 23:07:18 +08:00
|
|
|
Lock_0_1();
|
2014-02-18 21:41:49 +08:00
|
|
|
L(2);
|
2014-02-18 20:50:31 +08:00
|
|
|
CreateAndDestroyManyLocks();
|
2014-02-18 21:41:49 +08:00
|
|
|
U(2);
|
2014-02-21 23:07:18 +08:00
|
|
|
Lock_1_0();
|
2014-02-19 22:17:25 +08:00
|
|
|
// CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock)
|
|
|
|
// CHECK-NOT: WARNING: ThreadSanitizer:
|
2014-02-18 20:50:31 +08:00
|
|
|
}
|
|
|
|
|
2014-02-18 21:41:49 +08:00
|
|
|
// 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() {
|
|
|
|
fprintf(stderr, "Starting Test4\n");
|
|
|
|
// CHECK: Starting Test4
|
2014-02-21 23:07:18 +08:00
|
|
|
Lock_0_1();
|
2014-02-18 21:41:49 +08:00
|
|
|
L(2);
|
|
|
|
CreateLockUnlockAndDestroyManyLocks();
|
|
|
|
U(2);
|
2014-02-21 23:07:18 +08:00
|
|
|
Lock_1_0();
|
|
|
|
// CHECK-NOT: WARNING: ThreadSanitizer:
|
|
|
|
}
|
|
|
|
|
|
|
|
void Test5() {
|
|
|
|
fprintf(stderr, "Starting Test5\n");
|
|
|
|
// CHECK: Starting Test5
|
|
|
|
RunThreads(&LockTest::Lock_0_1, &LockTest::Lock_1_0);
|
|
|
|
// CHECK: WARNING: ThreadSanitizer: lock-order-inversion
|
2014-02-19 22:17:25 +08:00
|
|
|
// CHECK-NOT: WARNING: ThreadSanitizer:
|
2014-02-18 21:41:49 +08:00
|
|
|
}
|
|
|
|
|
2014-02-24 19:45:47 +08:00
|
|
|
void Test6() {
|
|
|
|
fprintf(stderr, "Starting Test6\n");
|
|
|
|
// CHECK: Starting Test6
|
|
|
|
// CHECK-NOT: WARNING: ThreadSanitizer:
|
|
|
|
RunThreads(&LockTest::Lock1_Loop_0, &LockTest::Lock1_Loop_1,
|
|
|
|
&LockTest::Lock1_Loop_2);
|
|
|
|
}
|
|
|
|
|
2014-02-25 16:24:15 +08:00
|
|
|
void Test7() {
|
|
|
|
fprintf(stderr, "Starting Test7\n");
|
|
|
|
// CHECK: Starting Test7
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
2014-02-18 17:08:03 +08:00
|
|
|
private:
|
2014-02-21 23:07:18 +08:00
|
|
|
void Lock2(size_t l1, size_t l2) { L(l1); L(l2); U(l2); U(l1); }
|
|
|
|
void Lock_0_1() { Lock2(0, 1); }
|
|
|
|
void Lock_1_0() { Lock2(1, 0); }
|
2014-02-24 19:45:47 +08:00
|
|
|
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, 100000); }
|
|
|
|
void Lock1_Loop_1() { Lock1_Loop(1, 100000); }
|
|
|
|
void Lock1_Loop_2() { Lock1_Loop(2, 100000); }
|
2014-02-21 23:07:18 +08:00
|
|
|
|
2014-02-18 20:50:31 +08:00
|
|
|
void CreateAndDestroyManyLocks() {
|
2014-02-25 16:42:34 +08:00
|
|
|
LockType create_many_locks_but_never_acquire[kDeadlockGraphSize];
|
2014-02-25 17:33:10 +08:00
|
|
|
(void)create_many_locks_but_never_acquire;
|
2014-02-18 20:50:31 +08:00
|
|
|
}
|
2014-02-25 17:33:10 +08:00
|
|
|
|
2014-02-18 21:41:49 +08:00
|
|
|
void CreateLockUnlockAndDestroyManyLocks() {
|
2014-02-25 16:42:34 +08:00
|
|
|
LockType many_locks[kDeadlockGraphSize];
|
2014-02-18 21:41:49 +08:00
|
|
|
for (size_t i = 0; i < kDeadlockGraphSize; i++) {
|
|
|
|
many_locks[i].lock();
|
|
|
|
many_locks[i].unlock();
|
|
|
|
}
|
|
|
|
}
|
2014-02-21 23:07:18 +08:00
|
|
|
|
|
|
|
// 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;
|
|
|
|
}
|
|
|
|
|
2014-02-24 19:45:47 +08:00
|
|
|
void RunThreads(void (LockTest::*f1)(), void (LockTest::*f2)(),
|
|
|
|
void (LockTest::*f3)() = 0) {
|
|
|
|
const int kNumThreads = 3;
|
2014-02-21 23:07:18 +08:00
|
|
|
pthread_t t[kNumThreads];
|
2014-02-24 19:45:47 +08:00
|
|
|
CB cb[kNumThreads] = {{f1, this}, {f2, this}, {f3, this}};
|
|
|
|
for (int i = 0; i < kNumThreads && cb[i].f; i++)
|
2014-02-21 23:07:18 +08:00
|
|
|
pthread_create(&t[i], 0, Thread, &cb[i]);
|
2014-02-24 19:45:47 +08:00
|
|
|
for (int i = 0; i < kNumThreads && cb[i].f; i++)
|
2014-02-21 23:07:18 +08:00
|
|
|
pthread_join(t[i], 0);
|
|
|
|
}
|
|
|
|
|
2014-02-18 20:50:31 +08:00
|
|
|
static const size_t kDeadlockGraphSize = 4096;
|
2014-02-18 17:08:03 +08:00
|
|
|
size_t n_;
|
2014-02-25 16:42:34 +08:00
|
|
|
LockType *locks_;
|
2014-02-18 17:08:03 +08:00
|
|
|
};
|
|
|
|
|
2014-02-25 16:42:34 +08:00
|
|
|
int main () {
|
2014-02-25 17:33:10 +08:00
|
|
|
{ LockTest t(5); t.Test1(); }
|
|
|
|
{ LockTest t(5); t.Test2(); }
|
|
|
|
{ LockTest t(5); t.Test3(); }
|
|
|
|
{ LockTest t(5); t.Test4(); }
|
|
|
|
{ LockTest t(5); t.Test5(); }
|
|
|
|
{ LockTest t(5); t.Test6(); }
|
|
|
|
{ LockTest t(10); t.Test7(); }
|
2014-02-18 17:08:03 +08:00
|
|
|
fprintf(stderr, "DONE\n");
|
|
|
|
// CHECK: DONE
|
|
|
|
}
|
|
|
|
|