• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
timer_cb(uv_timer_t * timer)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 
socket_cb(uv_poll_t * poll,int status,int events)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 
run_timer_loop_once(void)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 
assert_wait_child(pid_t child_pid)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 
TEST_IMPL(fork_timer)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 
TEST_IMPL(fork_socketpair)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 
TEST_IMPL(fork_socketpair_started)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 
fork_signal_to_child_cb(uv_signal_t * handle,int signum)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 
TEST_IMPL(fork_signal_to_child)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 
TEST_IMPL(fork_signal_to_child_closed)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 
create_file(const char * name)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 
touch_file(const char * name)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 
timer_cb_touch(uv_timer_t * timer)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 
fs_event_cb_file_current_dir(uv_fs_event_t * handle,const char * filename,int events,int status)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 
assert_watch_file_current_dir(uv_loop_t * const loop,int file_or_dir)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 
_do_fork_fs_events_child(int file_or_dir)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 
TEST_IMPL(fork_fs_events_child)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 
TEST_IMPL(fork_fs_events_child_dir)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 
TEST_IMPL(fork_fs_events_file_parent_child)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 
work_cb(uv_work_t * req)610 static void work_cb(uv_work_t* req) {
611   work_cb_count++;
612 }
613 
614 
after_work_cb(uv_work_t * req,int status)615 static void after_work_cb(uv_work_t* req, int status) {
616   ASSERT(status == 0);
617   after_work_cb_count++;
618 }
619 
620 
assert_run_work(uv_loop_t * const loop)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__
TEST_IMPL(fork_threadpool_queue_work_simple)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