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 22 #include "uv.h" 23 #include "task.h" 24 25 #include <string.h> 26 27 28 /* 29 * The idea behind the test is as follows. 30 * Certain handle types are stored in a queue internally. 31 * Extra care should be taken for removal of a handle from the queue while iterating over the queue. 32 * (i.e., QUEUE_REMOVE() called within QUEUE_FOREACH()) 33 * This usually happens when someone closes or stops a handle from within its callback. 34 * So we need to check that we haven't screwed the queue on close/stop. 35 * To do so we do the following (for each handle type): 36 * 1. Create and start 3 handles (#0, #1, and #2). 37 * 38 * The queue after the start() calls: 39 * ..=> [queue head] <=> [handle] <=> [handle #1] <=> [handle] <=.. 40 * 41 * 2. Trigger handles to fire (for uv_idle_t, uv_prepare_t, and uv_check_t there is nothing to do). 42 * 43 * 3. In the callback for the first-executed handle (#0 or #2 depending on handle type) 44 * stop the handle and the next one (#1). 45 * (for uv_idle_t, uv_prepare_t, and uv_check_t callbacks are executed in the reverse order as they are start()'ed, 46 * so callback for handle #2 will be called first) 47 * 48 * The queue after the stop() calls: 49 * correct foreach "next" | 50 * \/ 51 * ..=> [queue head] <==============================> [handle] <=.. 52 * [ ] <- [handle] <=> [handle #1] -> [ ] 53 * /\ 54 * wrong foreach "next" | 55 * 56 * 4. The callback for handle #1 shouldn't be called because the handle #1 is stopped in the previous step. 57 * However, if QUEUE_REMOVE() is not handled properly within QUEUE_FOREACH(), the callback _will_ be called. 58 */ 59 60 static const unsigned first_handle_number_idle = 2; 61 static const unsigned first_handle_number_prepare = 2; 62 static const unsigned first_handle_number_check = 2; 63 #ifdef __linux__ 64 static const unsigned first_handle_number_fs_event = 0; 65 #endif 66 67 68 #define DEFINE_GLOBALS_AND_CBS(name, ...) \ 69 static uv_##name##_t (name)[3]; \ 70 static unsigned name##_cb_calls[3]; \ 71 \ 72 static void name##2_cb(__VA_ARGS__) { \ 73 ASSERT(handle == &(name)[2]); \ 74 if (first_handle_number_##name == 2) { \ 75 uv_close((uv_handle_t*)&(name)[2], NULL); \ 76 uv_close((uv_handle_t*)&(name)[1], NULL); \ 77 } \ 78 name##_cb_calls[2]++; \ 79 } \ 80 \ 81 static void name##1_cb(__VA_ARGS__) { \ 82 ASSERT(handle == &(name)[1]); \ 83 ASSERT(0 && "Shouldn't be called" && (&name[0])); \ 84 } \ 85 \ 86 static void name##0_cb(__VA_ARGS__) { \ 87 ASSERT(handle == &(name)[0]); \ 88 if (first_handle_number_##name == 0) { \ 89 uv_close((uv_handle_t*)&(name)[0], NULL); \ 90 uv_close((uv_handle_t*)&(name)[1], NULL); \ 91 } \ 92 name##_cb_calls[0]++; \ 93 } \ 94 \ 95 static const uv_##name##_cb name##_cbs[] = { \ 96 name##0_cb, \ 97 name##1_cb, \ 98 name##2_cb, \ 99 }; 100 101 #define INIT_AND_START(name, loop) \ 102 do { \ 103 size_t i; \ 104 for (i = 0; i < ARRAY_SIZE(name); i++) { \ 105 int r; \ 106 r = uv_##name##_init((loop), &(name)[i]); \ 107 ASSERT(r == 0); \ 108 \ 109 r = uv_##name##_start(&(name)[i], name##_cbs[i]); \ 110 ASSERT(r == 0); \ 111 } \ 112 } while (0) 113 114 #define END_ASSERTS(name) \ 115 do { \ 116 ASSERT(name##_cb_calls[0] == 1); \ 117 ASSERT(name##_cb_calls[1] == 0); \ 118 ASSERT(name##_cb_calls[2] == 1); \ 119 } while (0) 120 121 DEFINE_GLOBALS_AND_CBS(idle, uv_idle_t* handle) 122 DEFINE_GLOBALS_AND_CBS(prepare, uv_prepare_t* handle) 123 DEFINE_GLOBALS_AND_CBS(check, uv_check_t* handle) 124 125 #ifdef __linux__ 126 DEFINE_GLOBALS_AND_CBS(fs_event, 127 uv_fs_event_t* handle, 128 const char* filename, 129 int events, 130 int status) 131 132 static const char watched_dir[] = "."; 133 static uv_timer_t timer; 134 static unsigned helper_timer_cb_calls; 135 136 137 static void init_and_start_fs_events(uv_loop_t* loop) { 138 size_t i; 139 for (i = 0; i < ARRAY_SIZE(fs_event); i++) { 140 int r; 141 r = uv_fs_event_init(loop, &fs_event[i]); 142 ASSERT(r == 0); 143 144 r = uv_fs_event_start(&fs_event[i], 145 (uv_fs_event_cb)fs_event_cbs[i], 146 watched_dir, 147 0); 148 ASSERT(r == 0); 149 } 150 } 151 152 static void helper_timer_cb(uv_timer_t* thandle) { 153 int r; 154 uv_fs_t fs_req; 155 156 /* fire all fs_events */ 157 r = uv_fs_utime(thandle->loop, &fs_req, watched_dir, 0, 0, NULL); 158 ASSERT(r == 0); 159 ASSERT(fs_req.result == 0); 160 ASSERT(fs_req.fs_type == UV_FS_UTIME); 161 ASSERT(strcmp(fs_req.path, watched_dir) == 0); 162 uv_fs_req_cleanup(&fs_req); 163 164 helper_timer_cb_calls++; 165 } 166 #endif 167 168 169 TEST_IMPL(queue_foreach_delete) { 170 uv_loop_t* loop; 171 int r; 172 173 loop = uv_default_loop(); 174 175 INIT_AND_START(idle, loop); 176 INIT_AND_START(prepare, loop); 177 INIT_AND_START(check, loop); 178 179 #ifdef __linux__ 180 init_and_start_fs_events(loop); 181 182 /* helper timer to trigger async and fs_event callbacks */ 183 r = uv_timer_init(loop, &timer); 184 ASSERT(r == 0); 185 186 r = uv_timer_start(&timer, helper_timer_cb, 0, 0); 187 ASSERT(r == 0); 188 #endif 189 190 r = uv_run(loop, UV_RUN_NOWAIT); 191 ASSERT(r == 1); 192 193 END_ASSERTS(idle); 194 END_ASSERTS(prepare); 195 END_ASSERTS(check); 196 197 #ifdef __linux__ 198 ASSERT(helper_timer_cb_calls == 1); 199 #endif 200 201 MAKE_VALGRIND_HAPPY(); 202 203 return 0; 204 } 205