test-queue-foreach-delete.c 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. /* Copyright The libuv project and contributors. All rights reserved.
  2. *
  3. * Permission is hereby granted, free of charge, to any person obtaining a copy
  4. * of this software and associated documentation files (the "Software"), to
  5. * deal in the Software without restriction, including without limitation the
  6. * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
  7. * sell copies of the Software, and to permit persons to whom the Software is
  8. * furnished to do so, subject to the following conditions:
  9. *
  10. * The above copyright notice and this permission notice shall be included in
  11. * all copies or substantial portions of the Software.
  12. *
  13. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  14. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  15. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  16. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  17. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  18. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
  19. * IN THE SOFTWARE.
  20. */
  21. #include "uv.h"
  22. #include "task.h"
  23. #include <string.h>
  24. /*
  25. * The idea behind the test is as follows.
  26. * Certain handle types are stored in a queue internally.
  27. * Extra care should be taken for removal of a handle from the queue while iterating over the queue.
  28. * (i.e., QUEUE_REMOVE() called within QUEUE_FOREACH())
  29. * This usually happens when someone closes or stops a handle from within its callback.
  30. * So we need to check that we haven't screwed the queue on close/stop.
  31. * To do so we do the following (for each handle type):
  32. * 1. Create and start 3 handles (#0, #1, and #2).
  33. *
  34. * The queue after the start() calls:
  35. * ..=> [queue head] <=> [handle] <=> [handle #1] <=> [handle] <=..
  36. *
  37. * 2. Trigger handles to fire (for uv_idle_t, uv_prepare_t, and uv_check_t there is nothing to do).
  38. *
  39. * 3. In the callback for the first-executed handle (#0 or #2 depending on handle type)
  40. * stop the handle and the next one (#1).
  41. * (for uv_idle_t, uv_prepare_t, and uv_check_t callbacks are executed in the reverse order as they are start()'ed,
  42. * so callback for handle #2 will be called first)
  43. *
  44. * The queue after the stop() calls:
  45. * correct foreach "next" |
  46. * \/
  47. * ..=> [queue head] <==============================> [handle] <=..
  48. * [ ] <- [handle] <=> [handle #1] -> [ ]
  49. * /\
  50. * wrong foreach "next" |
  51. *
  52. * 4. The callback for handle #1 shouldn't be called because the handle #1 is stopped in the previous step.
  53. * However, if QUEUE_REMOVE() is not handled properly within QUEUE_FOREACH(), the callback _will_ be called.
  54. */
  55. static const unsigned first_handle_number_idle = 2;
  56. static const unsigned first_handle_number_prepare = 2;
  57. static const unsigned first_handle_number_check = 2;
  58. #ifdef __linux__
  59. static const unsigned first_handle_number_fs_event = 0;
  60. #endif
  61. #define DEFINE_GLOBALS_AND_CBS(name, ...) \
  62. static uv_##name##_t (name)[3]; \
  63. static unsigned name##_cb_calls[3]; \
  64. \
  65. static void name##2_cb(__VA_ARGS__) { \
  66. ASSERT(handle == &(name)[2]); \
  67. if (first_handle_number_##name == 2) { \
  68. uv_close((uv_handle_t*)&(name)[2], NULL); \
  69. uv_close((uv_handle_t*)&(name)[1], NULL); \
  70. } \
  71. name##_cb_calls[2]++; \
  72. } \
  73. \
  74. static void name##1_cb(__VA_ARGS__) { \
  75. ASSERT(handle == &(name)[1]); \
  76. ASSERT(0 && "Shouldn't be called" && (&name[0])); \
  77. } \
  78. \
  79. static void name##0_cb(__VA_ARGS__) { \
  80. ASSERT(handle == &(name)[0]); \
  81. if (first_handle_number_##name == 0) { \
  82. uv_close((uv_handle_t*)&(name)[0], NULL); \
  83. uv_close((uv_handle_t*)&(name)[1], NULL); \
  84. } \
  85. name##_cb_calls[0]++; \
  86. } \
  87. \
  88. static const uv_##name##_cb name##_cbs[] = { \
  89. name##0_cb, \
  90. name##1_cb, \
  91. name##2_cb, \
  92. };
  93. #define INIT_AND_START(name, loop) \
  94. do { \
  95. size_t i; \
  96. for (i = 0; i < ARRAY_SIZE(name); i++) { \
  97. int r; \
  98. r = uv_##name##_init((loop), &(name)[i]); \
  99. ASSERT(r == 0); \
  100. \
  101. r = uv_##name##_start(&(name)[i], name##_cbs[i]); \
  102. ASSERT(r == 0); \
  103. } \
  104. } while (0)
  105. #define END_ASSERTS(name) \
  106. do { \
  107. ASSERT(name##_cb_calls[0] == 1); \
  108. ASSERT(name##_cb_calls[1] == 0); \
  109. ASSERT(name##_cb_calls[2] == 1); \
  110. } while (0)
  111. DEFINE_GLOBALS_AND_CBS(idle, uv_idle_t* handle)
  112. DEFINE_GLOBALS_AND_CBS(prepare, uv_prepare_t* handle)
  113. DEFINE_GLOBALS_AND_CBS(check, uv_check_t* handle)
  114. #ifdef __linux__
  115. DEFINE_GLOBALS_AND_CBS(fs_event,
  116. uv_fs_event_t* handle,
  117. const char* filename,
  118. int events,
  119. int status)
  120. static const char watched_dir[] = ".";
  121. static uv_timer_t timer;
  122. static unsigned helper_timer_cb_calls;
  123. static void init_and_start_fs_events(uv_loop_t* loop) {
  124. size_t i;
  125. for (i = 0; i < ARRAY_SIZE(fs_event); i++) {
  126. int r;
  127. r = uv_fs_event_init(loop, &fs_event[i]);
  128. ASSERT(r == 0);
  129. r = uv_fs_event_start(&fs_event[i],
  130. (uv_fs_event_cb)fs_event_cbs[i],
  131. watched_dir,
  132. 0);
  133. ASSERT(r == 0);
  134. }
  135. }
  136. static void helper_timer_cb(uv_timer_t* thandle) {
  137. int r;
  138. uv_fs_t fs_req;
  139. /* fire all fs_events */
  140. r = uv_fs_utime(thandle->loop, &fs_req, watched_dir, 0, 0, NULL);
  141. ASSERT(r == 0);
  142. ASSERT(fs_req.result == 0);
  143. ASSERT(fs_req.fs_type == UV_FS_UTIME);
  144. ASSERT(strcmp(fs_req.path, watched_dir) == 0);
  145. uv_fs_req_cleanup(&fs_req);
  146. helper_timer_cb_calls++;
  147. }
  148. #endif
  149. TEST_IMPL(queue_foreach_delete) {
  150. uv_loop_t* loop;
  151. int r;
  152. loop = uv_default_loop();
  153. INIT_AND_START(idle, loop);
  154. INIT_AND_START(prepare, loop);
  155. INIT_AND_START(check, loop);
  156. #ifdef __linux__
  157. init_and_start_fs_events(loop);
  158. /* helper timer to trigger async and fs_event callbacks */
  159. r = uv_timer_init(loop, &timer);
  160. ASSERT(r == 0);
  161. r = uv_timer_start(&timer, helper_timer_cb, 0, 0);
  162. ASSERT(r == 0);
  163. #endif
  164. r = uv_run(loop, UV_RUN_NOWAIT);
  165. ASSERT(r == 1);
  166. END_ASSERTS(idle);
  167. END_ASSERTS(prepare);
  168. END_ASSERTS(check);
  169. #ifdef __linux__
  170. ASSERT(helper_timer_cb_calls == 1);
  171. #endif
  172. MAKE_VALGRIND_HAPPY();
  173. return 0;
  174. }