/*** Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: WakeOnAddress.hpp Date: 2023-3-11 Author: Reece Note: In emulation mode (*): 1: Wakes occur in FIFO order 2: uWordSize can be any length not exceeding 32 bytes otherwise 1: Wakes are orderless 2: uWordSize must be less than or equal to 8 bytes 3: only the least significant 32bits are guaranteed to be used as wake signals in either mode: 1: WaitOnAddress[...] can wake at any-time if a fast path permits. (we only care about strict guarantees during the deep slow-path yield operation. after the first pass, after a cache miss, after a fast path succeeds, it's anybodys guess who will *return* first. on the other hand, a set of 5 threads already in the kernel *should* wake in the expected order. otherwise, WaitOnAddress[...] just assumes pTargetAddress != pCompareAddress is an orderless return condition.) * By default: UNIXes and targets below/inc Windows 7 will be in userland emulation mode for performance reasons. * Linux and other targets can directly interface with their futex interface under a smaller wrapper; * however, these applications are limited to internal synchronization primitives. The added bloat * of the WaitOnAddress/FUTEX/atomic wait emulation layer improves performance in real world dumb * code with spurious wakes, odd word sizes, and pointer alignments. Not to mention some targets * are stuck with semaphores or condition variables to start off with, and therefore need this * for the sake of porting modern applications. The aforementioned synchronization primitives * are written with OS specific optimizations in mind, and therefore consider emulation bloat. * bPreferEmulatedWakeOnAddress disables the emulation layer, if theres a reasonable native * interface available. * Defer to ThreadingConfig::bPreferEmulatedWakeOnAddress = !AuBuild::kIsNtDerived Note: UntilEqual (new experimental) variants yield until a specified pCompareAddress value. The base variants treat pCompareAddress as the previous CAS return value. ***/ #pragma once namespace Aurora::Threading { AUKN_SYM void WakeAllOnAddress(const void *pTargetAddress); AUKN_SYM void WakeOnAddress(const void *pTargetAddress); // WakeAllOnAddress with a uNMaximumThreads which may or may not be respected AUKN_SYM void WakeNOnAddress(const void *pTargetAddress, AuUInt8 uNMaximumThreads); // On systems with processors of shared execution pipelines, these try-series of operations will spin (eg: mm_pause) for a configurable // amount of time, so long as the the process-wide state isn't overly contested. This means you can use these arbitrarily without // worrying about an accidental thundering mm_pause herd. If you wish to call WaitOnAddress[...] afterwards, you should report you already // spun via optAlreadySpun. If the application is configured to spin later on, this hint may be used to prevent a double spin. AUKN_SYM bool TryWaitOnAddress(const void *pTargetAddress, const void *pCompareAddress, AuUInt8 uWordSize); AUKN_SYM bool TryWaitOnAddressUntilEqual(const void *pTargetAddress, const void *pCompareAddress, AuUInt8 uWordSize); // On systems with processors of shared execution pipelines, these try-series of operations will spin (eg: mm_pause) for a configurable // amount of time, so long as the the process-wide state isn't overly contested. This means you can use these arbitrarily without // worrying about an accidental thundering mm_pause herd. If you wish to call WaitOnAddress[...] afterwards, you should report you already // spun via optAlreadySpun. If the application is configured to spin later on, this hint may be used to prevent a double spin. // In the case of a pTargetAddress != pCompareAddress condition, the optional check parameter is used to verify the wake condition. // Otherwise, spinning will continue. AUKN_SYM bool TryWaitOnAddressEx(const void *pTargetAddress, const void *pCompareAddress, AuUInt8 uWordSize, const AuFunction &check); AUKN_SYM bool TryWaitOnAddressUntilEqualEx(const void *pTargetAddress, const void *pCompareAddress, AuUInt8 uWordSize, const AuFunction &check); // Relative timeout variant of nanosecond resolution WoA. 0 = indefinite AUKN_SYM bool WaitOnAddress(const void *pTargetAddress, const void *pCompareAddress, AuUInt8 uWordSize, AuUInt64 qwNanoseconds, AuOptional optAlreadySpun = {} /*hint: do not spin before switching. subject to global config.*/); // Relative timeout variant of nanosecond resolution WoA. 0 = indefinite AUKN_SYM bool WaitOnAddressUntilEqual(const void *pTargetAddress, const void *pCompareAddress, AuUInt8 uWordSize, AuUInt64 qwNanoseconds, AuOptional optAlreadySpun = {} /*hint: do not spin before switching. subject to global config.*/); // Absolute timeout variant of nanosecond resolution WoA. Nanoseconds are in steady clock time. 0 = indefinite AUKN_SYM bool WaitOnAddressSteady(const void *pTargetAddress, const void *pCompareAddress, AuUInt8 uWordSize, AuUInt64 qwNanoseconds, AuOptional optAlreadySpun = {} /*hint: do not spin before switching. subject to global config.*/); // Absolute timeout variant of nanosecond resolution WoA. Nanoseconds are in steady clock time. 0 = indefinite AUKN_SYM bool WaitOnAddressUntilEqualSteady(const void *pTargetAddress, const void *pCompareAddress, AuUInt8 uWordSize, AuUInt64 qwNanoseconds, AuOptional optAlreadySpun = {} /*hint: do not spin before switching. subject to global config.*/); }