Home | History | Annotate | Line # | Download | only in test
test-threadpool-cancel.c revision 1.1
      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 
     53 static void work_cb(uv_work_t* req) {
     54   uv_sem_wait(pause_sems + (req - pause_reqs));
     55 }
     56 
     57 
     58 static void done_cb(uv_work_t* req, int status) {
     59   uv_sem_destroy(pause_sems + (req - pause_reqs));
     60 }
     61 
     62 
     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 
     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 
     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 
     97 static void getaddrinfo_cb(uv_getaddrinfo_t* req,
     98                            int status,
     99                            struct addrinfo* res) {
    100   ASSERT(status == UV_EAI_CANCELED);
    101   ASSERT(res == NULL);
    102   uv_freeaddrinfo(res);  /* Should not crash. */
    103 }
    104 
    105 
    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(hostname == NULL);
    112   ASSERT(service == NULL);
    113 }
    114 
    115 
    116 static void work2_cb(uv_work_t* req) {
    117   ASSERT(0 && "work2_cb called");
    118 }
    119 
    120 
    121 static void done2_cb(uv_work_t* req, int status) {
    122   ASSERT(status == UV_ECANCELED);
    123   done2_cb_called++;
    124 }
    125 
    126 
    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 
    145 static void nop_done_cb(uv_work_t* req, int status) {
    146   ASSERT(status == UV_ECANCELED);
    147   done_cb_called++;
    148 }
    149 
    150 
    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 
    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 
    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 
    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 
    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 
    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 
    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