GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/src/node_watchdog.cc Lines: 142 150 94.7 %
Date: 2016-07-18 Branches: 35 54 64.8 %

Line Exec Source
1
#include "node_watchdog.h"
2
#include "node_internals.h"
3
#include "util.h"
4
#include "util-inl.h"
5
#include <algorithm>
6
7
namespace node {
8
9
9
Watchdog::Watchdog(v8::Isolate* isolate, uint64_t ms) : isolate_(isolate),
10
                                                        timed_out_(false),
11
9
                                                        destroyed_(false) {
12
  int rc;
13
9
  loop_ = new uv_loop_t;
14
9
  CHECK(loop_);
15
9
  rc = uv_loop_init(loop_);
16
9
  CHECK_EQ(0, rc);
17
18
9
  rc = uv_async_init(loop_, &async_, &Watchdog::Async);
19
9
  CHECK_EQ(0, rc);
20
21
9
  rc = uv_timer_init(loop_, &timer_);
22
9
  CHECK_EQ(0, rc);
23
24
9
  rc = uv_timer_start(&timer_, &Watchdog::Timer, ms, 0);
25
9
  CHECK_EQ(0, rc);
26
27
9
  rc = uv_thread_create(&thread_, &Watchdog::Run, this);
28
9
  CHECK_EQ(0, rc);
29
9
}
30
31
32
18
Watchdog::~Watchdog() {
33
9
  Destroy();
34
9
}
35
36
37
void Watchdog::Dispose() {
38
  Destroy();
39
}
40
41
42
9
void Watchdog::Destroy() {
43
9
  if (destroyed_) {
44
    return;
45
  }
46
47
9
  uv_async_send(&async_);
48
9
  uv_thread_join(&thread_);
49
50
9
  uv_close(reinterpret_cast<uv_handle_t*>(&async_), nullptr);
51
52
  // UV_RUN_DEFAULT so that libuv has a chance to clean up.
53
9
  uv_run(loop_, UV_RUN_DEFAULT);
54
55
9
  int rc = uv_loop_close(loop_);
56
9
  CHECK_EQ(0, rc);
57
9
  delete loop_;
58
9
  loop_ = nullptr;
59
60
9
  destroyed_ = true;
61
}
62
63
64
9
void Watchdog::Run(void* arg) {
65
9
  Watchdog* wd = static_cast<Watchdog*>(arg);
66
67
  // UV_RUN_DEFAULT the loop will be stopped either by the async or the
68
  // timer handle.
69
9
  uv_run(wd->loop_, UV_RUN_DEFAULT);
70
71
  // Loop ref count reaches zero when both handles are closed.
72
  // Close the timer handle on this side and let Destroy() close async_
73
9
  uv_close(reinterpret_cast<uv_handle_t*>(&wd->timer_), nullptr);
74
9
}
75
76
77
5
void Watchdog::Async(uv_async_t* async) {
78
5
  Watchdog* w = ContainerOf(&Watchdog::async_, async);
79
5
  uv_stop(w->loop_);
80
5
}
81
82
83
4
void Watchdog::Timer(uv_timer_t* timer) {
84
4
  Watchdog* w = ContainerOf(&Watchdog::timer_, timer);
85
4
  w->timed_out_ = true;
86
4
  uv_stop(w->loop_);
87
4
  w->isolate()->TerminateExecution();
88
4
}
89
90
91
32
SigintWatchdog::~SigintWatchdog() {
92
16
  Destroy();
93
16
}
94
95
96
void SigintWatchdog::Dispose() {
97
  Destroy();
98
}
99
100
101
18
SigintWatchdog::SigintWatchdog(v8::Isolate* isolate)
102
18
    : isolate_(isolate), received_signal_(false), destroyed_(false) {
103
  // Register this watchdog with the global SIGINT/Ctrl+C listener.
104
18
  SigintWatchdogHelper::GetInstance()->Register(this);
105
  // Start the helper thread, if that has not already happened.
106
18
  SigintWatchdogHelper::GetInstance()->Start();
107
18
}
108
109
110
16
void SigintWatchdog::Destroy() {
111
16
  if (destroyed_) {
112
    return;
113
  }
114
115
16
  destroyed_ = true;
116
117
16
  SigintWatchdogHelper::GetInstance()->Unregister(this);
118
16
  SigintWatchdogHelper::GetInstance()->Stop();
119
}
120
121
122
void SigintWatchdog::HandleSigint() {
123
5
  received_signal_ = true;
124
5
  isolate_->TerminateExecution();
125
}
126
127
#ifdef __POSIX__
128
21
void* SigintWatchdogHelper::RunSigintWatchdog(void* arg) {
129
  // Inside the helper thread.
130
  bool is_stopping;
131
132
  do {
133
28
    uv_sem_wait(&instance.sem_);
134
28
    is_stopping = InformWatchdogsAboutSignal();
135
28
  } while (!is_stopping);
136
137
21
  return nullptr;
138
}
139
140
141
7
void SigintWatchdogHelper::HandleSignal(int signum) {
142
7
  uv_sem_post(&instance.sem_);
143
7
}
144
145
#else
146
147
// Windows starts a separate thread for executing the handler, so no extra
148
// helper thread is required.
149
BOOL WINAPI SigintWatchdogHelper::WinCtrlCHandlerRoutine(DWORD dwCtrlType) {
150
  if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
151
    InformWatchdogsAboutSignal();
152
153
    // Return true because the signal has been handled.
154
    return TRUE;
155
  } else {
156
    return FALSE;
157
  }
158
}
159
#endif
160
161
162
28
bool SigintWatchdogHelper::InformWatchdogsAboutSignal() {
163
28
  uv_mutex_lock(&instance.list_mutex_);
164
165
28
  bool is_stopping = false;
166
#ifdef __POSIX__
167
28
  is_stopping = instance.stopping_;
168
#endif
169
170
  // If there are no listeners and the helper thread has been awoken by a signal
171
  // (= not when stopping it), indicate that by setting has_pending_signal_.
172
28
  if (instance.watchdogs_.empty() && !is_stopping) {
173
3
    instance.has_pending_signal_ = true;
174
  }
175
176
33
  for (auto it : instance.watchdogs_)
177
5
    it->HandleSigint();
178
179
28
  uv_mutex_unlock(&instance.list_mutex_);
180
28
  return is_stopping;
181
}
182
183
184
39
int SigintWatchdogHelper::Start() {
185
39
  int ret = 0;
186
39
  uv_mutex_lock(&mutex_);
187
188
39
  if (start_stop_count_++ > 0) {
189
    goto dont_start;
190
  }
191
192
#ifdef __POSIX__
193
21
  CHECK_EQ(has_running_thread_, false);
194
21
  has_pending_signal_ = false;
195
21
  stopping_ = false;
196
197
  sigset_t sigmask;
198
21
  sigfillset(&sigmask);
199
21
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &sigmask));
200
21
  ret = pthread_create(&thread_, nullptr, RunSigintWatchdog, nullptr);
201
21
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
202
21
  if (ret != 0) {
203
    goto dont_start;
204
  }
205
21
  has_running_thread_ = true;
206
207
21
  RegisterSignalHandler(SIGINT, HandleSignal);
208
#else
209
  SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, TRUE);
210
#endif
211
212
 dont_start:
213
39
  uv_mutex_unlock(&mutex_);
214
39
  return ret;
215
}
216
217
218
1600
bool SigintWatchdogHelper::Stop() {
219
1600
  uv_mutex_lock(&mutex_);
220
1600
  uv_mutex_lock(&list_mutex_);
221
222
1600
  bool had_pending_signal = has_pending_signal_;
223
224
1600
  if (--start_stop_count_ > 0) {
225
16
    uv_mutex_unlock(&list_mutex_);
226
16
    goto dont_stop;
227
  }
228
229
#ifdef __POSIX__
230
  // Set stopping now because it's only protected by list_mutex_.
231
1584
  stopping_ = true;
232
#endif
233
234
3168
  watchdogs_.clear();
235
1584
  uv_mutex_unlock(&list_mutex_);
236
237
#ifdef __POSIX__
238
1584
  if (!has_running_thread_) {
239
    goto dont_stop;
240
  }
241
242
  // Wake up the helper thread.
243
21
  uv_sem_post(&sem_);
244
245
  // Wait for the helper thread to finish.
246
21
  CHECK_EQ(0, pthread_join(thread_, nullptr));
247
21
  has_running_thread_ = false;
248
249
21
  RegisterSignalHandler(SIGINT, SignalExit, true);
250
#else
251
  SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, FALSE);
252
#endif
253
254
21
  had_pending_signal = has_pending_signal_;
255
 dont_stop:
256
1600
  uv_mutex_unlock(&mutex_);
257
258
1600
  has_pending_signal_ = false;
259
1600
  return had_pending_signal;
260
}
261
262
263
18
void SigintWatchdogHelper::Register(SigintWatchdog* wd) {
264
18
  uv_mutex_lock(&list_mutex_);
265
266
18
  watchdogs_.push_back(wd);
267
268
18
  uv_mutex_unlock(&list_mutex_);
269
18
}
270
271
272
16
void SigintWatchdogHelper::Unregister(SigintWatchdog* wd) {
273
16
  uv_mutex_lock(&list_mutex_);
274
275
48
  auto it = std::find(watchdogs_.begin(), watchdogs_.end(), wd);
276
277
48
  CHECK_NE(it, watchdogs_.end());
278
32
  watchdogs_.erase(it);
279
280
16
  uv_mutex_unlock(&list_mutex_);
281
16
}
282
283
284
1565
SigintWatchdogHelper::SigintWatchdogHelper()
285
    : start_stop_count_(0),
286
3130
      has_pending_signal_(false) {
287
#ifdef __POSIX__
288
1565
  has_running_thread_ = false;
289
1565
  stopping_ = false;
290
1565
  CHECK_EQ(0, uv_sem_init(&sem_, 0));
291
#endif
292
293
1565
  CHECK_EQ(0, uv_mutex_init(&mutex_));
294
1565
  CHECK_EQ(0, uv_mutex_init(&list_mutex_));
295
1565
}
296
297
298
4695
SigintWatchdogHelper::~SigintWatchdogHelper() {
299
1565
  start_stop_count_ = 0;
300
1565
  Stop();
301
302
#ifdef __POSIX__
303
1565
  CHECK_EQ(has_running_thread_, false);
304
1565
  uv_sem_destroy(&sem_);
305
#endif
306
307
1565
  uv_mutex_destroy(&mutex_);
308
1565
  uv_mutex_destroy(&list_mutex_);
309
1565
}
310
311
1565
SigintWatchdogHelper SigintWatchdogHelper::instance;
312
313
3130
}  // namespace node