4#include "Stroika/Foundation/StroikaPreComp.h"
6#include "Stroika/Foundation/Common/StroikaConfig.h"
10#if qStroika_Foundation_Common_Platform_Windows
19#include "Stroika/Foundation/Containers/Set.h"
21#include "Stroika/Foundation/Debug/Main.h"
27#include "DLLSupport.h"
28#include "Exceptions.h"
30#include "TimeOutException.h"
32#if qStroika_Foundation_Common_Platform_POSIX
33#include "Platform/POSIX/SignalBlock.h"
34#include "SignalHandlers.h"
36#if qStroika_Foundation_Common_Platform_Windows
37#include "Platform/Windows/WaitSupport.h"
57#ifndef qSupportSetThreadNameDebuggerCall_
58#if qStroika_Foundation_Debug_AssertionsChecked && qStroika_Foundation_Common_Platform_Windows
59#define qSupportSetThreadNameDebuggerCall_ 1
62#ifndef qSupportSetThreadNameDebuggerCall_
63#if qStroika_Foundation_Common_Platform_POSIX
64#define qSupportSetThreadNameDebuggerCall_ 1
67#ifndef qSupportSetThreadNameDebuggerCall_
68#define qSupportSetThreadNameDebuggerCall_ 0
71using namespace Characters;
72using namespace Execution;
75 thread_local unsigned int t_InterruptionSuppressDepth_{0};
78#if qStroika_Foundation_Execution_Thread_SupportThreadStatistics
81 mutex sThreadSupportStatsMutex_;
82 set<Thread::IDType> sRunningThreads_;
84 struct AllThreadsDeadDetector_ {
85 AllThreadsDeadDetector_ ()
87 Require (sRunningThreads_.empty ());
89 ~AllThreadsDeadDetector_ ()
92 if (not sRunningThreads_.empty ()) {
93 DbgTrace (
"Threads {} running"_f, Thread::GetStatistics ().fRunningThreads);
94 Require (sRunningThreads_.empty ());
99 AllThreadsDeadDetector_ sAllThreadsDeadDetector_;
103#if qStroika_Foundation_Common_Platform_Windows
105#if (_WIN32_WINNT < 0x0502)
111 using NTSTATUS = LONG;
112#define STATUS_SUCCESS ((NTSTATUS)0x00000000)
113 using KPRIORITY = LONG;
114 struct THREAD_BASIC_INFORMATION {
116 PVOID TebBaseAddress;
118 KAFFINITY AffinityMask;
120 KPRIORITY BasePriority;
122 enum THREAD_INFORMATION_CLASS {
123 ThreadBasicInformation = 0,
125 using pfnNtQueryInformationThread = NTSTATUS (__stdcall*) (HANDLE, THREAD_INFORMATION_CLASS, PVOID, ULONG, PULONG);
128 DWORD MyGetThreadId_ (HANDLE thread)
130#if (_WIN32_WINNT >= 0x0502)
131 return ::GetThreadId (thread);
135 using namespace XXX_;
136 static DLLLoader ntdll (SDKSTR (
"ntdll.dll"));
137 static pfnNtQueryInformationThread NtQueryInformationThread =
138 (pfnNtQueryInformationThread)ntdll.GetProcAddress (
"NtQueryInformationThread");
139 if (NtQueryInformationThread ==
nullptr)
141 THREAD_BASIC_INFORMATION tbi{};
142 THREAD_INFORMATION_CLASS tic = ThreadBasicInformation;
143 if (::NtQueryInformationThread (thread, tic, &tbi,
sizeof (tbi),
nullptr) != STATUS_SUCCESS) {
146 return tbi.ClientId.UniqueThread;
154#if qStroika_Foundation_Common_Platform_POSIX
160#if qStroika_Foundation_Common_Platform_POSIX
163SignalHandler kCallInRepThreadAbortProcSignalHandler_ = SIG_IGN;
171Thread::SuppressInterruptionInContext::SuppressInterruptionInContext ()
173 ++t_InterruptionSuppressDepth_;
176Thread::SuppressInterruptionInContext::~SuppressInterruptionInContext ()
178 Assert (t_InterruptionSuppressDepth_ >= 1);
179 t_InterruptionSuppressDepth_--;
195Thread::AbortException::AbortException ()
205Thread::IndexRegistrar::IndexRegistrar ()
207 Assert (not fInitialized_);
208 fInitialized_ =
true;
211Thread::IndexRegistrar::~IndexRegistrar ()
213 Assert (fInitialized_);
214 fInitialized_ =
false;
217unsigned int Thread::IndexRegistrar::GetIndex (
const IDType& threadID,
bool* wasNew)
219 if (not fInitialized_) {
220 if (wasNew !=
nullptr) {
225 [[maybe_unused]] lock_guard critSec{fMutex_};
226 auto i = fShownThreadIDs_.find (threadID);
227 unsigned int threadIndex2Show = 0;
228 if (i == fShownThreadIDs_.end ()) {
229 threadIndex2Show =
static_cast<unsigned int> (fShownThreadIDs_.size ());
230 fShownThreadIDs_.insert ({threadID, threadIndex2Show});
233 threadIndex2Show = i->second;
235 if (wasNew !=
nullptr) {
236 *wasNew = i == fShownThreadIDs_.end ();
238 return threadIndex2Show;
246Thread::Ptr::Rep_::Rep_ (
const function<
void ()>& runnable, [[maybe_unused]]
const optional<Configuration>& configuration)
247 : fRunnable_{runnable}
250#if qStroika_Foundation_Common_Platform_POSIX
251 static bool sDidInit_{
false};
254 kCallInRepThreadAbortProcSignalHandler_ =
SignalHandler{Rep_::InterruptionSignalHandler_, SignalHandler::Type::eDirect};
256#elif qStroika_Foundation_Common_Platform_Windows
257 if (configuration.has_value () and configuration->fThrowInterruptExceptionInsideUserAPC.has_value ()) {
258 fThrowInterruptExceptionInsideUserAPC_ = configuration->fThrowInterruptExceptionInsideUserAPC.value ();
263Thread::Ptr::Rep_::~Rep_ ()
279 if (fThreadValid_ and fThread_.joinable ()) {
284void Thread::Ptr::Rep_::Run_ ()
289 catch (
const AbortException&) {
295#if USE_NOISY_TRACE_IN_THIS_MODULE_
296 DbgTrace (
"in Thread::Ptr::Rep_::Run_ () - saving caught exception to repropagate later ({})"_f, current_exception ());
298 fSavedException_ = current_exception ();
304Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_THREAD
Characters::String Thread::Ptr::Rep_::ToString ()
const
308 if (fRefCountBumpedInsideThreadMainEvent_.PeekIsSet ()) {
310 sb <<
"id: "sv << GetID ();
312 sb <<
", index: " << IndexRegistrar::sThe.GetIndex (GetID ());
315 if (not fThreadName_.empty ()) {
316 sb <<
", name: "sv << fThreadName_;
318 sb <<
", status: "sv << PeekStatusForToString_ ();
320 sb <<
", abortRequested: "sv << fAbortRequested_.load ();
321 sb <<
", refCountBumpedEvent: "sv << fRefCountBumpedInsideThreadMainEvent_.PeekIsSet ();
322 sb <<
", startReadyToTransitionToRunningEvent_: "sv << fStartReadyToTransitionToRunningEvent_.PeekIsSet ();
323 sb <<
", threadDoneAndCanJoin: "sv << fThreadDoneAndCanJoin_.PeekIsSet ();
324 if (fSavedException_.load () !=
nullptr) [[unlikely]] {
325 sb <<
", savedException: "sv << fSavedException_.load ();
327 if (fInitialPriority_.load () != nullopt) [[unlikely]] {
328 sb <<
", initialPriority: "sv << fInitialPriority_.load ();
330#if qStroika_Foundation_Common_Platform_Windows
331 sb <<
", throwInterruptExceptionInsideUserAPC: "sv << fThrowInterruptExceptionInsideUserAPC_;
337void Thread::Ptr::Rep_::ApplyThreadName2OSThreadObject ()
339 if (GetNativeHandle () != NativeHandleType{}) {
340#if qSupportSetThreadNameDebuggerCall_
341#if qStroika_Foundation_Common_Platform_Windows
342 if (::IsDebuggerPresent ()) {
344 struct THREADNAME_INFO {
351 THREADNAME_INFO info;
353 info.dwType = 0x1000;
354 info.szName = useThreadName.c_str ();
355 info.dwThreadID = MyGetThreadId_ (GetNativeHandle ());
358 IgnoreExceptionsForCall (::RaiseException (0x406D1388, 0,
sizeof (info) /
sizeof (DWORD), (ULONG_PTR*)&info));
360#elif qStroika_Foundation_Common_Platform_POSIX && (__GLIBC__ > 2 or (__GLIBC__ == 2 and __GLIBC_MINOR__ >= 12))
364 constexpr size_t kMaxNameLen_{16 - 1};
366 if (narrowThreadName.length () > kMaxNameLen_) {
367 narrowThreadName.erase (kMaxNameLen_);
369 ::pthread_setname_np (GetNativeHandle (), narrowThreadName.c_str ());
375void Thread::Ptr::Rep_::ApplyPriority (Priority priority)
377#if USE_NOISY_TRACE_IN_THIS_MODULE_
379 "Thread::Ptr::Rep_::ApplyPriority",
"threads={}, priority={}"_f, Characters::ToString (*
this), Characters::ToString (priority))};
382 if (nh != NativeHandleType{}) {
383#if qStroika_Foundation_Common_Platform_Windows
385 case Priority::eLowest:
386 Verify (::SetThreadPriority (nh, THREAD_PRIORITY_LOWEST));
388 case Priority::eBelowNormal:
389 Verify (::SetThreadPriority (nh, THREAD_PRIORITY_BELOW_NORMAL));
391 case Priority::eNormal:
392 Verify (::SetThreadPriority (nh, THREAD_PRIORITY_NORMAL));
394 case Priority::eAboveNormal:
395 Verify (::SetThreadPriority (nh, THREAD_PRIORITY_ABOVE_NORMAL));
397 case Priority::eHighest:
398 Verify (::SetThreadPriority (nh, THREAD_PRIORITY_HIGHEST));
403#elif qStroika_Foundation_Common_Platform_POSIX
424 int schedulingPolicy{};
427 Verify (::pthread_getschedparam (nh, &schedulingPolicy, ¶m) == 0);
428 priorityMin = ::sched_get_priority_min (schedulingPolicy);
429 priorityMax = ::sched_get_priority_max (schedulingPolicy);
430#if USE_NOISY_TRACE_IN_THIS_MODULE_
431 DbgTrace (
"schedulingPolicy={}, default-priority={}, sPriorityMin_={}, priorityMax={}"_f, schedulingPolicy,
432 param.sched_priority, priorityMin, priorityMax);
435 int newPThreadPriority{priorityMin};
437 case Priority::eLowest:
438 newPThreadPriority = priorityMin;
440 case Priority::eBelowNormal:
441 newPThreadPriority = (priorityMax - priorityMin) * .25 + priorityMin;
443 case Priority::eNormal:
444 newPThreadPriority = (priorityMax - priorityMin) * .5 + priorityMin;
446 case Priority::eAboveNormal:
447 newPThreadPriority = (priorityMax - priorityMin) * .75 + priorityMin;
449 case Priority::eHighest:
450 newPThreadPriority = priorityMax;
454 newPThreadPriority = (priorityMax - priorityMin) * .5 + priorityMin;
456#if USE_NOISY_TRACE_IN_THIS_MODULE_
457 DbgTrace (
"Setting os thread priority for thread %{} to %{}"_f, (
long long int)(nh), newPThreadPriority);
464 sp.sched_priority = newPThreadPriority;
465 Verify (::pthread_setschedparam (nh, schedulingPolicy, &sp) == 0 or errno == EPERM);
473void Thread::Ptr::Rep_::ThreadMain_ (
const shared_ptr<Rep_> thisThreadRep)
noexcept
477 Characters::ToString (thisThreadRep))};
478#if qStroika_Foundation_Debug_AssertionsChecked
479 Require (Debug::AppearsDuringMainLifetime ());
480 [[maybe_unused]]
auto&& cleanupCheckMain =
Finally ([] ()
noexcept { Require (Debug::AppearsDuringMainLifetime ()); });
485 SuppressInterruptionInContext suppressInterruptionsOfThisThreadCallerKnowsWeHaveItBumpedAndCanProceed;
490 thisThreadRep->fRefCountBumpedInsideThreadMainEvent_.Set ();
494#if qCompilerAndStdLib_thread_local_static_inline_twice_Buggy
495 sCurrentThreadRep_BWA_ () = thisThreadRep;
497 sCurrentThreadRep_ = thisThreadRep;
500 [[maybe_unused]]
IDType thisThreadID = GetCurrentThreadID ();
502#if qStroika_Foundation_Execution_Thread_SupportThreadStatistics
504 Require (Debug::AppearsDuringMainLifetime ());
505 [[maybe_unused]] lock_guard critSec{sThreadSupportStatsMutex_};
506#if qStroika_Foundation_Debug_ShowThreadIndex
508 "Adding thread index {} to sRunningThreads_ ({})"_f, IndexRegistrar::sThe.GetIndex (thisThreadID),
511 DbgTrace (
"Adding thread id {} to sRunningThreads_ ({})"_f, thisThreadID, sRunningThreads_);
513 Verify (sRunningThreads_.insert (thisThreadID).second);
515 [[maybe_unused]]
auto&& cleanup =
Finally ([thisThreadID] ()
noexcept {
516 SuppressInterruptionInContext suppressThreadInterrupts;
517 Require (Debug::AppearsDuringMainLifetime ());
518 [[maybe_unused]] lock_guard critSec{sThreadSupportStatsMutex_};
519#if qStroika_Foundation_Debug_ShowThreadIndex
521 "removing thread index {} from sRunningThreads_ ({})"_f, IndexRegistrar::sThe.GetIndex (thisThreadID),
524 DbgTrace (
"removing thread id {} from sRunningThreads_ ({})"_f, thisThreadID, sRunningThreads_);
526 Verify (sRunningThreads_.erase (thisThreadID) == 1);
531#if qStroika_Foundation_Common_Platform_POSIX
536 sigemptyset (&mySet);
537 (void)sigaddset (&mySet, SignalUsedForThreadInterrupt ());
538 Verify (::pthread_sigmask (SIG_UNBLOCK, &mySet,
nullptr) == 0);
539#if USE_NOISY_TRACE_IN_THIS_MODULE_
540 DbgTrace (
"Just set SIG_UNBLOCK for signal {} in this thread"_f, SignalToName (SignalUsedForThreadInterrupt ()));
544 thisThreadRep->fStartReadyToTransitionToRunningEvent_.Wait ();
546 [[maybe_unused]]
auto&& cleanupThreadDoneEventSetter =
Finally ([thisThreadRep] ()
noexcept {
548 thisThreadRep->fThreadDoneAndCanJoin_.Set ();
551 Assert (thisThreadID == thisThreadRep->GetID ());
553 bool doRun = not thisThreadRep->fAbortRequested_ and not thisThreadRep->IsDone_ ();
555#if __cpp_lib_jthread >= 201911
559 stop_callback stopCallback{thisThreadRep->fStopToken_, [=] () {
561 DbgTrace (
"Something triggered stop_token request stop, so doing abort to make sure we are in an aborting (flag) state."_f);
563 if (not thisThreadRep->fAbortRequested_) [[unlikely]] {
564 IgnoreExceptionsForCall (Ptr{thisThreadRep}.Abort ());
570 DbgTrace (
"In Thread::Rep_::ThreadMain_ - set state to RUNNING for thread: {}"_f, thisThreadRep->ToString ());
571 thisThreadRep->Run_ ();
572 DbgTrace (
"In Thread::Rep_::ThreadProc_ - setting state to COMPLETED for thread: {}"_f, thisThreadRep->ToString ());
575 catch (
const AbortException&) {
576 SuppressInterruptionInContext suppressCtx;
577 DbgTrace (
"In Thread::Rep_::ThreadProc_ - setting state to COMPLETED (InterruptException) for thread: {}"_f, thisThreadRep->ToString ());
578 thisThreadRep->fThreadDoneAndCanJoin_.Set ();
581 SuppressInterruptionInContext suppressCtx;
582 DbgTrace (
"In Thread::Rep_::ThreadProc_ - setting state to COMPLETED (due to EXCEPTION) for thread: {}"_f, thisThreadRep->ToString ());
583 thisThreadRep->fThreadDoneAndCanJoin_.Set ();
586 catch (
const AbortException&) {
587 DbgTrace (
"SERIOUS ERROR in Thread::Rep_::ThreadMain_ () - uncaught InterruptException - see sigsetmask stuff above - somehow not "
592 DbgTrace (
"SERIOUS ERROR in Thread::Rep_::ThreadMain_ () - uncaught exception"_f);
597void Thread::Ptr::Rep_::NotifyOfInterruptionFromAnyThread_ ()
604 Require (not IsDone_ ());
606 Require (fAbortRequested_);
610 if (GetCurrentThreadID () == GetID ()) [[unlikely]] {
624#if qStroika_Foundation_Common_Platform_POSIX
626 [[maybe_unused]] lock_guard critSec{sHandlerInstalled_};
627 if (not sHandlerInstalled_) {
629 sHandlerInstalled_ =
true;
632 (void)
SendSignal (GetNativeHandle (), SignalUsedForThreadInterrupt ());
633#elif qStroika_Foundation_Common_Platform_Windows
634 Verify (::QueueUserAPC (&CalledInRepThreadAbortProc_, GetNativeHandle (),
reinterpret_cast<ULONG_PTR
> (
this)));
639#if qStroika_Foundation_Common_Platform_POSIX
640void Thread::Ptr::Rep_::InterruptionSignalHandler_ (SignalID signal)
noexcept
653#elif qStroika_Foundation_Common_Platform_Windows
654void CALLBACK Thread::Ptr::Rep_::CalledInRepThreadAbortProc_ (ULONG_PTR lpParameter)
657 [[maybe_unused]] Ptr::Rep_* rep =
reinterpret_cast<Ptr::Rep_*
> (lpParameter);
658 Require (GetCurrentThreadID () == rep->GetID ());
659 if (rep->fThrowInterruptExceptionInsideUserAPC_) [[unlikely]] {
679 if (cfg->fStackSize) {
682 if (cfg->fStackGuard) {
685#if qStroika_Foundation_Common_Platform_Windows
686 if (cfg->fThrowInterruptExceptionInsideUserAPC) {
687 result.fThrowInterruptExceptionInsideUserAPC = *cfg->fThrowInterruptExceptionInsideUserAPC;
698 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
702 fRep_->fInitialPriority_.store (priority);
706 fRep_->ApplyPriority (priority);
712 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
713 return fRep_ ==
nullptr ?
String{} : fRep_->fThreadName_;
719 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
720#if USE_NOISY_TRACE_IN_THIS_MODULE_
721 TraceContextBumper ctx{
"Thread::SetThreadName",
"thisThreadID={}, threadName = '{}'"_f, GetID (), threadName};
723 if (fRep_->fThreadName_ != threadName) {
724 fRep_->fThreadName_ = threadName.
As<wstring> ();
725 fRep_->ApplyThreadName2OSThreadObject ();
731 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
732 return fRep_ ==
nullptr ?
"nullptr"sv : fRep_->ToString ();
737 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
740 Require (not fRep_->fStartEverInitiated_);
741#if qStroika_Foundation_Debug_AssertionsChecked
743 auto s = GetStatus ();
744 Require (s == Status::eNotYetRunning or s == Status::eAborting);
773 fRep_->fStartEverInitiated_ =
true;
774 if (fRep_->fAbortRequested_) [[unlikely]] {
778#if __cpp_lib_jthread >= 201911
779 fRep_->fStopToken_ = fRep_->fStopSource_.get_token ();
780 fRep_->fThread_ = jthread{[
this] () ->
void { Rep_::ThreadMain_ (fRep_); }};
782 fRep_->fThread_ = thread{[
this] () ->
void { Rep_::ThreadMain_ (fRep_); }};
784 fRep_->fThreadValid_ =
true;
787 fRep_->fRefCountBumpedInsideThreadMainEvent_.Wait ();
793 fRep_->ApplyThreadName2OSThreadObject ();
794 if (optional<Priority> p = fRep_->fInitialPriority_.load ()) {
795 fRep_->ApplyPriority (*p);
798 fRep_->fStartReadyToTransitionToRunningEvent_.Set ();
803 for (
auto s = GetStatus (); s != Status::eNotYetRunning; s = GetStatus ()) {
807 this_thread::yield ();
809#if qStroika_Foundation_Debug_AssertionsChecked
810 auto s = GetStatus ();
811 Ensure (s == Status::eRunning or s == Status::eAborting or s == Status::eCompleted);
818 Require (*
this !=
nullptr);
819 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
821#if __cpp_lib_jthread >= 201911
822 bool wasAborted = fRep_->fAbortRequested_;
825 fRep_->fAbortRequested_ =
true;
826 if (fRep_->fStartEverInitiated_) {
827#if __cpp_lib_jthread >= 201911
830 if (not wasAborted) [[likely]] {
831 DbgTrace (
"Transitioned state to aborting, so calling fThread_.get_stop_source ().request_stop ();"_f);
832 fRep_->fStopSource_.request_stop ();
843 fRep_->fThreadDoneAndCanJoin_.Set ();
845 if (not IsDone ()) [[likely]] {
847 fRep_->NotifyOfInterruptionFromAnyThread_ ();
849#if USE_NOISY_TRACE_IN_THIS_MODULE_
850 DbgTrace (
"leaving *this = {}"_f, *
this);
857 "*this={}, timeoutAt={}"_f,
ToString (), timeoutAt)};
859 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
867#if USE_NOISY_TRACE_IN_THIS_MODULE_
870 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
871 if (fRep_ and fRep_->IsDone_ () and fRep_->fSavedException_.load () !=
nullptr) {
873 ReThrow (fRep_->fSavedException_.load (),
"Rethrowing exception across threads");
880 if (not WaitForDoneUntilQuietly (timeoutAt)) {
887#if USE_NOISY_TRACE_IN_THIS_MODULE_
889 "*this={}, timeoutAt={}"_f,
ToString (), timeoutAt)};
891 Require (*
this !=
nullptr);
892 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
894 if (fRep_->fThreadDoneAndCanJoin_.WaitUntilQuietly (timeoutAt) == WaitableEvent::WaitStatus::eTriggered) {
905 if (fRep_->fThreadValid_ and fRep_->fThread_.joinable ()) {
907 fRep_->fThread_.join ();
911 Assert (timeoutAt <= Time::GetTickCount ());
915#if qStroika_Foundation_Common_Platform_Windows
918 AssertExternallySynchronizedMutex::ReadContext declareContext{fThisAssertExternallySynchronized_};
919 Require (*
this !=
nullptr);
921 HANDLE thread = fRep_->GetNativeHandle ();
922 if (thread == INVALID_HANDLE_VALUE) {
927 while (GetStatus () != Status::eCompleted) {
929 if (time2Wait <= 0s) {
943Thread::CleanupPtr::~CleanupPtr ()
945 if (*
this !=
nullptr) {
946 SuppressInterruptionInContext suppressInterruption;
961Thread::Ptr Thread::New (
const function<
void ()>& fun2CallOnce,
const optional<Characters::String>& name,
const optional<Configuration>& configuration)
964 Ptr ptr =
Ptr{Memory::MakeSharedPtr<Ptr::Rep_> (fun2CallOnce, CombineCFGs_ (configuration))};
973 return sDefaultConfiguration_.load ();
978 auto result = sDefaultConfiguration_.load ();
979 if (newConfiguration) {
980 sDefaultConfiguration_.store (newConfiguration.value ());
985#if qStroika_Foundation_Execution_Thread_SupportThreadStatistics
986Thread::Statistics Thread::GetStatistics ()
988 [[maybe_unused]] lock_guard critSec{sThreadSupportStatsMutex_};
995#if USE_NOISY_TRACE_IN_THIS_MODULE_
1003#if USE_NOISY_TRACE_IN_THIS_MODULE_
1005 "threads={}, timeoutAt={}"_f, threads, timeoutAt)};
1016#if USE_NOISY_TRACE_IN_THIS_MODULE_
1025#if qStroika_Foundation_Common_Platform_POSIX
1027 SignalID sSignalUsedForThreadInterrupt_ = SIGUSR2;
1029SignalID Thread::SignalUsedForThreadInterrupt () noexcept
1031 return sSignalUsedForThreadInterrupt_;
1033SignalID Thread::SignalUsedForThreadInterrupt (optional<SignalID> signalNumber)
1035 SignalID result = sSignalUsedForThreadInterrupt_;
1037 [[maybe_unused]] lock_guard critSec{sHandlerInstalled_};
1038 if (sHandlerInstalled_) {
1040 sHandlerInstalled_ =
false;
1042 sSignalUsedForThreadInterrupt_ = signalNumber.value ();
1044 if (not sHandlerInstalled_) {
1046 sHandlerInstalled_ =
true;
1063string Thread::FormatThreadID_A (
Thread::IDType threadID,
const FormatThreadInfo& formatThreadInfo)
1074#if qStroika_Foundation_Common_Platform_Windows
1075 constexpr size_t kSizeOfThreadID_ =
sizeof (DWORD);
1076#elif qStroika_Foundation_Common_Platform_POSIX
1077 constexpr size_t kSizeOfThreadID_ =
sizeof (pthread_t);
1084 if constexpr (kSizeOfThreadID_ >=
sizeof (uint64_t)) {
1085 uint64_t threadIDInt = 0;
1087 return formatThreadInfo.fIncludeLeadingZeros ? Characters::CString::Format (
"0x%016llx", threadIDInt)
1088 : Characters::CString::Format (
"0x%llx", threadIDInt);
1091 uint32_t threadIDInt = 0;
1100 constexpr bool kUse16BitThreadIDsIfTheyFit_{
false};
1101 const bool kUse16Bit_ = kUse16BitThreadIDsIfTheyFit_ and threadIDInt <= 0xffff;
1103 return formatThreadInfo.fIncludeLeadingZeros ? Characters::CString::Format (
"0x%04x", threadIDInt)
1104 : Characters::CString::Format (
"0x%x", threadIDInt);
1107 return formatThreadInfo.fIncludeLeadingZeros ? Characters::CString::Format (
"0x%08x", threadIDInt)
1108 : Characters::CString::Format (
"0x%x", threadIDInt);
1113#if qCompilerAndStdLib_ThreadLocalInlineDupSymbol_Buggy
1114#if __cpp_lib_jthread >= 201911
1120optional<stop_token> Thread::GetCurrentThreadStopToken ()
1123 return curThread.GetStopToken ();
1132#if qCompilerAndStdLib_ThreadLocalInlineDupSymbol_Buggy
1156 if (shared_ptr<Ptr::Rep_> thisRunningThreadRep =
1157#
if qCompilerAndStdLib_thread_local_static_inline_twice_Buggy
1158 Ptr::sCurrentThreadRep_BWA_ ().lock ()
1160 Ptr::sCurrentThreadRep_.lock ()
1163 if (t_InterruptionSuppressDepth_ == 0) [[likely]] {
1164 if (thisRunningThreadRep->fAbortRequested_) [[unlikely]] {
1166 Debug::TraceContextBumper ctx{
"Thread::CheckForInterruption",
"insideAbortLogic=true, thisThreadID={}"_f, GetCurrentThreadID ()};
1167 Throw (Thread::AbortException::kThe);
1170#if qStroika_Foundation_Debug_DefaultTracingOn
1171 else if (thisRunningThreadRep->fAbortRequested_) {
1172 static atomic<unsigned int> sSuperSuppress_{};
1173 if (++sSuperSuppress_ <= 1) {
1174 IgnoreExceptionsForCall (
1175 DbgTrace (
"Suppressed interrupt throw: t_InterruptionSuppressDepth_={}, t_Interrupting_={}, backtrace: {}"_f,
1176 t_InterruptionSuppressDepth_, thisRunningThreadRep->fAbortRequested_.load (), Debug::BackTrace::Capture ()));
1197 this_thread::yield ();
#define RequireNotReached()
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
#define WeakAssertNotImplemented()
#define RequireNotNull(p)
#define AssertNotReached()
time_point< RealtimeClock, DurationSeconds > TimePointSeconds
TimePointSeconds is a simpler approach to chrono::time_point, which doesn't require using templates e...
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
#define qStroika_Foundation_Debug_ShowThreadIndex
if true, emit a much shorter thread ID, making - I suspect (testing) for terser and clearer tracelogs...
#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 string AsNarrowSDKString() const
static String FromNarrowSDKString(const char *from)
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
NOT a real mutex - just a debugging infrastructure support tool so in debug builds can be assured thr...
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
nonvirtual void AddSignalHandler(SignalID signal, const SignalHandler &handler)
nonvirtual void RemoveSignalHandler(SignalID signal, const SignalHandler &handler)
static SignalHandlerRegistry sThe
Wrap any object with Synchronized<> and it can be used similarly to the base type,...
Thread::Ptr is a (unsynchronized) smart pointer referencing an internally synchronized std::thread ob...
nonvirtual void SetThreadName(const Characters::String &threadName) const
nonvirtual void WaitForDoneUntil(Time::TimePointSeconds timeoutAt) const
nonvirtual void Abort() const
Abort gracefully shuts down and terminates the given thread (using cooperative multitasking).
nonvirtual bool WaitForDoneUntilQuietly(Time::TimePointSeconds timeoutAt) const
nonvirtual void Start() const
nonvirtual void ThrowIfDoneWithException() const
nonvirtual NativeHandleType GetNativeHandle() const noexcept
nonvirtual Characters::String ToString() const
nonvirtual void AbortAndWaitForDoneUntil(Time::TimePointSeconds timeoutAt) const
Abort () the thread, and then WaitForDone () - but if doesn't finish fast enough, send extra aborts.
nonvirtual Characters::String GetThreadName() const
nonvirtual void SetThreadPriority(Priority priority=Priority::eNormal) const
static const TimeOutException kThe
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
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 RESULT_CONTAINER Map(ELEMENT_MAPPER &&elementMapper) const
functional API which iterates over all members of an Iterable, applies a map function to each element...
String ToString(T &&t, ARGS... args)
Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except fo...
Ptr New(const function< void()> &fun2CallOnce, const optional< Characters::String > &name, const optional< Configuration > &configuration)
void CheckForInterruption()
void AbortAndWaitForDoneUntil(const Traversal::Iterable< Ptr > &threads, Time::TimePointSeconds timeoutAt)
bool IsCurrentThreadInterruptible()
thread::native_handle_type NativeHandleType
void Start(const Traversal::Iterable< Ptr > &threads)
wstring FormatThreadID(Thread::IDType threadID, const FormatThreadInfo &formatInfo={})
void WaitForDone(const Traversal::Iterable< Ptr > &threads, Time::DurationSeconds timeout=Time::kInfinity)
dont_inline void Yield()
calls CheckForInterruption, and std::this_thread::yield ()
Configuration DefaultConfiguration() noexcept
void WaitForDoneUntil(const Traversal::Iterable< Ptr > &threads, Time::TimePointSeconds timeoutAt)
void Abort(const Traversal::Iterable< Ptr > &threads)
foreach Thread t: t.Abort ()
void AbortAndWaitForDone(const Traversal::Iterable< Ptr > &threads, Time::DurationSeconds timeout=Time::kInfinity)
shorthand for AbortAndWaitForDoneUntil (Time::GetTickCount () + timeout)
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
errno_t SendSignal(thread::native_handle_type target, SignalID signal)
EXPERIMENTAL SUPPORT FOR THREAD STACK (and maybe other) settings.
optional< size_t > fStackSize