1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
|
// RUN: %clangxx_tsan %s -o %t
// RUN: TSAN_OPTIONS=detect_deadlocks=1 not %t 2>&1 | FileCheck %s
#include <pthread.h>
#undef NDEBUG
#include <assert.h>
#include <stdio.h>
class PaddedLock {
public:
PaddedLock() { assert(0 == pthread_mutex_init(&mu_, 0)); }
~PaddedLock() {
assert(0 == pthread_mutex_destroy(&mu_));
(void)padding_;
}
void lock() { assert(0 == pthread_mutex_lock(&mu_)); }
void unlock() { assert(0 == pthread_mutex_unlock(&mu_)); }
private:
pthread_mutex_t mu_;
char padding_[64 - sizeof(pthread_mutex_t)];
};
class LockTest {
public:
LockTest(size_t n) : n_(n), locks_(new PaddedLock[n]) { }
~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();
}
void *A(size_t i) {
assert(i < n_);
return &locks_[i];
}
// Simple lock order onversion.
void Test1() {
fprintf(stderr, "Starting Test1\n");
// CHECK: Starting Test1
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]*]]
L(0); L(1); U(0); U(1);
L(1); L(0); U(0); U(1);
// 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:
}
// Simple lock order inversion with 3 locks.
void Test2() {
fprintf(stderr, "Starting Test2\n");
// CHECK: Starting Test2
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]*]]
L(0); L(1); U(0); U(1);
L(1); L(2); U(1); U(2);
L(2); L(0); U(0); U(2);
// CHECK: WARNING: ThreadSanitizer: lock-order-inversion (potential deadlock)
// 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:
// 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() {
fprintf(stderr, "Starting Test3\n");
// CHECK: Starting Test3
L(0); L(1); U(0); U(1);
L(2);
CreateAndDestroyManyLocks();
U(2);
L(1); L(0); U(0); U(1);
// 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() {
fprintf(stderr, "Starting Test4\n");
// CHECK: Starting Test4
L(0); L(1); U(0); U(1);
L(2);
CreateLockUnlockAndDestroyManyLocks();
U(2);
L(1); L(0); U(0); U(1);
// CHECK-NOT: WARNING: ThreadSanitizer:
}
private:
void CreateAndDestroyManyLocks() {
PaddedLock create_many_locks_but_never_acquire[kDeadlockGraphSize];
}
void CreateLockUnlockAndDestroyManyLocks() {
PaddedLock many_locks[kDeadlockGraphSize];
for (size_t i = 0; i < kDeadlockGraphSize; i++) {
many_locks[i].lock();
many_locks[i].unlock();
}
}
static const size_t kDeadlockGraphSize = 4096;
size_t n_;
PaddedLock *locks_;
};
int main() {
{ LockTest t(5); t.Test1(); }
{ LockTest t(5); t.Test2(); }
{ LockTest t(5); t.Test3(); }
{ LockTest t(5); t.Test4(); }
fprintf(stderr, "DONE\n");
// CHECK: DONE
}
|