Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
SignalHandlers.cpp
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2026. All rights reserved
3 */
4#include "Stroika/Foundation/StroikaPreComp.h"
5
6#include <condition_variable>
7#include <mutex>
8
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"
21#endif
25#include "Stroika/Foundation/Execution/WaitableEvent.h"
26
27#include "SignalHandlers.h"
28
29using namespace Stroika::Foundation;
31using namespace Stroika::Foundation::Execution;
32using namespace Stroika::Foundation::Memory;
33
35using Containers::Set;
36
37// Comment this in to turn on aggressive noisy DbgTrace in this module
38//#define USE_NOISY_TRACE_IN_THIS_MODULE_ 1
39
40// maybe useful while debugging signal code, but VERY unsafe
41// and could easily be the source of bugs/deadlocks!
42#ifndef qDoDbgTraceOnSignalHandlers_
43#define qDoDbgTraceOnSignalHandlers_ 0
44#endif
45
46// maybe useful while debugging signal code, but VERY unsafe
47// and could easily be the source of bugs/deadlocks!
48#ifndef qDoBacktraceOnFirstPassSignalHandler_
49#define qDoBacktraceOnFirstPassSignalHandler_ 0
50#endif
51
52// Use this for POSIX, since condition_variables aren't safe on POSIX (signals)
53// https://github.com/SophistSolutions/Stroika/issues/753 (STK-617)
54// https://stackoverflow.com/questions/31117959/waking-up-thread-from-signal-handler
55// -- LGP 2017-09-10
56#ifndef qConditionVariablesSafeInAsyncSignalHandlers_
57#define qConditionVariablesSafeInAsyncSignalHandlers_ !qStroika_Platform_POSIX
58#endif
59
60/*
61 ********************************************************************************
62 *************************** Execution::SignalHandler ***************************
63 ********************************************************************************
64 */
65Characters::String SignalHandler::ToString () const
66{
68 sb << "{"sv;
69 sb << "type: "sv << GetType ();
70 // rough guess what to print...
71 if (fCall_.target_type () == typeid (void (*) (SignalID))) {
72 sb << ", target: "sv << "{}"_f(reinterpret_cast<const void*> (fCall_.target<void (*) (SignalID)> ()));
73 }
74 else {
75 // type only/mainly interesting if not one of the above so we're printing nullptr
76 sb << ", targetType: "sv << fCall_.target_type ();
77 }
78 sb << "}"sv;
79 return sb;
80}
81
82/*
83 ********************************************************************************
84 *********** Execution::SignalHandlerRegistry::SafeSignalsManager ***************
85 ********************************************************************************
86 */
87/*
88 * Design note:
89 * Though it would be logical to use a BlockQueue<> here to implement the signal forwarding,
90 * we cannot since that code allocates memory, and could deadlock.
91 */
92class SignalHandlerRegistry::SafeSignalsManager::Rep_ final {
93private:
94 void waitForNextSig_ ()
95 {
96#if qConditionVariablesSafeInAsyncSignalHandlers_
97 Assert (not qStroika_Platform_POSIX); // this strategy not safe with POSIX signals
98 unique_lock<mutex> lk{fRecievedSig_NotSureWhatMutexFor_};
99 fRecievedSig_.wait_for (lk, chrono::seconds (100), [this] () { return fWorkMaybeAvailable_.load (); });
100#else
101 fRecievedSig_.Wait ();
102#endif
103 }
104 void tell2WakeAfterDataUpdate_ ()
105 {
106#if qConditionVariablesSafeInAsyncSignalHandlers_
107 Assert (not qStroika_Platform_POSIX); // this strategy not safe with POSIX signals
108 fRecievedSig_.notify_one ();
109 {
110 [[maybe_unused]] auto&& lk = lock_guard{fRecievedSig_NotSureWhatMutexFor_};
111 fWorkMaybeAvailable_ = true;
112 }
113 fRecievedSig_.notify_one ();
114#else
115 fWorkMaybeAvailable_ = true;
116 fRecievedSig_.Set ();
117#endif
118 }
119
120public:
121 Rep_ ()
122 {
123#if USE_NOISY_TRACE_IN_THIS_MODULE_
124 Debug::TraceContextBumper trcCtx{L"Stroika::Foundation::Execution::SignalHandlerRegistry::SafeSignalsManager::Rep_::CTOR",
126#endif
127 fBlockingQueuePusherThread_ = Thread::New (
128 [this] () {
129 // This is a safe context
130 Debug::TraceContextBumper trcCtx{"Stroika::Foundation::Execution::Signals::{}::fBlockingQueueDelegatorThread_"};
131 while (true) {
132 Debug::TraceContextBumper trcCtx1{"Waiting for next safe signal"};
134 waitForNextSig_ ();
135#if USE_NOISY_TRACE_IN_THIS_MODULE_
136 DbgTrace ("fRecievedSig_ wait complete (either arrival or timeout): fLastSignalRecieved_ = {}"_f, fLastSignalRecieved_.load ());
137#endif
138 if (fLastSignalRecieved_ < NSIG) {
139 Again:
140 for (int i = 0; i < NSIG; ++i) {
141 while (fIncomingSignalCounts_[i] > 0) {
142 DbgTrace ("fIncomingSignalCounts_[{}] = {}"_f, i, fIncomingSignalCounts_[i].load ());
143 fIncomingSignalCounts_[i]--;
144 Debug::TraceContextBumper trcCtx2{"Invoking SAFE signal handlers"};
145 for (SignalHandler sh : fHandlers_.rwget ()->LookupValue (i)) {
146 Assert (sh.GetType () == SignalHandler::Type::eSafe);
147 IgnoreExceptionsExceptThreadAbortForCall (sh (i));
148 }
149 }
150 }
151
152 // reset fLastSignalRecieved_ as a shortcut
153 fLastSignalRecieved_ = NSIG;
154 // but check for races, and reset
155 for (int i = 0; i < NSIG; ++i) {
156 while (fIncomingSignalCounts_[i] > 0) {
157 fLastSignalRecieved_ = i;
158 DbgTrace ("Rare, but possible race avoidance"_f);
159 goto Again;
160 }
161 }
162 }
163 // When we set fWorkMaybeAvailable_ false, do one more time around loop so no race - if we set from true to false
164 // we always recehck protected data (and mutex not signal safe)
165 if (fWorkMaybeAvailable_.exchange (false)) {
166 goto Again;
167 }
168 }
169 },
170 Thread::eAutoStart, "Signal Handler Safe Execution Thread"sv);
171 }
172
173public:
174 ~Rep_ ()
175 {
176 Debug::TraceContextBumper trcCtx{"Stroika::Foundation::Execution::SignalHandlerRegistry::SafeSignalsManager::Rep_::~Rep_",
177 Stroika_Foundation_Debug_OptionalizeTraceArgs ("this={}"_f, static_cast<const void*> (this))};
178 Thread::SuppressInterruptionInContext suppressInterruption;
179 fBlockingQueuePusherThread_.Abort ();
180 tell2WakeAfterDataUpdate_ ();
181 fBlockingQueuePusherThread_.AbortAndWaitForDone ();
182 }
183
184public:
185 /*
186 * Called at VERY UNSAFE TIME. NEVER allocates memory, or use locks.
187 */
188 void NotifyOfArrivalOfPossiblySafeSignal (SignalID signal)
189 {
190 Require (0 <= signal and signal < static_cast<SignalID> (std::size (fIncomingSignalCounts_)));
191 // Check fHanlderAvailable_ [] as a performance optimizaiton. This gets called by direct-sginals, even when there are no safe signals to
192 // be delegated to
193 if (fHanlderAvailable_[signal]) {
194 fIncomingSignalCounts_[signal]++;
195 fLastSignalRecieved_ = signal; // used as a quick check
196 tell2WakeAfterDataUpdate_ ();
197 }
198 }
199
200public:
201 Set<SignalID> GetHandledSignals () const
202 {
203 return Set<SignalID>{fHandlers_.cget ()->Keys ()};
204 }
205
206public:
207 Set<SignalHandler> GetSignalHandlers (SignalID signal) const
208 {
209 return fHandlers_.cget ()->LookupValue (signal);
210 }
211
212public:
213 void Remove (SignalID signal)
214 {
215 fHandlers_.rwget ()->RemoveIf (signal);
216 PopulateSafeSignalHandlersCache_ (signal);
217 }
218
219public:
220 void Add (SignalID signal, const Containers::Set<SignalHandler>& safeHandlers)
221 {
222 fHandlers_.rwget ()->Add (signal, safeHandlers);
223 PopulateSafeSignalHandlersCache_ (signal);
224 }
225
226private:
227 void PopulateSafeSignalHandlersCache_ (SignalID signal)
228 {
229 Require (0 <= signal and signal < static_cast<SignalID> (std::size (fHanlderAvailable_)));
230 fHanlderAvailable_[signal] = fHandlers_.rwget ()->Lookup (signal).has_value ();
231 }
232
233private:
235 bool fHanlderAvailable_[NSIG]{}; // if true post to blocking q
236private:
237 /*
238 * Instead of maintaining an acutal Q, just maintain a count of number of signals recieved for each signal number.
239 * This means signals not necessarily delivered in order, but this appraoch has the advantage of using a small amount of memory to
240 * essentially guarantee no overflow
241 */
242 atomic<unsigned int> fIncomingSignalCounts_[NSIG]{};
243 atomic<SignalID> fLastSignalRecieved_{NSIG};
244 Thread::Ptr fBlockingQueuePusherThread_; // no need to synchonize cuz only called from thread which constructs/destroys safetymfg
245private:
246 atomic<bool> fWorkMaybeAvailable_{false};
247#if qConditionVariablesSafeInAsyncSignalHandlers_
248 mutex fRecievedSig_NotSureWhatMutexFor_;
249 condition_variable fRecievedSig_;
250#else
252#endif
253};
254
255/*
256 ********************************************************************************
257 *********** Execution::SignalHandlerRegistry::SafeSignalsManager ***************
258 ********************************************************************************
259 */
260SignalHandlerRegistry::SafeSignalsManager::SafeSignalsManager ()
261{
262 Debug::TraceContextBumper trcCtx{"Stroika::Foundation::Execution::SignalHandlerRegistry::SafeSignalsManager::CTOR"};
263#if defined(__cpp_lib_atomic_shared_ptr)
264 Require (sTheRep_.load () == nullptr);
265 sTheRep_.store (MakeSharedPtr<SignalHandlerRegistry::SafeSignalsManager::Rep_> ());
266#else
267 Require (atomic_load (&sTheRep_) == nullptr);
268 atomic_store (&sTheRep_, MakeSharedPtr<SignalHandlerRegistry::SafeSignalsManager::Rep_> ());
269#endif
270}
271
272SignalHandlerRegistry::SafeSignalsManager::~SafeSignalsManager ()
273{
274 Debug::TraceContextBumper trcCtx{"Stroika::Foundation::Execution::SignalHandlerRegistry::SafeSignalsManager::DTOR"};
275#if defined(__cpp_lib_atomic_shared_ptr)
276 SignalHandlerRegistry::SafeSignalsManager::sTheRep_.store (shared_ptr<Rep_>{}); // this will wait for shutdown of safe processing thread to shut down
277#else
278 atomic_store (&SignalHandlerRegistry::SafeSignalsManager::sTheRep_, shared_ptr<Rep_>{}); // this will wait for shutdown of safe processing thread to shut down
279#endif
280}
281
282/*
283 ********************************************************************************
284 ******************** Execution::SignalHandlerRegistry **************************
285 ********************************************************************************
286 */
287const SignalHandler SignalHandlerRegistry::kIGNORED = SignalHandler{SIG_IGN, SignalHandler::Type::eDirect};
288
289SignalHandlerRegistry::SignalHandlerRegistry ()
290{
292 [[maybe_unused]] static int nConstructed = 0;
293 ++nConstructed;
294 Assert (nConstructed == 1);
295 }
296 Debug::TraceContextBumper trcCtx{"Stroika::Foundation::Execution::SignalHandlerRegistry::CTOR"};
297}
298
299SignalHandlerRegistry::~SignalHandlerRegistry ()
300{
301 Debug::TraceContextBumper trcCtx{"Stroika::Foundation::Execution::SignalHandlerRegistry::DTOR"};
302#if defined(__cpp_lib_atomic_shared_ptr)
303 Assert (SafeSignalsManager::sTheRep_.load () == nullptr); // must be cleared first
304#else
305 Assert (atomic_load (&SafeSignalsManager::sTheRep_) == nullptr); // must be cleared first
306#endif
307}
308
310{
311 Set<SignalID> result{fDirectHandlers_.cget ()->Keys ()};
312#if defined(__cpp_lib_atomic_shared_ptr)
313 if (shared_ptr<SafeSignalsManager::Rep_> tmp = SafeSignalsManager::sTheRep_.load ()) {
314#else
315 if (shared_ptr<SafeSignalsManager::Rep_> tmp = atomic_load (&SafeSignalsManager::sTheRep_)) {
316#endif
317 result += tmp->GetHandledSignals ();
318 }
319 return result;
320}
321
323{
324 Set<SignalHandler> result = fDirectHandlers_.cget ()->LookupValue (signal);
325#if defined(__cpp_lib_atomic_shared_ptr)
326 if (shared_ptr<SafeSignalsManager::Rep_> tmp = SafeSignalsManager::sTheRep_.load ()) {
327#else
328 if (shared_ptr<SafeSignalsManager::Rep_> tmp = atomic_load (&SafeSignalsManager::sTheRep_)) {
329#endif
330
331 result += tmp->GetSignalHandlers (signal);
332 }
333 return result;
334}
335
337{
339}
340
341void SignalHandlerRegistry::SetSignalHandlers (SignalID signal, const SignalHandler& handler)
342{
343 SetSignalHandlers (signal, Set<SignalHandler> ({handler}));
344}
345
346void SignalHandlerRegistry::SetSignalHandlers (SignalID signal, const Set<SignalHandler>& handlers)
347{
349 "Stroika::Foundation::Execution::SignalHandlerRegistry::{}::SetSignalHandlers", "signal: {}, handlers: {}"_f, SignalToName (signal), handlers)};
350
351 Set<SignalHandler> directHandlers;
352 Set<SignalHandler> safeHandlers;
353 handlers.Apply ([&directHandlers, &safeHandlers] (SignalHandler si) {
354 switch (si.GetType ()) {
355 case SignalHandler::Type::eDirect: {
356 directHandlers.Add (si);
357 } break;
358 case SignalHandler::Type::eSafe: {
359 safeHandlers.Add (si);
360 } break;
361 }
362 });
363 Assert (directHandlers.size () + safeHandlers.size () == handlers.size ());
364
365 shared_ptr<SignalHandlerRegistry::SafeSignalsManager::Rep_> tmp = SignalHandlerRegistry::SafeSignalsManager::sTheRep_;
366
367 if (not safeHandlers.empty ()) {
368 // To use safe signal handlers, you must have a SignalHandlerRegistry::SafeSignalsManager
369 // defined first. It is recommended that you define an instance of
370 // SignalHandlerRegistry::SafeSignalsManager handler; should be defined in main ()
371#if defined(__cpp_lib_atomic_shared_ptr)
372 Require (SafeSignalsManager::sTheRep_.load () != nullptr);
373#else
374 Require (atomic_load (&SafeSignalsManager::sTheRep_) != nullptr);
375#endif
376 }
377
378 auto sigSetHandler = [] (SignalID signal, [[maybe_unused]] void (*fun) (int)) {
379#if qStroika_Platform_POSIX
380 struct sigaction sa{};
381 sa.sa_handler = fun;
382 Verify (sigemptyset (&sa.sa_mask) == 0); // nb: cannot use :: on macos - macro - LGP 2016-12-30
383 sa.sa_flags = 0; // important NOT to set SA_RESTART for interrupt() - but maybe for others helpful - maybe add option?
384 Verify (::sigaction (signal, &sa, nullptr) == 0);
385#else
386 Verify (::signal (signal, FirstPassSignalHandler_) != SIG_ERR);
387#endif
388 };
389
390 {
391 auto l = fDirectHandlers_.rwget ();
392 if (directHandlers.empty ()) {
393 l->RemoveIf (signal);
394 }
395 else {
396 l->Add (signal, directHandlers);
397 }
398 // @todo see https://github.com/SophistSolutions/Stroika/issues/599 (STK-465)
399 Require (0 <= signal and signal < static_cast<SignalID> (std::size (fDirectSignalHandlersCache_)));
400 vector<function<void (SignalID)>> shs;
401 for (const SignalHandler& sh : l->LookupValue (signal)) {
402 shs.push_back (sh);
403 }
404 {
405// Poor man's interlock/mutex, which avoids any memory allocation/stdc++ locks
406#if qStroika_Platform_POSIX
407 // Can easily deadlock if we try to access this lock while recieving the signal
408 Platform::POSIX::ScopedBlockCurrentThreadSignal blockAllSignals2ThisThread{};
409#endif
410 Again:
411 [[maybe_unused]] auto&& cleanup = Finally ([this] () noexcept { fDirectSignalHandlersCache_Lock_--; });
412 if (fDirectSignalHandlersCache_Lock_++ == 0) {
413 fDirectSignalHandlersCache_[signal] = shs;
414 }
415 else {
416 Execution::Sleep (1ms); // not sure how long 2 wait
417 goto Again;
418 }
419 }
420 }
421
422 if (tmp != nullptr) {
423 if (safeHandlers.empty ()) {
424 tmp->Remove (signal);
425 }
426 else {
427 tmp->Add (signal, safeHandlers);
428 }
429 }
430
431 // And set the actual signal handlers
432 if (handlers.empty ()) {
433 sigSetHandler (signal, SIG_DFL);
434 }
435 else if (handlers.size () == 1 and handlers.Contains (SignalHandlerRegistry::kIGNORED)) {
436 sigSetHandler (signal, SIG_IGN);
437 }
438 else {
439 sigSetHandler (signal, FirstPassSignalHandler_);
440 }
441}
442
443void SignalHandlerRegistry::AddSignalHandler (SignalID signal, const SignalHandler& handler)
444{
446 s.Add (handler);
447 SetSignalHandlers (signal, s);
448}
449
450void SignalHandlerRegistry::RemoveSignalHandler (SignalID signal, const SignalHandler& handler)
451{
453 Require (s.Contains (handler));
454 s.Remove (handler);
455 SetSignalHandlers (signal, s);
456}
457
458void SignalHandlerRegistry::DefaultCrashSignalHandler ([[maybe_unused]] SignalID signal)
459{
460 DbgTrace ("Serious Signal Error trapped: {} ... Aborting"_f, SignalToName (signal));
461 abort ();
462}
463
465{
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);
474#endif
475 return results;
476}
477
479{
480 for (SignalID s : forSignals) {
481 if (s != SIGABRT) {
482 SetSignalHandlers (s, handler);
483 }
484 }
485}
486
487// use no_sanitize(thread) to workaround https://github.com/SophistSolutions/Stroika/issues/812 (STK-677)
488Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_THREAD void SignalHandlerRegistry::FirstPassSignalHandler_ (SignalID signal)
489{
490 /*
491 * Important example / stack backtrace to bear in mind:
492 * #0 __lll_lock_wait_private () at ../sysdeps/unix/sysv/linux/x86_64/lowlevellock.S:95
493 * #1 0x00007f4edcd924fc in __GI___libc_malloc (bytes=139975802748960) at malloc.c:2891
494 * #2 0x0000000000540168 in operator new(unsigned long) ()
495 * #3 0x000000000042581e in __gnu_cxx::new_allocator<std::_Rb_tree_node<std::thread::id> >::allocate (this=0x7f4eb7ffd380, __n=1) at /usr/include/c++/4.9/ext/new_allocator.h:104
496 * #4 0x0000000000423e54 in std::allocator_traits<std::allocator<std::_Rb_tree_node<std::thread::id> > >::allocate (__a=..., __n=1) at /usr/include/c++/4.9/bits/alloc_traits.h:357
497 * #5 0x000000000042161b in std::_Rb_tree<std::thread::id, std::thread::id, std::_Identity<std::thread::id>, std::less<std::thread::id>, std::allocator<std::thread::id> >::_M_get_node (
498 * this=0x7f4eb7ffd380) at /usr/include/c++/4.9/bits/stl_tree.h:385
499 * #6 0x000000000041eae1 in std::_Rb_tree<std::thread::id, std::thread::id, std::_Identity<std::thread::id>, std::less<std::thread::id>, std::allocator<std::thread::id> >::_M_create_node<std::thread::id>(std::thread::id&&) (this=0x7f4eb7ffd380) at /usr/include/c++/4.9/bits/stl_tree.h:417
500 * #7 0x000000000041c348 in std::_Rb_tree<std::thread::id, std::thread::id, std::_Identity<std::thread::id>, std::less<std::thread::id>, std::allocator<std::thread::id> >::_M_insert_<std::thread::id>(std::_Rb_tree_node_base*, std::_Rb_tree_node_base*, std::thread::id&&) (this=0x7f4eb7ffd380, __x=0x0, __p=0x7f4eb7ffd388,
501 * __v=<unknown type in /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Builds/gcc-4.9-debug-no-TPC/Test35, CU 0x0, DIE 0x5e560>) at /usr/include/c++/4.9/bits/stl_tree.h:1143
502 * #8 0x000000000041858f in std::_Rb_tree<std::thread::id, std::thread::id, std::_Identity<std::thread::id>, std::less<std::thread::id>, std::allocator<std::thread::id> >::_M_insert_equal<std::thread::id>(std::thread::id&&) (this=0x7f4eb7ffd380, __v=<unknown type in /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Builds/gcc-4.9-debug-no-TPC/Test35, CU 0x0, DIE 0x5944e>)
503 * at /usr/include/c++/4.9/bits/stl_tree.h:1523
504 * #9 0x0000000000414a37 in std::__cxx1998::multiset<std::thread::id, std::less<std::thread::id>, std::allocator<std::thread::id> >::insert(std::thread::id&&) (this=0x7f4eb7ffd380,
505 * __x=<unknown type in /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Builds/gcc-4.9-debug-no-TPC/Test35, CU 0x0, DIE 0x54234>) at /usr/include/c++/4.9/bits/stl_multiset.h:498
506 * #10 0x0000000000412303 in std::__debug::multiset<std::thread::id, std::less<std::thread::id>, std::allocator<std::thread::id> >::insert(std::thread::id&&) (this=0x7f4eb7ffd380,
507 * __x=<unknown type in /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Builds/gcc-4.9-debug-no-TPC/Test35, CU 0x0, DIE 0x507d4>) at /usr/include/c++/4.9/debug/multiset.h:257
508 * #11 0x0000000000410f61 in Stroika::Foundation::Debug::AssertExternallySynchronizedChecker::lock_shared (this=0x7f4eb7ffd370)
509 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/../Containers/../Memory/../Debug/AssertExternallySynchronizedChecker.inl:92
510 * #12 0x00000000004157d8 in std::shared_lock<Stroika::Foundation::Debug::AssertExternallySynchronizedChecker const>::shared_lock (this=0x7f4eb7ffd270, m=...)
511 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Configuration/Private/Defaults_CompilerAndStdLib_.h:2123
512 * #13 0x000000000048ce64 in Stroika::Foundation::Traversal::Iterable<Stroika::Foundation::Execution::SignalHandler>::_SafeReadRepAccessor<Stroika::Foundation::Containers::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Containers::DefaultTraits::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::DefaultEqualsComparer<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::ComparerWithEquals<Stroika::Foundation::Execution::SignalHandler> > > >::_IRep>::_SafeReadRepAccessor (this=0x7f4eb7ffd270, it=0x7f4eb7ffd370)
513 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/../Characters/Concrete/../../Containers/../Traversal/Iterable.inl:88
514 * #14 0x000000000048dd70 in Stroika::Foundation::Containers::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Containers::DefaultTraits::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::DefaultEqualsComparer<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::ComparerWithEquals<Stroika::Foundation::Execution::SignalHandler> > > >::_AssertRepValidType (this=0x7f4eb7ffd370) at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/../Characters/Concrete/../../Containers/Set.inl:282
515 * #15 0x0000000000496803 in Stroika::Foundation::Containers::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Containers::DefaultTraits::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::DefaultEqualsComparer<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::ComparerWithEquals<Stroika::Foundation::Execution::SignalHandler> > > >::Set(Stroika::Foundation::Memory::SharedPtr<Stroika::Foundation::Containers::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Containers::DefaultTraits::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::DefaultEqualsComparer<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::ComparerWithEquals<Stroika::Foundation::Execution::SignalHandler> > > >::_IRep>&&) (this=0x7f4eb7ffd370, src=<unknown type in /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Builds/gcc-4.9-debug-no-TPC/Test35, CU 0x267e27, DIE 0x2d5751>)
516 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/../Characters/Concrete/../../Containers/Set.inl:67
517 * #16 0x0000000000494ff0 in Stroika::Foundation::Containers::Concrete::Set_stdset<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Containers::Concrete::Set_stdset_DefaultTraits<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::DefaultEqualsComparer<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::ComparerWithEquals<Stroika::Foundation::Execution::SignalHandler> >, Stroika::Foundation::Common::ComparerWithWellOrder<Stroika::Foundation::Execution::SignalHandler> > >::Set_stdset (this=0x7f4eb7ffd370)
518 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/../Characters/Concrete/../../Containers/Factory/../Concrete/Set_stdset.inl:244
519 * #17 0x0000000000493c39 in Stroika::Foundation::Containers::Concrete::Set_Factory<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Containers::DefaultTraits::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::DefaultEqualsComparer<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::ComparerWithEquals<Stroika::Foundation::Execution::SignalHandler> > > >::Default_SFINAE_<Stroika::Foundation::Execution::SignalHandler>(Stroika::Foundation::Execution::SignalHandler*, std::enable_if<Stroika::Foundation::Configuration::has_lt<Stroika::Foundation::Execution::SignalHandler>::value&&std::is_same<Stroika::Foundation::Containers::DefaultTraits::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::DefaultEqualsComparer<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::ComparerWithEquals<Stroika::Foundation::Execution::SignalHandler> > >, Stroika::Foundation::Containers::DefaultTraits::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::DefaultEqualsComparer<Stroika::Foundation::Execution::SignalHandler, std::conditional<Stroika::Foundation::Configuration::has_eq<Stroika::Foundation::Execution::SignalHandler>::value&&std::is_convertible<Stroika::Foundation::Configuration::Private_::eq_result_impl<Stroika::Foundation::Execution::SignalHandler>::type, bool>::value, Stroika::Foundation::Common::ComparerWithEquals<Stroika::Foundation::Execution::SignalHandler>, std::conditional<Stroika::Foundation::Configuration::has_lt<Stroika::Foundation::Execution::SignalHandler>::value&&std::is_convertible<Stroika::Foundation::Configuration::Private_::lt_result_impl<Stroika::Foundation::Execution::SignalHandler>::type, bool>::value, Stroika::Foundation::Common::ComparerWithWellOrder<Stroika::Foundation::Execution::SignalHandler>, std::shared_ptr<int> >::type>::type> > >::value, void>::type*) ()
520 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/../Characters/Concrete/../../Containers/Factory/Set_Factory.inl:63
521 * #18 0x0000000000491336 in Stroika::Foundation::Containers::Concrete::Set_Factory<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Containers::DefaultTraits::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::DefaultEqualsComparer<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::ComparerWithEquals<Stroika::Foundation::Execution::SignalHandler> > > >::Default_ () at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/../Characters/Concrete/../../Containers/Factory/Set_Factory.inl:57
522 * #19 0x000000000048dbf1 in Stroika::Foundation::Containers::Concrete::Set_Factory<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Containers::DefaultTraits::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::DefaultEqualsComparer<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::ComparerWithEquals<Stroika::Foundation::Execution::SignalHandler> > > >::mk () at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/../Characters/Concrete/../../Containers/Factory/Set_Factory.inl:44
523 * #20 0x000000000048abbf in Stroika::Foundation::Containers::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Containers::DefaultTraits::Set<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::DefaultEqualsComparer<Stroika::Foundation::Execution::SignalHandler, Stroika::Foundation::Common::ComparerWithEquals<Stroika::Foundation::Execution::SignalHandler> > > >::Set (this=0x7f4eb7ffd5e0) at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/../Characters/Concrete/../../Containers/Set.inl:23
524 * #21 0x0000000000488de6 in Stroika::Foundation::Execution::SignalHandlerRegistry::FirstPassSignalHandler_ (signal=12)
525 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/SignalHandlers.cpp:422
526 * #22 <signal handler called>
527 * #23 _int_malloc (av=av@entry=0x7f4ea8000020, bytes=bytes@entry=8160) at malloc.c:3769
528 * #24 0x00007f4edcd9250e in __GI___libc_malloc (bytes=8160) at malloc.c:2895
529 * #25 0x0000000000540168 in operator new(unsigned long) ()
530 * #26 0x000000000043b180 in __gnu_cxx::new_allocator<char>::allocate (this=0x7f4eb7ffdf3f, __n=8160) at /usr/include/c++/4.9/ext/new_allocator.h:104
531 * #27 0x000000000043b023 in std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::_Rep::_S_create (__capacity=2033, __old_capacity=816, __alloc=...)
532 * at /usr/include/c++/4.9/bits/basic_string.tcc:607
533 * #28 0x000000000044236e in std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::_Rep::_M_clone (this=0x7f4ea8002450, __alloc=..., __res=51)
534 * at /usr/include/c++/4.9/bits/basic_string.tcc:629
535 * #29 0x0000000000443e42 in std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::reserve (this=0x7f4eb7ffe0c0, __res=867) at /usr/include/c++/4.9/bits/basic_string.tcc:510
536 * #30 0x0000000000480971 in std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::append (this=0x7f4eb7ffe0c0, __str=...) at /usr/include/c++/4.9/bits/basic_string.tcc:332
537 * #31 0x0000000000527955 in std::basic_string<wchar_t, std::char_traits<wchar_t>, std::allocator<wchar_t> >::operator+= (this=0x7f4eb7ffe0c0, __str=...) at /usr/include/c++/4.9/bits/basic_string.h:950
538 * #32 0x0000000000527342 in Stroika::Foundation::Debug::BackTrace (maxFrames=4294967295) at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Debug/BackTrace.cpp:50
539 * #33 0x0000000000485f61 in Stroika::Foundation::Execution::Private_::GetBT_ws () at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/Exceptions.cpp:40
540 * #34 0x0000000000485e6e in Stroika::Foundation::Execution::Private_::GetBT_s () at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/Exceptions.cpp:30
541 * #35 0x00000000004c56a8 in Stroika::Foundation::Execution::Throw<Stroika::Foundation::Execution::Thread::AbortException> (e2Throw=...)
542 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/../Characters/../Memory/../Execution/Exceptions.inl:40
543 * #36 0x00000000004c2afc in Stroika::Foundation::Execution::CheckForInterruption () at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/Thread.cpp:993
544 * #37 0x00000000004f036c in Stroika::Foundation::Execution::WaitableEvent::WE_::WaitUntilQuietly (this=0x7ffe1997bf80, timeoutAt=1.7976931348623157e+308)
545 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/WaitableEvent.cpp:83
546 * #38 0x00000000004f02bd in Stroika::Foundation::Execution::WaitableEvent::WE_::WaitUntil (this=0x7ffe1997bf80, timeoutAt=1.7976931348623157e+308)
547 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/WaitableEvent.cpp:52
548 * #39 0x000000000041169b in Stroika::Foundation::Execution::WaitableEvent::Wait (this=0x7ffe1997bf80, timeout=1.7976931348623157e+308)
549 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/WaitableEvent.inl:100
550 * #40 0x00000000004d4578 in Stroika::Foundation::Execution::ThreadPool::WaitForNextTask_(Stroika::Foundation::Execution::Function<void ()>*) (this=0x7ffe1997beb0, result=0x1841390)
551 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/ThreadPool.cpp:444
552 * #41 0x00000000004d5288 in Stroika::Foundation::Execution::ThreadPool::MyRunnable_::Run (this=0x1841350)
553 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/ThreadPool.cpp:57
554 * #42 0x00000000004d45db in Stroika::Foundation::Execution::ThreadPool::<lambda()>::operator()(void) const (__closure=0x1840ee0)
555 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/ThreadPool.cpp:454
556 * #43 0x00000000004d4a14 in std::_Function_handler<void(), Stroika::Foundation::Execution::ThreadPool::mkThread_()::<lambda()> >::_M_invoke(const std::_Any_data &) (__functor=...)
557 * at /usr/include/c++/4.9/functional:2039
558 * #44 0x000000000041298a in std::function<void ()>::operator()() const (this=0x18413d0) at /usr/include/c++/4.9/functional:2439
559 * #45 0x00000000004c4c43 in Stroika::Foundation::Execution::Function<void ()>::operator()<>() const (this=0x1841410)
560 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/../Execution/Function.inl:48
561 * #46 0x00000000004c06b6 in Stroika::Foundation::Execution::Thread::Rep_::Run_ (this=0x1841410) at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/Thread.cpp:317
562 * #47 0x00000000004c0c2f in Stroika::Foundation::Execution::Thread::Rep_::ThreadMain_ (thisThreadRep=0x7ffe1997b820)
563 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/Thread.cpp:425
564 * #48 0x00000000004c03ee in Stroika::Foundation::Execution::Thread::Rep_::<lambda()>::operator()(void) const (__closure=0x1840c68)
565 * at /media/Sandbox/lewis-Sandbox/Stroika-DevRoot/Library/Sources/Stroika/Foundation/Execution/Thread.cpp:286
566 * #49 0x00000000004c420c in std::_Bind_simple<Stroika::Foundation::Execution::Thread::Rep_::DoCreate(std::shared_ptr<Stroika::Foundation::Execution::Thread::Rep_>*)::<lambda()>()>::_M_invoke<>(std::_Index_tuple<>) (this=0x1840c68) at /usr/include/c++/4.9/functional:1700
567 * #50 0x00000000004c413e in std::_Bind_simple<Stroika::Foundation::Execution::Thread::Rep_::DoCreate(std::shared_ptr<Stroika::Foundation::Execution::Thread::Rep_>*)::<lambda()>()>::operator()(void) (
568 * this=0x1840c68) at /usr/include/c++/4.9/functional:1688
569 * #51 0x00000000004c40ac in std::thread::_Impl<std::_Bind_simple<Stroika::Foundation::Execution::Thread::Rep_::DoCreate(std::shared_ptr<Stroika::Foundation::Execution::Thread::Rep_>*)::<lambda()>()> >::_M_run(void) (this=0x1840c50) at /usr/include/c++/4.9/thread:115
570 * #52 0x0000000000589630 in execute_native_thread_routine ()
571 * #53 0x00007f4edd8056aa in start_thread (arg=0x7f4eb7fff700) at pthread_create.c:333
572 * #54 0x00007f4edce14eed in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109
573 *
574 * ANALYSIS OF ABOVE:
575 * At frame 22 (#22 <signal handler called>) - we have AT LEAST the malloc lock held (we could have more in principle)
576 * We receive a signal
577 * Deeper down, at frame 11 (#11 0x0000000000410f61) we acquire an unrelated lock, and AssertExternallySynchronizedChecker
578 * which does more memory allocations.
579 * deadlocking at frame 0 (#0 __lll_lock_wait_private)
580 *
581 * BUT - worse than deadlocking this thread (thats bad enuf) - we acquired a lock on AssertExternallySynchronizedChecker
582 * which we will never give up, and kill any other thread doing much of anything.
583 *
584 * SUMMARY:
585 * > THIS CODE - CAN NEVER SAFELY CALL MALLOC!!!!
586 * > Be CAREFUL to do as little as possible here.
587 */
588#if qDoDbgTraceOnSignalHandlers_
589 Debug::TraceContextBumper trcCtx{L"Stroika::Foundation::Execution::SignalHandlerRegistry::FirstPassSignalHandler_", L"signal = %s",
590 SignalToName (signal).c_str ()};
591#endif
592#if qDoBacktraceOnFirstPassSignalHandler_ and qStroika_Foundation_Debug_DefaultTracingOn
593 {
594 wstring tmp{Debug::BackTrace ()};
595 if (not tmp.empty ()) {
596 DbgTrace (L"BackTrace: %s", tmp.c_str ());
597 }
598 }
599#endif
600
601 /*
602 * This (SignalHandlerRegistry::sThe SHOULD be safe. It can allocate memory, but only the first time
603 * called, and this cannot be our first call.
604 */
606
607 {
608 /*
609 * Pretty sure this all allocates no memory, so should be safe/lock free
610 *
611 * @todo see https://github.com/SophistSolutions/Stroika/issues/599 (STK-465)
612 *
613 * Poor man's interlock/mutex, which avoids any memory allocation/stdc++ locks
614 *
615 * \note If you see a thread-sanitizer warning here - see
616 * https://github.com/SophistSolutions/Stroika/issues/128 (STK-647)
617 */
618 Require (0 <= signal and signal < static_cast<SignalID> (std::size (SHR.fDirectSignalHandlersCache_)));
619 Again:
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) {
626 (*shi) (signal);
627 }
628 }
629 else {
630 goto Again;
631 }
632 }
633
634 //
635 // I THINK/HOPE it safe to increment/decrement the reference count on the shared_ptr.
636 // But this isn't guaranteed by anything I'm aware of.
637 //
638#if defined(__cpp_lib_atomic_shared_ptr)
639 shared_ptr<SignalHandlerRegistry::SafeSignalsManager::Rep_> tmp = SignalHandlerRegistry::SafeSignalsManager::sTheRep_.load ();
640#else
641 shared_ptr<SignalHandlerRegistry::SafeSignalsManager::Rep_> tmp = atomic_load (&SignalHandlerRegistry::SafeSignalsManager::sTheRep_);
642#endif
643 if (tmp != nullptr) {
644 tmp->NotifyOfArrivalOfPossiblySafeSignal (signal);
645 }
646}
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
Definition Assertions.h:49
#define Verify(c)
Definition Assertions.h:422
#define DbgTrace
Definition Trace.h:327
#define Stroika_Foundation_Debug_OptionalizeTraceArgs(...)
Definition Trace.h:288
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,...
Definition String.h:201
nonvirtual tuple< const wchar_t *, wstring_view > c_str(Memory::StackBuffer< wchar_t > *possibleBackingStore) const
Definition String.inl:1053
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.
Definition Set.inl:217
nonvirtual void Add(ArgByValueType< value_type > item)
Definition Set.inl:138
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
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...
Definition Thread.h:357
nonvirtual size_t size() const
Returns the number of items contained.
Definition Iterable.inl:311
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.
Definition Iterable.inl:317
Ptr New(const function< void()> &fun2CallOnce, const optional< Characters::String > &name, const optional< Configuration > &configuration)
Definition Thread.cpp:887
void Sleep(Time::Duration seconds2Wait)
Definition Sleep.inl:101
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
Definition Finally.inl:31