thread_collision_warner_unittest.cc 9.5 KB

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