Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
SignalHandlers.h
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Execution_SignalHandlers_h_
5#define _Stroika_Foundation_Execution_SignalHandlers_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <compare>
10#include <csignal>
11#include <functional>
12
13#include "Stroika/Foundation/Common/Common.h"
15#include "Stroika/Foundation/Containers/Mapping.h"
16#include "Stroika/Foundation/Containers/Set.h"
17#include "Stroika/Foundation/Execution/Signals.h"
19
20/**
21 * Description:
22 *
23 * This module defines support for POSIX (and std c++ defined) Signals (not to be confused
24 * with the 'Signals and slots' design pattern which is largely unrelated). (POSIX) Signals have all sorts of tricky
25 * rules for their manipulation/control. This doesn't eliminate those, but it makes most common cases much easier
26 * to handle safely. In particular, many cases of signals can be 'handled' not as signals (safe signal handlers).
27 *
28 *
29 * TODO:
30 * @todo https://github.com/SophistSolutions/Stroika/issues/601 (STK-467) Lose shared_ptr<> with SignalHandlerRegistry::SafeSignalsManager::Rep_
31 * and change semantics - assert or autodelete - safe handlers on destroy of safe signal mgr.
32 *
33 * @todo Small issue - AddSignalHandler versus SetSignalHandler (). This can be confusing. I had a bug
34 * which was we setup DEFAULT signal handlers, and then in the BasicUNIX Serviced code - did
35 * AddSignalHandler (SIGINT). Issue is that we process BOTH handlers - one to set an event
36 * object to cleanly shutdown and the other to HARD ABORT!
37 *
38 * This API encourages that mistake. I changed the Service code to use SetSignalHandler - but
39 * That has the default of being hard to debug/non-modular. Maybe have a "SetDefaultHandler"
40 * or "SetFallbackHandler" - and that is invoked ONLY if no others? Or maybe a property of all
41 * handlers?
42 *
43 * @todo Consider use of threadpool for SafeSignalsManager
44 * Good idea since we may need to handle signals quickly so as to not overflow
45 * fixed size incoming buffer.
46 *
47 * @todo Do overload (or some such) for (sa_sigaction)(int, siginfo_t *, void *); Allow
48 * these to be (more or less) interchangable with regular SignalHandler.
49 *
50 * @todo Consider adding "ThreadSignalHandler" facility - where we register a set of handlers
51 * that ONLY apply when the signal is sent to the given (argument with register) thread.
52 * If we do this - we will want to write cooperating code with the thread start/end
53 * stuff so these get cleared out appropriately.
54 * Consider how this might be useful for stuff like SIGPIPE handling?
55 */
56
58
59 /**
60 * A key feature of SignalHandler versus function<void(SignalID)> is that you can compare them
61 *
62 * Note that to do so, you must save the original SignalHandler you create to later remove it by value:
63 * creating another SignalHandler (even with the same arguments) does not compare equal (@see operator<=>).
64 *
65 * Also, signal handlers come with a flag indicating that they are intended to be run in a 'safe' manner
66 * or a direct signal handling manner.
67 *
68 * \note BEWARE - these may be copied during invocation, which for 'direct' signal handers is a
69 * dangerous, finicky place. Copy must not do operations (like allocate memory) which would be
70 * unsafe during signal (direct) handling.
71 */
73 public:
74 /**
75 * A direct (eDirect) signal handler is invoked in the stack context in which the
76 * signal is delivered. Direct signal handlers are VERY UNSAFE and use carefully,
77 * since they can EASILY produce deadlocks.
78 *
79 * A 'safe' (eSafe) signal handler is run in a separate thread context.
80 *
81 * @see SignalHandlerRegistry::SafeSignalsManager.
82 *
83 * \note Common::DefaultNames<> supported
84 */
85 enum class Type {
86 eDirect,
87 eSafe,
88
89 eDEFAULT = eSafe,
90
91 Stroika_Define_Enum_Bounds (eDirect, eSafe)
92 };
93
94 public:
95 /**
96 */
97 static constexpr Type eDirect = Type::eDirect;
98
99 public:
100 /**
101 */
102 static constexpr Type eSafe = Type::eSafe;
103
104 public:
105 /**
106 * Any overload can be used for safe exception handlers, but only the noexcept overload may be used
107 * for 'direct' exception calls (as a documentation hint - no real need).
108 *
109 * \note https://stackoverflow.com/questions/38760784/how-will-c17-exception-specifier-type-system-work
110 * and the existing compiler vs2k17 15.9.3 - dont support this noexcept on function objects, so
111 * try again later.
112 */
113 SignalHandler () = delete;
114 SignalHandler (const SignalHandler&) = default;
115 SignalHandler (void (*signalHandler) (SignalID) noexcept, Type type = Type::eDEFAULT);
116 SignalHandler (void (*signalHandler) (SignalID), Type type = Type::eDEFAULT);
117 SignalHandler (const function<void (SignalID)>& signalHandler, Type type = Type::eDEFAULT);
118
119 public:
120 nonvirtual Type GetType () const;
121
122 public:
123 /**
124 * Invoke the actual signal handler.
125 */
126 nonvirtual void operator() (SignalID i) const;
127
128 public:
129 /**
130 */
131 nonvirtual Characters::String ToString () const;
132
133 public:
134 /**
135 * \brief Each SignalHandler constructed is its own, kept by its copies: another made the same way - even from the same
136 * function - is not equal to it. So to remove one, keep the one added.
137 */
138 nonvirtual strong_ordering operator<=> (const SignalHandler& rhs) const;
139
140 public:
141 /**
142 * \brief True only for the same SignalHandler, or a copy of it - @see operator<=>
143 */
144 nonvirtual bool operator== (const SignalHandler& rhs) const;
145
146 private:
147 Type fType_;
148 function<void (SignalID)> fCall_;
150 };
151
152 /**
153 * SignalHandlerRegistry is a singleton object. If used - it itself registers signal handlers
154 * for each supported signal.
155 *
156 * The user can then add in their own 'handlers' for those signals, and they are ALL called -
157 * one after the other (TDB how threads work with this).
158 *
159 * When an platform signal-handler is installed (via 'sigaction' for example) -
160 * and then later UNINSTALLED (due to changes in GetHandledSignals) - this code resets the
161 * signal handler to SIG_DFL (not the previous value).
162 *
163 * \note The SignalHandlerRegistry must only be accessed after the start of main, and must not
164 * be accessed after main has completed.
165 *
166 * To use 'safe' signal handlers, be sure to read about and use
167 * @see SignalHandlerRegistry::SafeSignalsManager
168 *
169 * \note Why not Execution::CallbackRegistry, and an ID to remove a handler by, as Stroika's other callbacks are: a direct
170 * handler runs in the signal's own context, where CallbackRegistry's lock, and the copy each Call makes, are not
171 * safe; and a signal's handlers are a set of values - got and set whole (GetSignalHandlers, SetSignalHandlers), with
172 * kIGNORED among them meaning 'ignore it' - which an ID-based registry does not model. So a SignalHandler stays a
173 * value with an identity: to remove one, keep the one added.
174 *
175 * \note \em Thread-Safety <a href="Thread-Safety.md#Internally-Synchronized-Thread-Safety">Internally-Synchronized-Thread-Safety</a>
176 *
177 */
179 public:
180 /**
181 * If this handler is set to the the ONLY handler for a given signal, then that signal handler is
182 * effectively ignored.
183 *
184 * To get the signal to be handled the DEFAULT way - remove all signal handlers.
185 */
187
188 public:
189 /**
190 * Access singleton implementation. None exists until this is called.
191 */
193 [[deprecated ("Since Stroika v3.0d21, use SignalHandlerRegistry::sThe")]] static SignalHandlerRegistry& Get ()
194 {
195 return sThe;
196 }
197
198 private:
200
201 public:
204
205 public:
206 nonvirtual SignalHandlerRegistry& operator= (const SignalHandlerRegistry&) = delete;
207
208 public:
209 class SafeSignalsManager;
210
211 public:
212 /**
213 * Returns the set of signals trapped by the SignalHandlerRegistry registry. Note - if not 'Installed ()' - these
214 * are tracked internally by Stroika but not actually installed in the OS.
215 */
217
218 public:
219 /**
220 * Returns the set of signals trapped by the SignalHandlerRegistry registry. This doesn't imply there is a handler.
221 * NB: A signal handler must be registered for a given signal number AND the signal number must be in GetHandledSignals () AND
222 * the SignalHandlerRegistry must be Installed () - to get the signal called.
223 *
224 * It is NOT an error to have a signal handler registered for a signal not in the set of GetHandledSignals () - or vice versa.
225 * Signals in the list of GetHandledSignals() with no handlers are effectively ignored.
226 */
227 nonvirtual Containers::Set<SignalHandler> GetSignalHandlers (SignalID signal) const;
228
229 public:
230 /**
231 * @see GetSignalHandlers().
232 *
233 * SetSignalHandlers () with NO arguments uninstalls all Stroika signal handlers for this signal.
234 * SetSignalHandlers () with ONE argument makes Stroika take-over the signal handling - and sets the set of hanlders to be
235 * exactly the one given (effectively removing any others previously added).
236 * SetSignalHandlers () with ONE a set of handlers registers all the given handlers.
237 *
238 * Note - if through ANY combination of set/add/remove - you have NO signal handler - this reverts to SIG_DFL, and if you have
239 * exactly ONE signal handler - and its kIGNORED- the signal will be ignored.
240 *
241 * \note Setting any 'Safe' signal handlers requires that SafeSignalsManager has been created.
242 */
243 nonvirtual void SetSignalHandlers (SignalID signal);
244 nonvirtual void SetSignalHandlers (SignalID signal, const SignalHandler& handler);
245 nonvirtual void SetSignalHandlers (SignalID signal, const Containers::Set<SignalHandler>& handlers);
246
247 public:
248 /**
249 * @see GetSignalHandlers().
250 *
251 * \note - subtlety - if you wish to later call RemoveSignalHandler, save the signalhandler in a static const of type
252 * SignalHandler, and re-use that value.
253 *
254 * \note Adding any 'Safe' signal handlers requires that SafeSignalsManager has been created.
255 */
256 nonvirtual void AddSignalHandler (SignalID signal, const SignalHandler& handler);
257
258 public:
259 /**
260 * @see GetSignalHandlers()
261 */
262 nonvirtual void RemoveSignalHandler (SignalID signal, const SignalHandler& handler);
263
264 public:
265 /**
266 * This signal handler simply prints error to the trace log, and calls 'abort' - which on most operating systems will allow the
267 * debugger to examine the errant code.
268 */
269 static void DefaultCrashSignalHandler (SignalID signal);
270
271 public:
272 /**
273 * These signals are generally associated with a programming error or bug, and these signals
274 * should generally be treated as a crash and terminate the program with a core-dump file.
275 * o SIGABRT
276 * o SIGILL
277 * o SIGFPE
278 * o SIGSEGV
279 * o SIGSYS (POSIX ONLY)
280 * o SIGBUS (POSIX ONLY)
281 * o SIGQUIT (POSIX ONLY)
282 */
284
285 public:
286 /**
287 * The set of signals given (by default GetStandardCrashSignals) will be set to the given handler
288 * (by default DefaultCrashSignalHandler).
289 *
290 * The only exception is SIGABRT will be intentionally ignored from this call because it prevents abort()
291 * from functioning properly. We COULD disable SIGABRT upon receipt of that signal (SIG_DFL) but that
292 * would be different than other signals handled, raise re-entrancy issues etc. Didn't seem worth while.
293 *
294 * \note By default these are created as 'unsafe' signal handlers, meaning that they are run
295 * on the thread that triggered the error. This is mostly because we (currently) have no other
296 * way to capture what thread we were running on at that point.
297 *
298 * This is bad, because we could deadlock in handling the crash. But realistically, we were crashing
299 * anyhow, so the better stack trace maybe worth it.
300 *
301 * @todo we may want to come up with some way to capture / pass along extra info to handlers, like
302 * the thread which recieved the signal. But for now...
303 */
304 nonvirtual void SetStandardCrashHandlerSignals (SignalHandler handler = SignalHandler{DefaultCrashSignalHandler, SignalHandler::Type::eDirect},
306
307 private:
308 RWSynchronized<Containers::Mapping<SignalID, Containers::Set<SignalHandler>>> fDirectHandlers_;
309
310 private:
311 mutable atomic<unsigned int> fDirectSignalHandlersCache_Lock_{0};
312 vector<function<void (SignalID)>> fDirectSignalHandlersCache_[NSIG];
313
314 private:
315 static void FirstPassSignalHandler_ (SignalID signal);
316 };
317 inline SignalHandlerRegistry SignalHandlerRegistry::sThe;
318
319 /**
320 * A direct (eDirect) signal handler is invoked in the stack context in which the
321 * signal is delivered.
322 *
323 * A 'safe' (eSafe) signal handler is run in a separate thread context.
324 *
325 * Direct is more performant, and has lower latency. However, since many signals are
326 * delivered on an arbitrary thread, this can easily lead to deadlocks!
327 * See http://stackoverflow.com/questions/3366307/why-is-malloc-not-async-signal-safe
328 *
329 * 'safe' signal handlers avoid this by automatically moving the signal delivery to a special
330 * thread that handles the signals one at a time, and careful to avoid any memory allocation
331 * in the direct signal handling thread.
332 *
333 * To use this feature, you must construct a SignalHandlerRegistry::SafeSignalsManager.
334 *
335 * \note This must be constructed BEFORE adding any safe signal handler, and
336 * must be destroyed before exiting main (so it can shutdown its own threads).
337 *
338 * And only one SafeSignalsManager instance can exist at a time.
339 *
340 * The easiest (and recommened) way to do this is to add the line:
341 * SignalHandlerRegistry::SafeSignalsManager safeSignalsMgr;
342 * to the beginning of main ([[maybe_unused]] int argc, [[maybe_unused]] const char* argv[])...
343 */
345 public:
346 /**
347 */
349 SafeSignalsManager (const SafeSignalsManager&) = delete;
350
351 public:
353
354 public:
355 nonvirtual SafeSignalsManager& operator= (const SafeSignalsManager&) = delete;
356
357 private:
358 class Rep_;
359#if defined(__cpp_lib_atomic_shared_ptr)
360 static inline atomic<shared_ptr<Rep_>> sTheRep_{nullptr};
361#else
362 static inline shared_ptr<Rep_> sTheRep_{nullptr};
363#endif
364
365 private:
366 friend class SignalHandlerRegistry;
367 };
368
369}
370
371/*
372 ********************************************************************************
373 ***************************** Implementation Details ***************************
374 ********************************************************************************
375 */
376#include "SignalHandlers.inl"
377
378#endif /*_Stroika_Foundation_Execution_SignalHandlers_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
An opaque ID - each New () one never given before, in this process - of a type of its own for each TA...
Definition UniqueID.h:50
static UniqueID New()
One never given before in this process - by New () of any TAG.
Definition UniqueID.inl:19
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
nonvirtual bool operator==(const SignalHandler &rhs) const
True only for the same SignalHandler, or a copy of it -.
nonvirtual strong_ordering operator<=>(const SignalHandler &rhs) const
Each SignalHandler constructed is its own, kept by its copies: another made the same way - even from ...
nonvirtual void operator()(SignalID i) const
static Containers::Set< SignalID > GetStandardCrashSignals()
nonvirtual void AddSignalHandler(SignalID signal, const SignalHandler &handler)
nonvirtual void RemoveSignalHandler(SignalID signal, const SignalHandler &handler)
nonvirtual void SetStandardCrashHandlerSignals(SignalHandler handler=SignalHandler{DefaultCrashSignalHandler, SignalHandler::Type::eDirect}, const Containers::Set< SignalID > &forSignals=GetStandardCrashSignals())
nonvirtual Containers::Set< SignalID > GetHandledSignals() const
nonvirtual Containers::Set< SignalHandler > GetSignalHandlers(SignalID signal) const