mpmcqueue_test.cc 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. /*
  2. *
  3. * Copyright 2019 gRPC authors.
  4. *
  5. * Licensed under the Apache License, Version 2.0 (the "License");
  6. * you may not use this file except in compliance with the License.
  7. * You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. *
  17. */
  18. #include "src/core/lib/iomgr/executor/mpmcqueue.h"
  19. #include <grpc/grpc.h>
  20. #include "src/core/lib/gprpp/thd.h"
  21. #include "test/core/util/test_config.h"
  22. #define TEST_NUM_ITEMS 10000
  23. // Testing items for queue
  24. struct WorkItem {
  25. int index;
  26. bool done;
  27. explicit WorkItem(int i) : index(i) { done = false; }
  28. };
  29. // Thread to "produce" items and put items into queue
  30. // It will also check that all items has been marked done and clean up all
  31. // produced items on destructing.
  32. class ProducerThread {
  33. public:
  34. ProducerThread(grpc_core::InfLenFIFOQueue* queue, int start_index,
  35. int num_items)
  36. : start_index_(start_index), num_items_(num_items), queue_(queue) {
  37. items_ = nullptr;
  38. thd_ = grpc_core::Thread(
  39. "mpmcq_test_producer_thd",
  40. [](void* th) { static_cast<ProducerThread*>(th)->Run(); }, this);
  41. }
  42. ~ProducerThread() {
  43. for (int i = 0; i < num_items_; ++i) {
  44. GPR_ASSERT(items_[i]->done);
  45. delete items_[i];
  46. }
  47. delete[] items_;
  48. }
  49. void Start() { thd_.Start(); }
  50. void Join() { thd_.Join(); }
  51. private:
  52. void Run() {
  53. items_ = new WorkItem*[num_items_];
  54. for (int i = 0; i < num_items_; ++i) {
  55. items_[i] = new WorkItem(start_index_ + i);
  56. queue_->Put(items_[i]);
  57. }
  58. }
  59. int start_index_;
  60. int num_items_;
  61. grpc_core::InfLenFIFOQueue* queue_;
  62. grpc_core::Thread thd_;
  63. WorkItem** items_;
  64. };
  65. // Thread to pull out items from queue
  66. class ConsumerThread {
  67. public:
  68. explicit ConsumerThread(grpc_core::InfLenFIFOQueue* queue) : queue_(queue) {
  69. thd_ = grpc_core::Thread(
  70. "mpmcq_test_consumer_thd",
  71. [](void* th) { static_cast<ConsumerThread*>(th)->Run(); }, this);
  72. }
  73. ~ConsumerThread() {}
  74. void Start() { thd_.Start(); }
  75. void Join() { thd_.Join(); }
  76. private:
  77. void Run() {
  78. // count number of Get() called in this thread
  79. int count = 0;
  80. WorkItem* item;
  81. while ((item = static_cast<WorkItem*>(queue_->Get(nullptr))) != nullptr) {
  82. count++;
  83. GPR_ASSERT(!item->done);
  84. item->done = true;
  85. }
  86. gpr_log(GPR_DEBUG, "ConsumerThread: %d times of Get() called.", count);
  87. }
  88. grpc_core::InfLenFIFOQueue* queue_;
  89. grpc_core::Thread thd_;
  90. };
  91. static void test_FIFO(void) {
  92. gpr_log(GPR_INFO, "test_FIFO");
  93. grpc_core::InfLenFIFOQueue large_queue;
  94. for (int i = 0; i < TEST_NUM_ITEMS; ++i) {
  95. large_queue.Put(static_cast<void*>(new WorkItem(i)));
  96. }
  97. GPR_ASSERT(large_queue.count() == TEST_NUM_ITEMS);
  98. for (int i = 0; i < TEST_NUM_ITEMS; ++i) {
  99. WorkItem* item = static_cast<WorkItem*>(large_queue.Get(nullptr));
  100. GPR_ASSERT(i == item->index);
  101. delete item;
  102. }
  103. }
  104. // Test if queue's behavior of expanding is correct. (Only does expansion when
  105. // it gets full, and each time expands to doubled size).
  106. static void test_space_efficiency(void) {
  107. gpr_log(GPR_INFO, "test_space_efficiency");
  108. grpc_core::InfLenFIFOQueue queue;
  109. for (int i = 0; i < queue.init_num_nodes(); ++i) {
  110. queue.Put(static_cast<void*>(new WorkItem(i)));
  111. }
  112. // Queue should not have been expanded at this time.
  113. GPR_ASSERT(queue.num_nodes() == queue.init_num_nodes());
  114. for (int i = 0; i < queue.init_num_nodes(); ++i) {
  115. WorkItem* item = static_cast<WorkItem*>(queue.Get(nullptr));
  116. queue.Put(item);
  117. }
  118. GPR_ASSERT(queue.num_nodes() == queue.init_num_nodes());
  119. for (int i = 0; i < queue.init_num_nodes(); ++i) {
  120. WorkItem* item = static_cast<WorkItem*>(queue.Get(nullptr));
  121. delete item;
  122. }
  123. // Queue never shrinks even it is empty.
  124. GPR_ASSERT(queue.num_nodes() == queue.init_num_nodes());
  125. GPR_ASSERT(queue.count() == 0);
  126. // queue empty now
  127. for (int i = 0; i < queue.init_num_nodes() * 2; ++i) {
  128. queue.Put(static_cast<void*>(new WorkItem(i)));
  129. }
  130. GPR_ASSERT(queue.count() == queue.init_num_nodes() * 2);
  131. // Queue should have been expanded once.
  132. GPR_ASSERT(queue.num_nodes() == queue.init_num_nodes() * 2);
  133. for (int i = 0; i < queue.init_num_nodes(); ++i) {
  134. WorkItem* item = static_cast<WorkItem*>(queue.Get(nullptr));
  135. delete item;
  136. }
  137. GPR_ASSERT(queue.count() == queue.init_num_nodes());
  138. // Queue will never shrink, should keep same number of node as before.
  139. GPR_ASSERT(queue.num_nodes() == queue.init_num_nodes() * 2);
  140. for (int i = 0; i < queue.init_num_nodes() + 1; ++i) {
  141. queue.Put(static_cast<void*>(new WorkItem(i)));
  142. }
  143. GPR_ASSERT(queue.count() == queue.init_num_nodes() * 2 + 1);
  144. // Queue should have been expanded twice.
  145. GPR_ASSERT(queue.num_nodes() == queue.init_num_nodes() * 4);
  146. for (int i = 0; i < queue.init_num_nodes() * 2 + 1; ++i) {
  147. WorkItem* item = static_cast<WorkItem*>(queue.Get(nullptr));
  148. delete item;
  149. }
  150. GPR_ASSERT(queue.count() == 0);
  151. GPR_ASSERT(queue.num_nodes() == queue.init_num_nodes() * 4);
  152. gpr_log(GPR_DEBUG, "Done.");
  153. }
  154. static void test_many_thread(void) {
  155. gpr_log(GPR_INFO, "test_many_thread");
  156. const int num_producer_threads = 10;
  157. const int num_consumer_threads = 20;
  158. grpc_core::InfLenFIFOQueue queue;
  159. ProducerThread** producer_threads = new ProducerThread*[num_producer_threads];
  160. ConsumerThread** consumer_threads = new ConsumerThread*[num_consumer_threads];
  161. gpr_log(GPR_DEBUG, "Fork ProducerThreads...");
  162. for (int i = 0; i < num_producer_threads; ++i) {
  163. producer_threads[i] =
  164. new ProducerThread(&queue, i * TEST_NUM_ITEMS, TEST_NUM_ITEMS);
  165. producer_threads[i]->Start();
  166. }
  167. gpr_log(GPR_DEBUG, "ProducerThreads Started.");
  168. gpr_log(GPR_DEBUG, "Fork ConsumerThreads...");
  169. for (int i = 0; i < num_consumer_threads; ++i) {
  170. consumer_threads[i] = new ConsumerThread(&queue);
  171. consumer_threads[i]->Start();
  172. }
  173. gpr_log(GPR_DEBUG, "ConsumerThreads Started.");
  174. gpr_log(GPR_DEBUG, "Waiting ProducerThreads to finish...");
  175. for (int i = 0; i < num_producer_threads; ++i) {
  176. producer_threads[i]->Join();
  177. }
  178. gpr_log(GPR_DEBUG, "All ProducerThreads Terminated.");
  179. gpr_log(GPR_DEBUG, "Terminating ConsumerThreads...");
  180. for (int i = 0; i < num_consumer_threads; ++i) {
  181. queue.Put(nullptr);
  182. }
  183. for (int i = 0; i < num_consumer_threads; ++i) {
  184. consumer_threads[i]->Join();
  185. }
  186. gpr_log(GPR_DEBUG, "All ConsumerThreads Terminated.");
  187. gpr_log(GPR_DEBUG, "Checking WorkItems and Cleaning Up...");
  188. for (int i = 0; i < num_producer_threads; ++i) {
  189. // Destructor of ProducerThread will do the check of WorkItems
  190. delete producer_threads[i];
  191. }
  192. delete[] producer_threads;
  193. for (int i = 0; i < num_consumer_threads; ++i) {
  194. delete consumer_threads[i];
  195. }
  196. delete[] consumer_threads;
  197. gpr_log(GPR_DEBUG, "Done.");
  198. }
  199. int main(int argc, char** argv) {
  200. grpc::testing::TestEnvironment env(argc, argv);
  201. grpc_init();
  202. test_FIFO();
  203. test_space_efficiency();
  204. test_many_thread();
  205. grpc_shutdown();
  206. return 0;
  207. }