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
33struct cancel_info {
34 void* reqs;
35 unsigned nreqs;
36 unsigned stride;
37 uv_timer_t timer_handle;
38};
39
40static unsigned fs_cb_called;
41static unsigned done_cb_called;
42static unsigned done2_cb_called;
43static unsigned timer_cb_called;
44static uv_work_t pause_reqs[4];
45static uv_sem_t pause_sems[ARRAY_SIZE(pause_reqs)];
46
47
48static void work_cb(uv_work_t* req) {
49 uv_sem_wait(pause_sems + (req - pause_reqs));
50}
51
52
53static void done_cb(uv_work_t* req, int status) {
54 uv_sem_destroy(pause_sems + (req - pause_reqs));
55}
56
57
58static 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
77static 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
85static 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
92static 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
101static 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
111static void work2_cb(uv_work_t* req) {
112 ASSERT(0 && "work2_cb called");
113}
114
115
116static void done2_cb(uv_work_t* req, int status) {
117 ASSERT(status == UV_ECANCELED);
118 done2_cb_called++;
119}
120
121
122static 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
140static void nop_done_cb(uv_work_t* req, int status) {
141 ASSERT(status == UV_ECANCELED);
142 done_cb_called++;
143}
144
145
146TEST_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
179TEST_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
215TEST_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
239TEST_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
293TEST_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