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 "debug_utils.h" |
25 |
|
|
#include "env-inl.h" |
26 |
|
|
#include "node_errors.h" |
27 |
|
|
#include "node_internals.h" |
28 |
|
|
#include "node_watchdog.h" |
29 |
|
|
#include "util-inl.h" |
30 |
|
|
|
31 |
|
|
namespace node { |
32 |
|
|
|
33 |
|
1017 |
Watchdog::Watchdog(v8::Isolate* isolate, uint64_t ms, bool* timed_out) |
34 |
|
1017 |
: isolate_(isolate), timed_out_(timed_out) { |
35 |
|
|
|
36 |
|
|
int rc; |
37 |
|
1017 |
rc = uv_loop_init(&loop_); |
38 |
✗✓ |
1017 |
if (rc != 0) { |
39 |
|
|
FatalError("node::Watchdog::Watchdog()", |
40 |
|
|
"Failed to initialize uv loop."); |
41 |
|
|
} |
42 |
|
|
|
43 |
|
2029 |
rc = uv_async_init(&loop_, &async_, [](uv_async_t* signal) { |
44 |
|
506 |
Watchdog* w = ContainerOf(&Watchdog::async_, signal); |
45 |
|
506 |
uv_stop(&w->loop_); |
46 |
|
3046 |
}); |
47 |
|
|
|
48 |
✗✓ |
1017 |
CHECK_EQ(0, rc); |
49 |
|
|
|
50 |
|
1017 |
rc = uv_timer_init(&loop_, &timer_); |
51 |
✗✓ |
1017 |
CHECK_EQ(0, rc); |
52 |
|
|
|
53 |
|
1017 |
rc = uv_timer_start(&timer_, &Watchdog::Timer, ms, 0); |
54 |
✗✓ |
1017 |
CHECK_EQ(0, rc); |
55 |
|
|
|
56 |
|
1017 |
rc = uv_thread_create(&thread_, &Watchdog::Run, this); |
57 |
✗✓ |
1017 |
CHECK_EQ(0, rc); |
58 |
|
1017 |
} |
59 |
|
|
|
60 |
|
|
|
61 |
|
1017 |
Watchdog::~Watchdog() { |
62 |
|
1017 |
uv_async_send(&async_); |
63 |
|
1017 |
uv_thread_join(&thread_); |
64 |
|
|
|
65 |
|
1017 |
uv_close(reinterpret_cast<uv_handle_t*>(&async_), nullptr); |
66 |
|
|
|
67 |
|
|
// UV_RUN_DEFAULT so that libuv has a chance to clean up. |
68 |
|
1017 |
uv_run(&loop_, UV_RUN_DEFAULT); |
69 |
|
|
|
70 |
|
1017 |
CheckedUvLoopClose(&loop_); |
71 |
|
1017 |
} |
72 |
|
|
|
73 |
|
|
|
74 |
|
1017 |
void Watchdog::Run(void* arg) { |
75 |
|
1017 |
Watchdog* wd = static_cast<Watchdog*>(arg); |
76 |
|
|
|
77 |
|
|
// UV_RUN_DEFAULT the loop will be stopped either by the async or the |
78 |
|
|
// timer handle. |
79 |
|
1017 |
uv_run(&wd->loop_, UV_RUN_DEFAULT); |
80 |
|
|
|
81 |
|
|
// Loop ref count reaches zero when both handles are closed. |
82 |
|
|
// Close the timer handle on this side and let ~Watchdog() close async_ |
83 |
|
1017 |
uv_close(reinterpret_cast<uv_handle_t*>(&wd->timer_), nullptr); |
84 |
|
1017 |
} |
85 |
|
|
|
86 |
|
511 |
void Watchdog::Timer(uv_timer_t* timer) { |
87 |
|
511 |
Watchdog* w = ContainerOf(&Watchdog::timer_, timer); |
88 |
|
511 |
*w->timed_out_ = true; |
89 |
|
511 |
w->isolate()->TerminateExecution(); |
90 |
|
511 |
uv_stop(&w->loop_); |
91 |
|
511 |
} |
92 |
|
|
|
93 |
|
|
|
94 |
|
96 |
SigintWatchdog::SigintWatchdog( |
95 |
|
|
v8::Isolate* isolate, bool* received_signal) |
96 |
|
96 |
: isolate_(isolate), received_signal_(received_signal) { |
97 |
|
|
// Register this watchdog with the global SIGINT/Ctrl+C listener. |
98 |
|
96 |
SigintWatchdogHelper::GetInstance()->Register(this); |
99 |
|
|
// Start the helper thread, if that has not already happened. |
100 |
|
96 |
SigintWatchdogHelper::GetInstance()->Start(); |
101 |
|
96 |
} |
102 |
|
|
|
103 |
|
|
|
104 |
|
94 |
SigintWatchdog::~SigintWatchdog() { |
105 |
|
94 |
SigintWatchdogHelper::GetInstance()->Unregister(this); |
106 |
|
94 |
SigintWatchdogHelper::GetInstance()->Stop(); |
107 |
|
94 |
} |
108 |
|
|
|
109 |
|
|
|
110 |
|
11 |
void SigintWatchdog::HandleSigint() { |
111 |
|
11 |
*received_signal_ = true; |
112 |
|
11 |
isolate_->TerminateExecution(); |
113 |
|
11 |
} |
114 |
|
|
|
115 |
|
|
#ifdef __POSIX__ |
116 |
|
112 |
void* SigintWatchdogHelper::RunSigintWatchdog(void* arg) { |
117 |
|
|
// Inside the helper thread. |
118 |
|
|
bool is_stopping; |
119 |
|
|
|
120 |
✓✓ |
112 |
do { |
121 |
|
112 |
uv_sem_wait(&instance.sem_); |
122 |
|
112 |
is_stopping = InformWatchdogsAboutSignal(); |
123 |
|
112 |
} while (!is_stopping); |
124 |
|
|
|
125 |
|
99 |
return nullptr; |
126 |
|
|
} |
127 |
|
|
|
128 |
|
13 |
void SigintWatchdogHelper::HandleSignal(int signum, |
129 |
|
|
siginfo_t* info, |
130 |
|
|
void* ucontext) { |
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 |
|
112 |
bool SigintWatchdogHelper::InformWatchdogsAboutSignal() { |
153 |
|
112 |
Mutex::ScopedLock list_lock(instance.list_mutex_); |
154 |
|
|
|
155 |
|
112 |
bool is_stopping = false; |
156 |
|
|
#ifdef __POSIX__ |
157 |
|
112 |
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 |
✓✓✓✓ ✓✓ |
112 |
if (instance.watchdogs_.empty() && !is_stopping) { |
163 |
|
3 |
instance.has_pending_signal_ = true; |
164 |
|
|
} |
165 |
|
|
|
166 |
✓✓ |
123 |
for (auto it : instance.watchdogs_) |
167 |
|
11 |
it->HandleSigint(); |
168 |
|
|
|
169 |
|
112 |
return is_stopping; |
170 |
|
|
} |
171 |
|
|
|
172 |
|
|
|
173 |
|
189 |
int SigintWatchdogHelper::Start() { |
174 |
|
189 |
Mutex::ScopedLock lock(mutex_); |
175 |
|
|
|
176 |
✓✓ |
189 |
if (start_stop_count_++ > 0) { |
177 |
|
90 |
return 0; |
178 |
|
|
} |
179 |
|
|
|
180 |
|
|
#ifdef __POSIX__ |
181 |
✗✓ |
99 |
CHECK_EQ(has_running_thread_, false); |
182 |
|
99 |
has_pending_signal_ = false; |
183 |
|
99 |
stopping_ = false; |
184 |
|
|
|
185 |
|
|
sigset_t sigmask; |
186 |
|
99 |
sigfillset(&sigmask); |
187 |
|
|
sigset_t savemask; |
188 |
✗✓ |
99 |
CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, &savemask)); |
189 |
|
99 |
sigmask = savemask; |
190 |
|
99 |
int ret = pthread_create(&thread_, nullptr, RunSigintWatchdog, nullptr); |
191 |
✗✓ |
99 |
CHECK_EQ(0, pthread_sigmask(SIG_SETMASK, &sigmask, nullptr)); |
192 |
✗✓ |
99 |
if (ret != 0) { |
193 |
|
|
return ret; |
194 |
|
|
} |
195 |
|
99 |
has_running_thread_ = true; |
196 |
|
|
|
197 |
|
99 |
RegisterSignalHandler(SIGINT, HandleSignal); |
198 |
|
|
#else |
199 |
|
|
if (watchdog_disabled_) { |
200 |
|
|
watchdog_disabled_ = false; |
201 |
|
|
} else { |
202 |
|
|
SetConsoleCtrlHandler(WinCtrlCHandlerRoutine, TRUE); |
203 |
|
|
} |
204 |
|
|
#endif |
205 |
|
|
|
206 |
|
99 |
return 0; |
207 |
|
|
} |
208 |
|
|
|
209 |
|
|
|
210 |
|
5222 |
bool SigintWatchdogHelper::Stop() { |
211 |
|
|
bool had_pending_signal; |
212 |
|
5222 |
Mutex::ScopedLock lock(mutex_); |
213 |
|
|
|
214 |
|
|
{ |
215 |
|
5222 |
Mutex::ScopedLock list_lock(list_mutex_); |
216 |
|
|
|
217 |
|
5222 |
had_pending_signal = has_pending_signal_; |
218 |
|
|
|
219 |
✓✓ |
5222 |
if (--start_stop_count_ > 0) { |
220 |
|
88 |
has_pending_signal_ = false; |
221 |
|
88 |
return had_pending_signal; |
222 |
|
|
} |
223 |
|
|
|
224 |
|
|
#ifdef __POSIX__ |
225 |
|
|
// Set stopping now because it's only protected by list_mutex_. |
226 |
|
5134 |
stopping_ = true; |
227 |
|
|
#endif |
228 |
|
|
|
229 |
✓✓ |
5134 |
watchdogs_.clear(); |
230 |
|
|
} |
231 |
|
|
|
232 |
|
|
#ifdef __POSIX__ |
233 |
✓✓ |
5134 |
if (!has_running_thread_) { |
234 |
|
5035 |
has_pending_signal_ = false; |
235 |
|
5035 |
return had_pending_signal; |
236 |
|
|
} |
237 |
|
|
|
238 |
|
|
// Wake up the helper thread. |
239 |
|
99 |
uv_sem_post(&sem_); |
240 |
|
|
|
241 |
|
|
// Wait for the helper thread to finish. |
242 |
✗✓ |
99 |
CHECK_EQ(0, pthread_join(thread_, nullptr)); |
243 |
|
99 |
has_running_thread_ = false; |
244 |
|
|
|
245 |
|
99 |
RegisterSignalHandler(SIGINT, SignalExit, true); |
246 |
|
|
#else |
247 |
|
|
watchdog_disabled_ = true; |
248 |
|
|
#endif |
249 |
|
|
|
250 |
|
99 |
had_pending_signal = has_pending_signal_; |
251 |
|
99 |
has_pending_signal_ = false; |
252 |
|
|
|
253 |
|
99 |
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 |
|
96 |
void SigintWatchdogHelper::Register(SigintWatchdog* wd) { |
265 |
|
96 |
Mutex::ScopedLock lock(list_mutex_); |
266 |
|
|
|
267 |
|
96 |
watchdogs_.push_back(wd); |
268 |
|
96 |
} |
269 |
|
|
|
270 |
|
|
|
271 |
|
94 |
void SigintWatchdogHelper::Unregister(SigintWatchdog* wd) { |
272 |
|
94 |
Mutex::ScopedLock lock(list_mutex_); |
273 |
|
|
|
274 |
|
94 |
auto it = std::find(watchdogs_.begin(), watchdogs_.end(), wd); |
275 |
|
|
|
276 |
✗✓ |
94 |
CHECK_NE(it, watchdogs_.end()); |
277 |
|
94 |
watchdogs_.erase(it); |
278 |
|
94 |
} |
279 |
|
|
|
280 |
|
|
|
281 |
|
5037 |
SigintWatchdogHelper::SigintWatchdogHelper() |
282 |
|
|
: start_stop_count_(0), |
283 |
|
5037 |
has_pending_signal_(false) { |
284 |
|
|
#ifdef __POSIX__ |
285 |
|
5037 |
has_running_thread_ = false; |
286 |
|
5037 |
stopping_ = false; |
287 |
✗✓ |
5037 |
CHECK_EQ(0, uv_sem_init(&sem_, 0)); |
288 |
|
|
#else |
289 |
|
|
watchdog_disabled_ = false; |
290 |
|
|
#endif |
291 |
|
5037 |
} |
292 |
|
|
|
293 |
|
|
|
294 |
|
10074 |
SigintWatchdogHelper::~SigintWatchdogHelper() { |
295 |
|
5037 |
start_stop_count_ = 0; |
296 |
|
5037 |
Stop(); |
297 |
|
|
|
298 |
|
|
#ifdef __POSIX__ |
299 |
✗✓ |
5037 |
CHECK_EQ(has_running_thread_, false); |
300 |
|
5037 |
uv_sem_destroy(&sem_); |
301 |
|
|
#endif |
302 |
|
5037 |
} |
303 |
|
|
|
304 |
|
5037 |
SigintWatchdogHelper SigintWatchdogHelper::instance; |
305 |
|
|
|
306 |
✓✗✓✗
|
15111 |
} // namespace node |