• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "base/threading/thread_collision_warner.h"
6 
7 #include <memory>
8 
9 #include "base/compiler_specific.h"
10 #include "base/dcheck_is_on.h"
11 #include "base/memory/raw_ptr.h"
12 #include "base/synchronization/lock.h"
13 #include "base/threading/platform_thread.h"
14 #include "base/threading/simple_thread.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16 
17 #if !DCHECK_IS_ON()
18 
19 // Would cause a memory leak otherwise.
20 #undef DFAKE_MUTEX
21 #define DFAKE_MUTEX(obj) std::unique_ptr<base::AsserterBase> obj
22 
23 // In non-DCHECK builds, we expect the AsserterBase::warn() to not happen
24 // because the ThreadCollisionWarner's implementation is going to be
25 // #ifdefined out.
26 #define EXPECT_NDCHECK_FALSE_DCHECK_TRUE EXPECT_FALSE
27 
28 #else
29 
30 // In DCHECK builds, we expect the AsserterBase::warn() to happen.
31 #define EXPECT_NDCHECK_FALSE_DCHECK_TRUE EXPECT_TRUE
32 
33 #endif
34 
35 
36 namespace {
37 
38 // This is the asserter used with ThreadCollisionWarner instead of the default
39 // DCheckAsserter. The method fail_state is used to know if a collision took
40 // place.
41 class AssertReporter : public base::AsserterBase {
42  public:
AssertReporter()43   AssertReporter()
44       : failed_(false) {}
45 
warn()46   void warn() override { failed_ = true; }
47 
48   ~AssertReporter() override = default;
49 
fail_state() const50   bool fail_state() const { return failed_; }
reset()51   void reset() { failed_ = false; }
52 
53  private:
54   bool failed_;
55 };
56 
57 }  // namespace
58 
TEST(ThreadCollisionTest,BookCriticalSection)59 TEST(ThreadCollisionTest, BookCriticalSection) {
60   AssertReporter* local_reporter = new AssertReporter();
61 
62   base::ThreadCollisionWarner warner(local_reporter);
63   EXPECT_FALSE(local_reporter->fail_state());
64 
65   {  // Pin section.
66     DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
67     EXPECT_FALSE(local_reporter->fail_state());
68     {  // Pin section.
69       DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
70       EXPECT_FALSE(local_reporter->fail_state());
71     }
72   }
73 }
74 
TEST(ThreadCollisionTest,ScopedRecursiveBookCriticalSection)75 TEST(ThreadCollisionTest, ScopedRecursiveBookCriticalSection) {
76   AssertReporter* local_reporter = new AssertReporter();
77 
78   base::ThreadCollisionWarner warner(local_reporter);
79   EXPECT_FALSE(local_reporter->fail_state());
80 
81   {  // Pin section.
82     DFAKE_SCOPED_RECURSIVE_LOCK(warner);
83     EXPECT_FALSE(local_reporter->fail_state());
84     {  // Pin section again (allowed by DFAKE_SCOPED_RECURSIVE_LOCK)
85       DFAKE_SCOPED_RECURSIVE_LOCK(warner);
86       EXPECT_FALSE(local_reporter->fail_state());
87     }  // Unpin section.
88   }  // Unpin section.
89 
90   // Check that section is not pinned
91   {  // Pin section.
92     DFAKE_SCOPED_LOCK(warner);
93     EXPECT_FALSE(local_reporter->fail_state());
94   }  // Unpin section.
95 }
96 
TEST(ThreadCollisionTest,ScopedBookCriticalSection)97 TEST(ThreadCollisionTest, ScopedBookCriticalSection) {
98   AssertReporter* local_reporter = new AssertReporter();
99 
100   base::ThreadCollisionWarner warner(local_reporter);
101   EXPECT_FALSE(local_reporter->fail_state());
102 
103   {  // Pin section.
104     DFAKE_SCOPED_LOCK(warner);
105     EXPECT_FALSE(local_reporter->fail_state());
106   }  // Unpin section.
107 
108   {  // Pin section.
109     DFAKE_SCOPED_LOCK(warner);
110     EXPECT_FALSE(local_reporter->fail_state());
111     {
112       // Pin section again (not allowed by DFAKE_SCOPED_LOCK)
113       DFAKE_SCOPED_LOCK(warner);
114       EXPECT_NDCHECK_FALSE_DCHECK_TRUE(local_reporter->fail_state());
115       // Reset the status of warner for further tests.
116       local_reporter->reset();
117     }  // Unpin section.
118   }  // Unpin section.
119 
120   {
121     // Pin section.
122     DFAKE_SCOPED_LOCK(warner);
123     EXPECT_FALSE(local_reporter->fail_state());
124   }  // Unpin section.
125 }
126 
TEST(ThreadCollisionTest,MTBookCriticalSectionTest)127 TEST(ThreadCollisionTest, MTBookCriticalSectionTest) {
128   class NonThreadSafeQueue {
129    public:
130     explicit NonThreadSafeQueue(base::AsserterBase* asserter)
131         : push_pop_(asserter) {
132     }
133 
134     NonThreadSafeQueue(const NonThreadSafeQueue&) = delete;
135     NonThreadSafeQueue& operator=(const NonThreadSafeQueue&) = delete;
136 
137     void push(int value) {
138       DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_);
139     }
140 
141     int pop() {
142       DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_);
143       return 0;
144     }
145 
146    private:
147     DFAKE_MUTEX(push_pop_);
148   };
149 
150   class QueueUser : public base::DelegateSimpleThread::Delegate {
151    public:
152     explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {}
153 
154     void Run() override {
155       queue_->push(0);
156       queue_->pop();
157     }
158 
159    private:
160     raw_ptr<NonThreadSafeQueue> queue_;
161   };
162 
163   AssertReporter* local_reporter = new AssertReporter();
164 
165   NonThreadSafeQueue queue(local_reporter);
166 
167   QueueUser queue_user_a(&queue);
168   QueueUser queue_user_b(&queue);
169 
170   base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
171   base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
172 
173   thread_a.Start();
174   thread_b.Start();
175 
176   thread_a.Join();
177   thread_b.Join();
178 
179   EXPECT_NDCHECK_FALSE_DCHECK_TRUE(local_reporter->fail_state());
180 }
181 
182 // This unittest accesses a queue in a non-thread-safe manner in an attempt to
183 // exercise the ThreadCollisionWarner code. When it's run under TSan, the test's
184 // assumptions pass, but the ThreadSanitizer detects unsafe access and raises a
185 // warning, causing this unittest to fail. Just ignore this test case when TSan
186 // is enabled.
187 #ifndef THREAD_SANITIZER
TEST(ThreadCollisionTest,MTScopedBookCriticalSectionTest)188 TEST(ThreadCollisionTest, MTScopedBookCriticalSectionTest) {
189   // Queue with a 5 seconds push execution time, hopefuly the two used threads
190   // in the test will enter the push at same time.
191   class NonThreadSafeQueue {
192    public:
193     explicit NonThreadSafeQueue(base::AsserterBase* asserter)
194         : push_pop_(asserter) {
195     }
196 
197     NonThreadSafeQueue(const NonThreadSafeQueue&) = delete;
198     NonThreadSafeQueue& operator=(const NonThreadSafeQueue&) = delete;
199 
200     void push(int value) {
201       DFAKE_SCOPED_LOCK(push_pop_);
202       base::PlatformThread::Sleep(base::Seconds(5));
203     }
204 
205     int pop() {
206       DFAKE_SCOPED_LOCK(push_pop_);
207       return 0;
208     }
209 
210    private:
211     DFAKE_MUTEX(push_pop_);
212   };
213 
214   class QueueUser : public base::DelegateSimpleThread::Delegate {
215    public:
216     explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {}
217 
218     void Run() override {
219       queue_->push(0);
220       queue_->pop();
221     }
222 
223    private:
224     raw_ptr<NonThreadSafeQueue> queue_;
225   };
226 
227   AssertReporter* local_reporter = new AssertReporter();
228 
229   NonThreadSafeQueue queue(local_reporter);
230 
231   QueueUser queue_user_a(&queue);
232   QueueUser queue_user_b(&queue);
233 
234   base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
235   base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
236 
237   thread_a.Start();
238   thread_b.Start();
239 
240   thread_a.Join();
241   thread_b.Join();
242 
243   EXPECT_NDCHECK_FALSE_DCHECK_TRUE(local_reporter->fail_state());
244 }
245 #endif  // THREAD_SANITIZER
246 
TEST(ThreadCollisionTest,MTSynchedScopedBookCriticalSectionTest)247 TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) {
248   // Queue with a 2 seconds push execution time, hopefuly the two used threads
249   // in the test will enter the push at same time.
250   class NonThreadSafeQueue {
251    public:
252     explicit NonThreadSafeQueue(base::AsserterBase* asserter)
253         : push_pop_(asserter) {
254     }
255 
256     NonThreadSafeQueue(const NonThreadSafeQueue&) = delete;
257     NonThreadSafeQueue& operator=(const NonThreadSafeQueue&) = delete;
258 
259     void push(int value) {
260       DFAKE_SCOPED_LOCK(push_pop_);
261       base::PlatformThread::Sleep(base::Seconds(2));
262     }
263 
264     int pop() {
265       DFAKE_SCOPED_LOCK(push_pop_);
266       return 0;
267     }
268 
269    private:
270     DFAKE_MUTEX(push_pop_);
271   };
272 
273   // This time the QueueUser class protects the non thread safe queue with
274   // a lock.
275   class QueueUser : public base::DelegateSimpleThread::Delegate {
276    public:
277     QueueUser(NonThreadSafeQueue* queue, base::Lock* lock)
278         : queue_(queue), lock_(lock) {}
279 
280     void Run() override {
281       {
282         base::AutoLock auto_lock(*lock_);
283         queue_->push(0);
284       }
285       {
286         base::AutoLock auto_lock(*lock_);
287         queue_->pop();
288       }
289     }
290    private:
291     raw_ptr<NonThreadSafeQueue> queue_;
292     raw_ptr<base::Lock> lock_;
293   };
294 
295   AssertReporter* local_reporter = new AssertReporter();
296 
297   NonThreadSafeQueue queue(local_reporter);
298 
299   base::Lock lock;
300 
301   QueueUser queue_user_a(&queue, &lock);
302   QueueUser queue_user_b(&queue, &lock);
303 
304   base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
305   base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
306 
307   thread_a.Start();
308   thread_b.Start();
309 
310   thread_a.Join();
311   thread_b.Join();
312 
313   EXPECT_FALSE(local_reporter->fail_state());
314 }
315 
TEST(ThreadCollisionTest,MTSynchedScopedRecursiveBookCriticalSectionTest)316 TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) {
317   // Queue with a 2 seconds push execution time, hopefuly the two used threads
318   // in the test will enter the push at same time.
319   class NonThreadSafeQueue {
320    public:
321     explicit NonThreadSafeQueue(base::AsserterBase* asserter)
322         : push_pop_(asserter) {
323     }
324 
325     NonThreadSafeQueue(const NonThreadSafeQueue&) = delete;
326     NonThreadSafeQueue& operator=(const NonThreadSafeQueue&) = delete;
327 
328     void push(int) {
329       DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
330       bar();
331       base::PlatformThread::Sleep(base::Seconds(2));
332     }
333 
334     int pop() {
335       DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
336       return 0;
337     }
338 
339     void bar() {
340       DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
341     }
342 
343    private:
344     DFAKE_MUTEX(push_pop_);
345   };
346 
347   // This time the QueueUser class protects the non thread safe queue with
348   // a lock.
349   class QueueUser : public base::DelegateSimpleThread::Delegate {
350    public:
351     QueueUser(NonThreadSafeQueue* queue, base::Lock* lock)
352         : queue_(queue), lock_(lock) {}
353 
354     void Run() override {
355       {
356         base::AutoLock auto_lock(*lock_);
357         queue_->push(0);
358       }
359       {
360         base::AutoLock auto_lock(*lock_);
361         queue_->bar();
362       }
363       {
364         base::AutoLock auto_lock(*lock_);
365         queue_->pop();
366       }
367     }
368    private:
369     raw_ptr<NonThreadSafeQueue> queue_;
370     raw_ptr<base::Lock> lock_;
371   };
372 
373   AssertReporter* local_reporter = new AssertReporter();
374 
375   NonThreadSafeQueue queue(local_reporter);
376 
377   base::Lock lock;
378 
379   QueueUser queue_user_a(&queue, &lock);
380   QueueUser queue_user_b(&queue, &lock);
381 
382   base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
383   base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
384 
385   thread_a.Start();
386   thread_b.Start();
387 
388   thread_a.Join();
389   thread_b.Join();
390 
391   EXPECT_FALSE(local_reporter->fail_state());
392 }
393