GCC Code Coverage Report
Directory: ../src/ Exec Total Coverage
File: /home/node-core-coverage/node-core-coverage/workdir/node/out/../src/node_watchdog.cc Lines: 136 142 95.8 %
Date: 2016-11-30 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
  if (rc != 0) {
17
    FatalError("node::Watchdog::Watchdog()",
18
               "Failed to initialize uv loop.");
19
  }
20
21
9
  rc = uv_async_init(loop_, &async_, &Watchdog::Async);
22
9
  CHECK_EQ(0, rc);
23
24
9
  rc = uv_timer_init(loop_, &timer_);
25
9
  CHECK_EQ(0, rc);
26
27
9
  rc = uv_timer_start(&timer_, &Watchdog::Timer, ms, 0);
28
9
  CHECK_EQ(0, rc);
29
30
9
  rc = uv_thread_create(&thread_, &Watchdog::Run, this);
31
9
  CHECK_EQ(0, rc);
32
9
}
33
34
35
18
Watchdog::~Watchdog() {
36
9
  Destroy();
37
9
}
38
39
40
void Watchdog::Dispose() {
41
  Destroy();
42
}
43
44
45
9
void Watchdog::Destroy() {
46
9
  if (destroyed_) {
47
    return;
48
  }
49
50
9
  uv_async_send(&async_);
51
9
  uv_thread_join(&thread_);
52
53
9
  uv_close(reinterpret_cast<uv_handle_t*>(&async_), nullptr);
54
55
  // UV_RUN_DEFAULT so that libuv has a chance to clean up.
56
9
  uv_run(loop_, UV_RUN_DEFAULT);
57
58
9
  int rc = uv_loop_close(loop_);
59
9
  CHECK_EQ(0, rc);
60
9
  delete loop_;
61
9
  loop_ = nullptr;
62
63
9
  destroyed_ = true;
64
}
65
66
67
9
void Watchdog::Run(void* arg) {
68
9
  Watchdog* wd = static_cast<Watchdog*>(arg);
69
70
  // UV_RUN_DEFAULT the loop will be stopped either by the async or the
71
  // timer handle.
72
9
  uv_run(wd->loop_, UV_RUN_DEFAULT);
73
74
  // Loop ref count reaches zero when both handles are closed.
75
  // Close the timer handle on this side and let Destroy() close async_
76
9
  uv_close(reinterpret_cast<uv_handle_t*>(&wd->timer_), nullptr);
77
9
}
78
79
80
5
void Watchdog::Async(uv_async_t* async) {
81
5
  Watchdog* w = ContainerOf(&Watchdog::async_, async);
82
5
  uv_stop(w->loop_);
83
5
}
84
85
86
4
void Watchdog::Timer(uv_timer_t* timer) {
87
4
  Watchdog* w = ContainerOf(&Watchdog::timer_, timer);
88
4
  w->timed_out_ = true;
89
4
  uv_stop(w->loop_);
90
4
  w->isolate()->TerminateExecution();
91
4
}
92
93
94
54
SigintWatchdog::~SigintWatchdog() {
95
27
  Destroy();
96
27
}
97
98
99
void SigintWatchdog::Dispose() {
100
  Destroy();
101
}
102
103
104
29
SigintWatchdog::SigintWatchdog(v8::Isolate* isolate)
105
29
    : isolate_(isolate), received_signal_(false), destroyed_(false) {
106
  // Register this watchdog with the global SIGINT/Ctrl+C listener.
107
29
  SigintWatchdogHelper::GetInstance()->Register(this);
108
  // Start the helper thread, if that has not already happened.
109
29
  SigintWatchdogHelper::GetInstance()->Start();
110
29
}
111
112
113
27
void SigintWatchdog::Destroy() {
114
27
  if (destroyed_) {
115
    return;
116
  }
117
118
27
  destroyed_ = true;
119
120
27
  SigintWatchdogHelper::GetInstance()->Unregister(this);
121
27
  SigintWatchdogHelper::GetInstance()->Stop();
122
}
123
124
125
void SigintWatchdog::HandleSigint() {
126
4
  received_signal_ = true;
127
4
  isolate_->TerminateExecution();
128
}
129
130
#ifdef __POSIX__
131
33
void* SigintWatchdogHelper::RunSigintWatchdog(void* arg) {
132
  // Inside the helper thread.
133
  bool is_stopping;
134
135
  do {
136
40
    uv_sem_wait(&instance.sem_);
137
40
    is_stopping = InformWatchdogsAboutSignal();
138
40
  } while (!is_stopping);
139
140
33
  return nullptr;
141
}
142
143
144
7
void SigintWatchdogHelper::HandleSignal(int signum) {
145
7
  uv_sem_post(&instance.sem_);
146
7
}
147
148
#else
149
150
// Windows starts a separate thread for executing the handler, so no extra
151
// helper thread is required.
152
BOOL WINAPI SigintWatchdogHelper::WinCtrlCHandlerRoutine(DWORD dwCtrlType) {
153
  if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
154
    InformWatchdogsAboutSignal();
155
156
    // Return true because the signal has been handled.
157
    return TRUE;
158
  } else {
159
    return FALSE;
160
  }
161
}
162
#endif
163
164
165
40
bool SigintWatchdogHelper::InformWatchdogsAboutSignal() {
166
80
  Mutex::ScopedLock list_lock(instance.list_mutex_);
167
168
40
  bool is_stopping = false;
169
#ifdef __POSIX__
170
40
  is_stopping = instance.stopping_;
171
#endif
172
173
  // If there are no listeners and the helper thread has been awoken by a signal
174
  // (= not when stopping it), indicate that by setting has_pending_signal_.
175

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