4#include "Stroika/Foundation/StroikaPreComp.h"
6#include <condition_variable>
12#include "Stroika/Foundation/Containers/Mapping.h"
17#include "Exceptions.h"
18#if qStroika_Foundation_Common_Platform_POSIX
19#include "Platform/POSIX/SemWaitableEvent.h"
20#include "Platform/POSIX/SignalBlock.h"
25#include "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 qConditionVariablesSafeInAsyncSignalHanlders
57#define qConditionVariablesSafeInAsyncSignalHanlders !qStroika_Foundation_Common_Platform_POSIX
69 sb <<
"type: "sv << GetType ();
71 Function<void (SignalID)>::STDFUNCTION stdFuncTarget =
static_cast<Function<
void (SignalID)
>::STDFUNCTION> (fCall_);
72 if (stdFuncTarget.target_type () ==
typeid (void (*) (SignalID))) {
73 sb <<
", target: "sv <<
"{}"_f(
reinterpret_cast<const void*
> (stdFuncTarget.target<void (*) (SignalID)> ()));
75 else if (stdFuncTarget.target_type () == typeid (
Function<
void (SignalID)>)) {
76 sb <<
", target: "sv <<
"{}"_f(
reinterpret_cast<const void*
> (stdFuncTarget.target<
Function<void (SignalID)>> ()));
80 sb <<
", target-type: "sv << stdFuncTarget.target_type ();
91DISABLE_COMPILER_MSC_WARNING_START (4351)
99 void waitForNextSig_ ()
101#if qConditionVariablesSafeInAsyncSignalHanlders
102 Assert (not qStroika_Foundation_Common_Platform_POSIX);
103 unique_lock<mutex> lk{fRecievedSig_NotSureWhatMutexFor_};
104 fRecievedSig_.wait_for (lk, chrono::seconds (100), [
this] () {
return fWorkMaybeAvailable_.load (); });
106 fRecievedSig_.Wait ();
109 void tell2WakeAfterDataUpdate_ ()
111#if qConditionVariablesSafeInAsyncSignalHanlders
112 Assert (not qStroika_Foundation_Common_Platform_POSIX);
113 fRecievedSig_.notify_one ();
115 [[maybe_unused]]
auto&& lk = lock_guard{fRecievedSig_NotSureWhatMutexFor_};
116 fWorkMaybeAvailable_ =
true;
118 fRecievedSig_.notify_one ();
120 fWorkMaybeAvailable_ =
true;
121 fRecievedSig_.Set ();
128#if USE_NOISY_TRACE_IN_THIS_MODULE_
129 Debug::TraceContextBumper trcCtx{L
"Stroika::Foundation::Execution::SignalHandlerRegistry::SafeSignalsManager::Rep_::CTOR",
140#if USE_NOISY_TRACE_IN_THIS_MODULE_
141 DbgTrace (
"fRecievedSig_ wait complete (either arrival or timeout): fLastSignalRecieved_ = {}"_f, fLastSignalRecieved_.load ());
143 if (fLastSignalRecieved_ < NSIG) {
145 for (
int i = 0; i < NSIG; ++i) {
146 while (fIncomingSignalCounts_[i] > 0) {
147 DbgTrace (
"fIncomingSignalCounts_[{}] = {}"_f, i, fIncomingSignalCounts_[i].load ());
148 fIncomingSignalCounts_[i]--;
150 for (
SignalHandler sh : fHandlers_.rwget ()->LookupValue (i)) {
151 Assert (sh.GetType () == SignalHandler::Type::eSafe);
152 IgnoreExceptionsExceptThreadAbortForCall (sh (i));
158 fLastSignalRecieved_ = NSIG;
160 for (
int i = 0; i < NSIG; ++i) {
161 while (fIncomingSignalCounts_[i] > 0) {
162 fLastSignalRecieved_ = i;
163 DbgTrace (
"Rare, but possible race avoidance"_f);
170 if (fWorkMaybeAvailable_.exchange (
false)) {
175 Thread::eAutoStart,
"Signal Handler Safe Execution Thread"sv);
181 Debug::TraceContextBumper trcCtx{
"Stroika::Foundation::Execution::SignalHandlerRegistry::SafeSignalsManager::Rep_::~Rep_",
184 fBlockingQueuePusherThread_.Abort ();
185 tell2WakeAfterDataUpdate_ ();
186 fBlockingQueuePusherThread_.AbortAndWaitForDone ();
193 void NotifyOfArrivalOfPossiblySafeSignal (SignalID signal)
195 Require (0 <= signal and signal <
static_cast<SignalID
> (NEltsOf (fIncomingSignalCounts_)));
198 if (fHanlderAvailable_[signal]) {
199 fIncomingSignalCounts_[signal]++;
200 fLastSignalRecieved_ = signal;
201 tell2WakeAfterDataUpdate_ ();
214 return fHandlers_.cget ()->LookupValue (signal);
218 void Remove (SignalID signal)
220 fHandlers_.rwget ()->
RemoveIf (signal);
221 PopulateSafeSignalHandlersCache_ (signal);
227 fHandlers_.rwget ()->Add (signal, safeHandlers);
228 PopulateSafeSignalHandlersCache_ (signal);
232 void PopulateSafeSignalHandlersCache_ (SignalID signal)
234 Require (0 <= signal and signal <
static_cast<SignalID
> (NEltsOf (fHanlderAvailable_)));
235 fHanlderAvailable_[signal] = fHandlers_.rwget ()->Lookup (signal).has_value ();
240 bool fHanlderAvailable_[NSIG]{};
247 atomic<unsigned int> fIncomingSignalCounts_[NSIG]{};
248 atomic<SignalID> fLastSignalRecieved_{NSIG};
251 atomic<bool> fWorkMaybeAvailable_{
false};
252#if qConditionVariablesSafeInAsyncSignalHanlders
253 mutex fRecievedSig_NotSureWhatMutexFor_;
254 condition_variable fRecievedSig_;
259DISABLE_COMPILER_MSC_WARNING_END (4351)
269#if __cpp_lib_atomic_shared_ptr >= 201711
270 Require (sTheRep_.load () ==
nullptr);
271 sTheRep_.store (make_shared<SignalHandlerRegistry::SafeSignalsManager::Rep_> ());
273 Require (atomic_load (&sTheRep_) ==
nullptr);
274 atomic_store (&sTheRep_, make_shared<SignalHandlerRegistry::SafeSignalsManager::Rep_> ());
278SignalHandlerRegistry::SafeSignalsManager::~SafeSignalsManager ()
281#if __cpp_lib_atomic_shared_ptr >= 201711
282 SignalHandlerRegistry::SafeSignalsManager::sTheRep_.store (shared_ptr<Rep_>{});
284 atomic_store (&SignalHandlerRegistry::SafeSignalsManager::sTheRep_, shared_ptr<Rep_>{});
301SignalHandlerRegistry::SignalHandlerRegistry ()
304 [[maybe_unused]]
static int nConstructed = 0;
306 Assert (nConstructed == 1);
311SignalHandlerRegistry::~SignalHandlerRegistry ()
314#if __cpp_lib_atomic_shared_ptr >= 201711
315 Assert (SafeSignalsManager::sTheRep_.load () ==
nullptr);
317 Assert (atomic_load (&SafeSignalsManager::sTheRep_) ==
nullptr);
324#if __cpp_lib_atomic_shared_ptr >= 201711
325 if (shared_ptr<SafeSignalsManager::Rep_> tmp = SafeSignalsManager::sTheRep_.load ()) {
327 if (shared_ptr<SafeSignalsManager::Rep_> tmp = atomic_load (&SafeSignalsManager::sTheRep_)) {
329 result += tmp->GetHandledSignals ();
337#if __cpp_lib_atomic_shared_ptr >= 201711
338 if (shared_ptr<SafeSignalsManager::Rep_> tmp = SafeSignalsManager::sTheRep_.load ()) {
340 if (shared_ptr<SafeSignalsManager::Rep_> tmp = atomic_load (&SafeSignalsManager::sTheRep_)) {
343 result += tmp->GetSignalHandlers (signal);
361 "Stroika::Foundation::Execution::SignalHandlerRegistry::{}::SetSignalHandlers",
"signal: {}, handlers: {}"_f, SignalToName (signal), handlers)};
366 switch (si.GetType ()) {
367 case SignalHandler::Type::eDirect: {
368 directHandlers.Add (si);
370 case SignalHandler::Type::eSafe: {
371 safeHandlers.
Add (si);
375 Assert (directHandlers.
size () + safeHandlers.
size () == handlers.
size ());
377 shared_ptr<SignalHandlerRegistry::SafeSignalsManager::Rep_> tmp = SignalHandlerRegistry::SafeSignalsManager::sTheRep_;
379 if (not safeHandlers.
empty ()) {
383#if __cpp_lib_atomic_shared_ptr >= 201711
384 Require (SafeSignalsManager::sTheRep_.load () !=
nullptr);
386 Require (atomic_load (&SafeSignalsManager::sTheRep_) !=
nullptr);
390 auto sigSetHandler = [] (SignalID signal, [[maybe_unused]] void (*fun) (int)) {
391#if qStroika_Foundation_Common_Platform_POSIX
392 struct sigaction sa{};
394 Verify (sigemptyset (&sa.sa_mask) == 0);
396 Verify (::sigaction (signal, &sa,
nullptr) == 0);
398 Verify (::signal (signal, FirstPassSignalHandler_) != SIG_ERR);
403 auto l = fDirectHandlers_.rwget ();
404 if (directHandlers.
empty ()) {
405 l->RemoveIf (signal);
408 l->Add (signal, directHandlers);
411 Require (0 <= signal and signal <
static_cast<SignalID
> (NEltsOf (fDirectSignalHandlersCache_)));
412 vector<function<void (SignalID)>> shs;
418#if qStroika_Foundation_Common_Platform_POSIX
423 [[maybe_unused]]
auto&& cleanup =
Finally ([
this] ()
noexcept { fDirectSignalHandlersCache_Lock_--; });
424 if (fDirectSignalHandlersCache_Lock_++ == 0) {
425 fDirectSignalHandlersCache_[signal] = shs;
434 if (tmp !=
nullptr) {
435 if (safeHandlers.
empty ()) {
436 tmp->Remove (signal);
439 tmp->Add (signal, safeHandlers);
444 if (handlers.
empty ()) {
445 sigSetHandler (signal, SIG_DFL);
448 sigSetHandler (signal, SIG_IGN);
451 sigSetHandler (signal, FirstPassSignalHandler_);
465 Require (s.Contains (handler));
472 DbgTrace (
"Serious Signal Error trapped: {} ... Aborting"_f, SignalToName (signal));
479 results.
Add (SIGABRT);
480 results.
Add (SIGILL);
481 results.
Add (SIGFPE);
482 results.
Add (SIGSEGV);
483#if qStroika_Foundation_Common_Platform_POSIX
484 results.
Add (SIGSYS);
485 results.
Add (SIGBUS);
492 for (SignalID s : forSignals) {
500Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_THREAD
void SignalHandlerRegistry::FirstPassSignalHandler_ (SignalID signal)
600#if qDoDbgTraceOnSignalHandlers_
601 Debug::TraceContextBumper trcCtx{L
"Stroika::Foundation::Execution::SignalHandlerRegistry::FirstPassSignalHandler_", L
"signal = %s",
602 SignalToName (signal).
c_str ()};
604#if qDoBacktraceOnFirstPassSignalHandler_ and qStroika_Foundation_Debug_DefaultTracingOn
606 wstring tmp{Debug::BackTrace ()};
607 if (not tmp.empty ()) {
608 DbgTrace (L
"BackTrace: %s", tmp.c_str ());
630 Require (0 <= signal and signal <
static_cast<SignalID
> (NEltsOf (SHR.fDirectSignalHandlersCache_)));
632 [[maybe_unused]]
auto&& cleanup =
Finally ([&SHR] ()
noexcept { SHR.fDirectSignalHandlersCache_Lock_--; });
633 if (SHR.fDirectSignalHandlersCache_Lock_++ == 0) {
634 const vector<function<void (SignalID)>>* shs = &SHR.fDirectSignalHandlersCache_[signal];
635 for (
auto shi = shs->begin (); shi != shs->end (); ++shi) {
648#if __cpp_lib_atomic_shared_ptr >= 201711
649 shared_ptr<SignalHandlerRegistry::SafeSignalsManager::Rep_> tmp = SignalHandlerRegistry::SafeSignalsManager::sTheRep_.load ();
651 shared_ptr<SignalHandlerRegistry::SafeSignalsManager::Rep_> tmp = atomic_load (&SignalHandlerRegistry::SafeSignalsManager::sTheRep_);
653 if (tmp !=
nullptr) {
654 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
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 bool RemoveIf(ArgByValueType< value_type > item)
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)
static SignalHandlerRegistry & Get()
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
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 void Apply(const function< void(ArgByValueType< T > item)> &doToElement, Execution::SequencePolicy seq=Execution::SequencePolicy::eDEFAULT) const
Run the argument function (or lambda) on each element of the container.
nonvirtual size_t size() const
Returns the number of items contained.
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 >