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/compiler_specific.h"
6#include "base/memory/scoped_ptr.h"
7#include "base/synchronization/lock.h"
8#include "base/threading/platform_thread.h"
9#include "base/threading/simple_thread.h"
10#include "base/threading/thread_collision_warner.h"
11#include "testing/gtest/include/gtest/gtest.h"
12
13// '' : local class member function does not have a body
14MSVC_PUSH_DISABLE_WARNING(4822)
15
16
17#if defined(NDEBUG)
18
19// Would cause a memory leak otherwise.
20#undef DFAKE_MUTEX
21#define DFAKE_MUTEX(obj) scoped_ptr<base::AsserterBase> obj
22
23// In Release, we expect the AsserterBase::warn() to not happen.
24#define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_FALSE
25
26#else
27
28// In Debug, we expect the AsserterBase::warn() to happen.
29#define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_TRUE
30
31#endif
32
33
34namespace {
35
36// This is the asserter used with ThreadCollisionWarner instead of the default
37// DCheckAsserter. The method fail_state is used to know if a collision took
38// place.
39class AssertReporter : public base::AsserterBase {
40 public:
41  AssertReporter()
42      : failed_(false) {}
43
44  virtual void warn() OVERRIDE {
45    failed_ = true;
46  }
47
48  virtual ~AssertReporter() {}
49
50  bool fail_state() const { return failed_; }
51  void reset() { failed_ = false; }
52
53 private:
54  bool failed_;
55};
56
57}  // namespace
58
59TEST(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
75TEST(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
97TEST(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_NDEBUG_FALSE_DEBUG_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
127TEST(ThreadCollisionTest, MTBookCriticalSectionTest) {
128  class NonThreadSafeQueue {
129   public:
130    explicit NonThreadSafeQueue(base::AsserterBase* asserter)
131        : push_pop_(asserter) {
132    }
133
134    void push(int value) {
135      DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_);
136    }
137
138    int pop() {
139      DFAKE_SCOPED_LOCK_THREAD_LOCKED(push_pop_);
140      return 0;
141    }
142
143   private:
144    DFAKE_MUTEX(push_pop_);
145
146    DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
147  };
148
149  class QueueUser : public base::DelegateSimpleThread::Delegate {
150   public:
151    explicit QueueUser(NonThreadSafeQueue& queue)
152        : queue_(queue) {}
153
154    virtual 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)
210        : queue_(queue) {}
211
212    virtual void Run() OVERRIDE {
213      queue_.push(0);
214      queue_.pop();
215    }
216
217   private:
218    NonThreadSafeQueue& queue_;
219  };
220
221  AssertReporter* local_reporter = new AssertReporter();
222
223  NonThreadSafeQueue queue(local_reporter);
224
225  QueueUser queue_user_a(queue);
226  QueueUser queue_user_b(queue);
227
228  base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
229  base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
230
231  thread_a.Start();
232  thread_b.Start();
233
234  thread_a.Join();
235  thread_b.Join();
236
237  EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
238}
239
240TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) {
241  // Queue with a 2 seconds push execution time, hopefuly the two used threads
242  // in the test will enter the push at same time.
243  class NonThreadSafeQueue {
244   public:
245    explicit NonThreadSafeQueue(base::AsserterBase* asserter)
246        : push_pop_(asserter) {
247    }
248
249    void push(int value) {
250      DFAKE_SCOPED_LOCK(push_pop_);
251      base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(2));
252    }
253
254    int pop() {
255      DFAKE_SCOPED_LOCK(push_pop_);
256      return 0;
257    }
258
259   private:
260    DFAKE_MUTEX(push_pop_);
261
262    DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
263  };
264
265  // This time the QueueUser class protects the non thread safe queue with
266  // a lock.
267  class QueueUser : public base::DelegateSimpleThread::Delegate {
268   public:
269    QueueUser(NonThreadSafeQueue& queue, base::Lock& lock)
270        : queue_(queue),
271          lock_(lock) {}
272
273    virtual void Run() OVERRIDE {
274      {
275        base::AutoLock auto_lock(lock_);
276        queue_.push(0);
277      }
278      {
279        base::AutoLock auto_lock(lock_);
280        queue_.pop();
281      }
282    }
283   private:
284    NonThreadSafeQueue& queue_;
285    base::Lock& lock_;
286  };
287
288  AssertReporter* local_reporter = new AssertReporter();
289
290  NonThreadSafeQueue queue(local_reporter);
291
292  base::Lock lock;
293
294  QueueUser queue_user_a(queue, lock);
295  QueueUser queue_user_b(queue, lock);
296
297  base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
298  base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
299
300  thread_a.Start();
301  thread_b.Start();
302
303  thread_a.Join();
304  thread_b.Join();
305
306  EXPECT_FALSE(local_reporter->fail_state());
307}
308
309TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) {
310  // Queue with a 2 seconds push execution time, hopefuly the two used threads
311  // in the test will enter the push at same time.
312  class NonThreadSafeQueue {
313   public:
314    explicit NonThreadSafeQueue(base::AsserterBase* asserter)
315        : push_pop_(asserter) {
316    }
317
318    void push(int) {
319      DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
320      bar();
321      base::PlatformThread::Sleep(base::TimeDelta::FromSeconds(2));
322    }
323
324    int pop() {
325      DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
326      return 0;
327    }
328
329    void bar() {
330      DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
331    }
332
333   private:
334    DFAKE_MUTEX(push_pop_);
335
336    DISALLOW_COPY_AND_ASSIGN(NonThreadSafeQueue);
337  };
338
339  // This time the QueueUser class protects the non thread safe queue with
340  // a lock.
341  class QueueUser : public base::DelegateSimpleThread::Delegate {
342   public:
343    QueueUser(NonThreadSafeQueue& queue, base::Lock& lock)
344        : queue_(queue),
345          lock_(lock) {}
346
347    virtual void Run() OVERRIDE {
348      {
349        base::AutoLock auto_lock(lock_);
350        queue_.push(0);
351      }
352      {
353        base::AutoLock auto_lock(lock_);
354        queue_.bar();
355      }
356      {
357        base::AutoLock auto_lock(lock_);
358        queue_.pop();
359      }
360    }
361   private:
362    NonThreadSafeQueue& queue_;
363    base::Lock& lock_;
364  };
365
366  AssertReporter* local_reporter = new AssertReporter();
367
368  NonThreadSafeQueue queue(local_reporter);
369
370  base::Lock lock;
371
372  QueueUser queue_user_a(queue, lock);
373  QueueUser queue_user_b(queue, lock);
374
375  base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
376  base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
377
378  thread_a.Start();
379  thread_b.Start();
380
381  thread_a.Join();
382  thread_b.Join();
383
384  EXPECT_FALSE(local_reporter->fail_state());
385}
386