xref: /aosp_15_r20/external/cronet/base/threading/thread_collision_warner_unittest.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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/memory/raw_ptr.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 #if defined(NDEBUG)
17 
18 // Would cause a memory leak otherwise.
19 #undef DFAKE_MUTEX
20 #define DFAKE_MUTEX(obj) std::unique_ptr<base::AsserterBase> obj
21 
22 // In Release, we expect the AsserterBase::warn() to not happen.
23 #define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_FALSE
24 
25 #else
26 
27 // In Debug, we expect the AsserterBase::warn() to happen.
28 #define EXPECT_NDEBUG_FALSE_DEBUG_TRUE EXPECT_TRUE
29 
30 #endif
31 
32 
33 namespace {
34 
35 // This is the asserter used with ThreadCollisionWarner instead of the default
36 // DCheckAsserter. The method fail_state is used to know if a collision took
37 // place.
38 class AssertReporter : public base::AsserterBase {
39  public:
AssertReporter()40   AssertReporter()
41       : failed_(false) {}
42 
warn()43   void warn() override { failed_ = true; }
44 
45   ~AssertReporter() override = default;
46 
fail_state() const47   bool fail_state() const { return failed_; }
reset()48   void reset() { failed_ = false; }
49 
50  private:
51   bool failed_;
52 };
53 
54 }  // namespace
55 
TEST(ThreadCollisionTest,BookCriticalSection)56 TEST(ThreadCollisionTest, BookCriticalSection) {
57   AssertReporter* local_reporter = new AssertReporter();
58 
59   base::ThreadCollisionWarner warner(local_reporter);
60   EXPECT_FALSE(local_reporter->fail_state());
61 
62   {  // Pin section.
63     DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
64     EXPECT_FALSE(local_reporter->fail_state());
65     {  // Pin section.
66       DFAKE_SCOPED_LOCK_THREAD_LOCKED(warner);
67       EXPECT_FALSE(local_reporter->fail_state());
68     }
69   }
70 }
71 
TEST(ThreadCollisionTest,ScopedRecursiveBookCriticalSection)72 TEST(ThreadCollisionTest, ScopedRecursiveBookCriticalSection) {
73   AssertReporter* local_reporter = new AssertReporter();
74 
75   base::ThreadCollisionWarner warner(local_reporter);
76   EXPECT_FALSE(local_reporter->fail_state());
77 
78   {  // Pin section.
79     DFAKE_SCOPED_RECURSIVE_LOCK(warner);
80     EXPECT_FALSE(local_reporter->fail_state());
81     {  // Pin section again (allowed by DFAKE_SCOPED_RECURSIVE_LOCK)
82       DFAKE_SCOPED_RECURSIVE_LOCK(warner);
83       EXPECT_FALSE(local_reporter->fail_state());
84     }  // Unpin section.
85   }  // Unpin section.
86 
87   // Check that section is not pinned
88   {  // Pin section.
89     DFAKE_SCOPED_LOCK(warner);
90     EXPECT_FALSE(local_reporter->fail_state());
91   }  // Unpin section.
92 }
93 
TEST(ThreadCollisionTest,ScopedBookCriticalSection)94 TEST(ThreadCollisionTest, ScopedBookCriticalSection) {
95   AssertReporter* local_reporter = new AssertReporter();
96 
97   base::ThreadCollisionWarner warner(local_reporter);
98   EXPECT_FALSE(local_reporter->fail_state());
99 
100   {  // Pin section.
101     DFAKE_SCOPED_LOCK(warner);
102     EXPECT_FALSE(local_reporter->fail_state());
103   }  // Unpin section.
104 
105   {  // Pin section.
106     DFAKE_SCOPED_LOCK(warner);
107     EXPECT_FALSE(local_reporter->fail_state());
108     {
109       // Pin section again (not allowed by DFAKE_SCOPED_LOCK)
110       DFAKE_SCOPED_LOCK(warner);
111       EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
112       // Reset the status of warner for further tests.
113       local_reporter->reset();
114     }  // Unpin section.
115   }  // Unpin section.
116 
117   {
118     // Pin section.
119     DFAKE_SCOPED_LOCK(warner);
120     EXPECT_FALSE(local_reporter->fail_state());
121   }  // Unpin section.
122 }
123 
TEST(ThreadCollisionTest,MTBookCriticalSectionTest)124 TEST(ThreadCollisionTest, MTBookCriticalSectionTest) {
125   class NonThreadSafeQueue {
126    public:
127     explicit NonThreadSafeQueue(base::AsserterBase* asserter)
128         : push_pop_(asserter) {
129     }
130 
131     NonThreadSafeQueue(const NonThreadSafeQueue&) = delete;
132     NonThreadSafeQueue& operator=(const NonThreadSafeQueue&) = delete;
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 
147   class QueueUser : public base::DelegateSimpleThread::Delegate {
148    public:
149     explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {}
150 
151     void Run() override {
152       queue_->push(0);
153       queue_->pop();
154     }
155 
156    private:
157     raw_ptr<NonThreadSafeQueue> queue_;
158   };
159 
160   AssertReporter* local_reporter = new AssertReporter();
161 
162   NonThreadSafeQueue queue(local_reporter);
163 
164   QueueUser queue_user_a(&queue);
165   QueueUser queue_user_b(&queue);
166 
167   base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
168   base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
169 
170   thread_a.Start();
171   thread_b.Start();
172 
173   thread_a.Join();
174   thread_b.Join();
175 
176   EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
177 }
178 
TEST(ThreadCollisionTest,MTScopedBookCriticalSectionTest)179 TEST(ThreadCollisionTest, MTScopedBookCriticalSectionTest) {
180   // Queue with a 5 seconds push execution time, hopefuly the two used threads
181   // in the test will enter the push at same time.
182   class NonThreadSafeQueue {
183    public:
184     explicit NonThreadSafeQueue(base::AsserterBase* asserter)
185         : push_pop_(asserter) {
186     }
187 
188     NonThreadSafeQueue(const NonThreadSafeQueue&) = delete;
189     NonThreadSafeQueue& operator=(const NonThreadSafeQueue&) = delete;
190 
191     void push(int value) {
192       DFAKE_SCOPED_LOCK(push_pop_);
193       base::PlatformThread::Sleep(base::Seconds(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 
205   class QueueUser : public base::DelegateSimpleThread::Delegate {
206    public:
207     explicit QueueUser(NonThreadSafeQueue* queue) : queue_(queue) {}
208 
209     void Run() override {
210       queue_->push(0);
211       queue_->pop();
212     }
213 
214    private:
215     raw_ptr<NonThreadSafeQueue> queue_;
216   };
217 
218   AssertReporter* local_reporter = new AssertReporter();
219 
220   NonThreadSafeQueue queue(local_reporter);
221 
222   QueueUser queue_user_a(&queue);
223   QueueUser queue_user_b(&queue);
224 
225   base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
226   base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
227 
228   thread_a.Start();
229   thread_b.Start();
230 
231   thread_a.Join();
232   thread_b.Join();
233 
234   EXPECT_NDEBUG_FALSE_DEBUG_TRUE(local_reporter->fail_state());
235 }
236 
TEST(ThreadCollisionTest,MTSynchedScopedBookCriticalSectionTest)237 TEST(ThreadCollisionTest, MTSynchedScopedBookCriticalSectionTest) {
238   // Queue with a 2 seconds push execution time, hopefuly the two used threads
239   // in the test will enter the push at same time.
240   class NonThreadSafeQueue {
241    public:
242     explicit NonThreadSafeQueue(base::AsserterBase* asserter)
243         : push_pop_(asserter) {
244     }
245 
246     NonThreadSafeQueue(const NonThreadSafeQueue&) = delete;
247     NonThreadSafeQueue& operator=(const NonThreadSafeQueue&) = delete;
248 
249     void push(int value) {
250       DFAKE_SCOPED_LOCK(push_pop_);
251       base::PlatformThread::Sleep(base::Seconds(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 
263   // This time the QueueUser class protects the non thread safe queue with
264   // a lock.
265   class QueueUser : public base::DelegateSimpleThread::Delegate {
266    public:
267     QueueUser(NonThreadSafeQueue* queue, base::Lock* lock)
268         : queue_(queue), lock_(lock) {}
269 
270     void Run() override {
271       {
272         base::AutoLock auto_lock(*lock_);
273         queue_->push(0);
274       }
275       {
276         base::AutoLock auto_lock(*lock_);
277         queue_->pop();
278       }
279     }
280    private:
281     raw_ptr<NonThreadSafeQueue> queue_;
282     raw_ptr<base::Lock> lock_;
283   };
284 
285   AssertReporter* local_reporter = new AssertReporter();
286 
287   NonThreadSafeQueue queue(local_reporter);
288 
289   base::Lock lock;
290 
291   QueueUser queue_user_a(&queue, &lock);
292   QueueUser queue_user_b(&queue, &lock);
293 
294   base::DelegateSimpleThread thread_a(&queue_user_a, "queue_user_thread_a");
295   base::DelegateSimpleThread thread_b(&queue_user_b, "queue_user_thread_b");
296 
297   thread_a.Start();
298   thread_b.Start();
299 
300   thread_a.Join();
301   thread_b.Join();
302 
303   EXPECT_FALSE(local_reporter->fail_state());
304 }
305 
TEST(ThreadCollisionTest,MTSynchedScopedRecursiveBookCriticalSectionTest)306 TEST(ThreadCollisionTest, MTSynchedScopedRecursiveBookCriticalSectionTest) {
307   // Queue with a 2 seconds push execution time, hopefuly the two used threads
308   // in the test will enter the push at same time.
309   class NonThreadSafeQueue {
310    public:
311     explicit NonThreadSafeQueue(base::AsserterBase* asserter)
312         : push_pop_(asserter) {
313     }
314 
315     NonThreadSafeQueue(const NonThreadSafeQueue&) = delete;
316     NonThreadSafeQueue& operator=(const NonThreadSafeQueue&) = delete;
317 
318     void push(int) {
319       DFAKE_SCOPED_RECURSIVE_LOCK(push_pop_);
320       bar();
321       base::PlatformThread::Sleep(base::Seconds(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 
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     raw_ptr<NonThreadSafeQueue> queue_;
360     raw_ptr<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