From 5756f92f464fd0f2d04dd05bc30b350010885f74 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 26 Jan 2015 23:15:20 +0100 Subject: [PATCH 1/3] src: do platform-specific initialization earlier Execute the per-platform initialization logic as early as possible, for two reasons: 1. It opens the way for an upcoming commit to simplify early SIGUSR1 handling. 2. It should make life easier for embedders because io.js no longer mucks around with the file descriptor limit or signal disposition of the process. PR-URL: https://github.com/iojs/io.js/pull/615 Reviewed-By: Sam Roberts --- src/node.cc | 61 ++++++++++++++++++++++++++++------------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/node.cc b/src/node.cc index b83485c9d39da1..6e1d5cc2d42389 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3322,6 +3322,36 @@ static void DebugEnd(const FunctionCallbackInfo& args) { } +inline void PlatformInit() { +#ifdef __POSIX__ + // Raise the open file descriptor limit. + struct rlimit lim; + if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) { + // Do a binary search for the limit. + rlim_t min = lim.rlim_cur; + rlim_t max = 1 << 20; + // But if there's a defined upper bound, don't search, just set it. + if (lim.rlim_max != RLIM_INFINITY) { + min = lim.rlim_max; + max = lim.rlim_max; + } + do { + lim.rlim_cur = min + (max - min) / 2; + if (setrlimit(RLIMIT_NOFILE, &lim)) { + max = lim.rlim_cur; + } else { + min = lim.rlim_cur; + } + } while (min + 1 < max); + } + // Ignore SIGPIPE + RegisterSignalHandler(SIGPIPE, SIG_IGN); + RegisterSignalHandler(SIGINT, SignalExit, true); + RegisterSignalHandler(SIGTERM, SignalExit, true); +#endif // __POSIX__ +} + + void Init(int* argc, const char** argv, int* exec_argc, @@ -3396,35 +3426,6 @@ void Init(int* argc, V8::SetArrayBufferAllocator(&ArrayBufferAllocator::the_singleton); -#ifdef __POSIX__ - // Raise the open file descriptor limit. - { // NOLINT (whitespace/braces) - struct rlimit lim; - if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) { - // Do a binary search for the limit. - rlim_t min = lim.rlim_cur; - rlim_t max = 1 << 20; - // But if there's a defined upper bound, don't search, just set it. - if (lim.rlim_max != RLIM_INFINITY) { - min = lim.rlim_max; - max = lim.rlim_max; - } - do { - lim.rlim_cur = min + (max - min) / 2; - if (setrlimit(RLIMIT_NOFILE, &lim)) { - max = lim.rlim_cur; - } else { - min = lim.rlim_cur; - } - } while (min + 1 < max); - } - } - // Ignore SIGPIPE - RegisterSignalHandler(SIGPIPE, SIG_IGN); - RegisterSignalHandler(SIGINT, SignalExit, true); - RegisterSignalHandler(SIGTERM, SignalExit, true); -#endif // __POSIX__ - if (!use_debug_agent) { RegisterDebugSignalHandler(); } @@ -3610,6 +3611,8 @@ Environment* CreateEnvironment(Isolate* isolate, int Start(int argc, char** argv) { + PlatformInit(); + const char* replaceInvalid = secure_getenv("NODE_INVALID_UTF8"); if (replaceInvalid == nullptr) From 63ae1d203aba94b9a35400acdf00ff968fb6eb05 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Mon, 26 Jan 2015 23:26:33 +0100 Subject: [PATCH 2/3] src: rework early debug signal handling Instead of installing an early debug signal handler, simply block the SIGUSR1 signal at start-up and unblock it when the debugger is ready. Both approaches are functionally equivalent but blocking the signal accomplishes it in fewer lines of code. PR-URL: https://github.com/iojs/io.js/pull/615 Reviewed-By: Sam Roberts --- src/node.cc | 41 ++++++++++++----------------------------- 1 file changed, 12 insertions(+), 29 deletions(-) diff --git a/src/node.cc b/src/node.cc index 6e1d5cc2d42389..71dd3a8a9d7e0f 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3096,22 +3096,6 @@ static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle) { #ifdef __POSIX__ -static volatile sig_atomic_t caught_early_debug_signal; - - -static void EarlyDebugSignalHandler(int signo) { - caught_early_debug_signal = 1; -} - - -static void InstallEarlyDebugSignalHandler() { - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = EarlyDebugSignalHandler; - sigaction(SIGUSR1, &sa, nullptr); -} - - static void EnableDebugSignalHandler(int signo) { // Call only async signal-safe functions here! v8::Debug::DebugBreak(*static_cast(&node_isolate)); @@ -3152,10 +3136,11 @@ void DebugProcess(const FunctionCallbackInfo& args) { static int RegisterDebugSignalHandler() { // FIXME(bnoordhuis) Should be per-isolate or per-context, not global. RegisterSignalHandler(SIGUSR1, EnableDebugSignalHandler); - // If we caught a SIGUSR1 during the bootstrap process, re-raise it - // now that the debugger infrastructure is in place. - if (caught_early_debug_signal) - raise(SIGUSR1); + // Unblock SIGUSR1. A pending SIGUSR1 signal will now be delivered. + sigset_t sigmask; + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGUSR1); + CHECK_EQ(0, pthread_sigmask(SIG_UNBLOCK, &sigmask, nullptr)); return 0; } #endif // __POSIX__ @@ -3324,6 +3309,13 @@ static void DebugEnd(const FunctionCallbackInfo& args) { inline void PlatformInit() { #ifdef __POSIX__ + sigset_t sigmask; + sigemptyset(&sigmask); + sigaddset(&sigmask, SIGUSR1); + CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr)); + RegisterSignalHandler(SIGPIPE, SIG_IGN); + RegisterSignalHandler(SIGINT, SignalExit, true); + RegisterSignalHandler(SIGTERM, SignalExit, true); // Raise the open file descriptor limit. struct rlimit lim; if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) { @@ -3344,10 +3336,6 @@ inline void PlatformInit() { } } while (min + 1 < max); } - // Ignore SIGPIPE - RegisterSignalHandler(SIGPIPE, SIG_IGN); - RegisterSignalHandler(SIGINT, SignalExit, true); - RegisterSignalHandler(SIGTERM, SignalExit, true); #endif // __POSIX__ } @@ -3618,11 +3606,6 @@ int Start(int argc, char** argv) { if (replaceInvalid == nullptr) WRITE_UTF8_FLAGS |= String::REPLACE_INVALID_UTF8; -#if !defined(_WIN32) - // Try hard not to lose SIGUSR1 signals during the bootstrap process. - InstallEarlyDebugSignalHandler(); -#endif - CHECK_GT(argc, 0); // Hack around with the argv pointer. Used for process.title = "blah". From dd47a8c78547db14ea0c7fc2f3375e8c9cb1a129 Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Tue, 27 Jan 2015 00:07:34 +0100 Subject: [PATCH 3/3] src: set default signal dispositions at start-up Signal dispositions are inherited by child processes. Restore ours to sane defaults in case our parent process changed it, to prevent quirky behavior when the parent does something silly like ignoring SIGSEGV. PR-URL: https://github.com/iojs/io.js/pull/615 Reviewed-By: Sam Roberts --- src/node.cc | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/node.cc b/src/node.cc index 71dd3a8a9d7e0f..72ddbe178e37b9 100644 --- a/src/node.cc +++ b/src/node.cc @@ -3313,9 +3313,24 @@ inline void PlatformInit() { sigemptyset(&sigmask); sigaddset(&sigmask, SIGUSR1); CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr)); - RegisterSignalHandler(SIGPIPE, SIG_IGN); + + // Restore signal dispositions, the parent process may have changed them. + struct sigaction act; + memset(&act, 0, sizeof(act)); + + // The hard-coded upper limit is because NSIG is not very reliable; on Linux, + // it evaluates to 32, 34 or 64, depending on whether RT signals are enabled. + // Counting up to SIGRTMIN doesn't work for the same reason. + for (unsigned nr = 1; nr < 32; nr += 1) { + if (nr == SIGKILL || nr == SIGSTOP) + continue; + act.sa_handler = (nr == SIGPIPE) ? SIG_IGN : SIG_DFL; + CHECK_EQ(0, sigaction(nr, &act, nullptr)); + } + RegisterSignalHandler(SIGINT, SignalExit, true); RegisterSignalHandler(SIGTERM, SignalExit, true); + // Raise the open file descriptor limit. struct rlimit lim; if (getrlimit(RLIMIT_NOFILE, &lim) == 0 && lim.rlim_cur != lim.rlim_max) {