Home | History | Annotate | Line # | Download | only in test
test-fork.c revision 1.1
      1 /* Copyright libuv project 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 /* These tests are Unix only. */
     23 #ifndef _WIN32
     24 
     25 #include <unistd.h>
     26 #include <sys/wait.h>
     27 #include <sys/socket.h>
     28 #include <string.h>
     29 
     30 #include "uv.h"
     31 #include "task.h"
     32 
     33 static int timer_cb_called;
     34 static int socket_cb_called;
     35 
     36 static void timer_cb(uv_timer_t* timer) {
     37   timer_cb_called++;
     38   uv_close((uv_handle_t*) timer, NULL);
     39 }
     40 
     41 
     42 static int socket_cb_read_fd;
     43 static int socket_cb_read_size;
     44 static char socket_cb_read_buf[1024];
     45 
     46 
     47 static void socket_cb(uv_poll_t* poll, int status, int events) {
     48   ssize_t cnt;
     49   socket_cb_called++;
     50   ASSERT(0 == status);
     51   printf("Socket cb got events %d\n", events);
     52   ASSERT(UV_READABLE == (events & UV_READABLE));
     53   if (socket_cb_read_fd) {
     54     cnt = read(socket_cb_read_fd, socket_cb_read_buf, socket_cb_read_size);
     55     ASSERT(cnt == socket_cb_read_size);
     56   }
     57   uv_close((uv_handle_t*) poll, NULL);
     58 }
     59 
     60 
     61 static void run_timer_loop_once(void) {
     62   uv_loop_t* loop;
     63   uv_timer_t timer_handle;
     64 
     65   loop = uv_default_loop();
     66 
     67   timer_cb_called = 0; /* Reset for the child. */
     68 
     69   ASSERT(0 == uv_timer_init(loop, &timer_handle));
     70   ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1, 0));
     71   ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT));
     72   ASSERT(1 == timer_cb_called);
     73 }
     74 
     75 
     76 static void assert_wait_child(pid_t child_pid) {
     77   pid_t waited_pid;
     78   int child_stat;
     79 
     80   waited_pid = waitpid(child_pid, &child_stat, 0);
     81   printf("Waited pid is %d with status %d\n", waited_pid, child_stat);
     82   if (waited_pid == -1) {
     83     perror("Failed to wait");
     84   }
     85   ASSERT(child_pid == waited_pid);
     86   ASSERT(WIFEXITED(child_stat)); /* Clean exit, not a signal. */
     87   ASSERT(!WIFSIGNALED(child_stat));
     88   ASSERT(0 == WEXITSTATUS(child_stat));
     89 }
     90 
     91 
     92 TEST_IMPL(fork_timer) {
     93   /* Timers continue to work after we fork. */
     94 
     95   /*
     96    * Establish the loop before we fork to make sure that it
     97    * has state to get reset after the fork.
     98    */
     99   pid_t child_pid;
    100 
    101   run_timer_loop_once();
    102   child_pid = fork();
    103   ASSERT(child_pid != -1);
    104 
    105   if (child_pid != 0) {
    106     /* parent */
    107     assert_wait_child(child_pid);
    108   } else {
    109     /* child */
    110     ASSERT(0 == uv_loop_fork(uv_default_loop()));
    111     run_timer_loop_once();
    112   }
    113 
    114   MAKE_VALGRIND_HAPPY();
    115   return 0;
    116 }
    117 
    118 
    119 TEST_IMPL(fork_socketpair) {
    120   /* A socket opened in the parent and accept'd in the
    121      child works after a fork. */
    122   pid_t child_pid;
    123   int socket_fds[2];
    124   uv_poll_t poll_handle;
    125 
    126   /* Prime the loop. */
    127   run_timer_loop_once();
    128 
    129   ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds));
    130 
    131   /* Create the server watcher in the parent, use it in the child. */
    132   ASSERT(0 == uv_poll_init(uv_default_loop(), &poll_handle, socket_fds[0]));
    133 
    134   child_pid = fork();
    135   ASSERT(child_pid != -1);
    136 
    137   if (child_pid != 0) {
    138     /* parent */
    139     ASSERT(3 == send(socket_fds[1], "hi\n", 3, 0));
    140     assert_wait_child(child_pid);
    141   } else {
    142     /* child */
    143     ASSERT(0 == uv_loop_fork(uv_default_loop()));
    144     ASSERT(0 == socket_cb_called);
    145     ASSERT(0 == uv_poll_start(&poll_handle, UV_READABLE, socket_cb));
    146     printf("Going to run the loop in the child\n");
    147     ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
    148     ASSERT(1 == socket_cb_called);
    149   }
    150 
    151   MAKE_VALGRIND_HAPPY();
    152   return 0;
    153 }
    154 
    155 
    156 TEST_IMPL(fork_socketpair_started) {
    157   /* A socket opened in the parent and accept'd in the
    158      child works after a fork, even if the watcher was already
    159      started, and then stopped in the parent. */
    160   pid_t child_pid;
    161   int socket_fds[2];
    162   int sync_pipe[2];
    163   char sync_buf[1];
    164   uv_poll_t poll_handle;
    165 
    166   ASSERT(0 == pipe(sync_pipe));
    167 
    168   /* Prime the loop. */
    169   run_timer_loop_once();
    170 
    171   ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds));
    172 
    173   /* Create and start the server watcher in the parent, use it in the child. */
    174   ASSERT(0 == uv_poll_init(uv_default_loop(), &poll_handle, socket_fds[0]));
    175   ASSERT(0 == uv_poll_start(&poll_handle, UV_READABLE, socket_cb));
    176 
    177   /* Run the loop AFTER the poll watcher is registered to make sure it
    178      gets passed to the kernel. Use NOWAIT and expect a non-zero
    179      return to prove the poll watcher is active.
    180   */
    181   ASSERT(1 == uv_run(uv_default_loop(), UV_RUN_NOWAIT));
    182 
    183   child_pid = fork();
    184   ASSERT(child_pid != -1);
    185 
    186   if (child_pid != 0) {
    187     /* parent */
    188     ASSERT(0 == uv_poll_stop(&poll_handle));
    189     uv_close((uv_handle_t*)&poll_handle, NULL);
    190     ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
    191     ASSERT(0 == socket_cb_called);
    192     ASSERT(1 == write(sync_pipe[1], "1", 1)); /* alert child */
    193     ASSERT(3 == send(socket_fds[1], "hi\n", 3, 0));
    194 
    195     ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
    196     ASSERT(0 == socket_cb_called);
    197 
    198     assert_wait_child(child_pid);
    199   } else {
    200     /* child */
    201     printf("Child is %d\n", getpid());
    202     ASSERT(1 == read(sync_pipe[0], sync_buf, 1)); /* wait for parent */
    203     ASSERT(0 == uv_loop_fork(uv_default_loop()));
    204     ASSERT(0 == socket_cb_called);
    205 
    206     printf("Going to run the loop in the child\n");
    207     socket_cb_read_fd = socket_fds[0];
    208     socket_cb_read_size = 3;
    209     ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT));
    210     ASSERT(1 == socket_cb_called);
    211     printf("Buf %s\n", socket_cb_read_buf);
    212     ASSERT(0 == strcmp("hi\n", socket_cb_read_buf));
    213   }
    214 
    215   MAKE_VALGRIND_HAPPY();
    216   return 0;
    217 }
    218 
    219 
    220 static int fork_signal_cb_called;
    221 
    222 void fork_signal_to_child_cb(uv_signal_t* handle, int signum)
    223 {
    224   fork_signal_cb_called = signum;
    225   uv_close((uv_handle_t*)handle, NULL);
    226 }
    227 
    228 
    229 TEST_IMPL(fork_signal_to_child) {
    230   /* A signal handler installed before forking
    231      is run only in the child when the child is signalled. */
    232   uv_signal_t signal_handle;
    233   pid_t child_pid;
    234   int sync_pipe[2];
    235   char sync_buf[1];
    236 
    237   fork_signal_cb_called = 0;    /* reset */
    238 
    239   ASSERT(0 == pipe(sync_pipe));
    240 
    241   /* Prime the loop. */
    242   run_timer_loop_once();
    243 
    244   ASSERT(0 == uv_signal_init(uv_default_loop(), &signal_handle));
    245   ASSERT(0 == uv_signal_start(&signal_handle, fork_signal_to_child_cb, SIGUSR1));
    246 
    247   child_pid = fork();
    248   ASSERT(child_pid != -1);
    249 
    250   if (child_pid != 0) {
    251     /* parent */
    252     ASSERT(1 == read(sync_pipe[0], sync_buf, 1)); /* wait for child */
    253     ASSERT(0 == kill(child_pid, SIGUSR1));
    254     /* Run the loop, make sure we don't get the signal. */
    255     printf("Running loop in parent\n");
    256     uv_unref((uv_handle_t*)&signal_handle);
    257     ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_NOWAIT));
    258     ASSERT(0 == fork_signal_cb_called);
    259     printf("Waiting for child in parent\n");
    260     assert_wait_child(child_pid);
    261   } else {
    262     /* child */
    263     ASSERT(0 == uv_loop_fork(uv_default_loop()));
    264     ASSERT(1 == write(sync_pipe[1], "1", 1)); /* alert parent */
    265     /* Get the signal. */
    266     ASSERT(0 != uv_loop_alive(uv_default_loop()));
    267     printf("Running loop in child\n");
    268     ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE));
    269     ASSERT(SIGUSR1 == fork_signal_cb_called);
    270   }
    271 
    272   MAKE_VALGRIND_HAPPY();
    273   return 0;
    274 }
    275 
    276 
    277 TEST_IMPL(fork_signal_to_child_closed) {
    278   /* A signal handler installed before forking
    279      doesn't get received anywhere when the child is signalled,
    280      but isnt running the loop. */
    281   uv_signal_t signal_handle;
    282   pid_t child_pid;
    283   int sync_pipe[2];
    284   int sync_pipe2[2];
    285   char sync_buf[1];
    286   int r;
    287 
    288   fork_signal_cb_called = 0;    /* reset */
    289 
    290   ASSERT(0 == pipe(sync_pipe));
    291   ASSERT(0 == pipe(sync_pipe2));
    292 
    293   /* Prime the loop. */
    294   run_timer_loop_once();
    295 
    296   ASSERT(0 == uv_signal_init(uv_default_loop(), &signal_handle));
    297   ASSERT(0 == uv_signal_start(&signal_handle, fork_signal_to_child_cb, SIGUSR1));
    298 
    299   child_pid = fork();
    300   ASSERT(child_pid != -1);
    301 
    302   if (child_pid != 0) {
    303     /* parent */
    304     printf("Wating on child in parent\n");
    305     ASSERT(1 == read(sync_pipe[0], sync_buf, 1)); /* wait for child */
    306     printf("Parent killing child\n");
    307     ASSERT(0 == kill(child_pid, SIGUSR1));
    308     /* Run the loop, make sure we don't get the signal. */
    309     printf("Running loop in parent\n");
    310     uv_unref((uv_handle_t*)&signal_handle); /* so the loop can exit;
    311                                                we *shouldn't* get any signals */
    312     run_timer_loop_once(); /* but while we share a pipe, we do, so
    313                               have something active. */
    314     ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE));
    315     printf("Signal in parent %d\n", fork_signal_cb_called);
    316     ASSERT(0 == fork_signal_cb_called);
    317     ASSERT(1 == write(sync_pipe2[1], "1", 1)); /* alert child */
    318     printf("Waiting for child in parent\n");
    319     assert_wait_child(child_pid);
    320   } else {
    321     /* Child. Our signal handler should still be installed. */
    322     ASSERT(0 == uv_loop_fork(uv_default_loop()));
    323     printf("Checking loop in child\n");
    324     ASSERT(0 != uv_loop_alive(uv_default_loop()));
    325     printf("Alerting parent in child\n");
    326     ASSERT(1 == write(sync_pipe[1], "1", 1)); /* alert parent */
    327     /* Don't run the loop. Wait for the parent to call us */
    328     printf("Waiting on parent in child\n");
    329     /* Wait for parent. read may fail if the parent tripped an ASSERT
    330        and exited, so this ASSERT is generous.
    331     */
    332     r = read(sync_pipe2[0], sync_buf, 1);
    333     ASSERT(-1 <= r && r <= 1);
    334     ASSERT(0 == fork_signal_cb_called);
    335     printf("Exiting child \n");
    336     /* Note that we're deliberately not running the loop
    337      * in the child, and also not closing the loop's handles,
    338      * so the child default loop can't be cleanly closed.
    339      * We need to explicitly exit to avoid an automatic failure
    340      * in that case.
    341      */
    342     exit(0);
    343   }
    344 
    345   MAKE_VALGRIND_HAPPY();
    346   return 0;
    347 }
    348 
    349 
    350 static void create_file(const char* name) {
    351   int r;
    352   uv_file file;
    353   uv_fs_t req;
    354 
    355   r = uv_fs_open(NULL, &req, name, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL);
    356   ASSERT(r >= 0);
    357   file = r;
    358   uv_fs_req_cleanup(&req);
    359   r = uv_fs_close(NULL, &req, file, NULL);
    360   ASSERT(r == 0);
    361   uv_fs_req_cleanup(&req);
    362 }
    363 
    364 
    365 static void touch_file(const char* name) {
    366   int r;
    367   uv_file file;
    368   uv_fs_t req;
    369   uv_buf_t buf;
    370 
    371   r = uv_fs_open(NULL, &req, name, O_RDWR, 0, NULL);
    372   ASSERT(r >= 0);
    373   file = r;
    374   uv_fs_req_cleanup(&req);
    375 
    376   buf = uv_buf_init("foo", 4);
    377   r = uv_fs_write(NULL, &req, file, &buf, 1, -1, NULL);
    378   ASSERT(r >= 0);
    379   uv_fs_req_cleanup(&req);
    380 
    381   r = uv_fs_close(NULL, &req, file, NULL);
    382   ASSERT(r == 0);
    383   uv_fs_req_cleanup(&req);
    384 }
    385 
    386 
    387 static int timer_cb_touch_called;
    388 
    389 static void timer_cb_touch(uv_timer_t* timer) {
    390   uv_close((uv_handle_t*)timer, NULL);
    391   touch_file("watch_file");
    392   timer_cb_touch_called++;
    393 }
    394 
    395 
    396 static int fs_event_cb_called;
    397 
    398 static void fs_event_cb_file_current_dir(uv_fs_event_t* handle,
    399                                          const char* filename,
    400                                          int events,
    401                                          int status) {
    402   ASSERT(fs_event_cb_called == 0);
    403   ++fs_event_cb_called;
    404   ASSERT(status == 0);
    405 #if defined(__APPLE__) || defined(__linux__)
    406   ASSERT(strcmp(filename, "watch_file") == 0);
    407 #else
    408   ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0);
    409 #endif
    410   uv_close((uv_handle_t*)handle, NULL);
    411 }
    412 
    413 
    414 static void assert_watch_file_current_dir(uv_loop_t* const loop, int file_or_dir) {
    415   uv_timer_t timer;
    416   uv_fs_event_t fs_event;
    417   int r;
    418 
    419   /* Setup */
    420   remove("watch_file");
    421   create_file("watch_file");
    422 
    423   r = uv_fs_event_init(loop, &fs_event);
    424   ASSERT(r == 0);
    425   /* watching a dir is the only way to get fsevents involved on apple
    426      platforms */
    427   r = uv_fs_event_start(&fs_event,
    428                         fs_event_cb_file_current_dir,
    429                         file_or_dir == 1 ? "." : "watch_file",
    430                         0);
    431   ASSERT(r == 0);
    432 
    433   r = uv_timer_init(loop, &timer);
    434   ASSERT(r == 0);
    435 
    436   r = uv_timer_start(&timer, timer_cb_touch, 100, 0);
    437   ASSERT(r == 0);
    438 
    439   ASSERT(timer_cb_touch_called == 0);
    440   ASSERT(fs_event_cb_called == 0);
    441 
    442   uv_run(loop, UV_RUN_DEFAULT);
    443 
    444   ASSERT(timer_cb_touch_called == 1);
    445   ASSERT(fs_event_cb_called == 1);
    446 
    447   /* Cleanup */
    448   remove("watch_file");
    449   fs_event_cb_called = 0;
    450   timer_cb_touch_called = 0;
    451   uv_run(loop, UV_RUN_DEFAULT); /* flush pending closes */
    452 }
    453 
    454 
    455 #define FS_TEST_FILE 0
    456 #define FS_TEST_DIR 1
    457 
    458 static int _do_fork_fs_events_child(int file_or_dir) {
    459   /* basic fsevents work in the child after a fork */
    460   pid_t child_pid;
    461   uv_loop_t loop;
    462 
    463   /* Watch in the parent, prime the loop and/or threads. */
    464   assert_watch_file_current_dir(uv_default_loop(), file_or_dir);
    465   child_pid = fork();
    466   ASSERT(child_pid != -1);
    467 
    468   if (child_pid != 0) {
    469     /* parent */
    470     assert_wait_child(child_pid);
    471   } else {
    472     /* child */
    473     /* Ee can watch in a new loop, but dirs only work
    474        if we're on linux. */
    475 #if defined(__APPLE__)
    476     file_or_dir = FS_TEST_FILE;
    477 #endif
    478     printf("Running child\n");
    479     uv_loop_init(&loop);
    480     printf("Child first watch\n");
    481     assert_watch_file_current_dir(&loop, file_or_dir);
    482     ASSERT(0 == uv_loop_close(&loop));
    483     printf("Child second watch default loop\n");
    484     /* Ee can watch in the default loop. */
    485     ASSERT(0 == uv_loop_fork(uv_default_loop()));
    486     /* On some platforms (OS X), if we don't update the time now,
    487      * the timer cb fires before the event loop enters uv__io_poll,
    488      * instead of after, meaning we don't see the change! This may be
    489      * a general race.
    490      */
    491     uv_update_time(uv_default_loop());
    492     assert_watch_file_current_dir(uv_default_loop(), file_or_dir);
    493 
    494     /* We can close the parent loop successfully too. This is
    495        especially important on Apple platforms where if we're not
    496        careful trying to touch the CFRunLoop, even just to shut it
    497        down, that we allocated in the FS_TEST_DIR case would crash. */
    498     ASSERT(0 == uv_loop_close(uv_default_loop()));
    499 
    500     printf("Exiting child \n");
    501   }
    502 
    503   MAKE_VALGRIND_HAPPY();
    504   return 0;
    505 
    506 }
    507 
    508 
    509 TEST_IMPL(fork_fs_events_child) {
    510 #if defined(NO_FS_EVENTS)
    511   RETURN_SKIP(NO_FS_EVENTS);
    512 #endif
    513   return _do_fork_fs_events_child(FS_TEST_FILE);
    514 }
    515 
    516 
    517 TEST_IMPL(fork_fs_events_child_dir) {
    518 #if defined(NO_FS_EVENTS)
    519   RETURN_SKIP(NO_FS_EVENTS);
    520 #endif
    521 #if defined(__APPLE__) || defined (__linux__)
    522   return _do_fork_fs_events_child(FS_TEST_DIR);
    523 #else
    524   /* You can't spin up a cfrunloop thread on an apple platform
    525      and then fork. See
    526      http://objectivistc.tumblr.com/post/16187948939/you-must-exec-a-core-foundation-fork-safety-tale
    527   */
    528   return 0;
    529 #endif
    530 }
    531 
    532 
    533 TEST_IMPL(fork_fs_events_file_parent_child) {
    534 #if defined(NO_FS_EVENTS)
    535   RETURN_SKIP(NO_FS_EVENTS);
    536 #endif
    537 #if defined(__sun) || defined(_AIX) || defined(__MVS__)
    538   /* It's not possible to implement this without additional
    539    * bookkeeping on SunOS. For AIX it is possible, but has to be
    540    * written. See https://github.com/libuv/libuv/pull/846#issuecomment-287170420
    541    * TODO: On z/OS, we need to open another message queue and subscribe to the
    542    * same events as the parent.
    543    */
    544   return 0;
    545 #else
    546   /* Establishing a started fs events watcher in the parent should
    547      still work in the child. */
    548   uv_timer_t timer;
    549   uv_fs_event_t fs_event;
    550   int r;
    551   pid_t child_pid;
    552   uv_loop_t* loop;
    553 
    554   loop = uv_default_loop();
    555 
    556   /* Setup */
    557   remove("watch_file");
    558   create_file("watch_file");
    559 
    560   r = uv_fs_event_init(loop, &fs_event);
    561   ASSERT(r == 0);
    562   r = uv_fs_event_start(&fs_event,
    563                         fs_event_cb_file_current_dir,
    564                         "watch_file",
    565                         0);
    566   ASSERT(r == 0);
    567 
    568   r = uv_timer_init(loop, &timer);
    569   ASSERT(r == 0);
    570 
    571   child_pid = fork();
    572   ASSERT(child_pid != -1);
    573   if (child_pid != 0) {
    574     /* parent */
    575     assert_wait_child(child_pid);
    576   } else {
    577     /* child */
    578     printf("Running child\n");
    579     ASSERT(0 == uv_loop_fork(loop));
    580 
    581     r = uv_timer_start(&timer, timer_cb_touch, 100, 0);
    582     ASSERT(r == 0);
    583 
    584     ASSERT(timer_cb_touch_called == 0);
    585     ASSERT(fs_event_cb_called == 0);
    586     printf("Running loop in child \n");
    587     uv_run(loop, UV_RUN_DEFAULT);
    588 
    589     ASSERT(timer_cb_touch_called == 1);
    590     ASSERT(fs_event_cb_called == 1);
    591 
    592     /* Cleanup */
    593     remove("watch_file");
    594     fs_event_cb_called = 0;
    595     timer_cb_touch_called = 0;
    596     uv_run(loop, UV_RUN_DEFAULT); /* Flush pending closes. */
    597   }
    598 
    599 
    600   MAKE_VALGRIND_HAPPY();
    601   return 0;
    602 #endif
    603 }
    604 
    605 
    606 static int work_cb_count;
    607 static int after_work_cb_count;
    608 
    609 
    610 static void work_cb(uv_work_t* req) {
    611   work_cb_count++;
    612 }
    613 
    614 
    615 static void after_work_cb(uv_work_t* req, int status) {
    616   ASSERT(status == 0);
    617   after_work_cb_count++;
    618 }
    619 
    620 
    621 static void assert_run_work(uv_loop_t* const loop) {
    622   uv_work_t work_req;
    623   int r;
    624 
    625   ASSERT(work_cb_count == 0);
    626   ASSERT(after_work_cb_count == 0);
    627   printf("Queue in %d\n", getpid());
    628   r = uv_queue_work(loop, &work_req, work_cb, after_work_cb);
    629   ASSERT(r == 0);
    630   printf("Running in %d\n", getpid());
    631   uv_run(loop, UV_RUN_DEFAULT);
    632 
    633   ASSERT(work_cb_count == 1);
    634   ASSERT(after_work_cb_count == 1);
    635 
    636   /* cleanup  */
    637   work_cb_count = 0;
    638   after_work_cb_count = 0;
    639 }
    640 
    641 
    642 #ifndef __MVS__
    643 TEST_IMPL(fork_threadpool_queue_work_simple) {
    644   /* The threadpool works in a child process. */
    645 
    646   pid_t child_pid;
    647   uv_loop_t loop;
    648 
    649   /* Prime the pool and default loop. */
    650   assert_run_work(uv_default_loop());
    651 
    652   child_pid = fork();
    653   ASSERT(child_pid != -1);
    654 
    655   if (child_pid != 0) {
    656     /* Parent. We can still run work. */
    657     assert_run_work(uv_default_loop());
    658     assert_wait_child(child_pid);
    659   } else {
    660     /* Child. We can work in a new loop. */
    661     printf("Running child in %d\n", getpid());
    662     uv_loop_init(&loop);
    663     printf("Child first watch\n");
    664     assert_run_work(&loop);
    665     uv_loop_close(&loop);
    666     printf("Child second watch default loop\n");
    667     /* We can work in the default loop. */
    668     ASSERT(0 == uv_loop_fork(uv_default_loop()));
    669     assert_run_work(uv_default_loop());
    670     printf("Exiting child \n");
    671   }
    672 
    673 
    674   MAKE_VALGRIND_HAPPY();
    675   return 0;
    676 }
    677 #endif /* !__MVS__ */
    678 
    679 #else
    680 
    681 typedef int file_has_no_tests; /* ISO C forbids an empty translation unit. */
    682 
    683 #endif /* !_WIN32 */
    684