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