Thread is a namespace for Stroika thread code,. More...
Classes | |
class | CleanupPtr |
struct | Configuration |
EXPERIMENTAL SUPPORT FOR THREAD STACK (and maybe other) settings. More... | |
class | IndexRegistrar |
class | Ptr |
Thread::Ptr is a (unsynchronized) smart pointer referencing an internally synchronized std::thread object (rep), with special feautres, including cancelation. More... | |
class | SuppressInterruptionInContext |
Typedefs | |
using | IDType = thread::id |
using | NativeHandleType = thread::native_handle_type |
Enumerations | |
enum | AutoStartFlag |
enum class | Status : uint8_t |
enum class | Priority |
Functions | |
void | CheckForInterruption () |
bool | IsCurrentThreadInterruptible () |
Configuration | DefaultConfiguration () noexcept |
Ptr | New (const function< void()> &fun2CallOnce, const optional< Characters::String > &name, const optional< Configuration > &configuration) |
void | Start (const Traversal::Iterable< Ptr > &threads) |
void | Abort (const Traversal::Iterable< Ptr > &threads) |
foreach Thread t: t.Abort () | |
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) |
| |
void | AbortAndWaitForDoneUntil (const Traversal::Iterable< Ptr > &threads, Time::TimePointSeconds timeoutAt) |
wstring | FormatThreadID (Thread::IDType threadID, const FormatThreadInfo &formatInfo={}) |
Ptr | GetCurrent () |
dont_inline void | Yield () |
calls CheckForInterruption, and std::this_thread::yield () | |
Thread is a namespace for Stroika thread code,.
Stroika Threads are built on std::jthread, so can be used mostly interoperably. However,Stroika threads add a number of very useful features to std::threads: o Simpler Cancelation/Interruption/Aborting (c++ 20 introduces thread cancelation via explicitly managed stop_tokens, but Stroika's thread cancelation uses this, but hides it - mostly). o EINTR handling (POSIX only)
as well as a couple modestly helpful features (that can be done other ways directly with std::thread): o Copyability (using Thread::Ptr) o Better lifetime management (the thread envelope - object you create - can go away, but the underlying thread can continue running, with its memory/resources being cleaned up autoamtically. o And several minor features like (mostly) portably setting thread priorities, names, etc and more
Note - the cancelation feature is very handy for building large scale applications which use worker tasks and things like thread pools, to be able to reclaim resources, cancel ongoing operations as useless, and maintain overall running system integrity.
Using the smartpointer wrapper Thread around a thread guarantees its reference counting will work safely - so that even when all external references go away, the fact that the thread is still running will keep the reference count non-zero.
Thread Aborting: The Stroika Thread class supports the idea of 'aborting' a thread (cooperatitve cancelation)
Thread Interruoption: Stroika v2.1 and earlier supported a feature like Thread-Abort, called 'Interrupt' - that acted like thread abort, except that it was not sticky - and once the interrupt was handled, it 'reset' to not interrupting anymore.
I never really found any compelling use case for this idea (borrowed from java thread interrupt).
And I think the scenarios where it MIGHT be helpful, could be handled just as easily with condition variables.
Also, c++20 added support which allows thead aborting (stop_token), but that doesn't work well/easily for basic interruption.
So - abandoned Thread::Interrupt(...) in Stroika v3.
Nomenclature Note: In some libraries, the term interruption, cancelation is used for thread aborting. > java uses interruption > boost uses cancelation, > POSIX uses cancelation (pthread_canel) > and .net uses Interrupt and Abort
The basic idea is that a thread goes off on its own, doing stuff, and an external force decides to tell it to stop.
Examples of this might be: (1) A search user interface, which starts searching as the user types. Once the process has received a certain number of characters it starts searching, but perhaps before the search is done, another character comes in, so the GUI code will want to Abort the existing search, and start a new one (with the extra character(s)). (2) A web server - which is serving up content, and it told to shut-down. It must interrupt existing in process processes - some maybe handling a read/write sequence, and some perhaps doing a socket listen/accept call.
When a thread is aborted, it (in that thread) throws class AbortException;
Thread 'interruption' happens via 'cancelation points'. Cancelation points are points in the code where we check to see if the current running thread has been interrupted (or aborted) and raise the appropriate exception.
This mechanism is almost completely co-operative, meaning that user code must call CheckForInterruption () in the right places to make interruption work. But Stroika is structured to make that happen automatically throughout most of its code, but having key routines (like Sleep, and WaitableEvents) automatically internally be cancelation points.
The only slight exception to this is on Windows, where we have the APC mechanism that will interupt a wide vareity of windows system calls (see docs below).
Thread interruption/aborting is tricky todo safely and portably. We take a safe, cooperative approach. (1) We maintain a thread-local-storage variable - saying if this thread has been aborted. Sprinkling CheckForInterruption throughout your code - will trigger an InterupptedException () or AbortException () in that thread context. Note a pointer to that TLS interruption variable is also stored in the thread 'rep' object, so it can be set (by Thread::Interrupt).
(2) WINDOWS ONLY: Async-injection (QueueUserAPC/Windows) - Alertable state - certain (alertable) functions get interrupted and return WAIT_IO_COMPLETION (like POSIX EINTR).
This allows interrupting windows waiting / blocking functions (most of them).
(3) POSIX ONLY: Signal injection - we send a special (defaults to SIG_USR2) signal to a particular thread. This triggers an EINTR on most UNIX system calls, which are automatically restarted in most cases (
This allows interrupting UNIX waiting / blocking functions (all of them?).
Handle_ErrNoResultInterruption() The short of it is that you need to catch EINTR and restart the call for these system calls: o read, readv, write, writev, ioctl o open() when dealing with a fifo o wait* o Anything socket based (send*, recv*, connect, accept etc) o flock and lock control with fcntl o mq_ functions which can block o futex o sem_wait (and timed wait) o pause, sigsuspend, sigtimedwait, sigwaitinfo o poll, epoll_wait, select and 'p' versions of the same o msgrcv, msgsnd, semop, semtimedop o close (although, on Linux, EINTR won't happen here) o any sleep functions (careful, you need to handle this are restart with different arguments) This is integrated with Stroika's thread cancelation mechanism.
Thread Cancelation Points A cancelation point is any (typically but not always blocking) function which will be interrupted (cause interruption exception) and stop blocking, when someone calls Thread::Interupt or Thread::Abort() on its thread object.
Roughly, these are subroutines which call CheckForInterruption () frequently, internally.
As its crucial to understand this in the API, we document each such function with Cancelation Point in its doc header. For example, the Sleep() overloads are cancelation points.
Equally important to understand, is when a function guarantees its NOT a cancelation point - which we will document with Not Cancelation Point, and typically also noexcept. The DbgTrace () calls fall into this category.
If you must have a thread running like that, use std::thread (though I'm not sure how well that will work either).
Note the Thread 'smartpointer' wrapper can be constructed/destructed statically (before/after main). You just cannot start a thread before main, nor allow it to still be running (not completed) before exiting. #if qStroika_Foundation_Execution_Thread_SupportThreadStatistics (defaults true in debug builds) an attempt is made to auto-detect this and diagnose it in the tracelog and with assertions.
using Stroika::Foundation::Execution::Thread::IDType = typedef thread::id |
Thread::IDType is a portable representation which is a key to currently existing system threads.
using Stroika::Foundation::Execution::Thread::NativeHandleType = typedef thread::native_handle_type |
|
strong |
|
strong |
void Stroika::Foundation::Execution::Thread::CheckForInterruption | ( | ) |
Our thread interruption (and abort) mechanism only throws at certain 'signalable' (alertable/cancelable) spots in the code - like sleeps, most reads, etc. This function will also trigger a throw if called inside a thread which is being aborted.
Any call to this routine is a 'cancelation point'.
Definition at line 1143 of file Thread.cpp.
bool Stroika::Foundation::Execution::Thread::IsCurrentThreadInterruptible | ( | ) |
Returns true iff it is potentially useful to call CheckForInterruption.
Definition at line 405 of file Thread.inl.
|
noexcept |
Return or Set the global default configuration for Thread object construction.
Definition at line 965 of file Thread.cpp.
Thread::Ptr Stroika::Foundation::Execution::Thread::New | ( | const function< void()> & | fun2CallOnce, |
const optional< Characters::String > & | name, | ||
const optional< Configuration > & | configuration | ||
) |
No arg- constructor is available for use in applications like thread pools. Also, a variety of cases, its handy to declare a Thread data member (and init in CTOR), but not specify what gets run until later.
After this call, GetStatus () returns Status::eNull
fun2CallOnce is called precisely once by this thread CTOR, but called in another thread with the arg 'arg'.
It's not expected one would need to use this often, but when you need it you need it early - before the thread has been constructed (generally) - or at least before started (sucks swapping stacks out on a running thread ;-))
Definition at line 955 of file Thread.cpp.
void Stroika::Foundation::Execution::Thread::Start | ( | const Traversal::Iterable< Ptr > & | threads | ) |
Definition at line 347 of file Thread.inl.
void Stroika::Foundation::Execution::Thread::Abort | ( | const Traversal::Iterable< Ptr > & | threads | ) |
foreach Thread t: t.Abort ()
Definition at line 987 of file Thread.cpp.
void Stroika::Foundation::Execution::Thread::WaitForDone | ( | const Traversal::Iterable< Ptr > & | threads, |
Time::DurationSeconds | timeout = Time::kInfinity |
||
) |
Definition at line 351 of file Thread.inl.
void Stroika::Foundation::Execution::Thread::WaitForDoneUntil | ( | const Traversal::Iterable< Ptr > & | threads, |
Time::TimePointSeconds | timeoutAt | ||
) |
Definition at line 1008 of file Thread.cpp.
void Stroika::Foundation::Execution::Thread::AbortAndWaitForDone | ( | const Traversal::Iterable< Ptr > & | threads, |
Time::DurationSeconds | timeout = Time::kInfinity |
||
) |
Definition at line 343 of file Thread.inl.
void Stroika::Foundation::Execution::Thread::AbortAndWaitForDoneUntil | ( | const Traversal::Iterable< Ptr > & | threads, |
Time::TimePointSeconds | timeoutAt | ||
) |
Definition at line 995 of file Thread.cpp.
wstring Stroika::Foundation::Execution::Thread::FormatThreadID | ( | Thread::IDType | threadID, |
const FormatThreadInfo & | formatInfo = {} |
||
) |
Represent the thread ID for display - typically as an integer.
Definition at line 1052 of file Thread.cpp.
Thread::Ptr Stroika::Foundation::Execution::Thread::GetCurrent | ( | ) |
If the current (calling) thread is a 'Stroika thread' - that thread Ptr is returned. If the current thread is NOT a stroika thread (e.g. the main thread) this will return a nullptr.
Definition at line 371 of file Thread.inl.
void Stroika::Foundation::Execution::Thread::Yield | ( | ) |
calls CheckForInterruption, and std::this_thread::yield ()
Definition at line 1180 of file Thread.cpp.