summaryrefslogtreecommitdiff
path: root/src/node_watchdog.cc
diff options
context:
space:
mode:
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