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-26 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
38
SigintWatchdog::~SigintWatchdog() {
92
19
  Destroy();
93
19
}
94
95
96
void SigintWatchdog::Dispose() {
97
  Destroy();
98
}
99
100
101
21
SigintWatchdog::SigintWatchdog(v8::Isolate* isolate)
102
21
    : isolate_(isolate), received_signal_(false), destroyed_(false) {
103
  // Register this watchdog with the global SIGINT/Ctrl+C listener.
104
21
  SigintWatchdogHelper::GetInstance()->Register(this);
105
  // Start the helper thread, if that has not already happened.
106
21
  SigintWatchdogHelper::GetInstance()->Start();
107
21
}
108
109
110
19
void SigintWatchdog::Destroy() {
111
19
  if (destroyed_) {
112
    return;
113
  }
114
115
19
  destroyed_ = true;
116
117
19
  SigintWatchdogHelper::GetInstance()->Unregister(this);
118
19
  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
24
void* SigintWatchdogHelper::RunSigintWatchdog(void* arg) {
129
  // Inside the helper thread.
130
  bool is_stopping;
131
132
  do {
133
31
    uv_sem_wait(&instance.sem_);
134
31
    is_stopping = InformWatchdogsAboutSignal();
135
31
  } while (!is_stopping);
136
137
24
  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
31
bool SigintWatchdogHelper::InformWatchdogsAboutSignal() {
163
31
  uv_mutex_lock(&instance.list_mutex_);
164
165
31
  bool is_stopping = false;
166
#ifdef __POSIX__
167
31
  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
31
  if (instance.watchdogs_.empty() && !is_stopping) {
173
3
    instance.has_pending_signal_ = true;
174
  }
175
176
36
  for (auto it : instance.watchdogs_)
177
5
    it->HandleSigint();
178
179
31
  uv_mutex_unlock(&instance.list_mutex_);
180
31
  return is_stopping;
181
}
182
183
184
45
int SigintWatchdogHelper::Start() {
185
45
  int ret = 0;
186
45
  uv_mutex_lock(&mutex_);
187
188
45
  if (start_stop_count_++ > 0) {
189
    goto dont_start;
190
  }
191
192
#ifdef __POSIX__
193
24
  CHECK_EQ(has_running_thread_, false);
194
24
  has_pending_signal_ = false;
195
24
  stopping_ = false;
196
197
  sigset_t sigmask;
198
24
  sigfillset(&sigmask);
199
24
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &sigmask));
200
24
  ret = pthread_create(&thread_, nullptr, RunSigintWatchdog, nullptr);
201
24
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
202
24
  if (ret != 0) {
203
    goto dont_start;
204
  }
205
24
  has_running_thread_ = true;
206
207
24
  RegisterSignalHandler(SIGINT, HandleSignal);
208
#else
209
  SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, TRUE);
210
#endif
211
212
 dont_start:
213
45
  uv_mutex_unlock(&mutex_);
214
45
  return ret;
215
}
216
217
218
1607
bool SigintWatchdogHelper::Stop() {
219
1607
  uv_mutex_lock(&mutex_);
220
1607
  uv_mutex_lock(&list_mutex_);
221
222
1607
  bool had_pending_signal = has_pending_signal_;
223
224
1607
  if (--start_stop_count_ > 0) {
225
19
    uv_mutex_unlock(&list_mutex_);
226
19
    goto dont_stop;
227
  }
228
229
#ifdef __POSIX__
230
  // Set stopping now because it's only protected by list_mutex_.
231
1588
  stopping_ = true;
232
#endif
233
234
3176
  watchdogs_.clear();
235
1588
  uv_mutex_unlock(&list_mutex_);
236
237
#ifdef __POSIX__
238
1588
  if (!has_running_thread_) {
239
    goto dont_stop;
240
  }
241
242
  // Wake up the helper thread.
243
24
  uv_sem_post(&sem_);
244
245
  // Wait for the helper thread to finish.
246
24
  CHECK_EQ(0, pthread_join(thread_, nullptr));
247
24
  has_running_thread_ = false;
248
249
24
  RegisterSignalHandler(SIGINT, SignalExit, true);
250
#else
251
  SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, FALSE);
252
#endif
253
254
24
  had_pending_signal = has_pending_signal_;
255
 dont_stop:
256
1607
  uv_mutex_unlock(&mutex_);
257
258
1607
  has_pending_signal_ = false;
259
1607
  return had_pending_signal;
260
}
261
262
263
21
void SigintWatchdogHelper::Register(SigintWatchdog* wd) {
264
21
  uv_mutex_lock(&list_mutex_);
265
266
21
  watchdogs_.push_back(wd);
267
268
21
  uv_mutex_unlock(&list_mutex_);
269
21
}
270
271
272
19
void SigintWatchdogHelper::Unregister(SigintWatchdog* wd) {
273
19
  uv_mutex_lock(&list_mutex_);
274
275
57
  auto it = std::find(watchdogs_.begin(), watchdogs_.end(), wd);
276
277
57
  CHECK_NE(it, watchdogs_.end());
278
38
  watchdogs_.erase(it);
279
280
19
  uv_mutex_unlock(&list_mutex_);
281
19
}
282
283
284
1566
SigintWatchdogHelper::SigintWatchdogHelper()
285
    : start_stop_count_(0),
286
3132
      has_pending_signal_(false) {
287
#ifdef __POSIX__
288
1566
  has_running_thread_ = false;
289
1566
  stopping_ = false;
290
1566
  CHECK_EQ(0, uv_sem_init(&sem_, 0));
291
#endif
292
293
1566
  CHECK_EQ(0, uv_mutex_init(&mutex_));
294
1566
  CHECK_EQ(0, uv_mutex_init(&list_mutex_));
295
1566
}
296
297
298
4698
SigintWatchdogHelper::~SigintWatchdogHelper() {
299
1566
  start_stop_count_ = 0;
300
1566
  Stop();
301
302
#ifdef __POSIX__
303
1566
  CHECK_EQ(has_running_thread_, false);
304
1566
  uv_sem_destroy(&sem_);
305
#endif
306
307
1566
  uv_mutex_destroy(&mutex_);
308
1566
  uv_mutex_destroy(&list_mutex_);
309
1566
}
310
311
1566
SigintWatchdogHelper SigintWatchdogHelper::instance;
312
313
3132
}  // namespace node