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

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

17859
NODE_MODULE_CONTEXT_AWARE_INTERNAL(watchdog, node::watchdog::Initialize)