cxx11_runqueue.cpp 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. // This file is part of Eigen, a lightweight C++ template library
  2. // for linear algebra.
  3. //
  4. // Copyright (C) 2016 Dmitry Vyukov <dvyukov@google.com>
  5. // Copyright (C) 2016 Benoit Steiner <benoit.steiner.goog@gmail.com>
  6. //
  7. // This Source Code Form is subject to the terms of the Mozilla
  8. // Public License v. 2.0. If a copy of the MPL was not distributed
  9. // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
  10. #define EIGEN_USE_THREADS
  11. #include <cstdlib>
  12. #include "main.h"
  13. #include <Eigen/CXX11/ThreadPool>
  14. // Visual studio doesn't implement a rand_r() function since its
  15. // implementation of rand() is already thread safe
  16. int rand_reentrant(unsigned int* s) {
  17. #ifdef EIGEN_COMP_MSVC_STRICT
  18. EIGEN_UNUSED_VARIABLE(s);
  19. return rand();
  20. #else
  21. return rand_r(s);
  22. #endif
  23. }
  24. void test_basic_runqueue()
  25. {
  26. RunQueue<int, 4> q;
  27. // Check empty state.
  28. VERIFY(q.Empty());
  29. VERIFY_IS_EQUAL(0u, q.Size());
  30. VERIFY_IS_EQUAL(0, q.PopFront());
  31. std::vector<int> stolen;
  32. VERIFY_IS_EQUAL(0u, q.PopBackHalf(&stolen));
  33. VERIFY_IS_EQUAL(0u, stolen.size());
  34. // Push one front, pop one front.
  35. VERIFY_IS_EQUAL(0, q.PushFront(1));
  36. VERIFY_IS_EQUAL(1u, q.Size());
  37. VERIFY_IS_EQUAL(1, q.PopFront());
  38. VERIFY_IS_EQUAL(0u, q.Size());
  39. // Push front to overflow.
  40. VERIFY_IS_EQUAL(0, q.PushFront(2));
  41. VERIFY_IS_EQUAL(1u, q.Size());
  42. VERIFY_IS_EQUAL(0, q.PushFront(3));
  43. VERIFY_IS_EQUAL(2u, q.Size());
  44. VERIFY_IS_EQUAL(0, q.PushFront(4));
  45. VERIFY_IS_EQUAL(3u, q.Size());
  46. VERIFY_IS_EQUAL(0, q.PushFront(5));
  47. VERIFY_IS_EQUAL(4u, q.Size());
  48. VERIFY_IS_EQUAL(6, q.PushFront(6));
  49. VERIFY_IS_EQUAL(4u, q.Size());
  50. VERIFY_IS_EQUAL(5, q.PopFront());
  51. VERIFY_IS_EQUAL(3u, q.Size());
  52. VERIFY_IS_EQUAL(4, q.PopFront());
  53. VERIFY_IS_EQUAL(2u, q.Size());
  54. VERIFY_IS_EQUAL(3, q.PopFront());
  55. VERIFY_IS_EQUAL(1u, q.Size());
  56. VERIFY_IS_EQUAL(2, q.PopFront());
  57. VERIFY_IS_EQUAL(0u, q.Size());
  58. VERIFY_IS_EQUAL(0, q.PopFront());
  59. // Push one back, pop one back.
  60. VERIFY_IS_EQUAL(0, q.PushBack(7));
  61. VERIFY_IS_EQUAL(1u, q.Size());
  62. VERIFY_IS_EQUAL(1u, q.PopBackHalf(&stolen));
  63. VERIFY_IS_EQUAL(1u, stolen.size());
  64. VERIFY_IS_EQUAL(7, stolen[0]);
  65. VERIFY_IS_EQUAL(0u, q.Size());
  66. stolen.clear();
  67. // Push back to overflow.
  68. VERIFY_IS_EQUAL(0, q.PushBack(8));
  69. VERIFY_IS_EQUAL(1u, q.Size());
  70. VERIFY_IS_EQUAL(0, q.PushBack(9));
  71. VERIFY_IS_EQUAL(2u, q.Size());
  72. VERIFY_IS_EQUAL(0, q.PushBack(10));
  73. VERIFY_IS_EQUAL(3u, q.Size());
  74. VERIFY_IS_EQUAL(0, q.PushBack(11));
  75. VERIFY_IS_EQUAL(4u, q.Size());
  76. VERIFY_IS_EQUAL(12, q.PushBack(12));
  77. VERIFY_IS_EQUAL(4u, q.Size());
  78. // Pop back in halves.
  79. VERIFY_IS_EQUAL(2u, q.PopBackHalf(&stolen));
  80. VERIFY_IS_EQUAL(2u, stolen.size());
  81. VERIFY_IS_EQUAL(10, stolen[0]);
  82. VERIFY_IS_EQUAL(11, stolen[1]);
  83. VERIFY_IS_EQUAL(2u, q.Size());
  84. stolen.clear();
  85. VERIFY_IS_EQUAL(1u, q.PopBackHalf(&stolen));
  86. VERIFY_IS_EQUAL(1u, stolen.size());
  87. VERIFY_IS_EQUAL(9, stolen[0]);
  88. VERIFY_IS_EQUAL(1u, q.Size());
  89. stolen.clear();
  90. VERIFY_IS_EQUAL(1u, q.PopBackHalf(&stolen));
  91. VERIFY_IS_EQUAL(1u, stolen.size());
  92. VERIFY_IS_EQUAL(8, stolen[0]);
  93. stolen.clear();
  94. VERIFY_IS_EQUAL(0u, q.PopBackHalf(&stolen));
  95. VERIFY_IS_EQUAL(0u, stolen.size());
  96. // Empty again.
  97. VERIFY(q.Empty());
  98. VERIFY_IS_EQUAL(0u, q.Size());
  99. VERIFY_IS_EQUAL(0, q.PushFront(1));
  100. VERIFY_IS_EQUAL(0, q.PushFront(2));
  101. VERIFY_IS_EQUAL(0, q.PushFront(3));
  102. VERIFY_IS_EQUAL(1, q.PopBack());
  103. VERIFY_IS_EQUAL(2, q.PopBack());
  104. VERIFY_IS_EQUAL(3, q.PopBack());
  105. VERIFY(q.Empty());
  106. VERIFY_IS_EQUAL(0u, q.Size());
  107. }
  108. // Empty tests that the queue is not claimed to be empty when is is in fact not.
  109. // Emptiness property is crucial part of thread pool blocking scheme,
  110. // so we go to great effort to ensure this property. We create a queue with
  111. // 1 element and then push 1 element (either front or back at random) and pop
  112. // 1 element (either front or back at random). So queue always contains at least
  113. // 1 element, but otherwise changes chaotically. Another thread constantly tests
  114. // that the queue is not claimed to be empty.
  115. void test_empty_runqueue()
  116. {
  117. RunQueue<int, 4> q;
  118. q.PushFront(1);
  119. std::atomic<bool> done(false);
  120. std::thread mutator([&q, &done]() {
  121. unsigned rnd = 0;
  122. std::vector<int> stolen;
  123. for (int i = 0; i < 1 << 18; i++) {
  124. if (rand_reentrant(&rnd) % 2)
  125. VERIFY_IS_EQUAL(0, q.PushFront(1));
  126. else
  127. VERIFY_IS_EQUAL(0, q.PushBack(1));
  128. if (rand_reentrant(&rnd) % 2)
  129. VERIFY_IS_EQUAL(1, q.PopFront());
  130. else {
  131. for (;;) {
  132. if (q.PopBackHalf(&stolen) == 1) {
  133. stolen.clear();
  134. break;
  135. }
  136. VERIFY_IS_EQUAL(0u, stolen.size());
  137. }
  138. }
  139. }
  140. done = true;
  141. });
  142. while (!done) {
  143. VERIFY(!q.Empty());
  144. int size = q.Size();
  145. VERIFY_GE(size, 1);
  146. VERIFY_LE(size, 2);
  147. }
  148. VERIFY_IS_EQUAL(1, q.PopFront());
  149. mutator.join();
  150. }
  151. // Stress is a chaotic random test.
  152. // One thread (owner) calls PushFront/PopFront, other threads call PushBack/
  153. // PopBack. Ensure that we don't crash, deadlock, and all sanity checks pass.
  154. void test_stress_runqueue()
  155. {
  156. static const int kEvents = 1 << 18;
  157. RunQueue<int, 8> q;
  158. std::atomic<int> total(0);
  159. std::vector<std::unique_ptr<std::thread>> threads;
  160. threads.emplace_back(new std::thread([&q, &total]() {
  161. int sum = 0;
  162. int pushed = 1;
  163. int popped = 1;
  164. while (pushed < kEvents || popped < kEvents) {
  165. if (pushed < kEvents) {
  166. if (q.PushFront(pushed) == 0) {
  167. sum += pushed;
  168. pushed++;
  169. }
  170. }
  171. if (popped < kEvents) {
  172. int v = q.PopFront();
  173. if (v != 0) {
  174. sum -= v;
  175. popped++;
  176. }
  177. }
  178. }
  179. total += sum;
  180. }));
  181. for (int i = 0; i < 2; i++) {
  182. threads.emplace_back(new std::thread([&q, &total]() {
  183. int sum = 0;
  184. for (int j = 1; j < kEvents; j++) {
  185. if (q.PushBack(j) == 0) {
  186. sum += j;
  187. continue;
  188. }
  189. EIGEN_THREAD_YIELD();
  190. j--;
  191. }
  192. total += sum;
  193. }));
  194. threads.emplace_back(new std::thread([&q, &total]() {
  195. int sum = 0;
  196. std::vector<int> stolen;
  197. for (int j = 1; j < kEvents;) {
  198. if (q.PopBackHalf(&stolen) == 0) {
  199. EIGEN_THREAD_YIELD();
  200. continue;
  201. }
  202. while (stolen.size() && j < kEvents) {
  203. int v = stolen.back();
  204. stolen.pop_back();
  205. VERIFY_IS_NOT_EQUAL(v, 0);
  206. sum += v;
  207. j++;
  208. }
  209. }
  210. while (stolen.size()) {
  211. int v = stolen.back();
  212. stolen.pop_back();
  213. VERIFY_IS_NOT_EQUAL(v, 0);
  214. while ((v = q.PushBack(v)) != 0) EIGEN_THREAD_YIELD();
  215. }
  216. total -= sum;
  217. }));
  218. }
  219. for (size_t i = 0; i < threads.size(); i++) threads[i]->join();
  220. VERIFY(q.Empty());
  221. VERIFY(total.load() == 0);
  222. }
  223. EIGEN_DECLARE_TEST(cxx11_runqueue)
  224. {
  225. CALL_SUBTEST_1(test_basic_runqueue());
  226. CALL_SUBTEST_2(test_empty_runqueue());
  227. CALL_SUBTEST_3(test_stress_runqueue());
  228. }