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., uv__queue_remove() called within uv__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 uv__queue_remove() is not handled properly within uv__queue_foreach(), the callback _will_ 58 * be called. 59 */ 60 61static const unsigned first_handle_number_idle = 2; 62static const unsigned first_handle_number_prepare = 2; 63static const unsigned first_handle_number_check = 2; 64#ifdef __linux__ 65static const unsigned first_handle_number_fs_event = 0; 66#endif 67 68 69#define DEFINE_GLOBALS_AND_CBS(name, ...) \ 70 static uv_##name##_t (name)[3]; \ 71 static unsigned name##_cb_calls[3]; \ 72 \ 73 static void name##2_cb(__VA_ARGS__) { \ 74 ASSERT_PTR_EQ(handle, &(name)[2]); \ 75 if (first_handle_number_##name == 2) { \ 76 uv_close((uv_handle_t*)&(name)[2], NULL); \ 77 uv_close((uv_handle_t*)&(name)[1], NULL); \ 78 } \ 79 name##_cb_calls[2]++; \ 80 } \ 81 \ 82 static void name##1_cb(__VA_ARGS__) { \ 83 ASSERT_PTR_EQ(handle, &(name)[1]); \ 84 ASSERT(0 && "Shouldn't be called" && (&name[0])); \ 85 } \ 86 \ 87 static void name##0_cb(__VA_ARGS__) { \ 88 ASSERT_PTR_EQ(handle, &(name)[0]); \ 89 if (first_handle_number_##name == 0) { \ 90 uv_close((uv_handle_t*)&(name)[0], NULL); \ 91 uv_close((uv_handle_t*)&(name)[1], NULL); \ 92 } \ 93 name##_cb_calls[0]++; \ 94 } \ 95 \ 96 static const uv_##name##_cb name##_cbs[] = { \ 97 name##0_cb, \ 98 name##1_cb, \ 99 name##2_cb, \ 100 }; 101 102#define INIT_AND_START(name, loop) \ 103 do { \ 104 size_t i; \ 105 for (i = 0; i < ARRAY_SIZE(name); i++) { \ 106 int r; \ 107 r = uv_##name##_init((loop), &(name)[i]); \ 108 ASSERT_OK(r); \ 109 \ 110 r = uv_##name##_start(&(name)[i], name##_cbs[i]); \ 111 ASSERT_OK(r); \ 112 } \ 113 } while (0) 114 115#define END_ASSERTS(name) \ 116 do { \ 117 ASSERT_EQ(1, name##_cb_calls[0]); \ 118 ASSERT_OK(name##_cb_calls[1]); \ 119 ASSERT_EQ(1, name##_cb_calls[2]); \ 120 } while (0) 121 122DEFINE_GLOBALS_AND_CBS(idle, uv_idle_t* handle) 123DEFINE_GLOBALS_AND_CBS(prepare, uv_prepare_t* handle) 124DEFINE_GLOBALS_AND_CBS(check, uv_check_t* handle) 125 126#ifdef __linux__ 127DEFINE_GLOBALS_AND_CBS(fs_event, 128 uv_fs_event_t* handle, 129 const char* filename, 130 int events, 131 int status) 132 133static const char watched_dir[] = "."; 134static uv_timer_t timer; 135static unsigned helper_timer_cb_calls; 136 137 138static void init_and_start_fs_events(uv_loop_t* loop) { 139 size_t i; 140 for (i = 0; i < ARRAY_SIZE(fs_event); i++) { 141 int r; 142 r = uv_fs_event_init(loop, &fs_event[i]); 143 ASSERT_OK(r); 144 145 r = uv_fs_event_start(&fs_event[i], 146 (uv_fs_event_cb)fs_event_cbs[i], 147 watched_dir, 148 0); 149 ASSERT_OK(r); 150 } 151} 152 153static void helper_timer_cb(uv_timer_t* thandle) { 154 int r; 155 uv_fs_t fs_req; 156 157 /* fire all fs_events */ 158 r = uv_fs_utime(thandle->loop, &fs_req, watched_dir, 0, 0, NULL); 159 ASSERT_OK(r); 160 ASSERT_OK(fs_req.result); 161 ASSERT_EQ(fs_req.fs_type, UV_FS_UTIME); 162 ASSERT_OK(strcmp(fs_req.path, watched_dir)); 163 uv_fs_req_cleanup(&fs_req); 164 165 helper_timer_cb_calls++; 166} 167#endif 168 169 170TEST_IMPL(queue_foreach_delete) { 171 uv_loop_t* loop; 172 int r; 173 174 loop = uv_default_loop(); 175 176 INIT_AND_START(idle, loop); 177 INIT_AND_START(prepare, loop); 178 INIT_AND_START(check, loop); 179 180#ifdef __linux__ 181 init_and_start_fs_events(loop); 182 183 /* helper timer to trigger async and fs_event callbacks */ 184 r = uv_timer_init(loop, &timer); 185 ASSERT_OK(r); 186 187 r = uv_timer_start(&timer, helper_timer_cb, 0, 0); 188 ASSERT_OK(r); 189#endif 190 191 r = uv_run(loop, UV_RUN_NOWAIT); 192 ASSERT_EQ(1, r); 193 194 END_ASSERTS(idle); 195 END_ASSERTS(prepare); 196 END_ASSERTS(check); 197 198#ifdef __linux__ 199 ASSERT_EQ(1, helper_timer_cb_calls); 200#endif 201 202 MAKE_VALGRIND_HAPPY(loop); 203 204 return 0; 205} 206