GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/out/../src/node_watchdog.cc Lines: 136 141 96.5 %
Date: 2016-09-10 Branches: 33 50 66.0 %

Line Branch 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
8
void Watchdog::Run(void* arg) {
65
8
  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
8
  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
56
SigintWatchdog::~SigintWatchdog() {
92
28
  Destroy();
93
28
}
94
95
96
void SigintWatchdog::Dispose() {
97
  Destroy();
98
}
99
100
101
30
SigintWatchdog::SigintWatchdog(v8::Isolate* isolate)
102
30
    : isolate_(isolate), received_signal_(false), destroyed_(false) {
103
  // Register this watchdog with the global SIGINT/Ctrl+C listener.
104
30
  SigintWatchdogHelper::GetInstance()->Register(this);
105
  // Start the helper thread, if that has not already happened.
106
30
  SigintWatchdogHelper::GetInstance()->Start();
107
30
}
108
109
110
28
void SigintWatchdog::Destroy() {
111
28
  if (destroyed_) {
112
    return;
113
  }
114
115
28
  destroyed_ = true;
116
117
28
  SigintWatchdogHelper::GetInstance()->Unregister(this);
118
28
  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
33
void* SigintWatchdogHelper::RunSigintWatchdog(void* arg) {
129
  // Inside the helper thread.
130
  bool is_stopping;
131
132
  do {
133
40
    uv_sem_wait(&instance.sem_);
134
40
    is_stopping = InformWatchdogsAboutSignal();
135
40
  } while (!is_stopping);
136
137
33
  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
40
bool SigintWatchdogHelper::InformWatchdogsAboutSignal() {
163
80
  Mutex::ScopedLock list_lock(instance.list_mutex_);
164
165
40
  bool is_stopping = false;
166
#ifdef __POSIX__
167
40
  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

40
  if (instance.watchdogs_.empty() && !is_stopping) {
173
3
    instance.has_pending_signal_ = true;
174
  }
175
176
45
  for (auto it : instance.watchdogs_)
177
5
    it->HandleSigint();
178
179
80
  return is_stopping;
180
}
181
182
183
63
int SigintWatchdogHelper::Start() {
184
189
  Mutex::ScopedLock lock(mutex_);
185
186
63
  if (start_stop_count_++ > 0) {
187
    return 0;
188
  }
189
190
#ifdef __POSIX__
191
33
  CHECK_EQ(has_running_thread_, false);
192
33
  has_pending_signal_ = false;
193
33
  stopping_ = false;
194
195
  sigset_t sigmask;
196
33
  sigfillset(&sigmask);
197
33
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &sigmask));
198
33
  int ret = pthread_create(&thread_, nullptr, RunSigintWatchdog, nullptr);
199
33
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
200
33
  if (ret != 0) {
201
    return ret;
202
  }
203
33
  has_running_thread_ = true;
204
205
33
  RegisterSignalHandler(SIGINT, HandleSignal);
206
#else
207
  SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, TRUE);
208
#endif
209
210
33
  return 0;
211
}
212
213
214
1695
bool SigintWatchdogHelper::Stop() {
215
  bool had_pending_signal;
216
5085
  Mutex::ScopedLock lock(mutex_);
217
218
  {
219
5057
    Mutex::ScopedLock list_lock(list_mutex_);
220
221
1695
    had_pending_signal = has_pending_signal_;
222
223
1695
    if (--start_stop_count_ > 0) {
224
28
      has_pending_signal_ = false;
225
56
      return had_pending_signal;
226
    }
227
228
#ifdef __POSIX__
229
    // Set stopping now because it's only protected by list_mutex_.
230
1667
    stopping_ = true;
231
#endif
232
233
3334
    watchdogs_.clear();
234
  }
235
236
#ifdef __POSIX__
237
1667
  if (!has_running_thread_) {
238
1634
    has_pending_signal_ = false;
239
1634
    return had_pending_signal;
240
  }
241
242
  // Wake up the helper thread.
243
33
  uv_sem_post(&sem_);
244
245
  // Wait for the helper thread to finish.
246
33
  CHECK_EQ(0, pthread_join(thread_, nullptr));
247
33
  has_running_thread_ = false;
248
249
33
  RegisterSignalHandler(SIGINT, SignalExit, true);
250
#else
251
  SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, FALSE);
252
#endif
253
254
33
  had_pending_signal = has_pending_signal_;
255
33
  has_pending_signal_ = false;
256
257
33
  return had_pending_signal;
258
}
259
260
261
3
bool SigintWatchdogHelper::HasPendingSignal() {
262
9
  Mutex::ScopedLock lock(list_mutex_);
263
264
6
  return has_pending_signal_;
265
}
266
267
268
30
void SigintWatchdogHelper::Register(SigintWatchdog* wd) {
269
90
  Mutex::ScopedLock lock(list_mutex_);
270
271
30
  watchdogs_.push_back(wd);
272
30
}
273
274
275
28
void SigintWatchdogHelper::Unregister(SigintWatchdog* wd) {
276
84
  Mutex::ScopedLock lock(list_mutex_);
277
278
84
  auto it = std::find(watchdogs_.begin(), watchdogs_.end(), wd);
279
280
84
  CHECK_NE(it, watchdogs_.end());
281
56
  watchdogs_.erase(it);
282
28
}
283
284
285
1636
SigintWatchdogHelper::SigintWatchdogHelper()
286
    : start_stop_count_(0),
287
3272
      has_pending_signal_(false) {
288
#ifdef __POSIX__
289
1636
  has_running_thread_ = false;
290
1636
  stopping_ = false;
291
1636
  CHECK_EQ(0, uv_sem_init(&sem_, 0));
292
#endif
293
1636
}
294
295
296
8180
SigintWatchdogHelper::~SigintWatchdogHelper() {
297
1636
  start_stop_count_ = 0;
298
1636
  Stop();
299
300
#ifdef __POSIX__
301
1636
  CHECK_EQ(has_running_thread_, false);
302
1636
  uv_sem_destroy(&sem_);
303
#endif
304
1636
}
305
306
1636
SigintWatchdogHelper SigintWatchdogHelper::instance;
307
308
3272
}  // namespace node