Node.js  v8.x
Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine
node_watchdog.cc
Go to the documentation of this file.
1 // Copyright Joyent, Inc. and other Node contributors.
2 //
3 // Permission is hereby granted, free of charge, to any person obtaining a
4 // copy of this software and associated documentation files (the
5 // "Software"), to deal in the Software without restriction, including
6 // without limitation the rights to use, copy, modify, merge, publish,
7 // distribute, sublicense, and/or sell copies of the Software, and to permit
8 // persons to whom the Software is furnished to do so, subject to the
9 // following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included
12 // in all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
17 // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
18 // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
19 // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
20 // USE OR OTHER DEALINGS IN THE SOFTWARE.
21 
22 #include "node_watchdog.h"
23 #include "node_internals.h"
24 #include "util.h"
25 #include "util-inl.h"
26 #include <algorithm>
27 
28 namespace node {
29 
30 Watchdog::Watchdog(v8::Isolate* isolate, uint64_t ms, bool* timed_out)
31  : isolate_(isolate), timed_out_(timed_out) {
32 
33  int rc;
34  loop_ = new uv_loop_t;
35  CHECK(loop_);
36  rc = uv_loop_init(loop_);
37  if (rc != 0) {
38  FatalError("node::Watchdog::Watchdog()",
39  "Failed to initialize uv loop.");
40  }
41 
42  rc = uv_async_init(loop_, &async_, &Watchdog::Async);
43  CHECK_EQ(0, rc);
44 
45  rc = uv_timer_init(loop_, &timer_);
46  CHECK_EQ(0, rc);
47 
48  rc = uv_timer_start(&timer_, &Watchdog::Timer, ms, 0);
49  CHECK_EQ(0, rc);
50 
51  rc = uv_thread_create(&thread_, &Watchdog::Run, this);
52  CHECK_EQ(0, rc);
53 }
54 
55 
56 Watchdog::~Watchdog() {
57  uv_async_send(&async_);
58  uv_thread_join(&thread_);
59 
60  uv_close(reinterpret_cast<uv_handle_t*>(&async_), nullptr);
61 
62  // UV_RUN_DEFAULT so that libuv has a chance to clean up.
63  uv_run(loop_, UV_RUN_DEFAULT);
64 
65  int rc = uv_loop_close(loop_);
66  CHECK_EQ(0, rc);
67  delete loop_;
68  loop_ = nullptr;
69 }
70 
71 
72 void Watchdog::Run(void* arg) {
73  Watchdog* wd = static_cast<Watchdog*>(arg);
74 
75  // UV_RUN_DEFAULT the loop will be stopped either by the async or the
76  // timer handle.
77  uv_run(wd->loop_, UV_RUN_DEFAULT);
78 
79  // Loop ref count reaches zero when both handles are closed.
80  // Close the timer handle on this side and let ~Watchdog() close async_
81  uv_close(reinterpret_cast<uv_handle_t*>(&wd->timer_), nullptr);
82 }
83 
84 
85 void Watchdog::Async(uv_async_t* async) {
86  Watchdog* w = ContainerOf(&Watchdog::async_, async);
87  uv_stop(w->loop_);
88 }
89 
90 
91 void Watchdog::Timer(uv_timer_t* timer) {
92  Watchdog* w = ContainerOf(&Watchdog::timer_, timer);
93  *w->timed_out_ = true;
94  w->isolate()->TerminateExecution();
95  uv_stop(w->loop_);
96 }
97 
98 
99 SigintWatchdog::SigintWatchdog(
100  v8::Isolate* isolate, bool* received_signal)
101  : isolate_(isolate), received_signal_(received_signal) {
102  // Register this watchdog with the global SIGINT/Ctrl+C listener.
103  SigintWatchdogHelper::GetInstance()->Register(this);
104  // Start the helper thread, if that has not already happened.
105  SigintWatchdogHelper::GetInstance()->Start();
106 }
107 
108 
109 SigintWatchdog::~SigintWatchdog() {
110  SigintWatchdogHelper::GetInstance()->Unregister(this);
111  SigintWatchdogHelper::GetInstance()->Stop();
112 }
113 
114 
115 void SigintWatchdog::HandleSigint() {
116  *received_signal_ = true;
117  isolate_->TerminateExecution();
118 }
119 
120 #ifdef __POSIX__
121 void* SigintWatchdogHelper::RunSigintWatchdog(void* arg) {
122  // Inside the helper thread.
123  bool is_stopping;
124 
125  do {
126  uv_sem_wait(&instance.sem_);
127  is_stopping = InformWatchdogsAboutSignal();
128  } while (!is_stopping);
129 
130  return nullptr;
131 }
132 
133 
134 void SigintWatchdogHelper::HandleSignal(int signum) {
135  uv_sem_post(&instance.sem_);
136 }
137 
138 #else
139 
140 // Windows starts a separate thread for executing the handler, so no extra
141 // helper thread is required.
142 BOOL WINAPI SigintWatchdogHelper::WinCtrlCHandlerRoutine(DWORD dwCtrlType) {
143  if (!instance.watchdog_disabled_ &&
144  (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT)) {
145  InformWatchdogsAboutSignal();
146 
147  // Return true because the signal has been handled.
148  return TRUE;
149  } else {
150  return FALSE;
151  }
152 }
153 #endif
154 
155 
156 bool SigintWatchdogHelper::InformWatchdogsAboutSignal() {
157  Mutex::ScopedLock list_lock(instance.list_mutex_);
158 
159  bool is_stopping = false;
160 #ifdef __POSIX__
161  is_stopping = instance.stopping_;
162 #endif
163 
164  // If there are no listeners and the helper thread has been awoken by a signal
165  // (= not when stopping it), indicate that by setting has_pending_signal_.
166  if (instance.watchdogs_.empty() && !is_stopping) {
167  instance.has_pending_signal_ = true;
168  }
169 
170  for (auto it : instance.watchdogs_)
171  it->HandleSigint();
172 
173  return is_stopping;
174 }
175 
176 
178  Mutex::ScopedLock lock(mutex_);
179 
180  if (start_stop_count_++ > 0) {
181  return 0;
182  }
183 
184 #ifdef __POSIX__
185  CHECK_EQ(has_running_thread_, false);
186  has_pending_signal_ = false;
187  stopping_ = false;
188 
189  sigset_t sigmask;
190  sigfillset(&sigmask);
191  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &sigmask));
192  int ret = pthread_create(&thread_, nullptr, RunSigintWatchdog, nullptr);
193  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
194  if (ret != 0) {
195  return ret;
196  }
197  has_running_thread_ = true;
198 
199  RegisterSignalHandler(SIGINT, HandleSignal);
200 #else
201  if (watchdog_disabled_) {
202  watchdog_disabled_ = false;
203  } else {
204  SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, TRUE);
205  }
206 #endif
207 
208  return 0;
209 }
210 
211 
212 bool SigintWatchdogHelper::Stop() {
213  bool had_pending_signal;
214  Mutex::ScopedLock lock(mutex_);
215 
216  {
217  Mutex::ScopedLock list_lock(list_mutex_);
218 
219  had_pending_signal = has_pending_signal_;
220 
221  if (--start_stop_count_ > 0) {
222  has_pending_signal_ = false;
223  return had_pending_signal;
224  }
225 
226 #ifdef __POSIX__
227  // Set stopping now because it's only protected by list_mutex_.
228  stopping_ = true;
229 #endif
230 
231  watchdogs_.clear();
232  }
233 
234 #ifdef __POSIX__
235  if (!has_running_thread_) {
236  has_pending_signal_ = false;
237  return had_pending_signal;
238  }
239 
240  // Wake up the helper thread.
241  uv_sem_post(&sem_);
242 
243  // Wait for the helper thread to finish.
244  CHECK_EQ(0, pthread_join(thread_, nullptr));
245  has_running_thread_ = false;
246 
247  RegisterSignalHandler(SIGINT, SignalExit, true);
248 #else
249  watchdog_disabled_ = true;
250 #endif
251 
252  had_pending_signal = has_pending_signal_;
253  has_pending_signal_ = false;
254 
255  return had_pending_signal;
256 }
257 
258 
259 bool SigintWatchdogHelper::HasPendingSignal() {
260  Mutex::ScopedLock lock(list_mutex_);
261 
262  return has_pending_signal_;
263 }
264 
265 
266 void SigintWatchdogHelper::Register(SigintWatchdog* wd) {
267  Mutex::ScopedLock lock(list_mutex_);
268 
269  watchdogs_.push_back(wd);
270 }
271 
272 
273 void SigintWatchdogHelper::Unregister(SigintWatchdog* wd) {
274  Mutex::ScopedLock lock(list_mutex_);
275 
276  auto it = std::find(watchdogs_.begin(), watchdogs_.end(), wd);
277 
278  CHECK_NE(it, watchdogs_.end());
279  watchdogs_.erase(it);
280 }
281 
282 
283 SigintWatchdogHelper::SigintWatchdogHelper()
284  : start_stop_count_(0),
285  has_pending_signal_(false) {
286 #ifdef __POSIX__
287  has_running_thread_ = false;
288  stopping_ = false;
289  CHECK_EQ(0, uv_sem_init(&sem_, 0));
290 #else
291  watchdog_disabled_ = false;
292 #endif
293 }
294 
295 
296 SigintWatchdogHelper::~SigintWatchdogHelper() {
297  start_stop_count_ = 0;
298  Stop();
299 
300 #ifdef __POSIX__
301  CHECK_EQ(has_running_thread_, false);
302  uv_sem_destroy(&sem_);
303 #endif
304 }
305 
306 SigintWatchdogHelper SigintWatchdogHelper::instance;
307 
308 } // namespace node
NO_RETURN void FatalError(const char *location, const char *message)
Definition: node.cc:2616
void SignalExit(int signo)
Definition: node.cc:3544
int Start(Isolate *isolate, IsolateData *isolate_data, int argc, const char *const *argv, int exec_argc, const char *const *exec_argv)
Definition: node.cc:4536