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: 167 199 83.9 %
Date: 2020-02-27 22:14:15 Branches: 41 74 55.4 %

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 <algorithm>
23
24
#include "async_wrap-inl.h"
25
#include "debug_utils-inl.h"
26
#include "env-inl.h"
27
#include "node_errors.h"
28
#include "node_internals.h"
29
#include "node_watchdog.h"
30
#include "util-inl.h"
31
32
namespace node {
33
34
using v8::Context;
35
using v8::FunctionCallbackInfo;
36
using v8::FunctionTemplate;
37
using v8::Object;
38
using v8::Value;
39
40
16
Watchdog::Watchdog(v8::Isolate* isolate, uint64_t ms, bool* timed_out)
41
16
    : isolate_(isolate), timed_out_(timed_out) {
42
43
  int rc;
44
16
  rc = uv_loop_init(&loop_);
45
16
  if (rc != 0) {
46
    FatalError("node::Watchdog::Watchdog()",
47
               "Failed to initialize uv loop.");
48
  }
49
50
42
  rc = uv_async_init(&loop_, &async_, [](uv_async_t* signal) {
51
5
    Watchdog* w = ContainerOf(&Watchdog::async_, signal);
52
5
    uv_stop(&w->loop_);
53
26
  });
54
55
16
  CHECK_EQ(0, rc);
56
57
16
  rc = uv_timer_init(&loop_, &timer_);
58
16
  CHECK_EQ(0, rc);
59
60
16
  rc = uv_timer_start(&timer_, &Watchdog::Timer, ms, 0);
61
16
  CHECK_EQ(0, rc);
62
63
16
  rc = uv_thread_create(&thread_, &Watchdog::Run, this);
64
16
  CHECK_EQ(0, rc);
65
16
}
66
67
68
32
Watchdog::~Watchdog() {
69
16
  uv_async_send(&async_);
70
16
  uv_thread_join(&thread_);
71
72
16
  uv_close(reinterpret_cast<uv_handle_t*>(&async_), nullptr);
73
74
  // UV_RUN_DEFAULT so that libuv has a chance to clean up.
75
16
  uv_run(&loop_, UV_RUN_DEFAULT);
76
77
16
  CheckedUvLoopClose(&loop_);
78
16
}
79
80
81
16
void Watchdog::Run(void* arg) {
82
16
  Watchdog* wd = static_cast<Watchdog*>(arg);
83
84
  // UV_RUN_DEFAULT the loop will be stopped either by the async or the
85
  // timer handle.
86
16
  uv_run(&wd->loop_, UV_RUN_DEFAULT);
87
88
  // Loop ref count reaches zero when both handles are closed.
89
  // Close the timer handle on this side and let ~Watchdog() close async_
90
16
  uv_close(reinterpret_cast<uv_handle_t*>(&wd->timer_), nullptr);
91
16
}
92
93
11
void Watchdog::Timer(uv_timer_t* timer) {
94
11
  Watchdog* w = ContainerOf(&Watchdog::timer_, timer);
95
11
  *w->timed_out_ = true;
96
11
  w->isolate()->TerminateExecution();
97
11
  uv_stop(&w->loop_);
98
11
}
99
100
101
116
SigintWatchdog::SigintWatchdog(
102
116
  v8::Isolate* isolate, bool* received_signal)
103
116
    : isolate_(isolate), received_signal_(received_signal) {
104
  // Register this watchdog with the global SIGINT/Ctrl+C listener.
105
116
  SigintWatchdogHelper::GetInstance()->Register(this);
106
  // Start the helper thread, if that has not already happened.
107
116
  SigintWatchdogHelper::GetInstance()->Start();
108
116
}
109
110
111
228
SigintWatchdog::~SigintWatchdog() {
112
114
  SigintWatchdogHelper::GetInstance()->Unregister(this);
113
114
  SigintWatchdogHelper::GetInstance()->Stop();
114
114
}
115
116
10
SignalPropagation SigintWatchdog::HandleSigint() {
117
10
  *received_signal_ = true;
118
10
  isolate_->TerminateExecution();
119
10
  return SignalPropagation::kStopPropagation;
120
}
121
122
2
void TraceSigintWatchdog::Init(Environment* env, Local<Object> target) {
123
2
  Local<FunctionTemplate> constructor = env->NewFunctionTemplate(New);
124
4
  constructor->InstanceTemplate()->SetInternalFieldCount(1);
125
  Local<v8::String> js_sigint_watch_dog =
126
2
      FIXED_ONE_BYTE_STRING(env->isolate(), "TraceSigintWatchdog");
127
2
  constructor->SetClassName(js_sigint_watch_dog);
128
4
  constructor->Inherit(HandleWrap::GetConstructorTemplate(env));
129
130
2
  env->SetProtoMethod(constructor, "start", Start);
131
2
  env->SetProtoMethod(constructor, "stop", Stop);
132
133
  target
134
4
      ->Set(env->context(),
135
            js_sigint_watch_dog,
136
10
            constructor->GetFunction(env->context()).ToLocalChecked())
137
      .Check();
138
2
}
139
140
1
void TraceSigintWatchdog::New(const FunctionCallbackInfo<Value>& args) {
141
  // This constructor should not be exposed to public javascript.
142
  // Therefore we assert that we are not trying to call this as a
143
  // normal function.
144
1
  CHECK(args.IsConstructCall());
145
1
  Environment* env = Environment::GetCurrent(args);
146
1
  new TraceSigintWatchdog(env, args.This());
147
1
}
148
149
2
void TraceSigintWatchdog::Start(const FunctionCallbackInfo<Value>& args) {
150
  TraceSigintWatchdog* watchdog;
151
2
  ASSIGN_OR_RETURN_UNWRAP(&watchdog, args.Holder());
152
  // Register this watchdog with the global SIGINT/Ctrl+C listener.
153
2
  SigintWatchdogHelper::GetInstance()->Register(watchdog);
154
  // Start the helper thread, if that has not already happened.
155
2
  int r = SigintWatchdogHelper::GetInstance()->Start();
156
2
  CHECK_EQ(r, 0);
157
}
158
159
2
void TraceSigintWatchdog::Stop(const FunctionCallbackInfo<Value>& args) {
160
  TraceSigintWatchdog* watchdog;
161
2
  ASSIGN_OR_RETURN_UNWRAP(&watchdog, args.Holder());
162
2
  SigintWatchdogHelper::GetInstance()->Unregister(watchdog);
163
2
  SigintWatchdogHelper::GetInstance()->Stop();
164
}
165
166
1
TraceSigintWatchdog::TraceSigintWatchdog(Environment* env, Local<Object> object)
167
    : HandleWrap(env,
168
                 object,
169
1
                 reinterpret_cast<uv_handle_t*>(&handle_),
170
1
                 AsyncWrap::PROVIDER_SIGINTWATCHDOG) {
171
3
  int r = uv_async_init(env->event_loop(), &handle_, [](uv_async_t* handle) {
172
    TraceSigintWatchdog* watchdog =
173
        ContainerOf(&TraceSigintWatchdog::handle_, handle);
174
    watchdog->signal_flag_ = SignalFlags::FromIdle;
175
    watchdog->HandleInterrupt();
176
2
  });
177
1
  CHECK_EQ(r, 0);
178
1
  uv_unref(reinterpret_cast<uv_handle_t*>(&handle_));
179
1
}
180
181
SignalPropagation TraceSigintWatchdog::HandleSigint() {
182
  /**
183
   * In case of uv loop polling, i.e. no JS currently running, activate the
184
   * loop to run a piece of JS code to trigger interruption.
185
   */
186
  CHECK_EQ(uv_async_send(&handle_), 0);
187
  env()->isolate()->RequestInterrupt(
188
      [](v8::Isolate* isolate, void* data) {
189
        TraceSigintWatchdog* self = static_cast<TraceSigintWatchdog*>(data);
190
        if (self->signal_flag_ == SignalFlags::None) {
191
          self->signal_flag_ = SignalFlags::FromInterrupt;
192
        }
193
        self->HandleInterrupt();
194
      },
195
      this);
196
  return SignalPropagation::kContinuePropagation;
197
}
198
199
void TraceSigintWatchdog::HandleInterrupt() {
200
  // Do not nest interrupts.
201
  if (interrupting) {
202
    return;
203
  }
204
  interrupting = true;
205
  if (signal_flag_ == SignalFlags::None) {
206
    return;
207
  }
208
  Environment* env_ = env();
209
  // FIXME: Before
210
  // https://github.com/nodejs/node/pull/29207#issuecomment-527667993 get
211
  // fixed, additional JavaScript code evaluation shall be prevented from
212
  // running during interruption.
213
  FPrintF(stderr,
214
      "KEYBOARD_INTERRUPT: Script execution was interrupted by `SIGINT`\n");
215
  if (signal_flag_ == SignalFlags::FromInterrupt) {
216
    PrintStackTrace(env_->isolate(),
217
                    v8::StackTrace::CurrentStackTrace(
218
                        env_->isolate(), 10, v8::StackTrace::kDetailed));
219
  }
220
  signal_flag_ = SignalFlags::None;
221
  interrupting = false;
222
223
  SigintWatchdogHelper::GetInstance()->Unregister(this);
224
  SigintWatchdogHelper::GetInstance()->Stop();
225
  raise(SIGINT);
226
}
227
228
#ifdef __POSIX__
229
134
void* SigintWatchdogHelper::RunSigintWatchdog(void* arg) {
230
  // Inside the helper thread.
231
  bool is_stopping;
232
233
13
  do {
234
134
    uv_sem_wait(&instance.sem_);
235
134
    is_stopping = InformWatchdogsAboutSignal();
236
134
  } while (!is_stopping);
237
238
121
  return nullptr;
239
}
240
241
13
void SigintWatchdogHelper::HandleSignal(int signum,
242
                                        siginfo_t* info,
243
                                        void* ucontext) {
244
13
  uv_sem_post(&instance.sem_);
245
13
}
246
247
#else
248
249
// Windows starts a separate thread for executing the handler, so no extra
250
// helper thread is required.
251
BOOL WINAPI SigintWatchdogHelper::WinCtrlCHandlerRoutine(DWORD dwCtrlType) {
252
  if (!instance.watchdog_disabled_ &&
253
      (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT)) {
254
    InformWatchdogsAboutSignal();
255
256
    // Return true because the signal has been handled.
257
    return TRUE;
258
  } else {
259
    return FALSE;
260
  }
261
}
262
#endif
263
264
265
134
bool SigintWatchdogHelper::InformWatchdogsAboutSignal() {
266
268
  Mutex::ScopedLock list_lock(instance.list_mutex_);
267
268
134
  bool is_stopping = false;
269
#ifdef __POSIX__
270
134
  is_stopping = instance.stopping_;
271
#endif
272
273
  // If there are no listeners and the helper thread has been awoken by a signal
274
  // (= not when stopping it), indicate that by setting has_pending_signal_.
275

134
  if (instance.watchdogs_.empty() && !is_stopping) {
276
3
    instance.has_pending_signal_ = true;
277
  }
278
279
134
  for (auto it = instance.watchdogs_.rbegin(); it != instance.watchdogs_.rend();
280
       it++) {
281
10
    SignalPropagation wp = (*it)->HandleSigint();
282
10
    if (wp == SignalPropagation::kStopPropagation) {
283
10
      break;
284
    }
285
  }
286
287
268
  return is_stopping;
288
}
289
290
291
231
int SigintWatchdogHelper::Start() {
292
462
  Mutex::ScopedLock lock(mutex_);
293
294
231
  if (start_stop_count_++ > 0) {
295
110
    return 0;
296
  }
297
298
#ifdef __POSIX__
299
121
  CHECK_EQ(has_running_thread_, false);
300
121
  has_pending_signal_ = false;
301
121
  stopping_ = false;
302
303
  sigset_t sigmask;
304
121
  sigfillset(&sigmask);
305
  sigset_t savemask;
306
121
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &savemask));
307
121
  sigmask = savemask;
308
121
  int ret = pthread_create(&thread_, nullptr, RunSigintWatchdog, nullptr);
309
121
  CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr));
310
121
  if (ret != 0) {
311
    return ret;
312
  }
313
121
  has_running_thread_ = true;
314
315
121
  RegisterSignalHandler(SIGINT, HandleSignal);
316
#else
317
  if (watchdog_disabled_) {
318
    watchdog_disabled_ = false;
319
  } else {
320
    SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, TRUE);
321
  }
322
#endif
323
324
121
  return 0;
325
}
326
327
328
4429
bool SigintWatchdogHelper::Stop() {
329
  bool had_pending_signal;
330
8858
  Mutex::ScopedLock lock(mutex_);
331
332
  {
333
8750
    Mutex::ScopedLock list_lock(list_mutex_);
334
335
4429
    had_pending_signal = has_pending_signal_;
336
337
4429
    if (--start_stop_count_ > 0) {
338
108
      has_pending_signal_ = false;
339
108
      return had_pending_signal;
340
    }
341
342
#ifdef __POSIX__
343
    // Set stopping now because it's only protected by list_mutex_.
344
4321
    stopping_ = true;
345
#endif
346
347
4321
    watchdogs_.clear();
348
  }
349
350
#ifdef __POSIX__
351
4321
  if (!has_running_thread_) {
352
4200
    has_pending_signal_ = false;
353
4200
    return had_pending_signal;
354
  }
355
356
  // Wake up the helper thread.
357
121
  uv_sem_post(&sem_);
358
359
  // Wait for the helper thread to finish.
360
121
  CHECK_EQ(0, pthread_join(thread_, nullptr));
361
121
  has_running_thread_ = false;
362
363
121
  RegisterSignalHandler(SIGINT, SignalExit, true);
364
#else
365
  watchdog_disabled_ = true;
366
#endif
367
368
121
  had_pending_signal = has_pending_signal_;
369
121
  has_pending_signal_ = false;
370
371
121
  return had_pending_signal;
372
}
373
374
375
3
bool SigintWatchdogHelper::HasPendingSignal() {
376
6
  Mutex::ScopedLock lock(list_mutex_);
377
378
6
  return has_pending_signal_;
379
}
380
381
118
void SigintWatchdogHelper::Register(SigintWatchdogBase* wd) {
382
236
  Mutex::ScopedLock lock(list_mutex_);
383
384
118
  watchdogs_.push_back(wd);
385
118
}
386
387
116
void SigintWatchdogHelper::Unregister(SigintWatchdogBase* wd) {
388
232
  Mutex::ScopedLock lock(list_mutex_);
389
390
116
  auto it = std::find(watchdogs_.begin(), watchdogs_.end(), wd);
391
392
116
  CHECK_NE(it, watchdogs_.end());
393
116
  watchdogs_.erase(it);
394
116
}
395
396
397
4202
SigintWatchdogHelper::SigintWatchdogHelper()
398
    : start_stop_count_(0),
399
4202
      has_pending_signal_(false) {
400
#ifdef __POSIX__
401
4202
  has_running_thread_ = false;
402
4202
  stopping_ = false;
403
4202
  CHECK_EQ(0, uv_sem_init(&sem_, 0));
404
#else
405
  watchdog_disabled_ = false;
406
#endif
407
4202
}
408
409
410
8404
SigintWatchdogHelper::~SigintWatchdogHelper() {
411
4202
  start_stop_count_ = 0;
412
4202
  Stop();
413
414
#ifdef __POSIX__
415
4202
  CHECK_EQ(has_running_thread_, false);
416
4202
  uv_sem_destroy(&sem_);
417
#endif
418
4202
}
419
420
4202
SigintWatchdogHelper SigintWatchdogHelper::instance;
421
422
namespace watchdog {
423
2
static void Initialize(Local<Object> target,
424
                       Local<Value> unused,
425
                       Local<Context> context,
426
                       void* priv) {
427
2
  Environment* env = Environment::GetCurrent(context);
428
2
  TraceSigintWatchdog::Init(env, target);
429
2
}
430
}  // namespace watchdog
431
432
}  // namespace node
433
434

16807
NODE_MODULE_CONTEXT_AWARE_INTERNAL(watchdog, node::watchdog::Initialize);