78 lines
5.3 KiB
C++
78 lines
5.3 KiB
C++
/***
|
|
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
|
|
***/
|
|
#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);
|
|
|
|
// 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);
|
|
|
|
// 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.*/);
|
|
|
|
// 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.*/);
|
|
} |