AuroraRuntime/Include/Aurora/Threading/WakeOnAddress.hpp

105 lines
7.1 KiB
C++
Raw Normal View History

/***
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
2023-12-05 10:08:49 +00:00
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
2023-12-23 03:10:17 +00:00
* 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);
2023-12-05 10:08:49 +00:00
// WakeAllOnAddress with a uNMaximumThreads which may or may not be respected
AUKN_SYM void WakeNOnAddress(const void *pTargetAddress,
AuUInt8 uNMaximumThreads);
2023-12-05 10:08:49 +00:00
// 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);
2023-12-05 10:08:49 +00:00
// 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<bool(const void *, const void *, AuUInt8)> &check);
AUKN_SYM bool TryWaitOnAddressUntilEqualEx(const void *pTargetAddress,
const void *pCompareAddress,
AuUInt8 uWordSize,
const AuFunction<bool(const void *, const void *, AuUInt8)> &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<bool> 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<bool> 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<bool> 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<bool> optAlreadySpun = {} /*hint: do not spin before switching. subject to global config.*/);
}