From 4234bcce486f5c037c9bcc5d121565b2bec9449c Mon Sep 17 00:00:00 2001 From: Ben Noordhuis Date: Wed, 16 Oct 2013 02:54:24 +0200 Subject: [PATCH] debugger: fix SIGUSR1 bootstrap race condition Before this commit, the SIGUSR1 signal handler wasn't installed until late in the bootstrapping process and we were prone to miss signals sent by other processes. This commit installs an early-boot signal handler that merely records the fact that we received a signal. Once the debugger infrastructure is in place, the signal is re-raised, kickstarting the debugger. Among other things, this means that simple/test-debugger-client is now _much_ less likely to fail. --- src/node.cc | 24 ++++++++++++++++++++++++ test/simple/test-debugger-client.js | 16 ++++++++++++---- 2 files changed, 36 insertions(+), 4 deletions(-) diff --git a/src/node.cc b/src/node.cc index 2c874ce1cf..1a3995c51c 100644 --- a/src/node.cc +++ b/src/node.cc @@ -2874,6 +2874,22 @@ static void DispatchDebugMessagesAsyncCallback(uv_async_t* handle, int status) { #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, NULL); +} + + static void EnableDebugSignalHandler(int signo) { // Call only async signal-safe functions here! v8::Debug::DebugBreak(*static_cast(&node_isolate)); @@ -2911,6 +2927,9 @@ 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); return 0; } #endif // __POSIX__ @@ -3270,6 +3289,11 @@ Environment* CreateEnvironment(Isolate* isolate, int Start(int argc, char** argv) { +#if !defined(_WIN32) + // Try hard not to lose SIGUSR1 signals during the bootstrap process. + InstallEarlyDebugSignalHandler(); +#endif + assert(argc > 0); // Hack around with the argv pointer. Used for process.title = "blah". diff --git a/test/simple/test-debugger-client.js b/test/simple/test-debugger-client.js index 25bb409000..21ddf5e1e6 100644 --- a/test/simple/test-debugger-client.js +++ b/test/simple/test-debugger-client.js @@ -181,11 +181,15 @@ function doTest(cb, done) { console.error('got stderr data %j', data); nodeProcess.stderr.resume(); b += data; - if (didTryConnect == false && - b.match(/Debugger listening on port/)) { + if (didTryConnect === false && b.match(/Debugger listening on port/)) { didTryConnect = true; - setTimeout(function() { + // The timeout is here to expose a race in the bootstrap process. + // Without the early SIGUSR1 debug handler, it effectively results + // in an infinite ECONNREFUSED loop. + setTimeout(tryConnect, 100); + + function tryConnect() { // Wait for some data before trying to connect var c = new debug.Client(); console.error('>>> connecting...'); @@ -202,7 +206,11 @@ function doTest(cb, done) { done(); }); }); - }); + c.on('error', function(err) { + if (err.code !== 'ECONNREFUSED') throw err; + setTimeout(tryConnect, 10); + }); + } } }); }