Stroika Library 3.0d25
 
Loading...
Searching...
No Matches
Stroika::Foundation::Execution Namespace Reference

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
 

Detailed Description

Note
Code-Status: Beta
Design Note (essentially) All exceptions thrown by Stroika (except where needed by quirks of underlying library integrated with) inherit from std::exception, or are Stroika::Foundation::Execution::SilentException.

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 (

See also
SDKString). But - its best to use Characters::ToString () on the caught exception, since this uses ExceptionStringHelper to properly handle characters the SDK character set might not allow representing some unicode characters.

TODO:

Note
Code-Status: Alpha
Note
Code-Status: Beta

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:

Note
Code-Status: Alpha
@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.
Code-Status: Alpha

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:

Note
Code-Status: Beta
Design Note (essentially) All exceptions thrown by Stroika (except where needed by quirks of underlying library integrated with) inherit from std::exception, or Stroika::Foundation::Execution::SilentException.

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 (

See also
SDKString). But - its best to check for inheriting from Exception<>, since the SDK character set might not allow representing some unicode characters.

Typedef Documentation

◆ LogicalCPUCoreSet

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.

◆ errno_t

The type of 'errno' variable.

Note
POSIX and C99 just say to assume its an int and doesn't define errno_t. But I find this type usage clearer.

Definition at line 55 of file Exceptions.h.

◆ QuickSynchronized

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.

◆ TimedSynchronized

template<typename T >
using Stroika::Foundation::Execution::TimedSynchronized = typedef Synchronized<T, Synchronized_Traits<recursive_timed_mutex> >
Note
- TimedSynchronized doesn't do shared_locks - just a single mutex lock (that is recursive, and allows timeouts)

Definition at line 786 of file Synchronized.h.

◆ RWSynchronized

template<typename T >
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).

Note
Use of RWSynchronized has HIGH PERFORMANCE OVERHEAD, and only makes sense when you have read locks held for a long time (and multiple threads doing so)

Definition at line 796 of file Synchronized.h.

Enumeration Type Documentation

◆ SequencePolicy

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.

Note
'eDEFAULT' is DEPRECATED, and there is deliberately no replacement enumerator. "I have no opinion, pick for me" is not a policy, and encoding it as one lies about what happens: eDEFAULT is an alias for eSeq, so a caller who meant 'you decide' silently pinned the implementation to sequential and left it no freedom at all.

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.

Note
HOW STROIKA DISPATCHES ON THIS - the rule for any code switching on a SequencePolicy: ONLY ePar selects a parallel std algorithm. Every other policy runs sequentially, and that fallthrough is DELIBERATE, not an unhandled case.

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.

Enumerator
eSeq 

default case - not parallelized

Equivalent to std::seq, std::execution::sequenced_policy

..."a parallel algorithm's execution may not be parallelized. The invocations of element access functions in parallel algorithms invoked with this policy (usually specified as std::execution::seq) are indeterminately sequenced in the calling thread."

ePar 

must synchronize shared data, can use mutex (or atomics), cuz each parallel execution in real thread

Equivalent to std::par, std::execution::parallel_policy

..."parallel algorithm's execution may be parallelized. The invocations of element access functions in parallel algorithms invoked with this policy (usually specified as std::execution::par) are permitted to execute in either the invoking thread or in a thread implicitly created by the library to support parallel algorithm execution. Any such invocations executing in the same thread are indeterminately sequenced with respect to each other.

eParUnseq 

Unclear how this differs from eUnseq, but no locks allowed.

Equivalent to std::par_unseq , std::execution::parallel_unsequenced_policy

..."a parallel algorithm's execution may be parallelized, vectorized, or migrated across threads (such as by a parent-stealing scheduler). The invocations of element access functions in parallel algorithms invoked with this policy are permitted to execute in an unordered fashion in unspecified threads, and unsequenced with respect to one another within each thread..

eUnseq 

SIMD, no locks allowed.

Equivalent to std::unseq, std::execution::unsequenced_policy

..."a parallel algorithm's execution may be parallelized, vectorized, or migrated across threads (such as by a parent-stealing scheduler). The invocations of element access functions in parallel algorithms invoked with this policy are permitted to execute in an unordered fashion in unspecified threads, and unsequenced with respect to one another within each thread..

Definition at line 46 of file Foundation/Execution/Common.h.

◆ InternallySynchronized

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 ()

Note
something marked eNotKnownInternallySynchronized - may in fact be internally synchronized.

Definition at line 58 of file Synchronized.h.

Function Documentation

◆ ThrowSystemErrNo()

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.

Precondition
sysErr != 0
Note
Since this calls
See also
ThrowError - it will throw a
SystemErrorException (or a derived type) or bad_alloc.
Note
stdc++ uses 'int' for the type of this error number, but Windows generally defines the type to be DWORD.
Which error_category it uses
o POSIX: std::system_category () o Windows: Platform::Windows::Win32_error_category () - the same value and name () ("system") as std::system_category (), but it maps more codes onto the portable errc conditions (
See also
Platform::Windows::Win32_error_category)

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 ()}.

Note
This follows the rule the standard library sets for its own errors ([value.error.codes], http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf): "That object's category() member shall return std::system_category() for errors originating from the operating system, or a reference to an implementation-defined error_category object for errors originating elsewhere." Windows is the deliberate exception - why: https://github.com/SophistSolutions/Stroika/issues/1192
Example Usage
#if qStroika_Platform_POSIX
ThrowSystemErrNo (errno);
#elif qStroika_Platform_Windows
ThrowSystemErrNo (::GetLastError ()); // works with this type of error # - GetLastError () is default if no arg provided
ThrowSystemErrNo (::WSAGetLastError ()); // or this
#endif

See:

See also
ThrowPOSIXErrNo ();
Note
zero arg versions only defined for POSIX and Windows platforms, and there the default is the obvious value for each platform - errno and GetLastError(). It is still an assertion (require) error to call these when errno / GetLastError () would return 0.

Definition at line 193 of file Exceptions.inl.

◆ CaptureCurrentActivities()

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.

Note
the intended purpose of this is to provide better 'context' for default exception messages, when exceptions are thrown (by Execution::Throw ()).
the TOP of the stack is the most specific activity, and the bottom of the stack is the initial, outermost task.

Definition at line 19 of file Activity.cpp.

◆ RunAll()

template<invocable<>... I>
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).

Note
no guarantee all run in parallel, but suggestion they are.

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.

Example Usage
auto results = RunAll ([] () { return 1; }, [] () { return 2; }, [] () { return 3; });
EXPECT_EQ (results, make_tuple (1, 2, 3));
auto RunAll(I... functions)
Run all the argument functions (logically/potentially) in parallel, and wait until they all complete.
Definition Async.inl:11
Example Usage
tuple<int> results = RunAll ([] () -> void {}, [] () { return 3; });
EXPECT_EQ (results, make_tuple (3));
Example Usage
int a = 0;
int b = 1;
int c = 2;
RunAll ([&] () { a = 3; }, [&] () { b = 4; }, [&] () { c = 5; });
EXPECT_EQ (a, 3);
EXPECT_EQ (b, 4);
EXPECT_EQ (c, 5);
Example Usage
static const auto kExcept_ = Execution::Exception {"Test exception"sv};
auto thrower = [] () { Execution::Throw (kExcept_); };
EXPECT_THROW (RunAll (thrower, [] () { return 3; }), Execution::Exception<>);
Exception<> is a replacement (subclass) for any std c++ exception class (e.g. the default 'std::excep...
Definition Exceptions.h:219
void Throw(T &&e2Throw)
like the builtin C++ 'throw', but stamps in the current Activity context and does helpful,...
Definition Throw.inl:54

Definition at line 11 of file Async.inl.

◆ GetCPUAffinity()

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.

Example Usage
// Pin to a core we are actually permitted to use. Never just name core 0: inside
// 'docker run --cpuset-cpus=2,3' it is not in our set, and asking for it fails outright.
if (optional<LogicalCPUCoreSet> allowed = GetCPUAffinity (); allowed and not allowed->empty ()) {
}
// ... or just say what you mean, which does exactly that:
Set<T> is a container of T, where once an item is added, additionally adds () do nothing.
nonvirtual optional< T > Min() const
optional< LogicalCPUCoreSet > GetCPUAffinity()
Which logical CPU cores this process may currently run on; nullopt if not knowable.
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,...

Definition at line 107 of file CPUAffinity.cpp.

◆ SetCPUAffinity()

void Stroika::Foundation::Execution::SetCPUAffinity ( const LogicalCPUCoreSet &  cores)

Restrict this process to the given logical CPU cores.

\req not cores.empty ()

Note
Deliberately NOT a precondition that each core exists, or that it is currently permitted. Both are environment rather than program invariant, and both can change between checking and calling - a cgroup cpuset can be rewritten, cores can be hotplugged - so asserting would be a race that reports a programmer error for something that is not one. The OS checks regardless, and reports it through the right channel: an exception here, or false from SetCPUAffinityQuietly ().
Naming a core outside the currently permitted set fails: the OS will probably not widen a restriction imposed from outside it (a cgroup cpuset, or a taskset wrapper).
See also
SetCPUAffinityQuietly - usually the one you want, since failing to pin is not an error.

Definition at line 135 of file CPUAffinity.cpp.

◆ SetCPUAffinityQuietly()

bool Stroika::Foundation::Execution::SetCPUAffinityQuietly ( const LogicalCPUCoreSet &  cores)
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.

◆ PinToOneLogicalCPUCoreQuietly()

optional< unsigned int > Stroika::Foundation::Execution::PinToOneLogicalCPUCoreQuietly ( )
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.

◆ ThrowPOSIXErrNo()

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

Precondition
errNo != 0
Note
Since this calls
See also
ThrowError - it will throw a
SystemErrorException (or a derived type) or bad_alloc.
Note
On a POSIX system, this amounts to a call to ThrowSystemErrNo. But even on a non-POSIX system, many APIs map their error numbers to POSIX error numbers so this can make sense to use. Also, on POSIX systems, its legal to call this with POSIX compatible extended (and therefore not POSIX) erorr nubmbers. In other words, you can call this with anything (except 0) you read out of errno on a POSIX system.
From http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4659.pdf - "If the argument ev corresponds to a POSIX errno value posv, the function shall return error_- condition(posv, generic_category()). Otherwise, the function"
this function takes errno as a default value because you almost always want to call it with the value from errno.

See:

See also
ThrowSystemErrNo ();

Definition at line 159 of file Exceptions.inl.

◆ ThrowPOSIXErrNoIfNegative()

template<typename INT_TYPE >
INT_TYPE Stroika::Foundation::Execution::ThrowPOSIXErrNoIfNegative ( INT_TYPE  returnCode)

Look at the argument value and if < 0,ThrowPOSIXErrNo (), and otherwise return it.

Note
Many POSIX - APIs - return a number which is zero if good, or -1 (or sometimes defined negative) if errno is set and there is an error. This function is useful for wrapping calls to those style functions. It checks if the argument result is negative (so -1 covers that) and throws and a POSIX (generic_error) SystemErrorException.

Definition at line 180 of file Exceptions.inl.

◆ ThrowError()

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.

This throws EITHER a SystemErrorException (a subclass of system_error), OR std::bad_alloc.

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.

Precondition
ec != error_code{}
What a caller must catch
This throws EITHER something derived from std::system_error, OR std::bad_alloc. There is no third possibility. So a caller which wants to handle everything needs:
try {
...
}
catch (const system_error& e) {
// everything except memory exhaustion; always test the CONDITION, not the value
if (Execution::IsA (e, errc::timed_out)) { ... }
DbgTrace ("{}"_f, Characters::ToString (e)); // NB: ToString (e), NOT e.what () - see below
}
catch (const bad_alloc&) {
// memory exhaustion ONLY - see below for why this is separate
}
#define DbgTrace
Definition Trace.h:327
String ToString(T &&t, ARGS... args)
Return a debug-friendly, display version of the argument: not guaranteed parsable or usable except fo...
Definition ToString.inl:424
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?
Note
Catch std::system_error, NOT SystemErrorException - even though today ThrowError () only ever throws the latter. Catching the base additionally covers errors from non-Stroika code (which throws plain system_error), and Stroika errors built over other system_error subclasses - IO::FileSystem::Exception is an Exception<filesystem_error>, and so is NOT a SystemErrorException.
See also
the note on SystemErrorException itself, which gives the same advice.

(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.)

Note
Catching the base costs you nothing - as long as you read the message with Characters::ToString (e) rather than e.what (). ToString () does a dynamic_cast to Execution::ExceptionStringHelper (
See also
Characters::ToStringDefaults::ToString), and a dynamic_cast cross-casts, so it finds the Stroika half through ANY base reference - system_error, or even std::exception. What it returns is As<String> (), which is GetFullErrorMessage (), so you get the full UNICODE text with the Activity stack already merged in - exactly what you would have got by catching the derived type.

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 (

See also
Exception<>).

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.

The one type promotion
The rule is deliberately narrow: promote only to a type the C++ standard itself already defines for that condition. That yields exactly one entry:
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).

What is preserved, and what is not
ThrowError () preserves what the error MEANS. It does not promise to preserve how that meaning is REPRESENTED. Where the two conflict it normalizes - sometimes lossily - so the error can be tested portably. In order of how much is given up:

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

See also
Platform::Windows::Win32_error_category, which ThrowSystemErrNo () tags them with - so nothing needs rewriting (as WAIT_TIMEOUT and ERROR_INTERNET_TIMEOUT once were). o 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.
Note
Why the bad_alloc case alone is allowed to be this lossy. Everywhere else, Stroika works hard to carry a UNICODE message and the Activity context (
See also
Exception<>). Here it deliberately does not, and the reason is that the enrichment would need the very resource that just ran out: building a message and copying an activity stack means several allocations, performed while reporting that an allocation failed. Under genuine memory pressure that can throw from inside the throw, turning a clean bad_alloc into something far harder to diagnose. std::bad_alloc is specified to be cheap to create for exactly this reason, and wrapping it would throw that away.

Little is lost in practice.

See also
Throw () already traces the message and the activities at the throw point, which is where anyone diagnosing this would look; and an out-of-memory failure is rarely recoverable, so rich structured context buys less here than anywhere else.

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.

See also
ThrowTranslatedExceptionIfNeeded_ in Exceptions.cpp for the mechanism.

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

See also
Throw () instead. That is the whole distinction between the two: ThrowError () normalizes, possibly lossily, in exchange for portability; Throw () is literal.

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.

How Stroika treats errc::timed_out, and what its default message says
Stroika treats errc::timed_out as the generic "the time limit for this operation expired" condition - nothing connection-specific. Every Stroika timeout answers to it: a Synchronized<> lock acquisition, Thread::WaitForDone (), a WaitableEvent, a socket read, a libcurl transfer. That uniformity is what makes 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.

Note
Changed in Stroika v3.0d25 - the DEFAULT message now reflects that. glibc renders ETIMEDOUT as "Connection timed out" (a BSD-sockets legacy - ETIMEDOUT began life as a connect () error), while MSVC renders it "timed out": so before v3.0d25 the very same lock timeout was described differently on each platform, and misleadingly on one. Stroika now substitutes **"Operation timed out"** when it builds a default message for errc::timed_out in 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 () (
See also
ThrowPOSIXErrNo) and keeps the platform's own wording, which is accurate there. A message you pass explicitly is never touched.

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.

See also
SystemErrorException, whose constructor is where a default message actually gets built.
Relationship to ThrowPOSIXErrNo () and ThrowSystemErrNo ()
All three funnel through here and behave identically once they do. They differ ONLY in which error_category the raw number gets tagged with - that is, in how the error_code is built:
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.)

Note
Matching is driven by the CONDITION, not the raw value, so any category which maps its own numbering onto std::errc (as the LibCurl and DNS categories both do) participates automatically. That is precisely what error_category::default_error_condition is for - a category author gets this simply by writing a correct mapping.
Example Usage
// wrapping a third party library which has its own error_category
if (status != kThirdPartyOK) [[unlikely]] {
ThrowError (error_code{status, ThirdParty_error_category ()});
}
void ThrowError(error_code ec)
throw a SystemErrorException with the given error_code - or std::bad_alloc for errc::not_enough_memor...
Raising a portable condition directly - the std::errc overload
When the error did not come from anywhere with its own numbering - Stroika itself has decided the operation timed out, say - there is no category to name, and ThrowError (errc::timed_out) says so directly:
ThrowError (errc::timed_out, "Timeout Expired"sv); // instead of
ThrowError (make_error_code (errc::timed_out), "Timeout Expired"sv);

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.

Note
Use the error_code overload whenever you HAVE a category. std::errc names a condition, and make_error_code () resolves it to 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:

See also
ThrowPOSIXErrNo ();
ThrowSystemErrNo ();

Definition at line 212 of file Exceptions.inl.

◆ Handle_ErrNoResultInterruption()

template<typename CALL >
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.

Note
The only HITCH with respect to automatically handling interruptability is that that its handled by 'restarting' the argument 'call' That means if it was partially completed, the provider of 'call' must accommodate that fact (use mutable lambda).

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.

Note
Cancelation Point

Definition at line 257 of file Exceptions.inl.

◆ ThrowPOSIXErrNoIfNull()

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.

Note
rarely useful, but some POSIX APIs such as getcwd() do return null on error.

Definition at line 272 of file Exceptions.inl.

◆ GetAssociatedErrorCode()

optional< error_code > Stroika::Foundation::Execution::GetAssociatedErrorCode ( const exception_ptr &  e)
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).

Example Usage
try {
/// do something that throws
}
catch (...) {
if (auto err = GetAssociatedErrorCode (current_exception ())) {
if (*err == errc::no_such_device) {
// This can happen on Linux when you start before you have a network connection - no problem - just keep trying
DbgTrace ("Got exception (errno: ENODEV) - while joining multicast group, so try again");
goto Again;
}
else {
}
}
}
void Sleep(Time::Duration seconds2Wait)
Definition Sleep.inl:101
optional< error_code > GetAssociatedErrorCode(const exception_ptr &e) noexcept

Definition at line 269 of file Exceptions.cpp.

◆ IsA()

bool Stroika::Foundation::Execution::IsA ( const error_code &  ec,
error_condition  cond 
)
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.

Note
basically same as e.code () == cond, but safer (less error prone and not MATCHES on differnt error_category, etc)
Example Usage
try {
t.WaitForDone (1s);
}
catch (const system_error& e) {
if (Execution::IsA (e, errc::timed_out)) {
...
}
}
Note
Why this exists, and why it takes an error_condition
    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.
On Windows, IsA () also answers for a Win32 code that arrived in std::system_category () (from the standard library, say) as if it were in Platform::Windows::Win32_error_category () - so it gets the conditions Microsoft's mapping leaves out. A raw e.code () == errc::X does not get that for such a code; IsA () is the one portable test.
The exception_ptr and exception overloads answer false for anything that carries no error code, which includes a null exception_ptr. They never throw.
See also
GetAssociatedErrorCode
Tests/37, Test5_error_code_condition_compares_ - which demonstrates the trap directly.

Definition at line 287 of file Exceptions.cpp.

◆ TranslateExceptionToOptional()

template<typename F >
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.

◆ Finally()

template<Common::INoThrowInvocable FUNCTION>
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

Note
SEE https://en.cppreference.com/w/cpp/experimental/scope_exit https://en.cppreference.com/w/cpp/experimental/scope_fail https://en.cppreference.com/w/cpp/experimental/scope_success (these may replace Finally - success/fail variants maybe better, and scope_exit I think basically the same, except not sure always available)
For cleanup which is important/critical to be completed, its sometimes wise to include Thread::SuppressInterruptionInContext suppressThreadInterrupts; in the function body
This is (now) done in such a way that no locks are used or memory allocated, except if argument lambda uses them/does it.
This automatically suppresses any expressions triggered in the argument function, but its usually best to pass in a noexcept lambda (future versions may be more efficient in that case).
Important parts of why this works: use auto so 'type' (size of struct for params) is dynamically calculated, and on stack with no memory allocation/indirection; and NAMED rvalue-reference so that its lifetime extends to the end of the scope in which its contained.
Example Usage
[[maybe_unused]]auto&& cleanup = Finally ([] () noexcept { Require (not sKnownBadBeforeMainOrAfterMain_); });
auto Finally(FUNCTION &&f) -> Private_::FinallySentry< FUNCTION >
Definition Finally.inl:31
Example Usage (more terse version of try/catch to report exception)
// Instead of
try {X (); } catch (...) { Logger::sThe.Log (Logger::eError, "mongoDB exception: {}"_f, e); }
// You can use
[[maybe_unused]]auto&& reportOnException = Finally ([] () noexcept { if (auto e = current_exception ()) { IgnoreExceptionsForCall (Logger::sThe.Log (Logger::eError, "exception: {}"_f, e)); } });
X ();
// OK - I know the first example looks shorter, but imagine you have to indent X, and its bigger (many lines)
void Log(Priority logLevel, const wchar_t *format,...)
Definition Logger.inl:27

Definition at line 31 of file Finally.inl.

◆ DefaultLoggingFatalErrorHandler()

void Stroika::Foundation::Execution::DefaultLoggingFatalErrorHandler ( const Characters::SDKChar *  msg)
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.

◆ DefaultLoggingCrashSignalHandler()

void Stroika::Foundation::Execution::DefaultLoggingCrashSignalHandler ( Execution::SignalID  signal)
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.

◆ GetEXEDir()

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.

◆ GetEXEPath() [1/2]

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.

◆ GetEXEPath() [2/2]

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.

◆ IsAddressReadOnlyAppLifetime()

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.

Returns
true if so; false if not - s may yet be read-only (another module's, or a read-only mapping) but nothing promises it lasts; nullopt if Stroika cannot tell on this platform (it can on Linux, MacOS and Windows). An empty s is false.

Cheap: a few comparisons, once the image has been examined (at first use).

Definition at line 23 of file Module.inl.

◆ FindExecutableInPath()

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.

Example Usage
if (optional<filesystem::path> pp = FindExecutableInPath ("bash"sv)) {
fArgs_ += String{*pp};
}
else {
Throw (IO::FileSystem::Exception{make_error_code (errc::no_such_file_or_directory), filesystem::path{"bash"sv}});
}
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
optional< filesystem::path > FindExecutableInPath(const filesystem::path &fn)
If fn refers to an executable - return it (using kPATH, and kPathEXT as appropriate)
Definition Module.cpp:336

Definition at line 336 of file Module.cpp.

◆ IsProcessRunning()

bool Stroika::Foundation::Execution::IsProcessRunning ( pid_t  pid)

Return true, if the pid can be verified to be running, false if verified not running.

Note
This obviously can be a race as processes can come and go quickly

Definition at line 23 of file Foundation/Execution/Process.cpp.

◆ MakeInputStreamWithProgress()

template<typename T >
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.

◆ SendSignal()

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

◆ Sleep()

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)

See also
SleepUntil

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.

Example Usage
Note
Sleep (0) will still yield the processor (so like std::thread::yield ())
Precondition
seconds2Wait >= 0
Postcondition
*remainingInSleep <= seconds2Wait
*remainingInSleep >= 0
Note
Thread-Safety Internally-Synchronized-Thread-Safety
Cancelation Point
Very similar to std::this_thread::sleep_for () - except for the overload returning remaining amount, and cancelation support

Definition at line 101 of file Sleep.inl.

◆ SleepUntil()

void Stroika::Foundation::Execution::SleepUntil ( Time::TimePointSeconds  untilTickCount)

Wait until the tickCount is >= the given value.

Example Usage
Time::DurationSeconds startedAt = Time::GetTickCount ();
do_something_dont_know_how_long_it_will_take();
Execution::SleepUntil (1.0s + startedAt); // make sure do_something_dont_know_how_long_it_will_take () took at least one second
chrono::duration< double > DurationSeconds
chrono::duration<double> - a time span (length of time) measured in seconds, but high precision.
Definition Realtime.h:57
void SleepUntil(Time::TimePointSeconds untilTickCount)
Definition Sleep.cpp:19
See also
Sleep ();
Note
Cancelation Point
Unlike Sleep () - this may or may not yield.
SleepUntil restarts if interrupted, so if it returns, it will return after untilTickCount
This may or may not end up calling Sleep(). It is not an error to call with a tickCount which has already passed: it just returns quickly, and may not yield.
Very similar to std::this_thread::sleep_until () - except for the cancelation support

Definition at line 19 of file Sleep.cpp.

◆ Throw()

template<typename T >
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.

Note
Throw () and ThrowError () are a deliberate pair, and choosing between them is a real decision - not a matter of which argument type you happen to have:
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
See also
Execution::ThrowError - so a code whose condition is errc::not_enough_memory arrives as a SystemErrorException rather than std::bad_alloc, and a caller who wrote catch (const bad_alloc&) does not see it.

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.

Note
Changed in Stroika v3.0d25 Throw () stamps the CURRENT Activity stack into the exception - but only when the exception did not specify one (GetActivities () == nullopt). It imbues a COPY, so the object you passed is not modified. That is what lets a 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.
See also
ExceptionStringHelper::ImbueActivities.
Note
What you hand Throw () also decides what the CATCHER gets. Stroika's Exception<> types - and so SystemErrorException and everything derived from it - preserve UNICODE message text, and can carry the Activity context described above (
See also
Exception<>). Throw (system_error{ec}) carries neither, and neither can be recovered later by whoever catches it. Since SystemErrorException IS a std::system_error, throwing the Stroika type costs nothing in interoperability - so prefer Throw (SystemErrorException{ec}), or better still ThrowError (ec).
Note
The overloads which take an extra trace message are JUST for convenience, and vector through the 1-arg overload.

Definition at line 54 of file Throw.inl.

◆ ReThrow()

void Stroika::Foundation::Execution::ReThrow ( )

Just a regular C++ rethrow, but with a DbgTrace message...

Definition at line 107 of file Throw.inl.

◆ ThrowIfNull()

template<equality_comparable_with< nullptr_t > T, typename E >
void Stroika::Foundation::Execution::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).

If the first argument is null, throw the second argument exception (which defaults to bad_alloc)

Definition at line 156 of file Throw.inl.

◆ ThrowIfFailed() [1/2]

template<typename EXPECTED >
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).

Definition at line 190 of file Throw.inl.

◆ ThrowIfFailed() [2/2]

template<typename EXPECTED >
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).

Definition at line 190 of file Throw.inl.

◆ TryLockUntil()

template<Common::StdCompat::TimedMutex TIMED_MUTEX, typename EXCEPTION >
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 (

See also
ThrowError (errc::timed_out)).

Definition at line 14 of file TimedLock.inl.

◆ UniqueLock()

template<Common::StdCompat::TimedMutex TIMED_MUTEX, typename EXCEPTION >
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 (

See also
ThrowError (errc::timed_out)).
Note
if this function returns (doesn't throw) - the required unique_lock<> OWNS the mutex.
ALIAS - TimedLockGuard. If you are hunting for a "timed lock guard" - a scope-bound guard over a timed_mutex that throws rather than quietly failing to lock - UniqueLock is that thing under a less obvious name. Execution::TimedLockGuard existed until Stroika v3.0d25, and was removed because unique_lock<> dominates any lock_guard analogue here: it is movable, returnable, can be released early, and is the only form condition_variable accepts.
See also
also TryLockUntil

Definition at line 34 of file TimedLock.inl.

◆ ThrowTimeoutExceptionAfter()

template<typename EXCEPTION >
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

See also
ThrowError (errc::timed_out).
Note
Test the CONDITION (errc::timed_out), NEVER a particular code such as error_code{ETIMEDOUT, system_category ()}. Only the condition matches timeouts from every source - libcurl, getaddrinfo, HRESULT, the OS, and Stroika's own waiting primitives.
See also
ThrowError for why, and for what errc::timed_out means to Stroika.

Throw a timeout (

See also
ThrowError (errc::timed_out)) if @Time::GetTickCount () is >= the given value.

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);

Note
Cancelation Point

Definition at line 21 of file Timeout.inl.

◆ ThrowIfTimeout()

template<typename EXCEPTION >
void Stroika::Foundation::Execution::ThrowIfTimeout ( cv_status  conditionVariableStatus,
EXCEPTION &&  exception2Throw 
)

Throw a timeout (.

See also
ThrowError (errc::timed_out)) if the given condition_variable wait timed out.
Note
- this function may not be called outside the context of a running main.

Definition at line 44 of file Timeout.inl.

◆ ThrowTimeOutException()

void Stroika::Foundation::Execution::ThrowTimeOutException ( )
Note
This entire header is DEPRECATED (Stroika v3.0d25) - the TimeOutException type and ThrowTimeOutException (), both removed at the next release-stage transition.

Everything that is NOT deprecated has moved out:

See also
Execution/Timeout.h for how Stroika reports a timeout and for ThrowTimeoutExceptionAfter () / ThrowIfTimeout (), and
Execution/TimedLock.h for TryLockUntil () / UniqueLock (). This file includes both, so code that included this one keeps compiling unchanged.

Definition at line 34 of file TimeOutException.h.

Variable Documentation

◆ kCPUAffinitySupported

constexpr bool Stroika::Foundation::Execution::kCPUAffinitySupported
constexpr
Initial value:
=
false

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.

◆ kPath

const LazyInitialized< Sequence< filesystem::path > > Stroika::Foundation::Execution::kPath
extern
Initial value:
{[] () -> Sequence<filesystem::path> {
if (const char* env_p = std::getenv ("PATH")) {
String pathVar = String::FromNarrowSDKString (env_p);
}
return {};
}}

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.

◆ kRawEnvironment

const LazyInitialized< Mapping< SDKString, SDKString > > Stroika::Foundation::Execution::kRawEnvironment
extern

convert getenv() to a Mapping of SDKString (in case some issue with charactor set conversion)

Note
LazyInitialized so if not used, nearly zero cost

Definition at line 271 of file Module.cpp.

◆ kEnvironment

const LazyInitialized< Mapping< String, String > > Stroika::Foundation::Execution::kEnvironment
extern
Initial value:
{[] () -> Mapping<String, String> {
Mapping<String, String> r;
for (auto i : kRawEnvironment ()) {
r.Add (String::FromSDKString (i.fKey), String::FromSDKString (i.fValue));
}
return r;
}}
const LazyInitialized< Containers::Mapping< Characters::SDKString, Characters::SDKString > > kRawEnvironment
convert getenv() to a Mapping of SDKString (in case some issue with charactor set conversion)
Definition Module.cpp:271

convert getenv() to a Mapping of Strings for easier access

Note
LazyInitialized so if not used, nearly zero cost

Definition at line 323 of file Module.cpp.

◆ kSpinLock_IsFasterThan_mutex

constexpr bool Stroika::Foundation::Execution::kSpinLock_IsFasterThan_mutex
constexpr
Initial value:
=
false

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.

Note
Stroika 2.0a155 and earlier - Execution::kSpinLock_IsFasterThan_mutex was always true
Stroika 2.0a156 through 3.0d24 - due to threadFence and https://github.com/SophistSolutions/Stroika/issues/628 (STK-494) - SpinLock slowed slightly, but its still notably faster than mutex on gcc/unix/windows (x86 only tested).
Stroika 3.0d25 and later - the standalone fences, and the BarrierType knob that selected among them, were both removed (
See also
SpinLock::try_lock ()). Careful about attributing the resulting speedup: on x86-64 those fences emitted NO instruction at all (an acquire or release fence is only a compiler barrier there), so what actually shrank the generated code was dropping the runtime barrier dispatch - try_lock () went 11 instructions to 5, unlock () 8 to 3. The fences still mattered, just not for speed on x86: ThreadSanitizer cannot model them, and on weakly ordered hardware they are real instructions.

Definition at line 74 of file SpinLock.h.