/src/zeek/src/threading/MsgThread.cc
Line | Count | Source |
1 | | // See the file "COPYING" in the main distribution directory for copyright. |
2 | | |
3 | | #include "zeek/threading/MsgThread.h" |
4 | | |
5 | | #include <fcntl.h> |
6 | | #include <unistd.h> |
7 | | #include <csignal> |
8 | | |
9 | | #include "zeek/DebugLogger.h" |
10 | | #include "zeek/Desc.h" |
11 | | #include "zeek/Obj.h" |
12 | | #include "zeek/RunState.h" |
13 | | #include "zeek/iosource/Manager.h" |
14 | | #include "zeek/threading/Manager.h" |
15 | | |
16 | | // Set by Zeek's main signal handler. |
17 | | extern int signal_val; |
18 | | |
19 | | namespace zeek::threading { |
20 | | namespace detail { |
21 | | |
22 | | ////// Messages. |
23 | | |
24 | | // Signals child thread to shutdown operation. |
25 | | class FinishMessage final : public InputMessage<MsgThread> { |
26 | | public: |
27 | | FinishMessage(MsgThread* thread, double network_time) |
28 | 0 | : InputMessage<MsgThread>("Finish", thread), network_time(network_time) {} |
29 | | |
30 | 0 | bool Process() override { |
31 | 0 | if ( Object()->child_finished ) |
32 | 0 | return true; |
33 | 0 | bool result = Object()->OnFinish(network_time); |
34 | 0 | Object()->Finished(); |
35 | 0 | return result; |
36 | 0 | } |
37 | | |
38 | | private: |
39 | | double network_time; |
40 | | }; |
41 | | |
42 | | // Signals main thread that operations shut down. |
43 | | class FinishedMessage final : public OutputMessage<MsgThread> { |
44 | | public: |
45 | 0 | FinishedMessage(MsgThread* thread) : OutputMessage<MsgThread>("FinishedMessage", thread) {} |
46 | | |
47 | 0 | bool Process() override { |
48 | 0 | Object()->main_finished = true; |
49 | 0 | return true; |
50 | 0 | } |
51 | | }; |
52 | | |
53 | | /// Sends a heartbeat to the child thread. |
54 | | class HeartbeatMessage final : public InputMessage<MsgThread> { |
55 | | public: |
56 | | HeartbeatMessage(MsgThread* thread, double arg_network_time, double arg_current_time) |
57 | 0 | : InputMessage<MsgThread>("Heartbeat", thread) { |
58 | 0 | network_time = arg_network_time; |
59 | 0 | current_time = arg_current_time; |
60 | 0 | } |
61 | | |
62 | 0 | bool Process() override { return Object()->OnHeartbeat(network_time, current_time); } |
63 | | |
64 | | private: |
65 | | double network_time; |
66 | | double current_time; |
67 | | }; |
68 | | |
69 | | // A message from the child to be passed on to the Reporter. |
70 | | class ReporterMessage final : public OutputMessage<MsgThread> { |
71 | | public: |
72 | | enum Type : uint8_t { INFO, WARNING, ERROR, FATAL_ERROR, FATAL_ERROR_WITH_CORE, INTERNAL_WARNING, INTERNAL_ERROR }; |
73 | | |
74 | | ReporterMessage(Type arg_type, MsgThread* thread, std::string_view arg_msg) |
75 | 0 | : OutputMessage<MsgThread>("ReporterMessage", thread) { |
76 | 0 | type = arg_type; |
77 | 0 | msg = util::copy_string(arg_msg.data(), arg_msg.size()); |
78 | 0 | } |
79 | | |
80 | 0 | ~ReporterMessage() override { delete[] msg; } |
81 | | |
82 | | bool Process() override; |
83 | | |
84 | | private: |
85 | | const char* msg; |
86 | | Type type; |
87 | | }; |
88 | | |
89 | | // A message from the child to the main process, requesting suicide. |
90 | | class KillMeMessage final : public OutputMessage<MsgThread> { |
91 | | public: |
92 | 0 | KillMeMessage(MsgThread* thread) : OutputMessage<MsgThread>("ReporterMessage", thread) {} |
93 | | |
94 | 0 | bool Process() override { |
95 | 0 | Object()->SignalStop(); |
96 | 0 | Object()->WaitForStop(); |
97 | 0 | thread_mgr->KillThread(Object()); |
98 | 0 | return true; |
99 | 0 | } |
100 | | }; |
101 | | |
102 | | // A debug message from the child to be passed on to the DebugLogger. |
103 | | class DebugMessage final : public OutputMessage<MsgThread> { |
104 | | public: |
105 | | DebugMessage(DebugStream arg_stream, MsgThread* thread, std::string_view arg_msg) |
106 | 850k | : OutputMessage<MsgThread>("DebugMessage", thread) { |
107 | 850k | stream = arg_stream; |
108 | 850k | msg = util::copy_string(arg_msg.data(), arg_msg.size()); |
109 | 850k | } |
110 | | |
111 | 0 | ~DebugMessage() override { delete[] msg; } |
112 | | |
113 | 0 | bool Process() override { |
114 | 0 | zeek::detail::debug_logger.Log(stream, "%s: %s", Object()->Name(), msg); |
115 | 0 | return true; |
116 | 0 | } |
117 | | |
118 | | private: |
119 | | const char* msg; |
120 | | DebugStream stream; |
121 | | }; |
122 | | |
123 | | // An event that the child wants to pass into the main event queue |
124 | | class SendEventMessage final : public OutputMessage<MsgThread> { |
125 | | public: |
126 | | SendEventMessage(MsgThread* thread, const char* name, const int num_vals, Value** val) |
127 | 0 | : OutputMessage<MsgThread>("SendEvent", thread), name(util::copy_string(name)), num_vals(num_vals), val(val) {} |
128 | | |
129 | 0 | ~SendEventMessage() override { delete[] name; } |
130 | | |
131 | 0 | bool Process() override { |
132 | 0 | bool success = thread_mgr->SendEvent(Object(), name, num_vals, val); |
133 | |
|
134 | 0 | if ( ! success ) |
135 | 0 | reporter->Error("SendEvent for event %s failed", name); |
136 | |
|
137 | 0 | return true; // We do not want to die if sendEvent fails because the event did not return. |
138 | 0 | } |
139 | | |
140 | | private: |
141 | | const char* name; |
142 | | const int num_vals; |
143 | | Value** val; |
144 | | }; |
145 | | |
146 | 0 | bool ReporterMessage::Process() { |
147 | 0 | switch ( type ) { |
148 | 0 | case INFO: reporter->Info("%s: %s", Object()->Name(), msg); break; |
149 | | |
150 | 0 | case WARNING: reporter->Warning("%s: %s", Object()->Name(), msg); break; |
151 | | |
152 | 0 | case ERROR: reporter->Error("%s: %s", Object()->Name(), msg); break; |
153 | | |
154 | 0 | case FATAL_ERROR: reporter->FatalError("%s: %s", Object()->Name(), msg); break; |
155 | | |
156 | 0 | case FATAL_ERROR_WITH_CORE: reporter->FatalErrorWithCore("%s: %s", Object()->Name(), msg); break; |
157 | | |
158 | 0 | case INTERNAL_WARNING: reporter->InternalWarning("%s: %s", Object()->Name(), msg); break; |
159 | | |
160 | 0 | case INTERNAL_ERROR: reporter->InternalError("%s: %s", Object()->Name(), msg); break; |
161 | | |
162 | 0 | default: reporter->InternalError("unknown ReporterMessage type %d", type); |
163 | 0 | } |
164 | | |
165 | 0 | return true; |
166 | 0 | } |
167 | | |
168 | | // This is the IO source used by MsgThread. |
169 | | // |
170 | | // The lifetime of the IO source is decoupled from |
171 | | // the thread. The thread may be terminated prior |
172 | | // to the IO source being properly unregistered and |
173 | | // removed by the IO manager. |
174 | | class IOSource : public iosource::IOSource { |
175 | | public: |
176 | 339 | explicit IOSource(MsgThread* thread) : thread(thread) { |
177 | 339 | if ( ! iosource_mgr->RegisterFd(flare.FD(), this) ) |
178 | 0 | reporter->InternalError("Failed to register MsgThread FD with iosource_mgr"); |
179 | | |
180 | 339 | SetClosed(false); |
181 | 339 | } |
182 | | |
183 | 0 | ~IOSource() override { |
184 | 0 | if ( ! iosource_mgr->UnregisterFd(flare.FD(), this) ) |
185 | 0 | reporter->InternalError("Failed to unregister MsgThread FD from iosource_mgr"); |
186 | 0 | } |
187 | | |
188 | 0 | void Process() override { |
189 | 0 | flare.Extinguish(); |
190 | |
|
191 | 0 | if ( thread ) |
192 | 0 | thread->Process(); |
193 | 0 | } |
194 | | |
195 | 0 | const char* Tag() override { return thread ? thread->Name() : "<MsgThread orphan>"; } |
196 | | |
197 | 0 | double GetNextTimeout() override { return -1; } |
198 | | |
199 | | |
200 | 851k | void Fire() { flare.Fire(); }; |
201 | | |
202 | 0 | void Close() { |
203 | 0 | thread = nullptr; |
204 | 0 | SetClosed(true); |
205 | 0 | } |
206 | | |
207 | | private: |
208 | | MsgThread* thread = nullptr; |
209 | | zeek::detail::Flare flare; |
210 | | }; |
211 | | |
212 | | } // namespace detail |
213 | | |
214 | | ////// Methods. |
215 | | |
216 | 851k | Message::~Message() { delete[] name; } |
217 | | |
218 | 339 | MsgThread::MsgThread() : BasicThread(), queue_in(this, nullptr), queue_out(nullptr, this) { |
219 | 339 | cnt_sent_in.store(0); |
220 | 339 | cnt_sent_out.store(0); |
221 | | |
222 | 339 | main_finished = false; |
223 | 339 | child_finished = false; |
224 | 339 | child_sent_finish = false; |
225 | 339 | failed = false; |
226 | 339 | thread_mgr->AddMsgThread(this); |
227 | | |
228 | 339 | io_source = new detail::IOSource(this); |
229 | | |
230 | | // Register IOSource as non-counting lifetime managed IO source. |
231 | 339 | iosource_mgr->Register(io_source, true); |
232 | 339 | } |
233 | | |
234 | 0 | MsgThread::~MsgThread() { |
235 | | // Unregister this thread from the IO source so we don't |
236 | | // get Process() callbacks anymore. The IO source itself |
237 | | // is life-time managed by the IO manager. |
238 | 0 | if ( io_source ) { |
239 | 0 | io_source->Close(); |
240 | 0 | io_source = nullptr; |
241 | 0 | } |
242 | 0 | } |
243 | | |
244 | 0 | void MsgThread::OnSignalStop() { |
245 | 0 | if ( main_finished || Killed() || child_sent_finish ) |
246 | 0 | return; |
247 | | |
248 | 0 | child_sent_finish = true; |
249 | | // Signal thread to terminate. |
250 | 0 | SendIn(new detail::FinishMessage(this, run_state::network_time), true); |
251 | 0 | } |
252 | | |
253 | 0 | void MsgThread::OnWaitForStop() { |
254 | 0 | int signal_count = 0; |
255 | 0 | int old_signal_val = signal_val; |
256 | 0 | signal_val = 0; |
257 | |
|
258 | 0 | int cnt = 0; |
259 | 0 | uint64_t last_size = 0; |
260 | 0 | uint64_t cur_size = 0; |
261 | |
|
262 | 0 | while ( ! main_finished ) { |
263 | | // Terminate if we get another kill signal. |
264 | 0 | if ( signal_val == SIGTERM || signal_val == SIGINT ) { |
265 | 0 | ++signal_count; |
266 | |
|
267 | 0 | if ( signal_count == 1 ) { |
268 | | // Abort all threads here so that we won't hang next |
269 | | // on another one. |
270 | 0 | fprintf(stderr, "received signal while waiting for thread %s, aborting all ...\n", Name()); |
271 | 0 | thread_mgr->KillThreads(); |
272 | 0 | } |
273 | 0 | else { |
274 | | // More than one signal. Abort processing |
275 | | // right away. on another one. |
276 | 0 | fprintf(stderr, "received another signal while waiting for thread %s, aborting processing\n", Name()); |
277 | 0 | exit(1); |
278 | 0 | } |
279 | | |
280 | 0 | signal_val = 0; |
281 | 0 | } |
282 | | |
283 | 0 | if ( ! Killed() ) |
284 | 0 | queue_in.WakeUp(); |
285 | |
|
286 | 0 | while ( HasOut() ) { |
287 | 0 | Message* msg = RetrieveOut(); |
288 | 0 | assert(msg); |
289 | | |
290 | 0 | if ( ! msg->Process() ) |
291 | 0 | reporter->Error("%s failed during thread termination", msg->Name()); |
292 | |
|
293 | 0 | delete msg; |
294 | 0 | } |
295 | | |
296 | 0 | if ( ! Killed() ) |
297 | 0 | usleep(1000); |
298 | 0 | } |
299 | | |
300 | 0 | signal_val = old_signal_val; |
301 | 0 | } |
302 | | |
303 | 0 | void MsgThread::OnKill() { |
304 | | // Ensure the IO source is closed and won't call Process() on this |
305 | | // thread anymore. The thread got killed, so the threading manager will |
306 | | // remove it forcefully soon. |
307 | 0 | if ( io_source ) { |
308 | 0 | io_source->Close(); |
309 | 0 | io_source = nullptr; |
310 | 0 | } |
311 | | |
312 | | // Send a message to unblock the reader if its currently waiting for |
313 | | // input. This is just an optimization to make it terminate more |
314 | | // quickly, even without the message it will eventually time out. |
315 | 0 | queue_in.WakeUp(); |
316 | 0 | } |
317 | | |
318 | 0 | void MsgThread::Heartbeat() { |
319 | 0 | if ( child_sent_finish ) |
320 | 0 | return; |
321 | | |
322 | 0 | SendIn(new detail::HeartbeatMessage(this, run_state::network_time, util::current_time())); |
323 | 0 | } |
324 | | |
325 | 0 | void MsgThread::Finished() { |
326 | 0 | child_finished = true; |
327 | 0 | SendOut(new detail::FinishedMessage(this)); |
328 | 0 | } |
329 | | |
330 | 850k | std::string MsgThread::BuildMsgWithLocation(const char* msg) { |
331 | 850k | ODesc desc; |
332 | | |
333 | 850k | if ( auto* location = GetLocationInfo() ) { |
334 | 0 | location->Describe(&desc); |
335 | 0 | desc.Add(": "); |
336 | 0 | } |
337 | | |
338 | 850k | desc.Add(msg); |
339 | 850k | return desc.Description(); |
340 | 850k | } |
341 | | |
342 | 0 | void MsgThread::Info(const char* msg) { |
343 | 0 | SendOut(new detail::ReporterMessage(detail::ReporterMessage::INFO, this, BuildMsgWithLocation(msg))); |
344 | 0 | } |
345 | | |
346 | 0 | void MsgThread::Warning(const char* msg) { |
347 | 0 | SendOut(new detail::ReporterMessage(detail::ReporterMessage::WARNING, this, BuildMsgWithLocation(msg))); |
348 | 0 | } |
349 | | |
350 | 0 | void MsgThread::Error(const char* msg) { |
351 | 0 | SendOut(new detail::ReporterMessage(detail::ReporterMessage::ERROR, this, BuildMsgWithLocation(msg))); |
352 | 0 | } |
353 | | |
354 | 0 | void MsgThread::FatalError(const char* msg) { |
355 | 0 | SendOut(new detail::ReporterMessage(detail::ReporterMessage::FATAL_ERROR, this, BuildMsgWithLocation(msg))); |
356 | 0 | } |
357 | | |
358 | 0 | void MsgThread::FatalErrorWithCore(const char* msg) { |
359 | 0 | SendOut( |
360 | 0 | new detail::ReporterMessage(detail::ReporterMessage::FATAL_ERROR_WITH_CORE, this, BuildMsgWithLocation(msg))); |
361 | 0 | } |
362 | | |
363 | 0 | void MsgThread::InternalWarning(const char* msg) { |
364 | 0 | SendOut(new detail::ReporterMessage(detail::ReporterMessage::INTERNAL_WARNING, this, BuildMsgWithLocation(msg))); |
365 | 0 | } |
366 | | |
367 | 0 | void MsgThread::InternalError(const char* msg) { |
368 | 0 | fprintf(stderr, "internal error in thread: %s\n", BuildMsgWithLocation(msg).c_str()); |
369 | 0 | abort(); |
370 | 0 | } |
371 | | |
372 | 850k | void MsgThread::Debug(DebugStream stream, const char* msg) { |
373 | 850k | SendOut(new detail::DebugMessage(stream, this, BuildMsgWithLocation(msg))); |
374 | 850k | } |
375 | | |
376 | 851k | void MsgThread::SendIn(BasicInputMessage* msg, bool force) { |
377 | 851k | if ( Terminating() && ! force ) { |
378 | 0 | delete msg; |
379 | 0 | return; |
380 | 0 | } |
381 | | |
382 | 851k | DBG_LOG(DBG_THREADING, "Sending '%s' to %s ...", msg->Name(), Name()); |
383 | | |
384 | 851k | queue_in.Put(msg); |
385 | 851k | ++cnt_sent_in; |
386 | | |
387 | 851k | zeek::thread_mgr->MessageIn(); |
388 | 851k | } |
389 | | |
390 | 850k | void MsgThread::SendOut(BasicOutputMessage* msg, bool force) { |
391 | 850k | if ( Terminating() && ! force ) { |
392 | 0 | delete msg; |
393 | 0 | return; |
394 | 0 | } |
395 | | |
396 | 850k | queue_out.Put(msg); |
397 | | |
398 | 850k | ++cnt_sent_out; |
399 | | |
400 | 850k | zeek::thread_mgr->MessageOut(); |
401 | | |
402 | 850k | if ( io_source ) |
403 | 851k | io_source->Fire(); |
404 | 850k | } |
405 | | |
406 | 0 | void MsgThread::SendEvent(const char* name, const int num_vals, Value** vals) { |
407 | 0 | SendOut(new detail::SendEventMessage(this, name, num_vals, vals)); |
408 | 0 | } |
409 | | |
410 | 0 | BasicOutputMessage* MsgThread::RetrieveOut() { |
411 | 0 | BasicOutputMessage* msg = queue_out.Get(); |
412 | 0 | if ( ! msg ) |
413 | 0 | return nullptr; |
414 | | |
415 | 0 | DBG_LOG(DBG_THREADING, "Retrieved '%s' from %s", msg->Name(), Name()); |
416 | |
|
417 | 0 | return msg; |
418 | 0 | } |
419 | | |
420 | 855k | BasicInputMessage* MsgThread::RetrieveIn() { |
421 | 855k | BasicInputMessage* msg = queue_in.Get(); |
422 | | |
423 | 855k | if ( ! msg ) |
424 | 3.93k | return nullptr; |
425 | | |
426 | 851k | #ifdef DEBUG |
427 | 851k | std::string s = Fmt("Retrieved '%s' in %s", msg->Name(), Name()); |
428 | 851k | Debug(DBG_THREADING, s.c_str()); |
429 | 851k | #endif |
430 | | |
431 | 851k | return msg; |
432 | 855k | } |
433 | | |
434 | 339 | void MsgThread::Run() { |
435 | 855k | while ( ! (child_finished || Killed()) ) { |
436 | 855k | BasicInputMessage* msg = RetrieveIn(); |
437 | | |
438 | 855k | if ( ! msg ) |
439 | 3.93k | continue; |
440 | | |
441 | 851k | bool result = msg->Process(); |
442 | | |
443 | 851k | delete msg; |
444 | | |
445 | 851k | if ( ! result ) { |
446 | 0 | Error("terminating thread"); |
447 | | |
448 | | // This will eventually kill this thread, but only |
449 | | // after all other outgoing messages (in particular |
450 | | // error messages have been processed by then main |
451 | | // thread). |
452 | 0 | SendOut(new detail::KillMeMessage(this)); |
453 | 0 | failed = true; |
454 | 0 | } |
455 | 851k | } |
456 | | |
457 | | // In case we haven't sent the finish method yet, do it now. Reading |
458 | | // global network_time here should be fine, it isn't changing |
459 | | // anymore. |
460 | 339 | if ( ! child_finished && ! Killed() ) { |
461 | 0 | OnFinish(run_state::network_time); |
462 | 0 | Finished(); |
463 | 0 | } |
464 | 339 | } |
465 | | |
466 | 0 | void MsgThread::GetStats(Stats* stats) { |
467 | 0 | stats->sent_in = cnt_sent_in.load(); |
468 | 0 | stats->sent_out = cnt_sent_out.load(); |
469 | 0 | stats->pending_in = queue_in.Size(); |
470 | 0 | stats->pending_out = queue_out.Size(); |
471 | 0 | queue_in.GetStats(&stats->queue_in_stats); |
472 | 0 | queue_out.GetStats(&stats->queue_out_stats); |
473 | 0 | } |
474 | | |
475 | 0 | void MsgThread::Process() { |
476 | 0 | while ( HasOut() ) { |
477 | 0 | Message* msg = RetrieveOut(); |
478 | 0 | assert(msg); |
479 | | |
480 | 0 | if ( ! msg->Process() ) { |
481 | 0 | reporter->Error("%s failed, terminating thread", msg->Name()); |
482 | 0 | SignalStop(); |
483 | 0 | } |
484 | |
|
485 | 0 | delete msg; |
486 | 0 | } |
487 | 0 | } |
488 | | |
489 | | } // namespace zeek::threading |