thread_collision_warner_unittest.cc 9.6 KB

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