From afe1a9066fe810c25f874469a92187ccd1e24c53 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Sun, 17 Dec 2017 19:07:53 -0500 Subject: [PATCH] Revert "unix: make loops and watchers usable after fork()" This reverts commit fd7ce57f2b14651482c227898f6999664db937de, because it is useless, and appears it may call `abort()` according to CI testing. --- CMakeLists.txt | 1 - Makefile.am | 1 - docs/src/loop.rst | 54 ---- include/uv.h | 1 - src/threadpool.c | 23 +- src/unix/aix.c | 7 - src/unix/async.c | 10 - src/unix/internal.h | 8 - src/unix/kqueue.c | 37 +-- src/unix/linux-core.c | 18 -- src/unix/linux-inotify.c | 67 ---- src/unix/loop.c | 33 -- src/unix/signal.c | 28 +- src/unix/sunos.c | 12 - src/win/core.c | 5 - test/test-fork.c | 680 --------------------------------------- test/test-list.h | 28 -- test/test.gyp | 1 - 18 files changed, 3 insertions(+), 1011 deletions(-) delete mode 100644 test/test-fork.c diff --git a/CMakeLists.txt b/CMakeLists.txt index bbe31512254..30779adb49c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,6 @@ set(uv_test_sources test/test-env-vars.c test/test-error.c test/test-fail-always.c - test/test-fork.c test/test-fs-copyfile.c test/test-fs-event.c test/test-fs-poll.c diff --git a/Makefile.am b/Makefile.am index b5e5318101c..10d9a074af3 100644 --- a/Makefile.am +++ b/Makefile.am @@ -175,7 +175,6 @@ test_run_tests_SOURCES = test/blackhole-server.c \ test/test-fs-event.c \ test/test-fs-poll.c \ test/test-fs.c \ - test/test-fork.c \ test/test-getters-setters.c \ test/test-get-currentexe.c \ test/test-get-loadavg.c \ diff --git a/docs/src/loop.rst b/docs/src/loop.rst index f089e83be25..cd928c87120 100644 --- a/docs/src/loop.rst +++ b/docs/src/loop.rst @@ -172,60 +172,6 @@ API Walk the list of handles: `walk_cb` will be executed with the given `arg`. -.. c:function:: int uv_loop_fork(uv_loop_t* loop) - - .. versionadded:: 1.12.0 - - Reinitialize any kernel state necessary in the child process after - a :man:`fork(2)` system call. - - Previously started watchers will continue to be started in the - child process. - - It is necessary to explicitly call this function on every event - loop created in the parent process that you plan to continue to - use in the child, including the default loop (even if you don't - continue to use it in the parent). This function must be called - before calling :c:func:`uv_run` or any other API function using - the loop in the child. Failure to do so will result in undefined - behaviour, possibly including duplicate events delivered to both - parent and child or aborting the child process. - - When possible, it is preferred to create a new loop in the child - process instead of reusing a loop created in the parent. New loops - created in the child process after the fork should not use this - function. - - This function is not implemented on Windows, where it returns ``UV_ENOSYS``. - - .. caution:: - - This function is experimental. It may contain bugs, and is subject to - change or removal. API and ABI stability is not guaranteed. - - .. note:: - - On Mac OS X, if directory FS event handles were in use in the - parent process *for any event loop*, the child process will no - longer be able to use the most efficient FSEvent - implementation. Instead, uses of directory FS event handles in - the child will fall back to the same implementation used for - files and on other kqueue-based systems. - - .. caution:: - - On AIX and SunOS, FS event handles that were already started in - the parent process at the time of forking will *not* deliver - events in the child process; they must be closed and restarted. - On all other platforms, they will continue to work normally - without any further intervention. - - .. caution:: - - Any previous value returned from :c:func:`uv_backend_fd` is now - invalid. That function must be called again to determine the - correct backend file descriptor. - .. c:function:: void* uv_loop_get_data(const uv_loop_t* loop) Returns `loop->data`. diff --git a/include/uv.h b/include/uv.h index bbca97c64ed..5cf6fc40c89 100644 --- a/include/uv.h +++ b/include/uv.h @@ -274,7 +274,6 @@ UV_EXTERN void uv_loop_delete(uv_loop_t*); UV_EXTERN size_t uv_loop_size(void); UV_EXTERN int uv_loop_alive(const uv_loop_t* loop); UV_EXTERN int uv_loop_configure(uv_loop_t* loop, uv_loop_option option, ...); -UV_EXTERN int uv_loop_fork(uv_loop_t* loop); UV_EXTERN int uv_run(uv_loop_t*, uv_run_mode mode); UV_EXTERN void uv_stop(uv_loop_t*); diff --git a/src/threadpool.c b/src/threadpool.c index 4258933c724..3c1d3fa529d 100644 --- a/src/threadpool.c +++ b/src/threadpool.c @@ -185,7 +185,7 @@ UV_DESTRUCTOR(static void cleanup(void)) { #endif -static void init_threads(void) { +static void init_once(void) { unsigned int i; const char* val; uv_sem_t sem; @@ -232,27 +232,6 @@ static void init_threads(void) { } -#ifndef _WIN32 -static void reset_once(void) { - uv_once_t child_once = UV_ONCE_INIT; - memcpy(&once, &child_once, sizeof(child_once)); -} -#endif - - -static void init_once(void) { -#ifndef _WIN32 - /* Re-initialize the threadpool after fork. - * Note that this discards the global mutex and condition as well - * as the work queue. - */ - if (pthread_atfork(NULL, NULL, &reset_once)) - abort(); -#endif - init_threads(); -} - - void uv__work_submit(uv_loop_t* loop, struct uv__work* w, enum uv__work_kind kind, diff --git a/src/unix/aix.c b/src/unix/aix.c index baac8e6c006..d9bee722021 100644 --- a/src/unix/aix.c +++ b/src/unix/aix.c @@ -104,13 +104,6 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } -int uv__io_fork(uv_loop_t* loop) { - uv__platform_loop_delete(loop); - - return uv__platform_loop_init(loop); -} - - int uv__io_check_fd(uv_loop_t* loop, int fd) { struct poll_ctl pc; diff --git a/src/unix/async.c b/src/unix/async.c index db8be85c7e9..b4ae78cde23 100644 --- a/src/unix/async.c +++ b/src/unix/async.c @@ -200,16 +200,6 @@ static int uv__async_start(uv_loop_t* loop) { } -int uv__async_fork(uv_loop_t* loop) { - if (loop->async_io_watcher.fd == -1) /* never started */ - return 0; - - uv__async_stop(loop); - - return uv__async_start(loop); -} - - void uv__async_stop(uv_loop_t* loop) { if (loop->async_io_watcher.fd == -1) return; diff --git a/src/unix/internal.h b/src/unix/internal.h index 8795ca51aef..e8523e7a33d 100644 --- a/src/unix/internal.h +++ b/src/unix/internal.h @@ -193,13 +193,10 @@ void uv__io_feed(uv_loop_t* loop, uv__io_t* w); int uv__io_active(const uv__io_t* w, unsigned int events); int uv__io_check_fd(uv_loop_t* loop, int fd); void uv__io_poll(uv_loop_t* loop, int timeout); /* in milliseconds or -1 */ -int uv__io_fork(uv_loop_t* loop); int uv__fd_exists(uv_loop_t* loop, int fd); /* async */ void uv__async_stop(uv_loop_t* loop); -int uv__async_fork(uv_loop_t* loop); - /* loop */ void uv__run_idle(uv_loop_t* loop); @@ -231,7 +228,6 @@ int uv_pipe_listen(uv_pipe_t* handle, int backlog, uv_connection_cb cb); void uv__signal_close(uv_signal_t* handle); void uv__signal_global_once_init(void); void uv__signal_loop_cleanup(uv_loop_t* loop); -int uv__signal_loop_fork(uv_loop_t* loop); /* platform specific */ uint64_t uv__hrtime(uv_clocktype_t type); @@ -288,10 +284,6 @@ UV_UNUSED(static char* uv__basename_r(const char* path)) { return s + 1; } -#if defined(__linux__) -int uv__inotify_fork(uv_loop_t* loop, void* old_watchers); -#endif - typedef int (*uv__peersockfunc)(int, struct sockaddr*, socklen_t*); int uv__getsockpeername(const uv_handle_t* handle, diff --git a/src/unix/kqueue.c b/src/unix/kqueue.c index 930f2da7126..13bead87a54 100644 --- a/src/unix/kqueue.c +++ b/src/unix/kqueue.c @@ -59,38 +59,6 @@ int uv__kqueue_init(uv_loop_t* loop) { } -#if defined(__APPLE__) -static int uv__has_forked_with_cfrunloop; -#endif - -int uv__io_fork(uv_loop_t* loop) { - int err; - loop->backend_fd = -1; - err = uv__kqueue_init(loop); - if (err) - return err; - -#if defined(__APPLE__) - if (loop->cf_state != NULL) { - /* We cannot start another CFRunloop and/or thread in the child - process; CF aborts if you try or if you try to touch the thread - at all to kill it. So the best we can do is ignore it from now - on. This means we can't watch directories in the same way - anymore (like other BSDs). It also means we cannot properly - clean up the allocated resources; calling - uv__fsevents_loop_delete from uv_loop_close will crash the - process. So we sidestep the issue by pretending like we never - started it in the first place. - */ - uv__has_forked_with_cfrunloop = 1; - uv__free(loop->cf_state); - loop->cf_state = NULL; - } -#endif - return err; -} - - int uv__io_check_fd(uv_loop_t* loop, int fd) { struct kevent ev; int rc; @@ -471,9 +439,6 @@ int uv_fs_event_start(uv_fs_event_t* handle, handle->cb = cb; #if defined(__APPLE__) - if (uv__has_forked_with_cfrunloop) - goto fallback; - /* Nullify field to perform checks later */ handle->cf_cb = NULL; handle->realpath = NULL; @@ -508,7 +473,7 @@ int uv_fs_event_stop(uv_fs_event_t* handle) { uv__handle_stop(handle); #if defined(__APPLE__) - if (uv__has_forked_with_cfrunloop || uv__fsevents_close(handle)) + if (uv__fsevents_close(handle)) #endif /* defined(__APPLE__) */ { uv__io_close(handle->loop, &handle->event_watcher); diff --git a/src/unix/linux-core.c b/src/unix/linux-core.c index 49c561be4ec..5ae31c56a65 100644 --- a/src/unix/linux-core.c +++ b/src/unix/linux-core.c @@ -108,24 +108,6 @@ int uv__platform_loop_init(uv_loop_t* loop) { } -int uv__io_fork(uv_loop_t* loop) { - int err; - void* old_watchers; - - old_watchers = loop->inotify_watchers; - - uv__close(loop->backend_fd); - loop->backend_fd = -1; - uv__platform_loop_delete(loop); - - err = uv__platform_loop_init(loop); - if (err) - return err; - - return uv__inotify_fork(loop, old_watchers); -} - - void uv__platform_loop_delete(uv_loop_t* loop) { if (loop->inotify_fd == -1) return; uv__io_stop(loop, &loop->inotify_read_watcher, POLLIN); diff --git a/src/unix/linux-inotify.c b/src/unix/linux-inotify.c index 7797f842524..eb27828573e 100644 --- a/src/unix/linux-inotify.c +++ b/src/unix/linux-inotify.c @@ -61,8 +61,6 @@ static void uv__inotify_read(uv_loop_t* loop, uv__io_t* w, unsigned int revents); -static void maybe_free_watcher_list(struct watcher_list* w, - uv_loop_t* loop); static int new_inotify_fd(void) { int err; @@ -110,71 +108,6 @@ static int init_inotify(uv_loop_t* loop) { } -int uv__inotify_fork(uv_loop_t* loop, void* old_watchers) { - /* Open the inotify_fd, and re-arm all the inotify watchers. */ - int err; - struct watcher_list* tmp_watcher_list_iter; - struct watcher_list* watcher_list; - struct watcher_list tmp_watcher_list; - QUEUE queue; - QUEUE* q; - uv_fs_event_t* handle; - char* tmp_path; - - if (old_watchers != NULL) { - /* We must restore the old watcher list to be able to close items - * out of it. - */ - loop->inotify_watchers = old_watchers; - - QUEUE_INIT(&tmp_watcher_list.watchers); - /* Note that the queue we use is shared with the start and stop() - * functions, making QUEUE_FOREACH unsafe to use. So we use the - * QUEUE_MOVE trick to safely iterate. Also don't free the watcher - * list until we're done iterating. c.f. uv__inotify_read. - */ - RB_FOREACH_SAFE(watcher_list, watcher_root, - CAST(&old_watchers), tmp_watcher_list_iter) { - watcher_list->iterating = 1; - QUEUE_MOVE(&watcher_list->watchers, &queue); - while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - handle = QUEUE_DATA(q, uv_fs_event_t, watchers); - /* It's critical to keep a copy of path here, because it - * will be set to NULL by stop() and then deallocated by - * maybe_free_watcher_list - */ - tmp_path = uv__strdup(handle->path); - assert(tmp_path != NULL); - QUEUE_REMOVE(q); - QUEUE_INSERT_TAIL(&watcher_list->watchers, q); - uv_fs_event_stop(handle); - - QUEUE_INSERT_TAIL(&tmp_watcher_list.watchers, &handle->watchers); - handle->path = tmp_path; - } - watcher_list->iterating = 0; - maybe_free_watcher_list(watcher_list, loop); - } - - QUEUE_MOVE(&tmp_watcher_list.watchers, &queue); - while (!QUEUE_EMPTY(&queue)) { - q = QUEUE_HEAD(&queue); - QUEUE_REMOVE(q); - handle = QUEUE_DATA(q, uv_fs_event_t, watchers); - tmp_path = handle->path; - handle->path = NULL; - err = uv_fs_event_start(handle, handle->cb, tmp_path, 0); - uv__free(tmp_path); - if (err) - return err; - } - } - - return 0; -} - - static struct watcher_list* find_watcher(uv_loop_t* loop, int wd) { struct watcher_list w; w.wd = wd; diff --git a/src/unix/loop.c b/src/unix/loop.c index c2a03d770f3..e9d61cfa7e4 100644 --- a/src/unix/loop.c +++ b/src/unix/loop.c @@ -110,39 +110,6 @@ int uv_loop_init(uv_loop_t* loop) { } -int uv_loop_fork(uv_loop_t* loop) { - int err; - unsigned int i; - uv__io_t* w; - - err = uv__io_fork(loop); - if (err) - return err; - - err = uv__async_fork(loop); - if (err) - return err; - - err = uv__signal_loop_fork(loop); - if (err) - return err; - - /* Rearm all the watchers that aren't re-queued by the above. */ - for (i = 0; i < loop->nwatchers; i++) { - w = loop->watchers[i]; - if (w == NULL) - continue; - - if (w->pevents != 0 && QUEUE_EMPTY(&w->watcher_queue)) { - w->events = 0; /* Force re-registration in uv__io_poll. */ - QUEUE_INSERT_TAIL(&loop->watcher_queue, &w->watcher_queue); - } - } - - return 0; -} - - void uv__loop_close(uv_loop_t* loop) { uv__signal_loop_cleanup(loop); uv__platform_loop_delete(loop); diff --git a/src/unix/signal.c b/src/unix/signal.c index 11cc487d7dd..dfa5f216c80 100644 --- a/src/unix/signal.c +++ b/src/unix/signal.c @@ -60,22 +60,6 @@ RB_GENERATE_STATIC(uv__signal_tree_s, uv_signal_s, tree_entry, uv__signal_compare) -static void uv__signal_global_reinit(void); - -static void uv__signal_global_init(void) { - if (uv__signal_lock_pipefd[0] == -1) - /* pthread_atfork can register before and after handlers, one - * for each child. This only registers one for the child. That - * state is both persistent and cumulative, so if we keep doing - * it the handler functions will be called multiple times. Thus - * we only want to do it once. - */ - if (pthread_atfork(NULL, NULL, &uv__signal_global_reinit)) - abort(); - - uv__signal_global_reinit(); -} - UV_DESTRUCTOR(static void uv__signal_global_fini(void)) { /* We can only use signal-safe functions here. @@ -97,7 +81,7 @@ UV_DESTRUCTOR(static void uv__signal_global_fini(void)) { } -static void uv__signal_global_reinit(void) { +static void uv__signal_global_init(void) { uv__signal_global_fini(); if (uv__make_pipe(uv__signal_lock_pipefd, 0)) @@ -276,16 +260,6 @@ static int uv__signal_loop_once_init(uv_loop_t* loop) { } -int uv__signal_loop_fork(uv_loop_t* loop) { - uv__io_stop(loop, &loop->signal_io_watcher, POLLIN); - uv__close(loop->signal_pipefd[0]); - uv__close(loop->signal_pipefd[1]); - loop->signal_pipefd[0] = -1; - loop->signal_pipefd[1] = -1; - return uv__signal_loop_once_init(loop); -} - - void uv__signal_loop_cleanup(uv_loop_t* loop) { QUEUE* q; diff --git a/src/unix/sunos.c b/src/unix/sunos.c index 2552a019eb4..93e26f25a13 100644 --- a/src/unix/sunos.c +++ b/src/unix/sunos.c @@ -99,18 +99,6 @@ void uv__platform_loop_delete(uv_loop_t* loop) { } -int uv__io_fork(uv_loop_t* loop) { -#if defined(PORT_SOURCE_FILE) - if (loop->fs_fd != -1) { - /* stop the watcher before we blow away its fileno */ - uv__io_stop(loop, &loop->fs_event_watcher, POLLIN); - } -#endif - uv__platform_loop_delete(loop); - return uv__platform_loop_init(loop); -} - - void uv__platform_invalidate_fd(uv_loop_t* loop, int fd) { struct port_event* events; uintptr_t i; diff --git a/src/win/core.c b/src/win/core.c index 59bce77bc1d..1808eec6d8e 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -216,11 +216,6 @@ uv_os_fd_t uv_backend_fd(const uv_loop_t* loop) { } -int uv_loop_fork(uv_loop_t* loop) { - return UV_ENOSYS; -} - - int uv_backend_timeout(const uv_loop_t* loop) { if (loop->stop_flag != 0) return 0; diff --git a/test/test-fork.c b/test/test-fork.c deleted file mode 100644 index 2cc1fef710d..00000000000 --- a/test/test-fork.c +++ /dev/null @@ -1,680 +0,0 @@ -/* Copyright libuv project contributors. All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to - * deal in the Software without restriction, including without limitation the - * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or - * sell copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in - * all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING - * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS - * IN THE SOFTWARE. - */ - -/* These tests are Unix only. */ -#ifndef _WIN32 - -#include -#include -#include -#include - -#include "uv.h" -#include "task.h" - -static int timer_cb_called; -static int socket_cb_called; - -static void timer_cb(uv_timer_t* timer) { - timer_cb_called++; - uv_close((uv_handle_t*) timer, NULL); -} - - -static int socket_cb_read_fd; -static int socket_cb_read_size; -static char socket_cb_read_buf[1024]; - - -static void socket_cb(uv_poll_t* poll, int status, int events) { - ssize_t cnt; - socket_cb_called++; - ASSERT(0 == status); - printf("Socket cb got events %d\n", events); - ASSERT(UV_READABLE == (events & UV_READABLE)); - if (socket_cb_read_fd) { - cnt = read(socket_cb_read_fd, socket_cb_read_buf, socket_cb_read_size); - ASSERT(cnt == socket_cb_read_size); - } - uv_close((uv_handle_t*) poll, NULL); -} - - -static void run_timer_loop_once(void) { - uv_loop_t* loop; - uv_timer_t timer_handle; - - loop = uv_default_loop(); - - timer_cb_called = 0; /* Reset for the child. */ - - ASSERT(0 == uv_timer_init(loop, &timer_handle)); - ASSERT(0 == uv_timer_start(&timer_handle, timer_cb, 1, 0)); - ASSERT(0 == uv_run(loop, UV_RUN_DEFAULT)); - ASSERT(1 == timer_cb_called); -} - - -static void assert_wait_child(pid_t child_pid) { - pid_t waited_pid; - int child_stat; - - waited_pid = waitpid(child_pid, &child_stat, 0); - printf("Waited pid is %d with status %d\n", waited_pid, child_stat); - if (waited_pid == -1) { - perror("Failed to wait"); - } - ASSERT(child_pid == waited_pid); - ASSERT(WIFEXITED(child_stat)); /* Clean exit, not a signal. */ - ASSERT(!WIFSIGNALED(child_stat)); - ASSERT(0 == WEXITSTATUS(child_stat)); -} - - -TEST_IMPL(fork_timer) { - /* Timers continue to work after we fork. */ - - /* - * Establish the loop before we fork to make sure that it - * has state to get reset after the fork. - */ - pid_t child_pid; - - run_timer_loop_once(); - child_pid = fork(); - ASSERT(child_pid != -1); - - if (child_pid != 0) { - /* parent */ - assert_wait_child(child_pid); - } else { - /* child */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); - run_timer_loop_once(); - } - - MAKE_VALGRIND_HAPPY(); - return 0; -} - - -TEST_IMPL(fork_socketpair) { - /* A socket opened in the parent and accept'd in the - child works after a fork. */ - pid_t child_pid; - int socket_fds[2]; - uv_poll_t poll_handle; - - /* Prime the loop. */ - run_timer_loop_once(); - - ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds)); - - /* Create the server watcher in the parent, use it in the child. */ - ASSERT(0 == uv_poll_init(uv_default_loop(), &poll_handle, socket_fds[0])); - - child_pid = fork(); - ASSERT(child_pid != -1); - - if (child_pid != 0) { - /* parent */ - ASSERT(3 == send(socket_fds[1], "hi\n", 3, 0)); - assert_wait_child(child_pid); - } else { - /* child */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); - ASSERT(0 == socket_cb_called); - ASSERT(0 == uv_poll_start(&poll_handle, UV_READABLE, socket_cb)); - printf("Going to run the loop in the child\n"); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(1 == socket_cb_called); - } - - MAKE_VALGRIND_HAPPY(); - return 0; -} - - -TEST_IMPL(fork_socketpair_started) { - /* A socket opened in the parent and accept'd in the - child works after a fork, even if the watcher was already - started, and then stopped in the parent. */ - pid_t child_pid; - int socket_fds[2]; - int sync_pipe[2]; - char sync_buf[1]; - uv_poll_t poll_handle; - - ASSERT(0 == pipe(sync_pipe)); - - /* Prime the loop. */ - run_timer_loop_once(); - - ASSERT(0 == socketpair(AF_UNIX, SOCK_STREAM, 0, socket_fds)); - - /* Create and start the server watcher in the parent, use it in the child. */ - ASSERT(0 == uv_poll_init(uv_default_loop(), &poll_handle, socket_fds[0])); - ASSERT(0 == uv_poll_start(&poll_handle, UV_READABLE, socket_cb)); - - /* Run the loop AFTER the poll watcher is registered to make sure it - gets passed to the kernel. Use NOWAIT and expect a non-zero - return to prove the poll watcher is active. - */ - ASSERT(1 == uv_run(uv_default_loop(), UV_RUN_NOWAIT)); - - child_pid = fork(); - ASSERT(child_pid != -1); - - if (child_pid != 0) { - /* parent */ - ASSERT(0 == uv_poll_stop(&poll_handle)); - uv_close((uv_handle_t*)&poll_handle, NULL); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(0 == socket_cb_called); - ASSERT(1 == write(sync_pipe[1], "1", 1)); /* alert child */ - ASSERT(3 == send(socket_fds[1], "hi\n", 3, 0)); - - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(0 == socket_cb_called); - - assert_wait_child(child_pid); - } else { - /* child */ - printf("Child is %d\n", getpid()); - ASSERT(1 == read(sync_pipe[0], sync_buf, 1)); /* wait for parent */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); - ASSERT(0 == socket_cb_called); - - printf("Going to run the loop in the child\n"); - socket_cb_read_fd = socket_fds[0]; - socket_cb_read_size = 3; - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_DEFAULT)); - ASSERT(1 == socket_cb_called); - printf("Buf %s\n", socket_cb_read_buf); - ASSERT(0 == strcmp("hi\n", socket_cb_read_buf)); - } - - MAKE_VALGRIND_HAPPY(); - return 0; -} - - -static int fork_signal_cb_called; - -void fork_signal_to_child_cb(uv_signal_t* handle, int signum) -{ - fork_signal_cb_called = signum; - uv_close((uv_handle_t*)handle, NULL); -} - - -TEST_IMPL(fork_signal_to_child) { - /* A signal handler installed before forking - is run only in the child when the child is signalled. */ - uv_signal_t signal_handle; - pid_t child_pid; - int sync_pipe[2]; - char sync_buf[1]; - - fork_signal_cb_called = 0; /* reset */ - - ASSERT(0 == pipe(sync_pipe)); - - /* Prime the loop. */ - run_timer_loop_once(); - - ASSERT(0 == uv_signal_init(uv_default_loop(), &signal_handle)); - ASSERT(0 == uv_signal_start(&signal_handle, fork_signal_to_child_cb, SIGUSR1)); - - child_pid = fork(); - ASSERT(child_pid != -1); - - if (child_pid != 0) { - /* parent */ - ASSERT(1 == read(sync_pipe[0], sync_buf, 1)); /* wait for child */ - ASSERT(0 == kill(child_pid, SIGUSR1)); - /* Run the loop, make sure we don't get the signal. */ - printf("Running loop in parent\n"); - uv_unref((uv_handle_t*)&signal_handle); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_NOWAIT)); - ASSERT(0 == fork_signal_cb_called); - printf("Waiting for child in parent\n"); - assert_wait_child(child_pid); - } else { - /* child */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); - ASSERT(1 == write(sync_pipe[1], "1", 1)); /* alert parent */ - /* Get the signal. */ - ASSERT(0 != uv_loop_alive(uv_default_loop())); - printf("Running loop in child\n"); - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); - ASSERT(SIGUSR1 == fork_signal_cb_called); - } - - MAKE_VALGRIND_HAPPY(); - return 0; -} - - -TEST_IMPL(fork_signal_to_child_closed) { - /* A signal handler installed before forking - doesn't get received anywhere when the child is signalled, - but isnt running the loop. */ - uv_signal_t signal_handle; - pid_t child_pid; - int sync_pipe[2]; - int sync_pipe2[2]; - char sync_buf[1]; - int r; - - fork_signal_cb_called = 0; /* reset */ - - ASSERT(0 == pipe(sync_pipe)); - ASSERT(0 == pipe(sync_pipe2)); - - /* Prime the loop. */ - run_timer_loop_once(); - - ASSERT(0 == uv_signal_init(uv_default_loop(), &signal_handle)); - ASSERT(0 == uv_signal_start(&signal_handle, fork_signal_to_child_cb, SIGUSR1)); - - child_pid = fork(); - ASSERT(child_pid != -1); - - if (child_pid != 0) { - /* parent */ - printf("Wating on child in parent\n"); - ASSERT(1 == read(sync_pipe[0], sync_buf, 1)); /* wait for child */ - printf("Parent killing child\n"); - ASSERT(0 == kill(child_pid, SIGUSR1)); - /* Run the loop, make sure we don't get the signal. */ - printf("Running loop in parent\n"); - uv_unref((uv_handle_t*)&signal_handle); /* so the loop can exit; - we *shouldn't* get any signals */ - run_timer_loop_once(); /* but while we share a pipe, we do, so - have something active. */ - ASSERT(0 == uv_run(uv_default_loop(), UV_RUN_ONCE)); - printf("Signal in parent %d\n", fork_signal_cb_called); - ASSERT(0 == fork_signal_cb_called); - ASSERT(1 == write(sync_pipe2[1], "1", 1)); /* alert child */ - printf("Waiting for child in parent\n"); - assert_wait_child(child_pid); - } else { - /* Child. Our signal handler should still be installed. */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); - printf("Checking loop in child\n"); - ASSERT(0 != uv_loop_alive(uv_default_loop())); - printf("Alerting parent in child\n"); - ASSERT(1 == write(sync_pipe[1], "1", 1)); /* alert parent */ - /* Don't run the loop. Wait for the parent to call us */ - printf("Waiting on parent in child\n"); - /* Wait for parent. read may fail if the parent tripped an ASSERT - and exited, so this ASSERT is generous. - */ - r = read(sync_pipe2[0], sync_buf, 1); - ASSERT(-1 <= r && r <= 1); - ASSERT(0 == fork_signal_cb_called); - printf("Exiting child \n"); - /* Note that we're deliberately not running the loop - * in the child, and also not closing the loop's handles, - * so the child default loop can't be cleanly closed. - * We need to explicitly exit to avoid an automatic failure - * in that case. - */ - exit(0); - } - - MAKE_VALGRIND_HAPPY(); - return 0; -} - - -static void create_file(const char* name) { - int r; - uv_os_fd_t file; - uv_fs_t req; - - r = uv_fs_open(NULL, &req, name, O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR, NULL); - ASSERT(r == 0); - file = req.result; - uv_fs_req_cleanup(&req); - r = uv_fs_close(NULL, &req, file, NULL); - ASSERT(r == 0); - uv_fs_req_cleanup(&req); -} - - -static void touch_file(const char* name) { - int r; - uv_os_fd_t file; - uv_fs_t req; - uv_buf_t buf; - - r = uv_fs_open(NULL, &req, name, O_RDWR, 0, NULL); - ASSERT(r == 0); - file = req.result; - uv_fs_req_cleanup(&req); - - buf = uv_buf_init("foo", 4); - r = uv_fs_write(NULL, &req, file, &buf, 1, -1, NULL); - ASSERT(r >= 0); - uv_fs_req_cleanup(&req); - - r = uv_fs_close(NULL, &req, file, NULL); - ASSERT(r == 0); - uv_fs_req_cleanup(&req); -} - - -static int timer_cb_touch_called; - -static void timer_cb_touch(uv_timer_t* timer) { - uv_close((uv_handle_t*)timer, NULL); - touch_file("watch_file"); - timer_cb_touch_called++; -} - - -static int fs_event_cb_called; - -static void fs_event_cb_file_current_dir(uv_fs_event_t* handle, - const char* filename, - int events, - int status) { - ASSERT(fs_event_cb_called == 0); - ++fs_event_cb_called; - ASSERT(status == 0); -#if defined(__APPLE__) || defined(__linux__) - ASSERT(strcmp(filename, "watch_file") == 0); -#else - ASSERT(filename == NULL || strcmp(filename, "watch_file") == 0); -#endif - uv_close((uv_handle_t*)handle, NULL); -} - - -static void assert_watch_file_current_dir(uv_loop_t* const loop, int file_or_dir) { - uv_timer_t timer; - uv_fs_event_t fs_event; - int r; - - /* Setup */ - remove("watch_file"); - create_file("watch_file"); - - r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); - /* watching a dir is the only way to get fsevents involved on apple - platforms */ - r = uv_fs_event_start(&fs_event, - fs_event_cb_file_current_dir, - file_or_dir == 1 ? "." : "watch_file", - 0); - ASSERT(r == 0); - - r = uv_timer_init(loop, &timer); - ASSERT(r == 0); - - r = uv_timer_start(&timer, timer_cb_touch, 100, 0); - ASSERT(r == 0); - - ASSERT(timer_cb_touch_called == 0); - ASSERT(fs_event_cb_called == 0); - - uv_run(loop, UV_RUN_DEFAULT); - - ASSERT(timer_cb_touch_called == 1); - ASSERT(fs_event_cb_called == 1); - - /* Cleanup */ - remove("watch_file"); - fs_event_cb_called = 0; - timer_cb_touch_called = 0; - uv_run(loop, UV_RUN_DEFAULT); /* flush pending closes */ -} - - -#define FS_TEST_FILE 0 -#define FS_TEST_DIR 1 - -static int _do_fork_fs_events_child(int file_or_dir) { - /* basic fsevents work in the child after a fork */ - pid_t child_pid; - uv_loop_t loop; - - /* Watch in the parent, prime the loop and/or threads. */ - assert_watch_file_current_dir(uv_default_loop(), file_or_dir); - child_pid = fork(); - ASSERT(child_pid != -1); - - if (child_pid != 0) { - /* parent */ - assert_wait_child(child_pid); - } else { - /* child */ - /* Ee can watch in a new loop, but dirs only work - if we're on linux. */ -#if defined(__APPLE__) - file_or_dir = FS_TEST_FILE; -#endif - printf("Running child\n"); - uv_loop_init(&loop); - printf("Child first watch\n"); - assert_watch_file_current_dir(&loop, file_or_dir); - ASSERT(0 == uv_loop_close(&loop)); - printf("Child second watch default loop\n"); - /* Ee can watch in the default loop. */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); - /* On some platforms (OS X), if we don't update the time now, - * the timer cb fires before the event loop enters uv__io_poll, - * instead of after, meaning we don't see the change! This may be - * a general race. - */ - uv_update_time(uv_default_loop()); - assert_watch_file_current_dir(uv_default_loop(), file_or_dir); - - /* We can close the parent loop successfully too. This is - especially important on Apple platforms where if we're not - careful trying to touch the CFRunLoop, even just to shut it - down, that we allocated in the FS_TEST_DIR case would crash. */ - ASSERT(0 == uv_loop_close(uv_default_loop())); - - printf("Exiting child \n"); - } - - MAKE_VALGRIND_HAPPY(); - return 0; - -} - - -TEST_IMPL(fork_fs_events_child) { -#if defined(NO_FS_EVENTS) - RETURN_SKIP(NO_FS_EVENTS); -#endif - return _do_fork_fs_events_child(FS_TEST_FILE); -} - - -TEST_IMPL(fork_fs_events_child_dir) { -#if defined(NO_FS_EVENTS) - RETURN_SKIP(NO_FS_EVENTS); -#endif -#if defined(__APPLE__) || defined (__linux__) - return _do_fork_fs_events_child(FS_TEST_DIR); -#else - /* You can't spin up a cfrunloop thread on an apple platform - and then fork. See - http://objectivistc.tumblr.com/post/16187948939/you-must-exec-a-core-foundation-fork-safety-tale - */ - return 0; -#endif -} - - -TEST_IMPL(fork_fs_events_file_parent_child) { -#if defined(NO_FS_EVENTS) - RETURN_SKIP(NO_FS_EVENTS); -#endif -#if defined(__sun) || defined(_AIX) || defined(__MVS__) - /* It's not possible to implement this without additional - * bookkeeping on SunOS. For AIX it is possible, but has to be - * written. See https://github.com/libuv/libuv/pull/846#issuecomment-287170420 - * TODO: On z/OS, we need to open another message queue and subscribe to the - * same events as the parent. - */ - return 0; -#else - /* Establishing a started fs events watcher in the parent should - still work in the child. */ - uv_timer_t timer; - uv_fs_event_t fs_event; - int r; - pid_t child_pid; - uv_loop_t* loop; - - loop = uv_default_loop(); - - /* Setup */ - remove("watch_file"); - create_file("watch_file"); - - r = uv_fs_event_init(loop, &fs_event); - ASSERT(r == 0); - r = uv_fs_event_start(&fs_event, - fs_event_cb_file_current_dir, - "watch_file", - 0); - ASSERT(r == 0); - - r = uv_timer_init(loop, &timer); - ASSERT(r == 0); - - child_pid = fork(); - ASSERT(child_pid != -1); - if (child_pid != 0) { - /* parent */ - assert_wait_child(child_pid); - } else { - /* child */ - printf("Running child\n"); - ASSERT(0 == uv_loop_fork(loop)); - - r = uv_timer_start(&timer, timer_cb_touch, 100, 0); - ASSERT(r == 0); - - ASSERT(timer_cb_touch_called == 0); - ASSERT(fs_event_cb_called == 0); - printf("Running loop in child \n"); - uv_run(loop, UV_RUN_DEFAULT); - - ASSERT(timer_cb_touch_called == 1); - ASSERT(fs_event_cb_called == 1); - - /* Cleanup */ - remove("watch_file"); - fs_event_cb_called = 0; - timer_cb_touch_called = 0; - uv_run(loop, UV_RUN_DEFAULT); /* Flush pending closes. */ - } - - - MAKE_VALGRIND_HAPPY(); - return 0; -#endif -} - - -static int work_cb_count; -static int after_work_cb_count; - - -static void work_cb(uv_work_t* req) { - work_cb_count++; -} - - -static void after_work_cb(uv_work_t* req, int status) { - ASSERT(status == 0); - after_work_cb_count++; -} - - -static void assert_run_work(uv_loop_t* const loop) { - uv_work_t work_req; - int r; - - ASSERT(work_cb_count == 0); - ASSERT(after_work_cb_count == 0); - printf("Queue in %d\n", getpid()); - r = uv_queue_work(loop, &work_req, work_cb, after_work_cb); - ASSERT(r == 0); - printf("Running in %d\n", getpid()); - uv_run(loop, UV_RUN_DEFAULT); - - ASSERT(work_cb_count == 1); - ASSERT(after_work_cb_count == 1); - - /* cleanup */ - work_cb_count = 0; - after_work_cb_count = 0; -} - - -#ifndef __MVS__ -TEST_IMPL(fork_threadpool_queue_work_simple) { - /* The threadpool works in a child process. */ - - pid_t child_pid; - uv_loop_t loop; - - /* Prime the pool and default loop. */ - assert_run_work(uv_default_loop()); - - child_pid = fork(); - ASSERT(child_pid != -1); - - if (child_pid != 0) { - /* Parent. We can still run work. */ - assert_run_work(uv_default_loop()); - assert_wait_child(child_pid); - } else { - /* Child. We can work in a new loop. */ - printf("Running child in %d\n", getpid()); - uv_loop_init(&loop); - printf("Child first watch\n"); - assert_run_work(&loop); - uv_loop_close(&loop); - printf("Child second watch default loop\n"); - /* We can work in the default loop. */ - ASSERT(0 == uv_loop_fork(uv_default_loop())); - assert_run_work(uv_default_loop()); - printf("Exiting child \n"); - } - - - MAKE_VALGRIND_HAPPY(); - return 0; -} -#endif /* !__MVS__ */ - - -#endif /* !_WIN32 */ diff --git a/test/test-list.h b/test/test-list.h index feb31e8ba66..2f31342fc47 100644 --- a/test/test-list.h +++ b/test/test-list.h @@ -452,20 +452,6 @@ TEST_DECLARE (handle_type_name) TEST_DECLARE (req_type_name) TEST_DECLARE (getters_setters) -#ifndef _WIN32 -TEST_DECLARE (fork_timer) -TEST_DECLARE (fork_socketpair) -TEST_DECLARE (fork_socketpair_started) -TEST_DECLARE (fork_signal_to_child) -TEST_DECLARE (fork_signal_to_child_closed) -TEST_DECLARE (fork_fs_events_child) -TEST_DECLARE (fork_fs_events_child_dir) -TEST_DECLARE (fork_fs_events_file_parent_child) -#ifndef __MVS__ -TEST_DECLARE (fork_threadpool_queue_work_simple) -#endif -#endif - TEST_DECLARE (idna_toascii) TEST_DECLARE (utf8_decode1) @@ -988,20 +974,6 @@ TASK_LIST_START TEST_ENTRY (req_type_name) TEST_ENTRY (getters_setters) -#ifndef _WIN32 - TEST_ENTRY (fork_timer) - TEST_ENTRY (fork_socketpair) - TEST_ENTRY (fork_socketpair_started) - TEST_ENTRY (fork_signal_to_child) - TEST_ENTRY (fork_signal_to_child_closed) - TEST_ENTRY (fork_fs_events_child) - TEST_ENTRY (fork_fs_events_child_dir) - TEST_ENTRY (fork_fs_events_file_parent_child) -#ifndef __MVS__ - TEST_ENTRY (fork_threadpool_queue_work_simple) -#endif -#endif - TEST_ENTRY (utf8_decode1) /* Doesn't work on z/OS because that platform uses EBCDIC, not ASCII. */ diff --git a/test/test.gyp b/test/test.gyp index 85c65a76bc1..3a19fc8afd6 100644 --- a/test/test.gyp +++ b/test/test.gyp @@ -30,7 +30,6 @@ 'test-emfile.c', 'test-env-vars.c', 'test-fail-always.c', - 'test-fork.c', 'test-fs.c', 'test-fs-copyfile.c', 'test-fs-event.c',