diff --git a/include/uv.h b/include/uv.h index 730aa15143..ba4d5eceda 100644 --- a/include/uv.h +++ b/include/uv.h @@ -2251,6 +2251,30 @@ UV_EXTERN extern uint64_t uv_hrtime(void); */ UV_EXTERN void uv_disable_stdio_inheritance(void); +/* + * On Unix, this function can be called early from main, before any threads + * are started, to set up a safe environment with respect to signal handling. + * + * This function does the following when it is called for the first time: + * - Starts a dummy thread, which consists of an infinite pause() loop. + * - Blocks all signals in the current thread. + * + * Remember that signal masks are thread-specific and inherited at thread + * creation. Therefore the dummy thread will have the same signals unblocked + * as the main thread did when this function was called. + * + * This will ensure that signals directed at the process will only be processed + * by this dummy thread. Most imporantly, system calls will not randomly fail + * with EINTR errors (unless signals are later unmasked by threads...). + * + * Calling this function is not necessary for libuv itself to operate + * correctly, since EINTR is handled by retrying calls. But code outside + * of libuv may not be handling EINTR. + * + * This function does nothing on Windows. + */ +UV_EXTERN void uv_sanitize_signal_handling(void); + /* * Opens a shared library. The filename is in utf-8. Returns 0 on success and * -1 on error. Call uv_dlerror(uv_lib_t*) to get the error message. diff --git a/src/unix/core.c b/src/unix/core.c index aaced4c927..87bf852a73 100644 --- a/src/unix/core.c +++ b/src/unix/core.c @@ -75,6 +75,10 @@ #endif static void uv__run_pending(uv_loop_t* loop); +static void uv__signal_thread_proc(void *arg); + +static int uv__sanitize_signals_done; +static uv_thread_t uv__signal_thread; /* Verify that uv_buf_t is ABI-compatible with struct iovec. */ STATIC_ASSERT(sizeof(uv_buf_t) == sizeof(struct iovec)); @@ -662,6 +666,29 @@ void uv_disable_stdio_inheritance(void) { } +void uv_sanitize_signal_handling(void) { + sigset_t full_ss; + sigset_t old_ss; + + /* Don't do it twice. + * This looks like a race condition but then it should only be + * called from main. */ + if (uv__sanitize_signals_done) { + return; + } + uv__sanitize_signals_done = 1; + + // Create dummy signal handling thread. + if (uv_thread_create(&uv__signal_thread, uv__signal_thread_proc, NULL)) + abort(); + + /* Block all signals in the current (main) thread. */ + sigfillset(&full_ss); + if (pthread_sigmask(SIG_SETMASK, &full_ss, &old_ss)) + abort(); +} + + static void uv__run_pending(uv_loop_t* loop) { QUEUE* q; uv__io_t* w; @@ -946,3 +973,11 @@ int uv__dup2_cloexec(int oldfd, int newfd) { return r; } } + + +static void uv__signal_thread_proc(void* arg) { + /* Handle signals by doing nothing. */ + while (1) { + pause(); + } +} diff --git a/src/win/core.c b/src/win/core.c index 5c3c63908c..d9affda624 100644 --- a/src/win/core.c +++ b/src/win/core.c @@ -456,3 +456,6 @@ int uv__socket_sockopt(uv_handle_t* handle, int optname, int* value) { return 0; } + +void uv_sanitize_signal_handling(void) { +}