8#include "Stroika/Foundation/Execution/WaitableEvent.h"
20 class Thread::Ptr::Rep_ {
22 Rep_ (
const function<
void ()>& runnable,
const optional<Configuration>& configuration);
26 nonvirtual
IDType GetID ()
const;
31 nonvirtual
Status PeekStatusForToString_ () const noexcept;
32 nonvirtual
bool IsDone_ () const noexcept;
37#if __cpp_lib_jthread >= 201911
39 nonvirtual stop_token GetStopToken ()
const;
43 nonvirtual Characters::String
ToString ()
const;
46 nonvirtual
void ApplyThreadName2OSThreadObject ();
49 nonvirtual
void ApplyPriority (
Priority priority);
52 nonvirtual
void Run_ ();
58 nonvirtual
void NotifyOfInterruptionFromAnyThread_ ();
61 static void ThreadMain_ (
const shared_ptr<Rep_> thisThreadRep)
noexcept;
64#if qStroika_Foundation_Common_Platform_POSIX
65 static void InterruptionSignalHandler_ (SignalID signal)
noexcept;
66#elif qStroika_Foundation_Common_Platform_Windows
67 static void CALLBACK CalledInRepThreadAbortProc_ (ULONG_PTR lpParameter);
71 function<void ()> fRunnable_;
72 atomic<bool> fAbortRequested_{
false};
74#if __cpp_lib_jthread >= 201911
75 stop_source fStopSource_;
76 stop_token fStopToken_;
81 atomic<bool> fStartEverInitiated_{
false};
82 atomic<bool> fThreadValid_{
false};
83 WaitableEvent fRefCountBumpedInsideThreadMainEvent_;
84 WaitableEvent fStartReadyToTransitionToRunningEvent_;
85 WaitableEvent fThreadDoneAndCanJoin_;
87 Synchronized<exception_ptr> fSavedException_;
88 Synchronized<optional<Priority>> fInitialPriority_;
89#if qStroika_Foundation_Common_Platform_Windows
90 bool fThrowInterruptExceptionInsideUserAPC_{
false};
103#if __cpp_lib_jthread >= 201911
104 inline stop_token Thread::Ptr::Rep_::GetStopToken ()
const
106 return this->fStopToken_;
111 return fThread_.get_id ();
115 return fThread_.native_handle ();
118 Stroika_Foundation_Debug_ATTRIBUTE_NO_SANITIZE_THREAD
inline Thread::Status Thread::Ptr::Rep_::PeekStatusForToString_ () const noexcept
120 if (fThreadDoneAndCanJoin_.PeekIsSet ()) {
121 return Status::eCompleted;
123 if (fAbortRequested_) {
124 return Status::eAborting;
126 if (fStartReadyToTransitionToRunningEvent_.PeekIsSet ()) {
127 return Status::eRunning;
129 return Status::eNotYetRunning;
131 inline bool Thread::Ptr::Rep_::IsDone_ () const noexcept
133 return fThreadDoneAndCanJoin_.GetIsSet ();
148 inline Thread::Ptr::Ptr (
const Ptr& src)
152 inline Thread::Ptr::Ptr (Ptr&& src) noexcept
153 : fRep_{move (src.fRep_)}
156 inline Thread::Ptr& Thread::Ptr::operator= (
const Ptr& rhs)
158 Debug::AssertExternallySynchronizedMutex::ReadContext readLock1{rhs.fThisAssertExternallySynchronized_};
159 Debug::AssertExternallySynchronizedMutex::WriteContext declareContext{fThisAssertExternallySynchronized_};
163 inline Thread::Ptr& Thread::Ptr::operator= (Ptr&& rhs)
noexcept
165 Debug::AssertExternallySynchronizedMutex::WriteContext declareWriteContext1{rhs.fThisAssertExternallySynchronized_};
166 Debug::AssertExternallySynchronizedMutex::WriteContext declareWriteContext2{fThisAssertExternallySynchronized_};
167 fRep_ = move (rhs.fRep_);
170 inline Thread::IDType Thread::Ptr::GetID ()
const
172 Debug::AssertExternallySynchronizedMutex::ReadContext declareReadContext{fThisAssertExternallySynchronized_};
173 if (fRep_ ==
nullptr) [[unlikely]] {
176 return fRep_->GetID ();
181 if (fRep_ ==
nullptr) [[unlikely]] {
184 return fRep_->GetNativeHandle ();
186#if __cpp_lib_jthread >= 201911
187 inline stop_token Thread::Ptr::GetStopToken ()
const
191 return fRep_->GetStopToken ();
194 inline void Thread::Ptr::reset () noexcept
199 inline function<void ()> Thread::Ptr::GetFunction ()
const
202 if (fRep_ ==
nullptr) [[unlikely]] {
205 return fRep_->fRunnable_;
207 inline bool Thread::Ptr::operator== (
const Ptr& rhs)
const
211 return fRep_ == rhs.fRep_;
213 inline bool Thread::Ptr::operator== (nullptr_t)
const
216 return fRep_ ==
nullptr;
218 inline strong_ordering Thread::Ptr::operator<=> (
const Ptr& rhs)
const
220 Debug::AssertExternallySynchronizedMutex::ReadContext readLock1{fThisAssertExternallySynchronized_};
221 Debug::AssertExternallySynchronizedMutex::ReadContext readLock2{rhs.fThisAssertExternallySynchronized_};
222 return Foundation::Common::StdCompat::compare_three_way{}(fRep_, rhs.fRep_);
224 inline strong_ordering Thread::Ptr::operator<=> (nullptr_t)
const
226 Debug::AssertExternallySynchronizedMutex::ReadContext readLock1{fThisAssertExternallySynchronized_};
227#if qCompilerAndStdLib_stdlib_compare_three_way_present_but_Buggy
228 return Foundation::Common::StdCompat::compare_three_way{}(fRep_,
nullptr);
230 return fRep_ <=>
nullptr;
233 inline Thread::Ptr::operator bool ()
const
236 return fRep_ !=
nullptr;
238#if qStroika_Foundation_Common_Platform_Windows
239 inline bool Thread::Ptr::ThrowInterruptExceptionInsideUserAPC () const noexcept
242 return fRep_ ==
nullptr ? false : fRep_->fThrowInterruptExceptionInsideUserAPC_;
244 inline bool Thread::Ptr::ThrowInterruptExceptionInsideUserAPC (optional<bool> throwInterruptExceptionInsideUserAPC)
247 bool result = fRep_ ==
nullptr ? false : fRep_->fThrowInterruptExceptionInsideUserAPC_;
248 if (throwInterruptExceptionInsideUserAPC) {
250 fRep_->fThrowInterruptExceptionInsideUserAPC_ = throwInterruptExceptionInsideUserAPC.value ();
259 if (fRep_->fThreadDoneAndCanJoin_.GetIsSet ()) {
260 return Status::eCompleted;
262 if (fRep_->fAbortRequested_) {
263 return Status::eAborting;
265 if (fRep_->fStartReadyToTransitionToRunningEvent_.GetIsSet ()) {
266 return Status::eRunning;
268 return Status::eNotYetRunning;
270 inline bool Thread::Ptr::IsDone ()
const
274 return fRep_->fThreadDoneAndCanJoin_.GetIsSet ();
278 JoinUntil (timeout + Time::GetTickCount ());
284 ThrowIfDoneWithException ();
302 inline Thread::CleanupPtr::CleanupPtr (AbortFlag abortFlag,
Ptr threadPtr)
304 , fAbort_{abortFlag == AbortFlag::eAbortBeforeWaiting}
310 Ptr::operator= (rhs);
319 inline Thread::Ptr Thread::New (
const function<
void ()>& fun2CallOnce,
const optional<Configuration>& configuration)
321 return New (fun2CallOnce, nullopt, configuration);
325 return New (fun2CallOnce, optional<Characters::String>{name}, configuration);
327 inline auto Thread::New (
const function<
void ()>& fun2CallOnce,
AutoStartFlag,
const optional<Characters::String>& name,
328 const optional<Configuration>& configuration) -> Ptr
330 Ptr ptr =
New (fun2CallOnce, name, configuration);
335 const optional<Configuration>& configuration) -> Ptr
337 return New (fun2CallOnce, AutoStartFlag::eAutoStart, optional<Characters::String>{name}, configuration);
339 inline auto Thread::New (
const function<
void ()>& fun2CallOnce,
AutoStartFlag,
const optional<Configuration>& configuration) -> Ptr
341 return New (fun2CallOnce, AutoStartFlag::eAutoStart, nullopt, configuration);
363 return this_thread::get_id ();
373#if qCompilerAndStdLib_thread_local_static_inline_twice_Buggy
374 return Ptr{Ptr::sCurrentThreadRep_BWA_ ().lock ()};
376 return Ptr{Ptr::sCurrentThreadRep_.lock ()};
380#if !qCompilerAndStdLib_ThreadLocalInlineDupSymbol_Buggy
381#if __cpp_lib_jthread >= 201911
387 inline optional<stop_token> Thread::GetCurrentThreadStopToken ()
390 return curThread.GetStopToken ();
399#if !qCompilerAndStdLib_ThreadLocalInlineDupSymbol_Buggy
407#if qCompilerAndStdLib_thread_local_static_inline_twice_Buggy
408 return Ptr::sCurrentThreadRep_BWA_ ().lock () !=
nullptr;
410 return Ptr::sCurrentThreadRep_.lock () !=
nullptr;
419 constexpr EnumNames<Execution::Thread::Status> DefaultNames<Execution::Thread::Status>::k{{{
420 {Execution::Thread::Status::eNotYetRunning, L
"Not-Yet-Running"},
421 {Execution::Thread::Status::eRunning, L
"Running"},
422 {Execution::Thread::Status::eAborting, L
"Aborting"},
423 {Execution::Thread::Status::eCompleted, L
"Completed"},
426 constexpr EnumNames<Execution::Thread::Priority> DefaultNames<Execution::Thread::Priority>::k{{{
427 {Execution::Thread::Priority::eLowest, L
"Lowest"},
428 {Execution::Thread::Priority::eBelowNormal, L
"Below-Normal"},
429 {Execution::Thread::Priority::eNormal, L
"Normal"},
430 {Execution::Thread::Priority::eAboveNormal, L
"Above-Normal"},
431 {Execution::Thread::Priority::eHighest, L
"Highest"},
#define RequireNotNull(p)
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.
String is like std::u32string, except it is much easier to use, often much more space efficient,...
shared_lock< const AssertExternallySynchronizedMutex > ReadContext
Instantiate AssertExternallySynchronizedMutex::ReadContext to designate an area of code where protect...
unique_lock< AssertExternallySynchronizedMutex > WriteContext
Instantiate AssertExternallySynchronizedMutex::WriteContext to designate an area of code where protec...
nonvirtual CleanupPtr & operator=(const Ptr &)
Thread::Ptr is a (unsynchronized) smart pointer referencing an internally synchronized std::thread ob...
nonvirtual void Start() const
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.
STRING_TYPE ToString(FLOAT_TYPE f, const ToStringOptions &options={})
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)
void WaitForDone(const Traversal::Iterable< Ptr > &threads, Time::DurationSeconds timeout=Time::kInfinity)
void WaitForDoneUntil(const Traversal::Iterable< Ptr > &threads, Time::TimePointSeconds timeoutAt)
void AbortAndWaitForDone(const Traversal::Iterable< Ptr > &threads, Time::DurationSeconds timeout=Time::kInfinity)
shorthand for AbortAndWaitForDoneUntil (Time::GetTickCount () + timeout)