GCC Code Coverage Report
Directory: ../ Exec Total Coverage
File: /home/iojs/build/workspace/node-test-commit-linux-coverage/nodes/benchmark/out/../src/node_watchdog.cc Lines: 132 134 98.5 %
Date: 2019-01-07 12:15:22 Branches: 34 50 68.0 %

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

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

10791
}  // namespace node