1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
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/macros.h"
11#include "base/synchronization/lock.h"
12#include "base/threading/platform_thread.h"
13#include "base/threading/simple_thread.h"
14#include "testing/gtest/include/gtest/gtest.h"
15
16// '' : local class member function does not have a body
17MSVC_PUSH_DISABLE_WARNING(4822)
18
19
20#if defined(NDEBUG)
21
22// Would cause a memory leak otherwise.
23#undef DFAKE_MUTEX
24#define DFAKE_MUTEX(obj) std::unique_ptr<base::AsserterBase> obj
25
26// In Release, we expect the AsserterBase::warn() to not happen.
27#define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_FALSE
28
29#else
30
31// In Debug, we expect the AsserterBase::warn() to happen.
32#define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_TRUE
33
34#endif
35
36
37namespace {
38
39// This is the asserter used with ThreadCollisionWarner instead of the default
40// DCheckAsserter. The method fail_state is used to know if a collision took
41// place.
42class AssertReporter : public base::AsserterBase {
43 public:
44  AssertReporter()
45      : failed_(false) {}
46
47  void warn() override { failed_ = true; }
48
49  ~AssertReporter() override {}
50
51  bool fail_state() const { return failed_; }
52  void reset() { failed_ = false; }
53
54 private:
55  bool failed_;
56};
57
58}  // namespace
59
60TEST(ThreadCollisionTest, BookCriticalSection) {
61  AssertReporter* local_reporter = new AssertReporter();
62
63  base::ThreadCollisionWarner warner(local_reporter);
64  EXPECT_FALSE(local_reporter->fail_state());
65
66  {  // Pin section.
67    DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
68    EXPECT_FALSE(local_reporter->fail_state());
69    {  // Pin section.
70      DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
71      EXPECT_FALSE(local_reporter->fail_state());
72    }
73  }
74}
75
76TEST(ThreadCollisionTest, ScopedRecursiveBookCriticalSection) {
77  AssertReporter* local_reporter = new AssertReporter();
78
79  base::ThreadCollisionWarner warner(local_reporter);
80  EXPECT_FALSE(local_reporter->fail_state());
81
82  {  // Pin section.
83    DFAKE_SCOPED_RECURSIVE_LOCK(warner);
84    EXPECT_FALSE(local_reporter->fail_state());
85    {  // Pin section again (allowed by DFAKE_SCOPED_RECURSIVE_LOCK)
86      DFAKE_SCOPED_RECURSIVE_LOCK(warner);
87      EXPECT_FALSE(local_reporter->fail_state());
88    }  // Unpin section.
89  }  // Unpin section.
90
91  // Check that section is not pinned
92  {  // Pin section.
93    DFAKE_SCOPED_LOCK(warner);
94    EXPECT_FALSE(local_reporter->fail_state());
95  }  // Unpin section.
96}
97
98TEST(ThreadCollisionTest, ScopedBookCriticalSection) {
99  AssertReporter* local_reporter = new AssertReporter();
100
101  base::ThreadCollisionWarner warner(local_reporter);
102  EXPECT_FALSE(local_reporter->fail_state());
103
104  {  // Pin section.
105    DFAKE_SCOPED_LOCK(warner);
106    EXPECT_FALSE(local_reporter->fail_state());
107  }  // Unpin section.
108
109  {  // Pin section.
110    DFAKE_SCOPED_LOCK(warner);
111    EXPECT_FALSE(local_reporter->fail_state());
112    {
113      // Pin section again (not allowed by DFAKE_SCOPED_LOCK)
114      DFAKE_SCOPED_LOCK(warner);
115      EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
116      // Reset the status of warner for further tests.
117      local_reporter->reset();
118    }  // Unpin section.
119  }  // Unpin section.
120
121  {
122    // Pin section.
123    DFAKE_SCOPED_LOCK(warner);
124    EXPECT_FALSE(local_reporter->fail_state());
125  }  // Unpin section.
126}
127
128TEST(ThreadCollisionTest, MTBookCriticalSectionTest) {
129  class NonThreadSafeQueue {
130   public:
131    explicit NonThreadSafeQueue(base::AsserterBase* asserter)
132        : push_pop_(asserter) {
133    }
134
135    void push(int value) {
136      DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_);
137    }
138
139    int pop() {
140      DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_);
141      return 0;
142    }
143
144   private:
145    DFAKE_MUTEX(push_pop_);
146
147    DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
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    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_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
180}
181
182TEST(ThreadCollisionTest, MTScopedBookCriticalSectionTest) {
183  // Queue with a 5 seconds push execution time, hopefuly the two used threads
184  // in the test will enter the push at same time.
185  class NonThreadSafeQueue {
186   public:
187    explicit NonThreadSafeQueue(base::AsserterBase* asserter)
188        : push_pop_(asserter) {
189    }
190
191    void push(int value) {
192      DFAKE_SCOPED_LOCK(push_pop_);
193      base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(5));
194    }
195
196    int pop() {
197      DFAKE_SCOPED_LOCK(push_pop_);
198      return 0;
199    }
200
201   private:
202    DFAKE_MUTEX(push_pop_);
203
204    DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
205  };
206
207  class QueueUser : public base::DelegateSimpleThread::Delegate {
208   public:
209    explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {}
210
211    void Run() override {
212      queue_->push(0);
213      queue_->pop();
214    }
215
216   private:
217    NonThreadSafeQueue* queue_;
218  };
219
220  AssertReporter* local_reporter = new AssertReporter();
221
222  NonThreadSafeQueue queue(local_reporter);
223
224  QueueUser queue_user_a(&queue);
225  QueueUser queue_user_b(&queue);
226
227  base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
228  base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
229
230  thread_a.Start();
231  thread_b.Start();
232
233  thread_a.Join();
234  thread_b.Join();
235
236  EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
237}
238
239TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) {
240  // Queue with a 2 seconds push execution time, hopefuly the two used threads
241  // in the test will enter the push at same time.
242  class NonThreadSafeQueue {
243   public:
244    explicit NonThreadSafeQueue(base::AsserterBase* asserter)
245        : push_pop_(asserter) {
246    }
247
248    void push(int value) {
249      DFAKE_SCOPED_LOCK(push_pop_);
250      base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(2));
251    }
252
253    int pop() {
254      DFAKE_SCOPED_LOCK(push_pop_);
255      return 0;
256    }
257
258   private:
259    DFAKE_MUTEX(push_pop_);
260
261    DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
262  };
263
264  // This time the QueueUser class protects the non thread safe queue with
265  // a lock.
266  class QueueUser : public base::DelegateSimpleThread::Delegate {
267   public:
268    QueueUser(NonThreadSafeQueue* queue, base::Lock* lock)
269        : queue_(queue), lock_(lock) {}
270
271    void Run() override {
272      {
273        base::AutoLock auto_lock(*lock_);
274        queue_->push(0);
275      }
276      {
277        base::AutoLock auto_lock(*lock_);
278        queue_->pop();
279      }
280    }
281   private:
282    NonThreadSafeQueue* queue_;
283    base::Lock* lock_;
284  };
285
286  AssertReporter* local_reporter = new AssertReporter();
287
288  NonThreadSafeQueue queue(local_reporter);
289
290  base::Lock lock;
291
292  QueueUser queue_user_a(&queue, &lock);
293  QueueUser queue_user_b(&queue, &lock);
294
295  base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
296  base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
297
298  thread_a.Start();
299  thread_b.Start();
300
301  thread_a.Join();
302  thread_b.Join();
303
304  EXPECT_FALSE(local_reporter->fail_state());
305}
306
307TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) {
308  // Queue with a 2 seconds push execution time, hopefuly the two used threads
309  // in the test will enter the push at same time.
310  class NonThreadSafeQueue {
311   public:
312    explicit NonThreadSafeQueue(base::AsserterBase* asserter)
313        : push_pop_(asserter) {
314    }
315
316    void push(int) {
317      DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
318      bar();
319      base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(2));
320    }
321
322    int pop() {
323      DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
324      return 0;
325    }
326
327    void bar() {
328      DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
329    }
330
331   private:
332    DFAKE_MUTEX(push_pop_);
333
334    DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
335  };
336
337  // This time the QueueUser class protects the non thread safe queue with
338  // a lock.
339  class QueueUser : public base::DelegateSimpleThread::Delegate {
340   public:
341    QueueUser(NonThreadSafeQueue* queue, base::Lock* lock)
342        : queue_(queue), lock_(lock) {}
343
344    void Run() override {
345      {
346        base::AutoLock auto_lock(*lock_);
347        queue_->push(0);
348      }
349      {
350        base::AutoLock auto_lock(*lock_);
351        queue_->bar();
352      }
353      {
354        base::AutoLock auto_lock(*lock_);
355        queue_->pop();
356      }
357    }
358   private:
359    NonThreadSafeQueue* queue_;
360    base::Lock* lock_;
361  };
362
363  AssertReporter* local_reporter = new AssertReporter();
364
365  NonThreadSafeQueue queue(local_reporter);
366
367  base::Lock lock;
368
369  QueueUser queue_user_a(&queue, &lock);
370  QueueUser queue_user_b(&queue, &lock);
371
372  base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
373  base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
374
375  thread_a.Start();
376  thread_b.Start();
377
378  thread_a.Join();
379  thread_b.Join();
380
381  EXPECT_FALSE(local_reporter->fail_state());
382}
383