4#include "Stroika/Foundation/StroikaPreComp.h"
6#include <condition_variable>
12#include "Stroika/Foundation/Containers/Mapping.h"
15#include "Stroika/Foundation/Execution/Common.h"
16#include "Stroika/Foundation/Execution/Exceptions.h"
18#if qStroika_Platform_POSIX
19#include "Stroika/Foundation/Execution/Platform/POSIX/SemWaitableEvent.h"
20#include "Stroika/Foundation/Execution/Platform/POSIX/SignalBlock.h"
25#include "Stroika/Foundation/Execution/WaitableEvent.h"
27#include "SignalHandlers.h"
32using namespace Stroika::Foundation::Memory;
42#ifndef qDoDbgTraceOnSignalHandlers_
43#define qDoDbgTraceOnSignalHandlers_ 0
48#ifndef qDoBacktraceOnFirstPassSignalHandler_
49#define qDoBacktraceOnFirstPassSignalHandler_ 0
56#ifndef qConditionVariablesSafeInAsyncSignalHandlers_
57#define qConditionVariablesSafeInAsyncSignalHandlers_ !qStroika_Platform_POSIX
69 sb <<
"type: "sv << GetType ();
71 if (fCall_.target_type () == typeid (
void (*) (SignalID))) {
72 sb <<
", target: "sv <<
"{}"_f(
reinterpret_cast<const void*
> (fCall_.target<void (*) (SignalID)> ()));
76 sb <<
", targetType: "sv << fCall_.target_type ();
92class SignalHandlerRegistry::SafeSignalsManager::Rep_ final {
94 void waitForNextSig_ ()
96#if qConditionVariablesSafeInAsyncSignalHandlers_
97 Assert (not qStroika_Platform_POSIX);
98 unique_lock<mutex> lk{fRecievedSig_NotSureWhatMutexFor_};
99 fRecievedSig_.wait_for (lk, chrono::seconds (100), [
this] () {
return fWorkMaybeAvailable_.load (); });
101 fRecievedSig_.Wait ();
104 void tell2WakeAfterDataUpdate_ ()
106#if qConditionVariablesSafeInAsyncSignalHandlers_
107 Assert (not qStroika_Platform_POSIX);
108 fRecievedSig_.notify_one ();
110 [[maybe_unused]]
auto&& lk = lock_guard{fRecievedSig_NotSureWhatMutexFor_};
111 fWorkMaybeAvailable_ =
true;
113 fRecievedSig_.notify_one ();
115 fWorkMaybeAvailable_ =
true;
116 fRecievedSig_.Set ();
123#if USE_NOISY_TRACE_IN_THIS_MODULE_
124 Debug::TraceContextBumper trcCtx{L
"Stroika::Foundation::Execution::SignalHandlerRegistry::SafeSignalsManager::Rep_::CTOR",
135#if USE_NOISY_TRACE_IN_THIS_MODULE_
136 DbgTrace (
"fRecievedSig_ wait complete (either arrival or timeout): fLastSignalRecieved_ = {}"_f, fLastSignalRecieved_.load ());
138 if (fLastSignalRecieved_ < NSIG) {
140 for (
int i = 0; i < NSIG; ++i) {
141 while (fIncomingSignalCounts_[i] > 0) {
142 DbgTrace (
"fIncomingSignalCounts_[{}] = {}"_f, i, fIncomingSignalCounts_[i].load ());
143 fIncomingSignalCounts_[i]--;
145 for (
SignalHandler sh : fHandlers_.rwget ()->LookupValue (i)) {
146 Assert (sh.GetType () == SignalHandler::Type::eSafe);
147 IgnoreExceptionsExceptThreadAbortForCall (sh (i));
153 fLastSignalRecieved_ = NSIG;
155 for (
int i = 0; i < NSIG; ++i) {
156 while (fIncomingSignalCounts_[i] > 0) {
157 fLastSignalRecieved_ = i;
158 DbgTrace (
"Rare, but possible race avoidance"_f);
165 if (fWorkMaybeAvailable_.exchange (
false)) {
170 Thread::eAutoStart,
"Signal Handler Safe Execution Thread"sv);
176 Debug::TraceContextBumper trcCtx{
"Stroika::Foundation::Execution::SignalHandlerRegistry::SafeSignalsManager::Rep_::~Rep_",
179 fBlockingQueuePusherThread_.Abort ();
180 tell2WakeAfterDataUpdate_ ();
181 fBlockingQueuePusherThread_.AbortAndWaitForDone ();
188 void NotifyOfArrivalOfPossiblySafeSignal (SignalID signal)
190 Require (0 <= signal and signal <
static_cast<SignalID
> (std::size (fIncomingSignalCounts_)));
193 if (fHanlderAvailable_[signal]) {
194 fIncomingSignalCounts_[signal]++;
195 fLastSignalRecieved_ = signal;
196 tell2WakeAfterDataUpdate_ ();
209 return fHandlers_.cget ()->LookupValue (signal);
213 void Remove (SignalID signal)
215 fHandlers_.rwget ()->RemoveIf (signal);
216 PopulateSafeSignalHandlersCache_ (signal);
222 fHandlers_.rwget ()->Add (signal, safeHandlers);
223 PopulateSafeSignalHandlersCache_ (signal);
227 void PopulateSafeSignalHandlersCache_ (SignalID signal)
229 Require (0 <= signal and signal <
static_cast<SignalID
> (std::size (fHanlderAvailable_)));
230 fHanlderAvailable_[signal] = fHandlers_.rwget ()->Lookup (signal).has_value ();
235 bool fHanlderAvailable_[NSIG]{};
242 atomic<unsigned int> fIncomingSignalCounts_[NSIG]{};
243 atomic<SignalID> fLastSignalRecieved_{NSIG};
246 atomic<bool> fWorkMaybeAvailable_{
false};
247#if qConditionVariablesSafeInAsyncSignalHandlers_
248 mutex fRecievedSig_NotSureWhatMutexFor_;
249 condition_variable fRecievedSig_;
260SignalHandlerRegistry::SafeSignalsManager::SafeSignalsManager ()
263#if defined(__cpp_lib_atomic_shared_ptr)
264 Require (sTheRep_.load () ==
nullptr);
265 sTheRep_.store (MakeSharedPtr<SignalHandlerRegistry::SafeSignalsManager::Rep_> ());
267 Require (atomic_load (&sTheRep_) ==
nullptr);
268 atomic_store (&sTheRep_, MakeSharedPtr<SignalHandlerRegistry::SafeSignalsManager::Rep_> ());
272SignalHandlerRegistry::SafeSignalsManager::~SafeSignalsManager ()
275#if defined(__cpp_lib_atomic_shared_ptr)
276 SignalHandlerRegistry::SafeSignalsManager::sTheRep_.store (shared_ptr<Rep_>{});
278 atomic_store (&SignalHandlerRegistry::SafeSignalsManager::sTheRep_, shared_ptr<Rep_>{});
289SignalHandlerRegistry::SignalHandlerRegistry ()
292 [[maybe_unused]]
static int nConstructed = 0;
294 Assert (nConstructed == 1);
299SignalHandlerRegistry::~SignalHandlerRegistry ()
302#if defined(__cpp_lib_atomic_shared_ptr)
303 Assert (SafeSignalsManager::sTheRep_.load () ==
nullptr);
305 Assert (atomic_load (&SafeSignalsManager::sTheRep_) ==
nullptr);
312#if defined(__cpp_lib_atomic_shared_ptr)
313 if (shared_ptr<SafeSignalsManager::Rep_> tmp = SafeSignalsManager::sTheRep_.load ()) {
315 if (shared_ptr<SafeSignalsManager::Rep_> tmp = atomic_load (&SafeSignalsManager::sTheRep_)) {
317 result += tmp->GetHandledSignals ();
325#if defined(__cpp_lib_atomic_shared_ptr)
326 if (shared_ptr<SafeSignalsManager::Rep_> tmp = SafeSignalsManager::sTheRep_.load ()) {
328 if (shared_ptr<SafeSignalsManager::Rep_> tmp = atomic_load (&SafeSignalsManager::sTheRep_)) {
331 result += tmp->GetSignalHandlers (signal);
349 "Stroika::Foundation::Execution::SignalHandlerRegistry::{}::SetSignalHandlers",
"signal: {}, handlers: {}"_f, SignalToName (signal), handlers)};
354 switch (si.GetType ()) {
355 case SignalHandler::Type::eDirect: {
356 directHandlers.Add (si);
358 case SignalHandler::Type::eSafe: {
359 safeHandlers.
Add (si);
363 Assert (directHandlers.
size () + safeHandlers.
size () == handlers.
size ());
365 shared_ptr<SignalHandlerRegistry::SafeSignalsManager::Rep_> tmp = SignalHandlerRegistry::SafeSignalsManager::sTheRep_;
367 if (not safeHandlers.
empty ()) {
371#if defined(__cpp_lib_atomic_shared_ptr)
372 Require (SafeSignalsManager::sTheRep_.load () !=
nullptr);
374 Require (atomic_load (&SafeSignalsManager::sTheRep_) !=
nullptr);
378 auto sigSetHandler = [] (SignalID signal, [[maybe_unused]] void (*fun) (int)) {
379#if qStroika_Platform_POSIX
380 struct sigaction sa{};
382 Verify (sigemptyset (&sa.sa_mask) == 0);
384 Verify (::sigaction (signal, &sa,
nullptr) == 0);
386 Verify (::signal (signal, FirstPassSignalHandler_) != SIG_ERR);
391 auto l = fDirectHandlers_.rwget ();
392 if (directHandlers.
empty ()) {
393 l->RemoveIf (signal);
396 l->Add (signal, directHandlers);
399 Require (0 <= signal and signal <
static_cast<SignalID
> (std::size (fDirectSignalHandlersCache_)));
400 vector<function<void (SignalID)>> shs;
406#if qStroika_Platform_POSIX
411 [[maybe_unused]]
auto&& cleanup =
Finally ([
this] ()
noexcept { fDirectSignalHandlersCache_Lock_--; });
412 if (fDirectSignalHandlersCache_Lock_++ == 0) {
413 fDirectSignalHandlersCache_[signal] = shs;
422 if (tmp !=
nullptr) {
423 if (safeHandlers.
empty ()) {
424 tmp->Remove (signal);
427 tmp->Add (signal, safeHandlers);
432 if (handlers.
empty ()) {
433 sigSetHandler (signal, SIG_DFL);
436 sigSetHandler (signal, SIG_IGN);
439 sigSetHandler (signal, FirstPassSignalHandler_);
453 Require (s.Contains (handler));
460 DbgTrace (
"Serious Signal Error trapped: {} ... Aborting"_f, SignalToName (signal));
467 results.
Add (SIGABRT);
468 results.
Add (SIGILL);
469 results.
Add (SIGFPE);
470 results.
Add (SIGSEGV);
471#if qStroika_Platform_POSIX
472 results.
Add (SIGSYS);
473 results.
Add (SIGBUS);
480 for (SignalID s : forSignals) {
488Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_THREAD
void SignalHandlerRegistry::FirstPassSignalHandler_ (SignalID signal)
588#if qDoDbgTraceOnSignalHandlers_
589 Debug::TraceContextBumper trcCtx{L
"Stroika::Foundation::Execution::SignalHandlerRegistry::FirstPassSignalHandler_", L
"signal = %s",
590 SignalToName (signal).
c_str ()};
592#if qDoBacktraceOnFirstPassSignalHandler_ and qStroika_Foundation_Debug_DefaultTracingOn
594 wstring tmp{Debug::BackTrace ()};
595 if (not tmp.empty ()) {
596 DbgTrace (L
"BackTrace: %s", tmp.c_str ());
618 Require (0 <= signal and signal <
static_cast<SignalID
> (std::size (SHR.fDirectSignalHandlersCache_)));
620 DISABLE_COMPILER_CLANG_WARNING_START (
"clang diagnostic ignored \"-Wunused-lambda-capture\"")
621 [[maybe_unused]]
auto&& cleanup =
Finally ([&SHR] ()
noexcept { SHR.fDirectSignalHandlersCache_Lock_--; });
622 DISABLE_COMPILER_CLANG_WARNING_END (
"clang diagnostic ignored \"-Wunused-lambda-capture\"")
623 if (SHR.fDirectSignalHandlersCache_Lock_++ == 0) {
624 const vector<function<void (SignalID)>>* shs = &SHR.fDirectSignalHandlersCache_[signal];
625 for (
auto shi = shs->begin (); shi != shs->end (); ++shi) {
638#if defined(__cpp_lib_atomic_shared_ptr)
639 shared_ptr<SignalHandlerRegistry::SafeSignalsManager::Rep_> tmp = SignalHandlerRegistry::SafeSignalsManager::sTheRep_.load ();
641 shared_ptr<SignalHandlerRegistry::SafeSignalsManager::Rep_> tmp = atomic_load (&SignalHandlerRegistry::SafeSignalsManager::sTheRep_);
643 if (tmp !=
nullptr) {
644 tmp->NotifyOfArrivalOfPossiblySafeSignal (signal);
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Similar to String, but intended to more efficiently construct a String. Mutable type (String is large...
String is like std::u32string, except it is much easier to use, often much more space efficient,...
nonvirtual tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
A Mapping uniquely associates two elements: a key and a value (use Assocation to allow multiple value...
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
nonvirtual void Remove(ArgByValueType< value_type > item)
Remove the item (given by value or iterator pointing to it) from the contain. The item MUST exist.
nonvirtual void Add(ArgByValueType< value_type > item)
static void DefaultCrashSignalHandler(SignalID signal)
static Containers::Set< SignalID > GetStandardCrashSignals()
nonvirtual void AddSignalHandler(SignalID signal, const SignalHandler &handler)
nonvirtual void SetSignalHandlers(SignalID signal)
nonvirtual void RemoveSignalHandler(SignalID signal, const SignalHandler &handler)
nonvirtual void SetStandardCrashHandlerSignals(SignalHandler handler=SignalHandler{DefaultCrashSignalHandler, SignalHandler::Type::eDirect}, const Containers::Set< SignalID > &forSignals=GetStandardCrashSignals())
static const SignalHandler kIGNORED
nonvirtual Containers::Set< SignalID > GetHandledSignals() const
nonvirtual Containers::Set< SignalHandler > GetSignalHandlers(SignalID signal) const
static SignalHandlerRegistry sThe
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
Thread::Ptr is a (unsynchronized) smart pointer referencing an internally synchronized std::thread ob...
nonvirtual size_t size() const
Returns the number of items contained.
nonvirtual void Apply(const function< void(ArgByValueType< T > item)> &doToElement) const
Run the argument function (or lambda) on each element of the container.
nonvirtual bool empty() const
Returns true iff size() == 0.
Ptr New(const function< void()> &fun2CallOnce, const optional< Characters::String > &name, const optional< Configuration > &configuration)
void CheckForInterruption()
void Sleep(Time::Duration seconds2Wait)
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >