Namespaces | |
| namespace | Thread |
| Thread is a namespace for Stroika thread code,. | |
Classes | |
| class | AbortableMutex |
| class | Activity |
| class | BlockingQueue |
| class | CallbackRegistry |
| The callbacks an object calls when something happens - each added, and removed by the ID Add gave it - with the guarantees that needs when they are called on a thread of the object's own, and added and removed on others. More... | |
| class | CommandLine |
| struct | ConditionVariable |
| class | DeclareActivity |
| class | DLLLoader |
| class | Exception |
| Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::exception'), which adds UNICODE String support. More... | |
| class | ExceptionStringHelper |
| class | FeatureNotSupportedException |
| class | Function |
| class | IntervalTimer |
| Manage interval timers - like the javascript setIntervalTimer API. More... | |
| class | InvalidCommandLineArgument |
| class | LazyEvalActivity |
| class | LazyInitialized |
| value-object, where the value construction is delayed until first needed (can be handy to avoid c++ include/initializer deadly embrace) More... | |
| class | Logger |
| A simple/portable wrapper on syslog/log4j/WindowsEventlog, with features like throttling, de-duping, varargs, etc. More... | |
| struct | ModuleGetterSetter |
| Helper to define synchronized, lazy constructed, module initialization (intended to work with DataExchange::OptionFile) More... | |
| class | NestedException |
| NestedException contains a new higher level error message (typically based on argument basedOnException) and preserves the original exception (which you can use to get its message, with Characters::ToString (fBasedOnException) More... | |
| struct | NullLock |
| class | OperationNotSupportedException |
| class | PIDLoop |
| class | ProcessRunner |
| Run an external command, with stdin/stdout/stderr as strings or as streams - like perl backticks. More... | |
| class | ProgressMonitor |
| class | RequiredComponentMissingException |
| class | SharedStaticData |
| class | SignalHandler |
| class | SignalHandlerRegistry |
| class | SilentException |
| class | SpinLock |
| class | Synchronized |
| Wrap any object with Synchronized<> and it can be used similarly to the base type, but safely in a thread safe manner, from multiple threads. This is similar to std::atomic. More... | |
| struct | Synchronized_Traits |
| class | SystemErrorException |
| class | ThreadPool |
| class | TimeOutException |
| class | UpdatableWaitForIOReady |
| Simple wrapper on WaitForIOReady (POSIX select/poll/etc API) - except it allows for the list if polled descriptors to be updated dynamically from any thread, and have that automatically wakeup any waiters to restart waiting. More... | |
| class | UserCanceledException |
| class | VirtualLockable |
| class | WaitableEvent |
| class | WaitForIOReady |
| struct | WhenTimeExceeded |
Concepts | |
| concept | IModuleGetterSetterImpl |
Typedefs | |
| using | LogicalCPUCoreSet = Containers::Set< unsigned int > |
| A set of 0-based logical CPU core numbers, as used by Get/SetCPUAffinity. | |
| using | errno_t = int |
| template<typename T > | |
| using | QuickSynchronized = conditional_t< kSpinLock_IsFasterThan_mutex, Synchronized< T, Synchronized_Traits< SpinLock > >, Synchronized< T, Synchronized_Traits< mutex > > > |
| template<typename T > | |
| using | TimedSynchronized = Synchronized< T, Synchronized_Traits< recursive_timed_mutex > > |
| template<typename T > | |
| using | RWSynchronized = Synchronized< T, Synchronized_Traits< shared_timed_mutex > > |
Enumerations | |
| enum class | SequencePolicy { eSeq , ePar , eParUnseq , eUnseq , Stroika_Define_Enum_Bounds } |
| equivalent which of 4 types being used std::execution::sequenced_policy, parallel_policy, etc... More... | |
| enum class | InternallySynchronized |
Functions | |
| void | ThrowSystemErrNo (int sysErr) |
| treats argument sysErr as a platform-defined error number, and calls ThrowError () with an error_code constructed from it. | |
| bool | AnyCurrentActivities () |
| Checks if CaptureCurrentActivities() would produce a non-empty stack (but faster) | |
| Containers::Stack< Activity<> > | CaptureCurrentActivities () |
| Returns a copyable preservable version of the current activities stack. | |
| template<invocable<>... I> | |
| auto | RunAll (I... functions) |
| Run all the argument functions (logically/potentially) in parallel, and wait until they all complete. | |
| optional< LogicalCPUCoreSet > | GetCPUAffinity () |
| Which logical CPU cores this process may currently run on; nullopt if not knowable. | |
| void | SetCPUAffinity (const LogicalCPUCoreSet &cores) |
| Restrict this process to the given logical CPU cores. | |
| bool | SetCPUAffinityQuietly (const LogicalCPUCoreSet &cores) noexcept |
| Like SetCPUAffinity (), but returns false rather than throwing. | |
| optional< unsigned int > | PinToOneLogicalCPUCoreQuietly () noexcept |
| Pin to a single logical CPU core chosen from those currently permitted; returns which, or nullopt. | |
| void | ThrowPOSIXErrNo (errno_t errNo=errno) |
treats argument errNo as a POSIX errno value, and calls ThrowError () with an error_code constructed from it | |
| template<typename INT_TYPE > | |
| INT_TYPE | ThrowPOSIXErrNoIfNegative (INT_TYPE returnCode) |
| void | ThrowError (error_code ec) |
| throw a SystemErrorException with the given error_code - or std::bad_alloc for errc::not_enough_memory, the one type promotion documented below. | |
| template<typename CALL > | |
| auto | Handle_ErrNoResultInterruption (CALL call) -> decltype(call()) |
| Handle UNIX EINTR system call behavior - fairly transparently - just effectively removes them from the set of errors that can be returned. | |
| void | ThrowPOSIXErrNoIfNull (void *returnValue) |
| optional< error_code > | GetAssociatedErrorCode (const exception_ptr &e) noexcept |
| bool | IsA (const error_code &ec, error_condition cond) noexcept |
| Does this error MEAN the given condition - regardless of how it happens to be represented? | |
| bool | IsA (const system_error &e, error_condition cond) noexcept |
| Does this error MEAN the given condition? | |
| bool | IsA (const exception &e, error_condition cond) noexcept |
| ...and false if it carries no error code at all | |
| bool | IsA (const exception_ptr &e, error_condition cond) noexcept |
| ...without having to rethrow it to find out | |
| template<typename F > | |
| auto | TranslateExceptionToOptional (F &&f) -> optional< remove_cvref_t< invoke_result_t< F > > > |
| template<Common::INoThrowInvocable FUNCTION> | |
| auto | Finally (FUNCTION &&f) -> Private_::FinallySentry< FUNCTION > |
| void | DefaultLoggingFatalErrorHandler (const Characters::SDKChar *msg) noexcept |
| void | DefaultLoggingCrashSignalHandler (Execution::SignalID signal) noexcept |
| filesystem::path | GetEXEDir () |
| filesystem::path | GetEXEPath () |
| filesystem::path | GetEXEPath (pid_t processID) |
| optional< bool > | IsAddressReadOnlyAppLifetime (span< const byte > s) |
| Is all of s in memory that is read-only for as long as the program runs - so a pointer into it may be kept, rather than its bytes copied? | |
| optional< filesystem::path > | FindExecutableInPath (const filesystem::path &fn) |
| If fn refers to an executable - return it (using kPATH, and kPathEXT as appropriate) | |
| template<> | |
| void | ThrowIfNull (const Private_::ConstVoidStar &p, const HRESULT &hr) |
| Template specialization for ThrowIfNull (), for thing being thrown HRESULT - really throw HRESULTErrorException. | |
| template<> | |
| void | ThrowIfNull (const Private_::ConstVoidStar &p, const HRESULT &hr) |
| Template specialization for ThrowIfNull (), for thing being thrown HRESULT - really throw HRESULTErrorException. | |
| bool | IsProcessRunning (pid_t pid) |
| pid_t | DetachedProcessRunner (const filesystem::path &executable, const Containers::Sequence< String > &args) |
| DEPRECATED. | |
| template<typename T > | |
| Streams::InputStream::Ptr< T > | MakeInputStreamWithProgress (const Streams::InputStream::Ptr< T > &in, ProgressMonitor::Updater progress) |
| errno_t | SendSignal (thread::native_handle_type target, SignalID signal) |
| void | Sleep (Time::Duration seconds2Wait) |
| void | SleepUntil (Time::TimePointSeconds untilTickCount) |
| template<typename T > | |
| void | Throw (T &&e2Throw) |
| like the builtin C++ 'throw', but stamps in the current Activity context and does helpful, type dependent DbgTrace () messages first | |
| void | ReThrow () |
| template<equality_comparable_with< nullptr_t > T, typename E > | |
| void | ThrowIfNull (const T &p, const E &e) |
| Throws an exception if the argument is null (nullptr, or nullopt, or similar). Optionally takes specific exception to throw (defaults to bad_alloc). | |
| template<typename EXPECTED > | |
| EXPECTED::value_type | ThrowIfFailed (const EXPECTED &e) |
| template<typename EXPECTED > | |
| auto | ThrowIfFailed (const EXPECTED &e) -> typename EXPECTED::value_type |
| template<Common::StdCompat::TimedMutex TIMED_MUTEX, typename EXCEPTION > | |
| void | TryLockUntil (TIMED_MUTEX &m, Time::TimePointSeconds afterTickCount, EXCEPTION &&exception2Throw) |
| template<Common::StdCompat::TimedMutex TIMED_MUTEX, typename EXCEPTION > | |
| unique_lock< TIMED_MUTEX > | UniqueLock (TIMED_MUTEX &m, const chrono::duration< double > &d, EXCEPTION &&exception2Throw) |
| template<typename EXCEPTION > | |
| void | ThrowTimeoutExceptionAfter (Time::TimePointSeconds afterTickCount, EXCEPTION &&exception2Throw) |
| How Stroika reports a timeout. | |
| template<typename EXCEPTION > | |
| void | ThrowIfTimeout (cv_status conditionVariableStatus, EXCEPTION &&exception2Throw) |
| Throw a timeout (. | |
| void | ThrowTimeOutException () |
Variables | |
| constexpr bool | kCPUAffinitySupported |
| Can CPU affinity be controlled on this platform at all? | |
| const LazyInitialized< Containers::Sequence< filesystem::path > > | kPath |
| const LazyInitialized< Containers::Mapping< Characters::SDKString, Characters::SDKString > > | kRawEnvironment |
| convert getenv() to a Mapping of SDKString (in case some issue with charactor set conversion) | |
| const LazyInitialized< Containers::Mapping< Characters::String, Characters::String > > | kEnvironment |
| convert getenv() to a Mapping of Strings for easier access | |
| constexpr bool | kSpinLock_IsFasterThan_mutex |
This means that any code which wishes to report an exception can catch these two types, and use the 'what()' method to report the text of the exception message.
Sadly, there is no documentation (I'm aware of) or specification of the character set/code page reported back by the what () on an exception. It tends to be ascii. Stroika guarantees that all exceptions it throws will use the current SDK characters (
TODO:
TODO:
So data pusher/buffer loop does select on external streams to see if data available.
This implies I must also be able to do the moral equivalent of selects on my BinaryInput/Output streams? Maybe, unless I do all the buffering... But at least for the stdin stream - I need to be able to check if/when there is new data available!!! TRICKY
And related - what if we create a runner, and then destroy the object? How to assure runner fully destroyed? Blocking/waiting or error or detached state?
Design Goals: o Be able to run simple processes and capture output with little overhead, and very easy to do (like perl backticks).
o Be able to support pipes between processes (either within the shell, or between Stroika threads)
o Support large data and blocking issues properly - automating avoidance of pipe full bugs which block processes
o Efficient/Low performance overhead
o For POSIX - simple to cleanly cleanup open sockets/resources (not needed on windows)
o Separate threading implementation from API, so easy to externally specify the thread stuff runs on (e.g. so you can use thread pools to run the processes).
o Work with stroika streams so its easy to have user-defined producers and consumers, and easy to hook together TextStreams (wrappers) - for format conversion/piping.
Design Overview o
TODO:
@todo BETTER INTEGRATE STROIKA THREAD/CANCELATION WITH THIS FORM OF CANCELATION (OK now - but could be better
especially if we could Abort on the main thread / any thread; then could lose the cancel flag in our
rep and just use the thread local rep for cancelation - same notaiton as thread cancel) ; and always have
a worker thread object.
--LGP 2023-12-06
@todo MAYBE allow copy - but just document its a smart pointer and copy just increments refcount.
NOT copy by value semantics.
@todo Look at stuff I did I HeatthFrame progress code to automatically increase displayed progress values
to monotonically when i have guesses - like for network activities. That should somehow integrate
with this - maybe as an optional 'updater' module/adapter?
@todo Not sure if we need to decompose into more component classes and use subtyping for stuff like
thread support. It seems it mgiht work like this, but I'm sure it's not yet elegant.
TODO:
Description:
This module defines support for POSIX (and std c++ defined) Signals (not to be confused
with the 'Signals and slots' design pattern which is largely unrelated). (POSIX) Signals have all sorts of tricky rules for their manipulation/control. This doesn't eliminate those, but it makes most common cases much easier to handle safely. In particular, many cases of signals can be 'handled' not as signals (safe signal handlers).
TODO:
This API encourages that mistake. I changed the Service code to use SetSignalHandler - but That has the default of being hard to debug/non-modular. Maybe have a "SetDefaultHandler" or "SetFallbackHandler" - and that is invoked ONLY if no others? Or maybe a property of all handlers?
Description:
This module defines support for POSIX (and std c++ defined) Signals (not to be confused
with the 'Signals and slots' design pattern which is largely unrelated).
TODO:
This means that any code which wishes to report an exception can catch these two types, and use the 'what()' method to report the text of the exception message.
Sadly, there is no documentation (I'm aware of) or specification of the character set/code page reported back by the what () on an exception. It tends to be ascii. Stroika guarantees that all exceptions it throws will use the current SDK characters (
| using Stroika::Foundation::Execution::LogicalCPUCoreSet = typedef Containers::Set<unsigned int> |
A set of 0-based logical CPU core numbers, as used by Get/SetCPUAffinity.
Deliberately a Set<> of core numbers and not a bitmask: it says what it means, it copies by value like the rest of Stroika, and it does not silently cap at 64 the way a uint64_t would.
Numbering matches the OS's logical core numbering, so an element is always < Common::GetNumberOfLogicalCPUCores ().
Definition at line 78 of file CPUAffinity.h.
| typedef int Stroika::Foundation::Execution::errno_t |
The type of 'errno' variable.
Definition at line 55 of file Exceptions.h.
| using Stroika::Foundation::Execution::QuickSynchronized = typedef conditional_t<kSpinLock_IsFasterThan_mutex, Synchronized<T, Synchronized_Traits<SpinLock> >, Synchronized<T, Synchronized_Traits<mutex> >> |
QuickSynchronized will always use a mutex which is quick, and not a cancelation point. It will typically be implemented using a std::mutex, or a SpinLock, whichever is faster. So - don't use this where you hold onto the lock for extended periods ;-).
Definition at line 779 of file Synchronized.h.
| using Stroika::Foundation::Execution::TimedSynchronized = typedef Synchronized<T, Synchronized_Traits<recursive_timed_mutex> > |
Definition at line 786 of file Synchronized.h.
| using Stroika::Foundation::Execution::RWSynchronized = typedef Synchronized<T, Synchronized_Traits<shared_timed_mutex> > |
RWSynchronized will always use some sort of mutex which supports multiple readers, and a single writer. Typically, using shared_mutex (or shared_timed_mutex).
Definition at line 796 of file Synchronized.h.
|
strong |
equivalent which of 4 types being used std::execution::sequenced_policy, parallel_policy, etc...
Much simpler to just have a single enum rather than these 4 classes. STL choice based on (probably more historical) C++ limitations with overloading (no constexpr) - not much point in that anymore, I don't think.
An API that wants to offer that choice provides an OVERLOAD taking no policy - see Iterable<T>::Apply (), Iterable<T>::Find (), Iterable<T>::OrderBy (). Not passing a policy is then genuinely different from passing one, the implementation is free to get smarter later, and no existing call site changes meaning when it does.
We have no unsequenced implementation, and answering a request for eUnseq/eParUnseq with execution::par would substitute a DIFFERENT policy than the caller asked for: par permits locks and demands no vectorization-safety, while the unsequenced policies forbid locking and may interleave calls within a single thread. So a caller who wrote a correct lock-free SIMD-safe callable would silently get threads, and one who passed eUnseq while holding a mutex would get exactly the hazard the policy told them to avoid.
Running sequentially is a legal execution of EVERY policy - a policy grants permission, it does not compel - so falling back is honest where substituting is not. Dispatch sites therefore read 'case ePar: <parallel>; default: <sequential>', never the inverse.
Definition at line 46 of file Foundation/Execution/Common.h.
|
strong |
The type InternallySynchronized is intended to be used as a flag to mark whether or not a given class/type/object is internally synchronized, or not.
Internally synchronized means that an object can be safely accessed from multiple threads at the same time. If an object is 'not known internally synchronized' - you must use mutexes or some similar means to assure the object in question is not accessed by multiple threads simultaneously.
It is typically provided as an optional argument to static New () methods, such as MemoryStream<>::New ()
Definition at line 58 of file Synchronized.h.
| void Stroika::Foundation::Execution::ThrowSystemErrNo | ( | int | sysErr | ) |
treats argument sysErr as a platform-defined error number, and calls ThrowError () with an error_code constructed from it.
So test what you catch with IsA (e, errc::...), or on Windows Platform::Windows::IsWin32Error (e.code (), ERROR_X) - never by comparing with error_code{n, system_category ()}.
See:
Definition at line 193 of file Exceptions.inl.
| Containers::Stack< Activity<> > Stroika::Foundation::Execution::CaptureCurrentActivities | ( | ) |
Returns a copyable preservable version of the current activities stack.
'render' each current activity on the current thread's activity stack as an Activity<> (so String based), and return the full list as a copyable stack of activities.
Definition at line 19 of file Activity.cpp.
| auto Stroika::Foundation::Execution::RunAll | ( | I... | functions | ) |
Run all the argument functions (logically/potentially) in parallel, and wait until they all complete.
Could be implemented with std::async, or ThreadPool.
If any function throws, an arbitrary one of those exceptions will be rethrown by RunAll.
All functions will complete before RunAll returns (regardless of whether any throw).
This function returns the value of all completed functions as a tuple, unless they return void, in which case they are skipped, and for the special case of all returning void, the RunAll return type is void.
| optional< LogicalCPUCoreSet > Stroika::Foundation::Execution::GetCPUAffinity | ( | ) |
Which logical CPU cores this process may currently run on; nullopt if not knowable.
This is a LIVE query every time, deliberately unlike Common::GetNumberOfLogicalCPUCores () which caches with an allowedStaleness. The core count barely changes; an affinity mask can be rewritten underneath a running process at any moment (a cgroup cpuset being edited, or a launcher applying taskset), and a stale answer here would be used to pick a core that is no longer permitted.
Returns nullopt when kCPUAffinitySupported is false, or when the query fails.
Definition at line 107 of file CPUAffinity.cpp.
| void Stroika::Foundation::Execution::SetCPUAffinity | ( | const LogicalCPUCoreSet & | cores | ) |
Restrict this process to the given logical CPU cores.
\req not cores.empty ()
Definition at line 135 of file CPUAffinity.cpp.
|
noexcept |
Like SetCPUAffinity (), but returns false rather than throwing.
False means unsupported on this platform, or the OS refused; either way nothing changed. Prefer this form: something that would merely like to be pinned should not fail because it happens to be running somewhere that will not allow it.
Definition at line 12 of file CPUAffinity.inl.
|
noexcept |
Pin to a single logical CPU core chosen from those currently permitted; returns which, or nullopt.
The measurement helper. It encodes the thing that is easy to get wrong: it chooses from the mask the caller ALREADY holds rather than naming a core. Hardcoding core 0 is what breaks under 'docker run –cpuset-cpus=2,3', where taskset reports "failed to set pid's affinity: Invalid argument" - and a launcher that ignores that failure while having already redirected its output leaves a zero-byte result file behind.
Returns nullopt if affinity is unsupported or could not be set, having changed nothing, so the caller can carry on unpinned - just noisier.
Definition at line 172 of file CPUAffinity.cpp.
| void Stroika::Foundation::Execution::ThrowPOSIXErrNo | ( | errno_t | errNo = errno | ) |
treats argument errNo as a POSIX errno value, and calls ThrowError () with an error_code constructed from it
See:
Definition at line 159 of file Exceptions.inl.
| INT_TYPE Stroika::Foundation::Execution::ThrowPOSIXErrNoIfNegative | ( | INT_TYPE | returnCode | ) |
Look at the argument value and if < 0,ThrowPOSIXErrNo (), and otherwise return it.
Definition at line 180 of file Exceptions.inl.
| void Stroika::Foundation::Execution::ThrowError | ( | error_code | ec | ) |
throw a SystemErrorException with the given error_code - or std::bad_alloc for errc::not_enough_memory, the one type promotion documented below.
Use this - rather than constructing and throwing a SystemErrorException directly - whenever you have an error_code from ANY error_category: the OS, or a third party library which defines its own category (libcurl, getaddrinfo/DNS, HRESULT, ...). It is what gives callers one portable story about how errors surface, independent of where they came from.
(For reference, the C++ standard defines exactly two subclasses of system_error: std::ios_base::failure and std::filesystem::filesystem_error. std::future_error is NOT one of them - it derives from logic_error - and neither are C++20's chrono local-time exceptions.)
e.what () is the lossy path: it is narrow SDK-charset, so characters the SDK code page cannot represent do not survive. That is the whole reason Exception<> exists (
If you want the activities structurally rather than as message text, dynamic_cast to Execution::ExceptionStringHelper yourself and call GetActivities ().
The bad_alloc case is the ONLY reason two handlers are needed, and it is deliberate: bad_alloc does not derive from system_error, because allocation failure is the one error C++ has always expected you to catch by its own standard type. Folding it in as just another error_code would be more uniform but would fight decades of convention, so it is called out separately instead.
| condition | thrown type | still a SystemErrorException? |
|---|---|---|
errc::not_enough_memory | std::bad_alloc | no |
Every other error_code - errc::timed_out included - throws a plain SystemErrorException: test a timeout with Execution::IsA (e, errc::timed_out).
o Usually nothing: the error_code is passed through untouched, and only the thrown type is chosen from it. e.code ().category () still names where the error came from, which is what makes library-specific diagnosis possible. o Windows codes keep their raw value: the conditions MSVC's system_category leaves out (a WinHTTP timeout, a refused connection, ...) come from
errc::not_enough_memory becomes std::bad_alloc, giving up the error_code entirely - bad_alloc has no code () - because that is the type C++ has always expected you to catch for allocation failure. This is also why it needs its own catch handler, and why GetAssociatedErrorCode () returns nullopt for it. It gives up the caller's message and the Activity stack too; that is deliberate, and the note below says why.Little is lost in practice.
Considered and rejected 2026-09-09: making this throw an Exception<bad_alloc> so the message and activities survive. It works, and catch (const bad_alloc&) would keep matching - but it trades a reliable failure for a richer one that is least reliable exactly when it matters. Revisit only with a concrete case where the lost context actually mattered.
So: test what the error means - e.code () == errc::whatever, or catch (const bad_alloc&) - and none of this normalization is visible to you. Test how it is represented - e.code ().value () == SOME_PLATFORM_CONSTANT - and it is.
If you do not want any of that - if you want the exception object you constructed delivered to the catcher unchanged - then say so by using
The narrow rule is what stops the table being arbitrary. The obvious question about any such table is "why this condition and not permission_denied, or connection_refused?" - and the answer is that C++ defines std::bad_alloc and defines no counterpart for the others. Nothing is invented.
Execution::IsA (e, errc::timed_out) the one portable timeout test.That is the condition's actual meaning, not a Stroika invention. POSIX specifies ETIMEDOUT - which std::errc::timed_out names - as the result of pthread_mutex_timedlock (), pthread_cond_timedwait (), sem_timedwait () and mq_timedreceive (), none of which involve a connection.
generic_category () - which is exactly the case where STROIKA decided something timed out, since make_error_code () always yields that category. A timeout reported by the OS arrives in system_category () (Nothing portable is being overridden: the standard specifies error_category::message () only as "a string that describes the error condition", so the text is implementation-defined and the two platforms already disagreed.
| function | category used |
|---|---|
ThrowError (ec) | whatever category you put in the error_code |
ThrowSystemErrNo (n) | system_category () (on Windows Platform::Windows::Win32_error_category ()) |
ThrowPOSIXErrNo (n) | system_category () on POSIX, else generic_category () |
So on a POSIX system ThrowPOSIXErrNo and ThrowSystemErrNo are the same call - errno IS the platform's native error space there, so one subsumes the other. They diverge only on Windows, where errno and GetLastError () are distinct numbering spaces and must not be tagged with the same category. Pick whichever names where your number actually came from.
The category is not just a label. It supplies default_error_condition () and equivalent (), which is what every e.code () == errc::something test - and the bad_alloc promotion above - is matched against. So tagging a number with the wrong category does not merely mislabel the error, it silently changes which conditions the error appears to satisfy. WAIT_TIMEOUT in a Win32 category compares equal to errc::timed_out; the same number tagged with generic_category () is interpreted as an errno instead, and matches something else or nothing at all. That - not tidiness - is why these two functions must stay separate on Windows. (And it is why Win32 codes get Stroika's Win32_error_category () rather than plain system_category (), whose mapping misses, for example, ERROR_WINHTTP_TIMEOUT.)
ThrowError (errc::timed_out) says so directly:This is exactly ThrowError (make_error_code (ec)) and nothing else. It is unambiguous with the error_code overloads because is_error_code_enum_v<errc> is false - std::errc has no implicit conversion to error_code - so overload resolution cannot pick the wrong one.
generic_category () - it asserts the error originated as a POSIX errno. That is true when Stroika raises the error itself; it is NOT true of an error handed back by the OS or a third-party library, and flattening one of those to an errc discards where it came from. Since e.code () == errc::whatever matches either way, nothing is gained by discarding the category.See:
Definition at line 212 of file Exceptions.inl.
| auto Stroika::Foundation::Execution::Handle_ErrNoResultInterruption | ( | CALL | call | ) | -> decltype (call ()) |
Handle UNIX EINTR system call behavior - fairly transparently - just effectively removes them from the set of errors that can be returned.
Run the given (argument) call. After each call, invoke Thread::CheckForInterruption (). If the call returns < 0 and errno == EINTR, repeat the call. If the result was < 0, but errno != EINTR, then ThrowErrNoIfNegative (); Then return the result.
This behavior is meant to work with the frequent POSIX API semantics of a return value of < 0 implying an error, and < 0 but errno == EINTR means retry the call. This API also provides a cancelation point - so it makes otherwise blocking calls (like select, or read) work well with thread interruption.
Definition at line 257 of file Exceptions.inl.
| void Stroika::Foundation::Execution::ThrowPOSIXErrNoIfNull | ( | void * | returnValue | ) |
Check the argument 'return value' from some function, and if its null, throw a SystemError exception with the current errno value.
Definition at line 272 of file Exceptions.inl.
|
noexcept |
This checks if the given exception_ptr is of a type that contains an error code, and if so it extracts the error code, and returns it (else nullopt).
Definition at line 269 of file Exceptions.cpp.
|
noexcept |
Does this error MEAN the given condition - regardless of how it happens to be represented?
This is the recommended way to test an error in Stroika code. It is e.code () == cond with the question asked once, correctly, and with overloads for the things you actually hold - including exception_ptr, which the standard gives you no way to interrogate without rethrowing.
std::error_code and std::error_condition look interchangeable and are not. A CODE is a
concrete value in some category - `errno`, a Win32 error, a libcurl status. A CONDITION is
the portable meaning those map onto. Comparing a code to a condition asks what the error
MEANS and works whatever category it arrived in. Comparing `e.code ().value ()` to a number
asks how it is REPRESENTED, and is right only if you guessed the category correctly - it
compiles, it looks reasonable, and it is usually wrong.
That trap is inherited from the c++ standard and Stroika cannot remove it. What it can do is
make the recommended form incapable of expressing it: the second parameter is an
error_condition BY VALUE, so `IsA (e, errc::timed_out)` converts implicitly and compiles,
while `IsA (e, ERROR_FILE_NOT_FOUND)` and `IsA (e, someErrorCode)` do not compile at all.
`e.code () == errc::X` remains perfectly correct, and existing code using it is fine.
e.code () == errc::X does not get that for such a code; IsA () is the one portable test.Definition at line 287 of file Exceptions.cpp.
| auto Stroika::Foundation::Execution::TranslateExceptionToOptional | ( | F && | f | ) | -> optional<remove_cvref_t<invoke_result_t<F>>> |
Wrap the the argument function (typically a lambda) in an OPTIONAL of the argument type, and return nullopt - dropping the exception on the floor.
Definition at line 285 of file Exceptions.inl.
| auto Stroika::Foundation::Execution::Finally | ( | FUNCTION && | f | ) | -> Private_::FinallySentry<FUNCTION> |
This helpful utility to plug a missing feature from C++11 - to have a block of code run at the end of a scope - regardless of whether you use normal exit or exception based exit.
This has some defects: o The syntax is ugly - the cleanup having to be declared BEFORE the block instead of afterwards (languages like C# with try/finally the finally goes at the end)
o Exceptions inside of cleanupCodeBlock must be suppressed
Inspired by: http://nerds-central.blogspot.com/2012/03/c11-trycatchfinally-pattern-using-raii.html
Definition at line 31 of file Finally.inl.
|
noexcept |
Sensible argument to RegisterDefaultFatalErrorHandlers, if using Logger (but beware - caller must assure Logger::Activate called appropriately)
Definition at line 587 of file Logger.cpp.
|
noexcept |
Sensible argument to SignalHandlerRegistry::sThe.SetStandardCrashHandlerSignals(...), if using Logger (but beware - caller must assure Logger::Activate called appropriately)
Definition at line 603 of file Logger.cpp.
| filesystem::path Stroika::Foundation::Execution::GetEXEDir | ( | ) |
The directory where the executable that is running this code is located. If this code is compiled into a DLL, this returns the executable directory for the underlying process/executable (not the DLL/so file).
Definition at line 47 of file Module.cpp.
| filesystem::path Stroika::Foundation::Execution::GetEXEPath | ( | ) |
The path where the executable that is running this code is located. If this code is compiled into a DLL, this returns the executable for the underlying process/executable (not the DLL/so file).
Definition at line 57 of file Module.cpp.
| filesystem::path Stroika::Foundation::Execution::GetEXEPath | ( | pid_t | processID | ) |
Return the full path to the given process (throws if not found).
NYI for WINDOWS.
Definition at line 105 of file Module.cpp.
| optional< bool > Stroika::Foundation::Execution::IsAddressReadOnlyAppLifetime | ( | span< const byte > | s | ) |
Is all of s in memory that is read-only for as long as the program runs - so a pointer into it may be kept, rather than its bytes copied?
That memory is the main executable's read-only image, where its string literals and other constant data are (and Stroika's own, since Stroika is linked statically). Another module's read-only image does not count: it can be unloaded.
Cheap: a few comparisons, once the image has been examined (at first use).
Definition at line 23 of file Module.inl.
| optional< filesystem::path > Stroika::Foundation::Execution::FindExecutableInPath | ( | const filesystem::path & | fn | ) |
If fn refers to an executable - return it (using kPATH, and kPathEXT as appropriate)
If fn is an absolute path, use that (possibly with suffixes appended). If fn is not absolute, try appending it to each path from kPATH, and redo same check.
On Windows, if ext is missing, also check kPathEXT
If no matches, return nullopt.
Definition at line 336 of file Module.cpp.
| bool Stroika::Foundation::Execution::IsProcessRunning | ( | pid_t | pid | ) |
Return true, if the pid can be verified to be running, false if verified not running.
Definition at line 23 of file Foundation/Execution/Process.cpp.
| Streams::InputStream::Ptr< T > Stroika::Foundation::Execution::MakeInputStreamWithProgress | ( | const Streams::InputStream::Ptr< T > & | in, |
| ProgressMonitor::Updater | progress | ||
| ) |
Take an input-stream object, and produce another identical, except that it updates the argument progress updater object. Note this works better if the argument 'in' supports RemainingLength, but guesses otherwise.
Definition at line 162 of file ProgressMonitor.inl.
| errno_t Stroika::Foundation::Execution::SendSignal | ( | thread::native_handle_type | target, |
| SignalID | signal | ||
| ) |
Send the given signal to a specific thread (within this process)
This function returns an errno error number. o if the error number is ESRCH, this just menans the target thread doesn't exist anymore, whcih can easily happen (race) - like if you are sending an abort to a thread but it exits first.
o other error numbres - for now - generate an assertion error. The function returns zero if no error
| void Stroika::Foundation::Execution::Sleep | ( | Time::Duration | seconds2Wait | ) |
The portable Sleep() function - will wait the given amount of time - blocking the calling thread. It CAN be interrupted (not talking about thread interrupt - e.g. POSIX signal).
If interrupted, one overload (/2) will return the amount of time remaining, allowing easy re-sleeping.
The other overload (/1) - will check for aborting, but otherwise keep sleeping through interrupts until the time has elapsed.
So use /2 to sleep until interrupted (except for thread interruptions which are respected immediately for either version)
Sleep\1: will restart sleeping to use up the full given sleep time, if interrupted (unless thread interruption causes throw) Sleep\2: may not use up the entire time given as argument, and if not, will place in remainingInSleep the time remaining.
| void Stroika::Foundation::Execution::SleepUntil | ( | Time::TimePointSeconds | untilTickCount | ) |
Wait until the tickCount is >= the given value.
| void Stroika::Foundation::Execution::Throw | ( | T && | e2Throw | ) |
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful, type dependent DbgTrace () messages first
Utility to call a Trace message (hopefully an appropriate one) for an exception being thrown.
o `ThrowError (ec)` - *"report this error in Stroika's NORMALIZED form."* It may change
the type thrown, and may give up part of how the error was represented, so that what
arrives can be tested portably. @see ThrowError for exactly what is preserved (the
meaning) and what can be given up (the representation).
o `Throw (x)` - *"throw exactly THIS."* No translation, no promotion, nothing lossy: the
catcher gets the object you constructed, with its TYPE and its MESSAGE unchanged. That
is a deliberate property, not a shortcoming: Throw (X) throwing something other than X
would be a far worse surprise. (It does fill in the Activity context when the exception
did not specify one - see below - which adds to what the exception reports without
changing what it is.)
The consequence is that constructing a SystemErrorException yourself and throwing it here skips the promotion documented at Doing that is perfectly legal, and occasionally what you want. But it is almost never what you want when the error_code came from the OS or from a library with its own error_category (libcurl's CURLE_OUT_OF_MEMORY, say): there, you want ThrowError (ec). If you have an error_code, reach for ThrowError () first, and use Throw () only if you have a reason to want the promotion suppressed.
static const exception - built once and thrown many times - report each throw's own context instead of freezing whichever context happened to be live the first time. Activities specified deliberately (passed to the constructor, or set with ImbueActivities ()) are never overwritten, and an exception thrown with a bare throw X{} gets no Activity context at all. | void Stroika::Foundation::Execution::ReThrow | ( | ) |
| void Stroika::Foundation::Execution::ThrowIfNull | ( | const T & | p, |
| const E & | e | ||
| ) |
| EXPECTED::value_type Stroika::Foundation::Execution::ThrowIfFailed | ( | const EXPECTED & | e | ) |
If the first argument is failed throw the exception, and else return the value. Works with std::expected, to Common::StdCompat::expected
For some reason, this doesn't appear to be part of the std::expected interface, so we define it here. Maybe there is a way with transform, etc, but it doesn't appear to be so (without using a two step process where you create the expected, and then can reference it in a second step, binding it into the lambda with transform_error or some such).
| auto Stroika::Foundation::Execution::ThrowIfFailed | ( | const EXPECTED & | e | ) |
If the first argument is failed throw the exception, and else return the value. Works with std::expected, to Common::StdCompat::expected
For some reason, this doesn't appear to be part of the std::expected interface, so we define it here. Maybe there is a way with transform, etc, but it doesn't appear to be so (without using a two step process where you create the expected, and then can reference it in a second step, binding it into the lambda with transform_error or some such).
| void Stroika::Foundation::Execution::TryLockUntil | ( | TIMED_MUTEX & | m, |
| Time::TimePointSeconds | afterTickCount, | ||
| EXCEPTION && | exception2Throw | ||
| ) |
Translate timed_mutex, or recursive_timed_mutex try_lock_until () calls which fail into a timeout exception (
Definition at line 14 of file TimedLock.inl.
| unique_lock< TIMED_MUTEX > Stroika::Foundation::Execution::UniqueLock | ( | TIMED_MUTEX & | m, |
| const chrono::duration< double > & | d, | ||
| EXCEPTION && | exception2Throw | ||
| ) |
Simple wrapper on construction of unique_lock<TIMED_MUTEX> - which translates the timeout into a timeout exception (
Definition at line 34 of file TimedLock.inl.
| void Stroika::Foundation::Execution::ThrowTimeoutExceptionAfter | ( | Time::TimePointSeconds | afterTickCount, |
| EXCEPTION && | exception2Throw | ||
| ) |
How Stroika reports a timeout.
Stroika has no dedicated timeout exception TYPE. A timeout is reported the way every other error is - as a SystemErrorException (which IS a std::system_error) carrying errc::timed_out - so it is caught and tested the standard C++ way:
\code
catch (const system_error& e) {
if (Execution::IsA (e, errc::timed_out)) {
...
}
}
\endcode
To raise one, say
Throw a timeout (
This function facilitates writing code like: Time::TimePointSeconds timeoutAfter = Time::GetTickCount () + 1.0; do_someting_dont_know_how_long_it_will_take(); Execution::ThrowTimeoutExceptionAfter (timeoutAfter);
Definition at line 21 of file Timeout.inl.
| void Stroika::Foundation::Execution::ThrowIfTimeout | ( | cv_status | conditionVariableStatus, |
| EXCEPTION && | exception2Throw | ||
| ) |
Throw a timeout (.
Definition at line 44 of file Timeout.inl.
| void Stroika::Foundation::Execution::ThrowTimeOutException | ( | ) |
Everything that is NOT deprecated has moved out:
Definition at line 34 of file TimeOutException.h.
|
constexpr |
Can CPU affinity be controlled on this platform at all?
When false: SetCPUAffinity () throws FeatureNotSupportedException, SetCPUAffinityQuietly () returns false without throwing (the check is if constexpr, so it costs nothing), GetCPUAffinity () returns nullopt, and PinToOneLogicalCPUCoreQuietly () returns nullopt having changed nothing. Test this, or just use a Quietly form - affinity is a tuning knob, and code that requires it cannot run on macOS.
Definition at line 89 of file CPUAffinity.h.
|
extern |
The set of system locations to look for an executable (note order matters, which is why this is a Sequence)
Definition at line 235 of file Module.cpp.
|
extern |
convert getenv() to a Mapping of SDKString (in case some issue with charactor set conversion)
Definition at line 271 of file Module.cpp.
|
extern |
convert getenv() to a Mapping of Strings for easier access
Definition at line 323 of file Module.cpp.
|
constexpr |
SpinLock and mutex can be nearly used interchangeably. Oftentimes, users will want to define a typedef which selects the faster implementation.
How much faster than a mutex? Tests/52 carries that comparison as a threshold test. Untrustworthy (not super well tested) results maybe still helpful:
+--------------+------------+----------------------+-----------+-------+
| OS | CPU | compiler | std lib | ratio |
+--------------+------------+----------------------+-----------+-------+
| Windows | x86-64 | MSVC 14.51.36231 | MSVC STL | 0.49 |
| macOS 26.5 | arm64 (M1) | Apple clang 17.0.0 | libc++ | 0.72 |
| Ubuntu 26.04 | x86-64 | g++ 16 | libstdc++ | 1.22 |
| Ubuntu 26.04 | x86-64 | g++ 15 (C++23) | libstdc++ | 1.25 |
+--------------+------------+----------------------+-----------+-------+
ratio = SpinLock time / mutex time, so BELOW 1.0 means SpinLock won. Tests/52 at
-x 15, Release builds, pinned to one core where the OS allows it (macOS does not).
There is no clang Release configuration to run Tests/52 from, so the compiler-versus-standard- library question was separated with a standalone replica of the same loop, all three built and run on one Linux box:
g++ 16 + libstdc++ 1.57 SpinLock's own time came out the SAME in all
clang++ 22 + libstdc++ 1.25 three (1.554 / 1.554 / 1.556s) - every bit of
clang++ 22 + libc++ 0.99 the spread is in how fast std::mutex is.
So this tracks the standard library rather than the compiler: libstdc++ has a very fast uncontended std::mutex and beats this spinlock whichever compiler builds it, libc++ on Linux is a wash, and MSVC STL and libc++-on-macOS lose to it clearly. Which is why kSpinLock_IsFasterThan_mutex below keys off the std-library macros. Beware comparing the two tables: the replica put g++/libstdc++ at 1.57 where Tests/52 said 1.22, so trust the SIGN of any of this and not the magnitude.
And read this caveat before believing any of it. That loop takes and releases the lock from a SINGLE thread, which is a spinlock's best case: no syscall, no futex, and never any actual waiting. Under real contention a spinlock burns CPU while it spins, and can be very much WORSE than a mutex - so treat these numbers as the ceiling rather than the expected value, and measure your own case if it is contended.
Definition at line 74 of file SpinLock.h.