LockFreeQueues.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. /*
  2. * Copyright (c) Contributors to the Open 3D Engine Project.
  3. * For complete copyright and license terms please see the LICENSE at the root of this distribution.
  4. *
  5. * SPDX-License-Identifier: Apache-2.0 OR MIT
  6. *
  7. */
  8. #include "UserTypes.h"
  9. #include <AzCore/std/parallel/thread.h>
  10. #include <AzCore/std/parallel/containers/lock_free_queue.h>
  11. #include <AzCore/std/parallel/containers/lock_free_stamped_queue.h>
  12. #include <AzCore/std/functional.h>
  13. #include <AzCore/std/smart_ptr/shared_ptr.h>
  14. namespace UnitTest
  15. {
  16. using namespace AZStd;
  17. using namespace UnitTestInternal;
  18. class LockFreeQueue
  19. : public LeakDetectionFixture
  20. {
  21. protected:
  22. #ifdef _DEBUG
  23. static const int NUM_ITERATIONS = 5000;
  24. #else
  25. static const int NUM_ITERATIONS = 100000;
  26. #endif
  27. public:
  28. template <class Q>
  29. void Push(Q* queue)
  30. {
  31. for (int i = 0; i < NUM_ITERATIONS; ++i)
  32. {
  33. queue->push(i);
  34. }
  35. }
  36. template <class Q>
  37. void Pop(Q* queue)
  38. {
  39. int expected = 0;
  40. while (expected < NUM_ITERATIONS)
  41. {
  42. typename Q::value_type value = NUM_ITERATIONS;
  43. if (queue->pop(&value))
  44. {
  45. if (value == expected)
  46. {
  47. ++m_counter;
  48. }
  49. ++expected;
  50. }
  51. }
  52. }
  53. atomic<int> m_counter;
  54. };
  55. TEST_F(LockFreeQueue, LockFreeQueue)
  56. {
  57. lock_free_queue<int, MyLockFreeAllocator> queue;
  58. int result;
  59. AZ_TEST_ASSERT(queue.empty());
  60. AZ_TEST_ASSERT(!queue.pop(&result));
  61. queue.push(20);
  62. AZ_TEST_ASSERT(!queue.empty());
  63. AZ_TEST_ASSERT(queue.pop(&result));
  64. AZ_TEST_ASSERT(result == 20);
  65. AZ_TEST_ASSERT(queue.empty());
  66. AZ_TEST_ASSERT(!queue.pop(&result));
  67. queue.push(20);
  68. queue.push(30);
  69. AZ_TEST_ASSERT(!queue.empty());
  70. AZ_TEST_ASSERT(queue.pop(&result));
  71. AZ_TEST_ASSERT(result == 20);
  72. AZ_TEST_ASSERT(!queue.empty());
  73. AZ_TEST_ASSERT(queue.pop(&result));
  74. AZ_TEST_ASSERT(result == 30);
  75. AZ_TEST_ASSERT(queue.empty());
  76. AZ_TEST_ASSERT(!queue.pop(&result));
  77. {
  78. m_counter = 0;
  79. AZStd::thread thread0(AZStd::bind(&LockFreeQueue::Push<decltype(queue)>, this, &queue));
  80. AZStd::thread thread1(AZStd::bind(&LockFreeQueue::Pop<decltype(queue)>, this, &queue));
  81. thread0.join();
  82. thread1.join();
  83. AZ_TEST_ASSERT(m_counter == NUM_ITERATIONS);
  84. AZ_TEST_ASSERT(queue.empty());
  85. }
  86. }
  87. struct SharedInt {
  88. SharedInt() : m_ptr(nullptr) {}
  89. SharedInt(int i) : m_ptr(new int(i)) {}
  90. bool operator==(const SharedInt& other)
  91. {
  92. if (!m_ptr || !other.m_ptr)
  93. {
  94. return false;
  95. }
  96. return *m_ptr == *other.m_ptr;
  97. }
  98. private:
  99. AZStd::shared_ptr<int> m_ptr;
  100. };
  101. TEST_F(LockFreeQueue, LockFreeQueueNonTrivialDestructor)
  102. {
  103. lock_free_queue<SharedInt, MyLockFreeAllocator> queue;
  104. SharedInt result;
  105. AZ_TEST_ASSERT(queue.empty());
  106. AZ_TEST_ASSERT(!queue.pop(&result));
  107. queue.push(20);
  108. AZ_TEST_ASSERT(!queue.empty());
  109. AZ_TEST_ASSERT(queue.pop(&result));
  110. AZ_TEST_ASSERT(result == 20);
  111. AZ_TEST_ASSERT(queue.empty());
  112. AZ_TEST_ASSERT(!queue.pop(&result));
  113. queue.push(20);
  114. queue.push(30);
  115. AZ_TEST_ASSERT(!queue.empty());
  116. AZ_TEST_ASSERT(queue.pop(&result));
  117. AZ_TEST_ASSERT(result == 20);
  118. AZ_TEST_ASSERT(!queue.empty());
  119. AZ_TEST_ASSERT(queue.pop(&result));
  120. AZ_TEST_ASSERT(result == 30);
  121. AZ_TEST_ASSERT(queue.empty());
  122. AZ_TEST_ASSERT(!queue.pop(&result));
  123. {
  124. m_counter = 0;
  125. AZStd::thread thread0(AZStd::bind(&LockFreeQueue::Push<decltype(queue)>, this, &queue));
  126. AZStd::thread thread1(AZStd::bind(&LockFreeQueue::Pop<decltype(queue)>, this, &queue));
  127. thread0.join();
  128. thread1.join();
  129. AZ_TEST_ASSERT(m_counter == NUM_ITERATIONS);
  130. AZ_TEST_ASSERT(queue.empty());
  131. }
  132. }
  133. TEST_F(LockFreeQueue, LockFreeStampedQueue)
  134. {
  135. lock_free_stamped_queue<int, MyLockFreeAllocator> queue;
  136. int result;
  137. AZ_TEST_ASSERT(queue.empty());
  138. AZ_TEST_ASSERT(!queue.pop(&result));
  139. queue.push(20);
  140. AZ_TEST_ASSERT(!queue.empty());
  141. AZ_TEST_ASSERT(queue.pop(&result));
  142. AZ_TEST_ASSERT(result == 20);
  143. AZ_TEST_ASSERT(queue.empty());
  144. AZ_TEST_ASSERT(!queue.pop(&result));
  145. queue.push(20);
  146. queue.push(30);
  147. AZ_TEST_ASSERT(!queue.empty());
  148. AZ_TEST_ASSERT(queue.pop(&result));
  149. AZ_TEST_ASSERT(result == 20);
  150. AZ_TEST_ASSERT(!queue.empty());
  151. AZ_TEST_ASSERT(queue.pop(&result));
  152. AZ_TEST_ASSERT(result == 30);
  153. AZ_TEST_ASSERT(queue.empty());
  154. AZ_TEST_ASSERT(!queue.pop(&result));
  155. {
  156. m_counter = 0;
  157. AZStd::thread thread0(AZStd::bind(&LockFreeQueue::Push<decltype(queue)>, this, &queue));
  158. AZStd::thread thread1(AZStd::bind(&LockFreeQueue::Pop<decltype(queue)>, this, &queue));
  159. thread0.join();
  160. thread1.join();
  161. AZ_TEST_ASSERT(m_counter == NUM_ITERATIONS);
  162. AZ_TEST_ASSERT(queue.empty());
  163. }
  164. }
  165. TEST_F(LockFreeQueue, LockFreeStampedQueueNonTrivialDestructor)
  166. {
  167. lock_free_stamped_queue<SharedInt, MyLockFreeAllocator> queue;
  168. SharedInt result;
  169. AZ_TEST_ASSERT(queue.empty());
  170. AZ_TEST_ASSERT(!queue.pop(&result));
  171. queue.push(20);
  172. AZ_TEST_ASSERT(!queue.empty());
  173. AZ_TEST_ASSERT(queue.pop(&result));
  174. AZ_TEST_ASSERT(result == 20);
  175. AZ_TEST_ASSERT(queue.empty());
  176. AZ_TEST_ASSERT(!queue.pop(&result));
  177. queue.push(20);
  178. queue.push(30);
  179. AZ_TEST_ASSERT(!queue.empty());
  180. AZ_TEST_ASSERT(queue.pop(&result));
  181. AZ_TEST_ASSERT(result == 20);
  182. AZ_TEST_ASSERT(!queue.empty());
  183. AZ_TEST_ASSERT(queue.pop(&result));
  184. AZ_TEST_ASSERT(result == 30);
  185. AZ_TEST_ASSERT(queue.empty());
  186. AZ_TEST_ASSERT(!queue.pop(&result));
  187. {
  188. m_counter = 0;
  189. AZStd::thread thread0(AZStd::bind(&LockFreeQueue::Push<decltype(queue)>, this, &queue));
  190. AZStd::thread thread1(AZStd::bind(&LockFreeQueue::Pop<decltype(queue)>, this, &queue));
  191. thread0.join();
  192. thread1.join();
  193. AZ_TEST_ASSERT(m_counter == NUM_ITERATIONS);
  194. AZ_TEST_ASSERT(queue.empty());
  195. }
  196. }
  197. }