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