1 | /* Copyright Joyent, Inc. and other Node 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 | #define INIT_CANCEL_INFO(ci, what) \ |
26 | do { \ |
27 | (ci)->reqs = (what); \ |
28 | (ci)->nreqs = ARRAY_SIZE(what); \ |
29 | (ci)->stride = sizeof((what)[0]); \ |
30 | } \ |
31 | while (0) |
32 | |
33 | struct cancel_info { |
34 | void* reqs; |
35 | unsigned nreqs; |
36 | unsigned stride; |
37 | uv_timer_t timer_handle; |
38 | }; |
39 | |
40 | static unsigned fs_cb_called; |
41 | static unsigned done_cb_called; |
42 | static unsigned done2_cb_called; |
43 | static unsigned timer_cb_called; |
44 | static uv_work_t pause_reqs[4]; |
45 | static uv_sem_t pause_sems[ARRAY_SIZE(pause_reqs)]; |
46 | |
47 | |
48 | static void work_cb(uv_work_t* req) { |
49 | uv_sem_wait(pause_sems + (req - pause_reqs)); |
50 | } |
51 | |
52 | |
53 | static void done_cb(uv_work_t* req, int status) { |
54 | uv_sem_destroy(pause_sems + (req - pause_reqs)); |
55 | } |
56 | |
57 | |
58 | static void saturate_threadpool(void) { |
59 | uv_loop_t* loop; |
60 | char buf[64]; |
61 | size_t i; |
62 | |
63 | snprintf(buf, |
64 | sizeof(buf), |
65 | "UV_THREADPOOL_SIZE=%lu" , |
66 | (unsigned long)ARRAY_SIZE(pause_reqs)); |
67 | putenv(buf); |
68 | |
69 | loop = uv_default_loop(); |
70 | for (i = 0; i < ARRAY_SIZE(pause_reqs); i += 1) { |
71 | ASSERT(0 == uv_sem_init(pause_sems + i, 0)); |
72 | ASSERT(0 == uv_queue_work(loop, pause_reqs + i, work_cb, done_cb)); |
73 | } |
74 | } |
75 | |
76 | |
77 | static void unblock_threadpool(void) { |
78 | size_t i; |
79 | |
80 | for (i = 0; i < ARRAY_SIZE(pause_reqs); i += 1) |
81 | uv_sem_post(pause_sems + i); |
82 | } |
83 | |
84 | |
85 | static void fs_cb(uv_fs_t* req) { |
86 | ASSERT(req->result == UV_ECANCELED); |
87 | uv_fs_req_cleanup(req); |
88 | fs_cb_called++; |
89 | } |
90 | |
91 | |
92 | static void getaddrinfo_cb(uv_getaddrinfo_t* req, |
93 | int status, |
94 | struct addrinfo* res) { |
95 | ASSERT(status == UV_EAI_CANCELED); |
96 | ASSERT(res == NULL); |
97 | uv_freeaddrinfo(res); /* Should not crash. */ |
98 | } |
99 | |
100 | |
101 | static void getnameinfo_cb(uv_getnameinfo_t* handle, |
102 | int status, |
103 | const char* hostname, |
104 | const char* service) { |
105 | ASSERT(status == UV_EAI_CANCELED); |
106 | ASSERT(hostname == NULL); |
107 | ASSERT(service == NULL); |
108 | } |
109 | |
110 | |
111 | static void work2_cb(uv_work_t* req) { |
112 | ASSERT(0 && "work2_cb called" ); |
113 | } |
114 | |
115 | |
116 | static void done2_cb(uv_work_t* req, int status) { |
117 | ASSERT(status == UV_ECANCELED); |
118 | done2_cb_called++; |
119 | } |
120 | |
121 | |
122 | static void timer_cb(uv_timer_t* handle) { |
123 | struct cancel_info* ci; |
124 | uv_req_t* req; |
125 | unsigned i; |
126 | |
127 | ci = container_of(handle, struct cancel_info, timer_handle); |
128 | |
129 | for (i = 0; i < ci->nreqs; i++) { |
130 | req = (uv_req_t*) ((char*) ci->reqs + i * ci->stride); |
131 | ASSERT(0 == uv_cancel(req)); |
132 | } |
133 | |
134 | uv_close((uv_handle_t*) &ci->timer_handle, NULL); |
135 | unblock_threadpool(); |
136 | timer_cb_called++; |
137 | } |
138 | |
139 | |
140 | static void nop_done_cb(uv_work_t* req, int status) { |
141 | ASSERT(status == UV_ECANCELED); |
142 | done_cb_called++; |
143 | } |
144 | |
145 | |
146 | TEST_IMPL(threadpool_cancel_getaddrinfo) { |
147 | uv_getaddrinfo_t reqs[4]; |
148 | struct cancel_info ci; |
149 | struct addrinfo hints; |
150 | uv_loop_t* loop; |
151 | int r; |
152 | |
153 | INIT_CANCEL_INFO(&ci, reqs); |
154 | loop = uv_default_loop(); |
155 | saturate_threadpool(); |
156 | |
157 | r = uv_getaddrinfo(loop, reqs + 0, getaddrinfo_cb, "fail" , NULL, NULL); |
158 | ASSERT(r == 0); |
159 | |
160 | r = uv_getaddrinfo(loop, reqs + 1, getaddrinfo_cb, NULL, "fail" , NULL); |
161 | ASSERT(r == 0); |
162 | |
163 | r = uv_getaddrinfo(loop, reqs + 2, getaddrinfo_cb, "fail" , "fail" , NULL); |
164 | ASSERT(r == 0); |
165 | |
166 | r = uv_getaddrinfo(loop, reqs + 3, getaddrinfo_cb, "fail" , NULL, &hints); |
167 | ASSERT(r == 0); |
168 | |
169 | ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); |
170 | ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); |
171 | ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); |
172 | ASSERT(1 == timer_cb_called); |
173 | |
174 | MAKE_VALGRIND_HAPPY(); |
175 | return 0; |
176 | } |
177 | |
178 | |
179 | TEST_IMPL(threadpool_cancel_getnameinfo) { |
180 | uv_getnameinfo_t reqs[4]; |
181 | struct sockaddr_in addr4; |
182 | struct cancel_info ci; |
183 | uv_loop_t* loop; |
184 | int r; |
185 | |
186 | r = uv_ip4_addr("127.0.0.1" , 80, &addr4); |
187 | ASSERT(r == 0); |
188 | |
189 | INIT_CANCEL_INFO(&ci, reqs); |
190 | loop = uv_default_loop(); |
191 | saturate_threadpool(); |
192 | |
193 | r = uv_getnameinfo(loop, reqs + 0, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); |
194 | ASSERT(r == 0); |
195 | |
196 | r = uv_getnameinfo(loop, reqs + 1, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); |
197 | ASSERT(r == 0); |
198 | |
199 | r = uv_getnameinfo(loop, reqs + 2, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); |
200 | ASSERT(r == 0); |
201 | |
202 | r = uv_getnameinfo(loop, reqs + 3, getnameinfo_cb, (const struct sockaddr*)&addr4, 0); |
203 | ASSERT(r == 0); |
204 | |
205 | ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); |
206 | ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); |
207 | ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); |
208 | ASSERT(1 == timer_cb_called); |
209 | |
210 | MAKE_VALGRIND_HAPPY(); |
211 | return 0; |
212 | } |
213 | |
214 | |
215 | TEST_IMPL(threadpool_cancel_work) { |
216 | struct cancel_info ci; |
217 | uv_work_t reqs[16]; |
218 | uv_loop_t* loop; |
219 | unsigned i; |
220 | |
221 | INIT_CANCEL_INFO(&ci, reqs); |
222 | loop = uv_default_loop(); |
223 | saturate_threadpool(); |
224 | |
225 | for (i = 0; i < ARRAY_SIZE(reqs); i++) |
226 | ASSERT(0 == uv_queue_work(loop, reqs + i, work2_cb, done2_cb)); |
227 | |
228 | ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); |
229 | ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); |
230 | ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); |
231 | ASSERT(1 == timer_cb_called); |
232 | ASSERT(ARRAY_SIZE(reqs) == done2_cb_called); |
233 | |
234 | MAKE_VALGRIND_HAPPY(); |
235 | return 0; |
236 | } |
237 | |
238 | |
239 | TEST_IMPL(threadpool_cancel_fs) { |
240 | struct cancel_info ci; |
241 | uv_fs_t reqs[26]; |
242 | uv_loop_t* loop; |
243 | unsigned n; |
244 | uv_buf_t iov; |
245 | |
246 | INIT_CANCEL_INFO(&ci, reqs); |
247 | loop = uv_default_loop(); |
248 | saturate_threadpool(); |
249 | iov = uv_buf_init(NULL, 0); |
250 | |
251 | /* Needs to match ARRAY_SIZE(fs_reqs). */ |
252 | n = 0; |
253 | ASSERT(0 == uv_fs_chmod(loop, reqs + n++, "/" , 0, fs_cb)); |
254 | ASSERT(0 == uv_fs_chown(loop, reqs + n++, "/" , 0, 0, fs_cb)); |
255 | ASSERT(0 == uv_fs_close(loop, reqs + n++, 0, fs_cb)); |
256 | ASSERT(0 == uv_fs_fchmod(loop, reqs + n++, 0, 0, fs_cb)); |
257 | ASSERT(0 == uv_fs_fchown(loop, reqs + n++, 0, 0, 0, fs_cb)); |
258 | ASSERT(0 == uv_fs_fdatasync(loop, reqs + n++, 0, fs_cb)); |
259 | ASSERT(0 == uv_fs_fstat(loop, reqs + n++, 0, fs_cb)); |
260 | ASSERT(0 == uv_fs_fsync(loop, reqs + n++, 0, fs_cb)); |
261 | ASSERT(0 == uv_fs_ftruncate(loop, reqs + n++, 0, 0, fs_cb)); |
262 | ASSERT(0 == uv_fs_futime(loop, reqs + n++, 0, 0, 0, fs_cb)); |
263 | ASSERT(0 == uv_fs_link(loop, reqs + n++, "/" , "/" , fs_cb)); |
264 | ASSERT(0 == uv_fs_lstat(loop, reqs + n++, "/" , fs_cb)); |
265 | ASSERT(0 == uv_fs_mkdir(loop, reqs + n++, "/" , 0, fs_cb)); |
266 | ASSERT(0 == uv_fs_open(loop, reqs + n++, "/" , 0, 0, fs_cb)); |
267 | ASSERT(0 == uv_fs_read(loop, reqs + n++, 0, &iov, 1, 0, fs_cb)); |
268 | ASSERT(0 == uv_fs_scandir(loop, reqs + n++, "/" , 0, fs_cb)); |
269 | ASSERT(0 == uv_fs_readlink(loop, reqs + n++, "/" , fs_cb)); |
270 | ASSERT(0 == uv_fs_realpath(loop, reqs + n++, "/" , fs_cb)); |
271 | ASSERT(0 == uv_fs_rename(loop, reqs + n++, "/" , "/" , fs_cb)); |
272 | ASSERT(0 == uv_fs_mkdir(loop, reqs + n++, "/" , 0, fs_cb)); |
273 | ASSERT(0 == uv_fs_sendfile(loop, reqs + n++, 0, 0, 0, 0, fs_cb)); |
274 | ASSERT(0 == uv_fs_stat(loop, reqs + n++, "/" , fs_cb)); |
275 | ASSERT(0 == uv_fs_symlink(loop, reqs + n++, "/" , "/" , 0, fs_cb)); |
276 | ASSERT(0 == uv_fs_unlink(loop, reqs + n++, "/" , fs_cb)); |
277 | ASSERT(0 == uv_fs_utime(loop, reqs + n++, "/" , 0, 0, fs_cb)); |
278 | ASSERT(0 == uv_fs_write(loop, reqs + n++, 0, &iov, 1, 0, fs_cb)); |
279 | ASSERT(n == ARRAY_SIZE(reqs)); |
280 | |
281 | ASSERT(0 == uv_timer_init(loop, &ci.timer_handle)); |
282 | ASSERT(0 == uv_timer_start(&ci.timer_handle, timer_cb, 10, 0)); |
283 | ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); |
284 | ASSERT(n == fs_cb_called); |
285 | ASSERT(1 == timer_cb_called); |
286 | |
287 | |
288 | MAKE_VALGRIND_HAPPY(); |
289 | return 0; |
290 | } |
291 | |
292 | |
293 | TEST_IMPL(threadpool_cancel_single) { |
294 | uv_loop_t* loop; |
295 | uv_work_t req; |
296 | |
297 | saturate_threadpool(); |
298 | loop = uv_default_loop(); |
299 | ASSERT(0 == uv_queue_work(loop, &req, (uv_work_cb) abort, nop_done_cb)); |
300 | ASSERT(0 == uv_cancel((uv_req_t*) &req)); |
301 | ASSERT(0 == done_cb_called); |
302 | unblock_threadpool(); |
303 | ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); |
304 | ASSERT(1 == done_cb_called); |
305 | |
306 | MAKE_VALGRIND_HAPPY(); |
307 | return 0; |
308 | } |
309 | |