• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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