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

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

12876
}  // namespace node