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