4#include <cx/thread/atomic.h>
13#define ASPIN_MAX_USEC 10
14#define ASPIN_INITIAL_TARGET 10
15#define ASPIN_NOSPIN (-2147483647 - 1)
18#define ASPIN_PERF_STATS
21typedef struct AdaptiveSpin
23 atomic(int32) spintarget;
24#ifdef ASPIN_PERF_STATS
25 atomic(intptr) stats_uncontended;
26 atomic(intptr) stats_spin;
27 atomic(intptr) stats_futex;
28 atomic(intptr) stats_capped;
29 atomic(intptr) stats_timeout;
30 atomic(intptr) stats_yield;
34typedef struct AdaptiveSpinState
46_meta_inline
void aspinRecordUncontended(_Inout_ AdaptiveSpin *aspin)
48#ifdef ASPIN_PERF_STATS
49 atomicFetchAdd(intptr, &aspin->stats_uncontended, 1, Relaxed);
55_meta_inline
void aspinRecordSpin(_Inout_ AdaptiveSpin *aspin)
57#ifdef ASPIN_PERF_STATS
58 atomicFetchAdd(intptr, &aspin->stats_spin, 1, Relaxed);
64_meta_inline
void aspinRecordFutex(_Inout_ AdaptiveSpin *aspin)
66#ifdef ASPIN_PERF_STATS
67 atomicFetchAdd(intptr, &aspin->stats_futex, 1, Relaxed);
73_meta_inline
void aspinRecordCapped(_Inout_ AdaptiveSpin *aspin)
75#ifdef ASPIN_PERF_STATS
76 atomicFetchAdd(intptr, &aspin->stats_capped, 1, Relaxed);
82_meta_inline
void aspinRecordTimeout(_Inout_ AdaptiveSpin *aspin)
84#ifdef ASPIN_PERF_STATS
85 atomicFetchAdd(intptr, &aspin->stats_timeout, 1, Relaxed);
91_meta_inline
void aspinRecordYield(_Inout_ AdaptiveSpin *aspin)
93#ifdef ASPIN_PERF_STATS
94 atomicFetchAdd(intptr, &aspin->stats_yield, 1, Relaxed);
100_meta_inline
void aspinInit(_Out_ AdaptiveSpin *aspin,
bool nospin)
102 memset(aspin, 0,
sizeof(AdaptiveSpin));
108 atomicStore(int32, &aspin->spintarget, nospin ? ASPIN_NOSPIN : ASPIN_INITIAL_TARGET, Relaxed);
111_meta_inline
void aspinBegin(_Inout_ AdaptiveSpin *aspin, _Out_ AdaptiveSpinState *ass, int64 timeout)
114 ass->start = ass->now;
115 ass->spincap = ass->start + ASPIN_MAX_USEC;
117 ass->curtarget = atomicLoad(int32, &aspin->spintarget, Relaxed);
118 if (ass->curtarget < 1 && ass->curtarget != ASPIN_NOSPIN)
119 ass->curtarget = ASPIN_INITIAL_TARGET;
120 ass->spincount = (ass->curtarget == ASPIN_NOSPIN) ? 0 : ass->curtarget * 2;
122 ass->rstate = (ass->now & 0xffffffff);
125_meta_inline
bool aspinSpin(_Inout_ AdaptiveSpin *aspin, _Inout_ AdaptiveSpinState *ass)
129 if ((ass->spincount & 7) == 7)
133 if (ass->spincount > 0 && ass->now > ass->spincap) {
135 atomicStore(int32, &aspin->spintarget, (ass->curtarget * 2 - ass->spincount) / 2, Relaxed);
137 aspinRecordCapped(aspin);
140 if (ass->spincount > 0) {
149_meta_inline
bool aspinTimeout(_Inout_ AdaptiveSpin *aspin, _Inout_ AdaptiveSpinState *ass)
157 if (ass->spincount == 0)
160 if (ass->now > ass->endtime) {
161 aspinRecordTimeout(aspin);
167_meta_inline
void aspinAdapt(_Inout_ AdaptiveSpin *aspin, _Inout_ AdaptiveSpinState *ass)
170 if (ass->now > ass->endtime)
173 if (ass->curtarget != ASPIN_NOSPIN) {
174 if (ass->spincount > 0) {
176 int32 realtarget = atomicLoad(int32, &aspin->spintarget, Relaxed);
177 atomicFetchAdd(int32, &aspin->spintarget, clamp(ass->curtarget - ass->spincount, -realtarget, realtarget) / 8 + 1, Relaxed);
178 aspinRecordSpin(aspin);
180 if (ass->now <= ass->spincap) {
182 atomicFetchAdd(int32, &aspin->spintarget, ass->curtarget / 8 + 1, Relaxed);
184 aspinRecordFutex(aspin);
187 aspinRecordFutex(aspin);
191_meta_inline int64 aspinTimeoutRemaining(_In_ AdaptiveSpinState *ass)
197_meta_inline
void aspinHandleContention(_Inout_opt_ AdaptiveSpin *aspin, _Inout_ AdaptiveSpinState *ass)
212 if (
lcgRandom(&ass->rstate) % (++ass->contention + 7) > 8) {
214 aspinRecordYield(aspin);
217 for(
int i = ass->contention * ass->contention; i >= 0; --i) {
223_meta_inline
void aspinEndContention(_Inout_ AdaptiveSpinState *ass)
Comparison and clamping macros.
CPU-specific operations and atomic primitives.
int32 lcgRandom(uint32 *state)
#define timeForever
Maximum representable time value (approximately year 294,276 CE)
Simple linear congruential pseudo-random number generator.
Operating system services.
Time manipulation and conversion functions.