1 | //===-- tsan_mutex.cpp ----------------------------------------------------===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | // |
9 | // This file is a part of ThreadSanitizer (TSan), a race detector. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | #include "sanitizer_common/sanitizer_atomic.h" |
13 | #include "tsan_interface.h" |
14 | #include "tsan_interface_ann.h" |
15 | #include "tsan_test_util.h" |
16 | #include "gtest/gtest.h" |
17 | #include <stdint.h> |
18 | |
19 | namespace __tsan { |
20 | |
21 | TEST_F(ThreadSanitizer, BasicMutex) { |
22 | ScopedThread t; |
23 | UserMutex m; |
24 | t.Create(m); |
25 | |
26 | t.Lock(m); |
27 | t.Unlock(m); |
28 | |
29 | CHECK(t.TryLock(m)); |
30 | t.Unlock(m); |
31 | |
32 | t.Lock(m); |
33 | CHECK(!t.TryLock(m)); |
34 | t.Unlock(m); |
35 | |
36 | t.Destroy(m); |
37 | } |
38 | |
39 | TEST_F(ThreadSanitizer, BasicSpinMutex) { |
40 | ScopedThread t; |
41 | UserMutex m(UserMutex::Spin); |
42 | t.Create(m); |
43 | |
44 | t.Lock(m); |
45 | t.Unlock(m); |
46 | |
47 | CHECK(t.TryLock(m)); |
48 | t.Unlock(m); |
49 | |
50 | t.Lock(m); |
51 | CHECK(!t.TryLock(m)); |
52 | t.Unlock(m); |
53 | |
54 | t.Destroy(m); |
55 | } |
56 | |
57 | TEST_F(ThreadSanitizer, BasicRwMutex) { |
58 | ScopedThread t; |
59 | UserMutex m(UserMutex::RW); |
60 | t.Create(m); |
61 | |
62 | t.Lock(m); |
63 | t.Unlock(m); |
64 | |
65 | CHECK(t.TryLock(m)); |
66 | t.Unlock(m); |
67 | |
68 | t.Lock(m); |
69 | CHECK(!t.TryLock(m)); |
70 | t.Unlock(m); |
71 | |
72 | t.ReadLock(m); |
73 | t.ReadUnlock(m); |
74 | |
75 | CHECK(t.TryReadLock(m)); |
76 | t.ReadUnlock(m); |
77 | |
78 | t.Lock(m); |
79 | CHECK(!t.TryReadLock(m)); |
80 | t.Unlock(m); |
81 | |
82 | t.ReadLock(m); |
83 | CHECK(!t.TryLock(m)); |
84 | t.ReadUnlock(m); |
85 | |
86 | t.ReadLock(m); |
87 | CHECK(t.TryReadLock(m)); |
88 | t.ReadUnlock(m); |
89 | t.ReadUnlock(m); |
90 | |
91 | t.Destroy(m); |
92 | } |
93 | |
94 | TEST_F(ThreadSanitizer, Mutex) { |
95 | UserMutex m; |
96 | MainThread t0; |
97 | t0.Create(m); |
98 | |
99 | ScopedThread t1, t2; |
100 | MemLoc l; |
101 | t1.Lock(m); |
102 | t1.Write1(ml: l); |
103 | t1.Unlock(m); |
104 | t2.Lock(m); |
105 | t2.Write1(ml: l); |
106 | t2.Unlock(m); |
107 | t2.Destroy(m); |
108 | } |
109 | |
110 | TEST_F(ThreadSanitizer, SpinMutex) { |
111 | UserMutex m(UserMutex::Spin); |
112 | MainThread t0; |
113 | t0.Create(m); |
114 | |
115 | ScopedThread t1, t2; |
116 | MemLoc l; |
117 | t1.Lock(m); |
118 | t1.Write1(ml: l); |
119 | t1.Unlock(m); |
120 | t2.Lock(m); |
121 | t2.Write1(ml: l); |
122 | t2.Unlock(m); |
123 | t2.Destroy(m); |
124 | } |
125 | |
126 | TEST_F(ThreadSanitizer, RwMutex) { |
127 | UserMutex m(UserMutex::RW); |
128 | MainThread t0; |
129 | t0.Create(m); |
130 | |
131 | ScopedThread t1, t2, t3; |
132 | MemLoc l; |
133 | t1.Lock(m); |
134 | t1.Write1(ml: l); |
135 | t1.Unlock(m); |
136 | t2.Lock(m); |
137 | t2.Write1(ml: l); |
138 | t2.Unlock(m); |
139 | t1.ReadLock(m); |
140 | t3.ReadLock(m); |
141 | t1.Read1(ml: l); |
142 | t3.Read1(ml: l); |
143 | t1.ReadUnlock(m); |
144 | t3.ReadUnlock(m); |
145 | t2.Lock(m); |
146 | t2.Write1(ml: l); |
147 | t2.Unlock(m); |
148 | t2.Destroy(m); |
149 | } |
150 | |
151 | TEST_F(ThreadSanitizer, StaticMutex) { |
152 | // Emulates statically initialized mutex. |
153 | UserMutex m; |
154 | m.StaticInit(); |
155 | { |
156 | ScopedThread t1, t2; |
157 | t1.Lock(m); |
158 | t1.Unlock(m); |
159 | t2.Lock(m); |
160 | t2.Unlock(m); |
161 | } |
162 | MainThread().Destroy(m); |
163 | } |
164 | |
165 | static void *singleton_thread(void *param) { |
166 | atomic_uintptr_t *singleton = (atomic_uintptr_t *)param; |
167 | for (int i = 0; i < 4*1024*1024; i++) { |
168 | int *val = (int *)atomic_load(a: singleton, mo: memory_order_acquire); |
169 | __tsan_acquire(addr: singleton); |
170 | __tsan_read4(addr: val); |
171 | CHECK_EQ(*val, 42); |
172 | } |
173 | return 0; |
174 | } |
175 | |
176 | TEST(DISABLED_BENCH_ThreadSanitizer, Singleton) { |
177 | const int kClockSize = 100; |
178 | const int kThreadCount = 8; |
179 | |
180 | // Puff off thread's clock. |
181 | for (int i = 0; i < kClockSize; i++) { |
182 | ScopedThread t1; |
183 | (void)t1; |
184 | } |
185 | // Create the singleton. |
186 | int val = 42; |
187 | __tsan_write4(addr: &val); |
188 | atomic_uintptr_t singleton; |
189 | __tsan_release(addr: &singleton); |
190 | atomic_store(a: &singleton, v: (uintptr_t)&val, mo: memory_order_release); |
191 | // Create reader threads. |
192 | pthread_t threads[kThreadCount]; |
193 | for (int t = 0; t < kThreadCount; t++) |
194 | pthread_create(&threads[t], 0, singleton_thread, &singleton); |
195 | for (int t = 0; t < kThreadCount; t++) |
196 | pthread_join(threads[t], 0); |
197 | } |
198 | |
199 | TEST(DISABLED_BENCH_ThreadSanitizer, StopFlag) { |
200 | const int kClockSize = 100; |
201 | const int kIters = 16*1024*1024; |
202 | |
203 | // Puff off thread's clock. |
204 | for (int i = 0; i < kClockSize; i++) { |
205 | ScopedThread t1; |
206 | (void)t1; |
207 | } |
208 | // Create the stop flag. |
209 | atomic_uintptr_t flag; |
210 | __tsan_release(addr: &flag); |
211 | atomic_store(a: &flag, v: 0, mo: memory_order_release); |
212 | // Read it a lot. |
213 | for (int i = 0; i < kIters; i++) { |
214 | uptr v = atomic_load(a: &flag, mo: memory_order_acquire); |
215 | __tsan_acquire(addr: &flag); |
216 | CHECK_EQ(v, 0); |
217 | } |
218 | } |
219 | |
220 | } // namespace __tsan |
221 | |