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