summaryrefslogtreecommitdiff
path: root/src/node_watchdog.cc
diff options
context:
space:
mode:
authorAnna Henningsen <anna@addaleax.net>2016-05-08 03:28:47 +0200
committerAnna Henningsen <anna@addaleax.net>2016-06-18 20:44:16 +0200
commit0815b9401d087202cd64458b6906a5225929fc5d (patch)
tree78fb84a7b6887091cd392e5d9feff11bae6edf96 /src/node_watchdog.cc
parentde9a84186e6da9e4e6ee9434aa89715bf3eb9172 (diff)
downloadandroid-node-v8-0815b9401d087202cd64458b6906a5225929fc5d.tar.gz
android-node-v8-0815b9401d087202cd64458b6906a5225929fc5d.tar.bz2
android-node-v8-0815b9401d087202cd64458b6906a5225929fc5d.zip
vm: add ability to break on sigint/ctrl+c
- Adds the `breakEvalOnSigint` option to `vm.runIn(This)Context`. This uses a watchdog thread to wait for SIGINT and generally works just like the existing `timeout` option. - Adds a method to the existing timer-based watchdog to check if it stopped regularly or by running into the timeout. This is used to tell a SIGINT abort from a timer-based one. - Adds (internal) `process._{start,stop}SigintWatchdog` methods to start/stop the watchdog thread used by the above option manually. This will be used in the REPL to set up SIGINT handling before entering terminal raw mode, so that there is no time window in which Ctrl+C fully aborts the process. PR-URL: https://github.com/nodejs/node/pull/6635 Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
Diffstat (limited to 'src/node_watchdog.cc')
-rw-r--r--src/node_watchdog.cc225
1 files changed, 225 insertions, 0 deletions
diff --git a/src/node_watchdog.cc b/src/node_watchdog.cc
index dfa7debd9a..8e94bc8d9d 100644
--- a/src/node_watchdog.cc
+++ b/src/node_watchdog.cc
@@ -1,10 +1,13 @@
#include "node_watchdog.h"
+#include "node_internals.h"
#include "util.h"
#include "util-inl.h"
+#include <algorithm>
namespace node {
Watchdog::Watchdog(v8::Isolate* isolate, uint64_t ms) : isolate_(isolate),
+ timed_out_(false),
destroyed_(false) {
int rc;
loop_ = new uv_loop_t;
@@ -79,9 +82,231 @@ void Watchdog::Async(uv_async_t* async) {
void Watchdog::Timer(uv_timer_t* timer) {
Watchdog* w = ContainerOf(&Watchdog::timer_, timer);
+ w->timed_out_ = true;
uv_stop(w->loop_);
w->isolate()->TerminateExecution();
}
+SigintWatchdog::~SigintWatchdog() {
+ Destroy();
+}
+
+
+void SigintWatchdog::Dispose() {
+ Destroy();
+}
+
+
+SigintWatchdog::SigintWatchdog(v8::Isolate* isolate)
+ : isolate_(isolate), destroyed_(false) {
+ // Register this watchdog with the global SIGINT/Ctrl+C listener.
+ SigintWatchdogHelper::GetInstance()->Register(this);
+ // Start the helper thread, if that has not already happened.
+ SigintWatchdogHelper::GetInstance()->Start();
+}
+
+
+void SigintWatchdog::Destroy() {
+ if (destroyed_) {
+ return;
+ }
+
+ destroyed_ = true;
+
+ SigintWatchdogHelper::GetInstance()->Unregister(this);
+ SigintWatchdogHelper::GetInstance()->Stop();
+}
+
+
+void SigintWatchdog::HandleSigint() {
+ isolate_->TerminateExecution();
+}
+
+#ifdef __POSIX__
+void* SigintWatchdogHelper::RunSigintWatchdog(void* arg) {
+ // Inside the helper thread.
+ bool is_stopping;
+
+ do {
+ uv_sem_wait(&instance.sem_);
+ is_stopping = InformWatchdogsAboutSignal();
+ } while (!is_stopping);
+
+ return nullptr;
+}
+
+
+void SigintWatchdogHelper::HandleSignal(int signum) {
+ uv_sem_post(&instance.sem_);
+}
+
+#else
+
+// Windows starts a separate thread for executing the handler, so no extra
+// helper thread is required.
+BOOL WINAPI SigintWatchdogHelper::WinCtrlCHandlerRoutine(DWORD dwCtrlType) {
+ if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
+ InformWatchdogsAboutSignal();
+
+ // Return true because the signal has been handled.
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+#endif
+
+
+bool SigintWatchdogHelper::InformWatchdogsAboutSignal() {
+ uv_mutex_lock(&instance.list_mutex_);
+
+ bool is_stopping = false;
+#ifdef __POSIX__
+ is_stopping = instance.stopping_;
+#endif
+
+ // If there are no listeners and the helper thread has been awoken by a signal
+ // (= not when stopping it), indicate that by setting has_pending_signal_.
+ if (instance.watchdogs_.empty() && !is_stopping) {
+ instance.has_pending_signal_ = true;
+ }
+
+ for (auto it : instance.watchdogs_)
+ it->HandleSigint();
+
+ uv_mutex_unlock(&instance.list_mutex_);
+ return is_stopping;
+}
+
+
+int SigintWatchdogHelper::Start() {
+ int ret = 0;
+ uv_mutex_lock(&mutex_);
+
+ if (start_stop_count_++ > 0) {
+ goto dont_start;
+ }
+
+#ifdef __POSIX__
+ CHECK_EQ(has_running_thread_, false);
+ has_pending_signal_ = false;
+ stopping_ = false;
+
+ sigset_t sigmask;
+ sigfillset(&sigmask);
+ CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &sigmask));
+ ret = pthread_create(&thread_, nullptr, RunSigintWatchdog, nullptr);
+ CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
+ if (ret != 0) {
+ goto dont_start;
+ }
+ has_running_thread_ = true;
+
+ RegisterSignalHandler(SIGINT, HandleSignal);
+#else
+ SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, TRUE);
+#endif
+
+ dont_start:
+ uv_mutex_unlock(&mutex_);
+ return ret;
+}
+
+
+bool SigintWatchdogHelper::Stop() {
+ uv_mutex_lock(&mutex_);
+ uv_mutex_lock(&list_mutex_);
+
+ bool had_pending_signal = has_pending_signal_;
+
+ if (--start_stop_count_ > 0) {
+ uv_mutex_unlock(&list_mutex_);
+ goto dont_stop;
+ }
+
+#ifdef __POSIX__
+ // Set stopping now because it's only protected by list_mutex_.
+ stopping_ = true;
+#endif
+
+ watchdogs_.clear();
+ uv_mutex_unlock(&list_mutex_);
+
+#ifdef __POSIX__
+ if (!has_running_thread_) {
+ goto dont_stop;
+ }
+
+ // Wake up the helper thread.
+ uv_sem_post(&sem_);
+
+ // Wait for the helper thread to finish.
+ CHECK_EQ(0, pthread_join(thread_, nullptr));
+ has_running_thread_ = false;
+
+ RegisterSignalHandler(SIGINT, SignalExit, true);
+#else
+ SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, FALSE);
+#endif
+
+ had_pending_signal = has_pending_signal_;
+ dont_stop:
+ uv_mutex_unlock(&mutex_);
+
+ has_pending_signal_ = false;
+ return had_pending_signal;
+}
+
+
+void SigintWatchdogHelper::Register(SigintWatchdog* wd) {
+ uv_mutex_lock(&list_mutex_);
+
+ watchdogs_.push_back(wd);
+
+ uv_mutex_unlock(&list_mutex_);
+}
+
+
+void SigintWatchdogHelper::Unregister(SigintWatchdog* wd) {
+ uv_mutex_lock(&list_mutex_);
+
+ auto it = std::find(watchdogs_.begin(), watchdogs_.end(), wd);
+
+ CHECK_NE(it, watchdogs_.end());
+ watchdogs_.erase(it);
+
+ uv_mutex_unlock(&list_mutex_);
+}
+
+
+SigintWatchdogHelper::SigintWatchdogHelper()
+ : start_stop_count_(0),
+ has_pending_signal_(false) {
+#ifdef __POSIX__
+ has_running_thread_ = false;
+ stopping_ = false;
+ CHECK_EQ(0, uv_sem_init(&sem_, 0));
+#endif
+
+ CHECK_EQ(0, uv_mutex_init(&mutex_));
+ CHECK_EQ(0, uv_mutex_init(&list_mutex_));
+};
+
+
+SigintWatchdogHelper::~SigintWatchdogHelper() {
+ start_stop_count_ = 0;
+ Stop();
+
+#ifdef __POSIX__
+ CHECK_EQ(has_running_thread_, false);
+ uv_sem_destroy(&sem_);
+#endif
+
+ uv_mutex_destroy(&mutex_);
+ uv_mutex_destroy(&list_mutex_);
+}
+
+SigintWatchdogHelper SigintWatchdogHelper::instance;
+
} // namespace node