testUVRAII.cxx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. #include <chrono>
  2. #include <iostream>
  3. #include <thread>
  4. #include <utility>
  5. #include <cm3p/uv.h>
  6. #include "cmUVHandlePtr.h"
  7. static void signal_reset_fn(uv_async_t* handle)
  8. {
  9. auto ptr = static_cast<cm::uv_async_ptr*>(handle->data);
  10. ptr->reset();
  11. }
  12. // A common pattern is to use an async signal to shutdown the server.
  13. static bool testAsyncShutdown()
  14. {
  15. uv_loop_t Loop;
  16. auto err = uv_loop_init(&Loop);
  17. if (err != 0) {
  18. std::cerr << "Could not init loop" << std::endl;
  19. return false;
  20. }
  21. {
  22. cm::uv_async_ptr signal;
  23. signal.init(Loop, &signal_reset_fn, &signal);
  24. std::thread([&] {
  25. std::this_thread::sleep_for(std::chrono::seconds(2));
  26. signal.send();
  27. })
  28. .detach();
  29. if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
  30. std::cerr << "Unclean exit state in testAsyncDtor" << std::endl;
  31. return false;
  32. }
  33. if (signal.get()) {
  34. std::cerr << "Loop exited with signal not being cleaned up" << std::endl;
  35. return false;
  36. }
  37. }
  38. uv_loop_close(&Loop);
  39. return true;
  40. }
  41. static void signal_fn(uv_async_t*)
  42. {
  43. }
  44. // Async dtor is sort of a pain; since it locks a mutex we must be sure its
  45. // dtor always calls reset otherwise the mutex is deleted then locked.
  46. static bool testAsyncDtor()
  47. {
  48. uv_loop_t Loop;
  49. auto err = uv_loop_init(&Loop);
  50. if (err != 0) {
  51. std::cerr << "Could not init loop" << std::endl;
  52. return false;
  53. }
  54. {
  55. cm::uv_async_ptr signal;
  56. signal.init(Loop, signal_fn);
  57. }
  58. if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
  59. std::cerr << "Unclean exit state in testAsyncDtor" << std::endl;
  60. return false;
  61. }
  62. uv_loop_close(&Loop);
  63. return true;
  64. }
  65. // Async needs a relatively stateful deleter; make sure that is properly
  66. // accounted for and doesn't try to hold on to invalid state when it is
  67. // moved
  68. static bool testAsyncMove()
  69. {
  70. uv_loop_t Loop;
  71. auto err = uv_loop_init(&Loop);
  72. if (err != 0) {
  73. std::cerr << "Could not init loop" << std::endl;
  74. return false;
  75. }
  76. {
  77. cm::uv_async_ptr signal;
  78. {
  79. cm::uv_async_ptr signalTmp;
  80. signalTmp.init(Loop, signal_fn);
  81. signal = std::move(signalTmp);
  82. }
  83. }
  84. if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
  85. std::cerr << "Unclean exit state in testAsyncDtor" << std::endl;
  86. return false;
  87. }
  88. uv_loop_close(&Loop);
  89. return true;
  90. }
  91. // When a type is castable to another uv type (pipe -> stream) here,
  92. // and the deleter is convertible as well, we should allow moves from
  93. // one type to the other.
  94. static bool testCrossAssignment()
  95. {
  96. uv_loop_t Loop;
  97. auto err = uv_loop_init(&Loop);
  98. if (err != 0) {
  99. std::cerr << "Could not init loop" << std::endl;
  100. return false;
  101. }
  102. {
  103. cm::uv_pipe_ptr pipe;
  104. pipe.init(Loop, 0);
  105. cm::uv_stream_ptr stream = std::move(pipe);
  106. if (pipe.get()) {
  107. std::cerr << "Move should be sure to invalidate the previous ptr"
  108. << std::endl;
  109. return false;
  110. }
  111. cm::uv_handle_ptr handle = std::move(stream);
  112. if (stream.get()) {
  113. std::cerr << "Move should be sure to invalidate the previous ptr"
  114. << std::endl;
  115. return false;
  116. }
  117. }
  118. if (uv_run(&Loop, UV_RUN_DEFAULT) != 0) {
  119. std::cerr << "Unclean exit state in testCrossAssignment" << std::endl;
  120. return false;
  121. }
  122. uv_loop_close(&Loop);
  123. return true;
  124. }
  125. // This test can't fail at run time; but this makes sure we have all our move
  126. // ctors created correctly.
  127. static bool testAllMoves()
  128. {
  129. using namespace cm;
  130. struct allTypes
  131. {
  132. uv_stream_ptr _7;
  133. uv_timer_ptr _8;
  134. uv_tty_ptr _9;
  135. uv_process_ptr _11;
  136. uv_pipe_ptr _12;
  137. uv_async_ptr _13;
  138. uv_signal_ptr _14;
  139. uv_handle_ptr _15;
  140. };
  141. allTypes a;
  142. allTypes b(std::move(a));
  143. allTypes c = std::move(b);
  144. return true;
  145. }
  146. static bool testLoopReset()
  147. {
  148. bool closed = false;
  149. cm::uv_loop_ptr loop;
  150. loop.init();
  151. uv_timer_t timer;
  152. uv_timer_init(loop, &timer);
  153. timer.data = &closed;
  154. uv_close(reinterpret_cast<uv_handle_t*>(&timer), [](uv_handle_t* handle) {
  155. auto closedPtr = static_cast<bool*>(handle->data);
  156. *closedPtr = true;
  157. });
  158. loop.reset();
  159. if (!closed) {
  160. std::cerr << "uv_loop_ptr did not finish" << std::endl;
  161. return false;
  162. }
  163. return true;
  164. }
  165. static bool testLoopDestructor()
  166. {
  167. bool closed = false;
  168. uv_timer_t timer;
  169. {
  170. cm::uv_loop_ptr loop;
  171. loop.init();
  172. uv_timer_init(loop, &timer);
  173. timer.data = &closed;
  174. uv_close(reinterpret_cast<uv_handle_t*>(&timer), [](uv_handle_t* handle) {
  175. auto closedPtr = static_cast<bool*>(handle->data);
  176. *closedPtr = true;
  177. });
  178. }
  179. if (!closed) {
  180. std::cerr << "uv_loop_ptr did not finish" << std::endl;
  181. return false;
  182. }
  183. return true;
  184. }
  185. int testUVRAII(int, char** const)
  186. {
  187. if ((testAsyncShutdown() &&
  188. testAsyncDtor() & testAsyncMove() & testCrossAssignment() &
  189. testAllMoves() & testLoopReset() & testLoopDestructor()) == 0) {
  190. return -1;
  191. }
  192. return 0;
  193. }