186 lines
5.2 KiB
C
186 lines
5.2 KiB
C
SharedFutexState shared_futex_state = ZI;
|
|
|
|
////////////////////////////////
|
|
//~ Startup
|
|
|
|
void InitFutexSystem(void)
|
|
{
|
|
SharedFutexState *g = &shared_futex_state;
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ State helpers
|
|
|
|
FiberNeqFutexState *FiberNeqFutexStateFromId(i16 fiber_id)
|
|
{
|
|
return &shared_futex_state.fiber_neq_states[fiber_id];
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ Not-equal futex operations
|
|
|
|
void FutexYieldNeq(volatile void *addr, void *cmp, u8 cmp_size)
|
|
{
|
|
SharedFutexState *g = &shared_futex_state;
|
|
FutexNeqListBin *bin = &g->neq_bins[RandU64FromSeed((u64)addr) % countof(g->neq_bins)];
|
|
|
|
b32 cancel = 0;
|
|
|
|
LockTicketMutex(&bin->tm);
|
|
{
|
|
/* Now that bin is locked, check if we should cancel insertion based on current value at address */
|
|
{
|
|
union
|
|
{
|
|
volatile void *v;
|
|
Atomic8 *v8;
|
|
Atomic16 *v16;
|
|
Atomic32 *v32;
|
|
Atomic64 *v64;
|
|
} addr_atomic;
|
|
union
|
|
{
|
|
void *v;
|
|
i8 *v8;
|
|
i16 *v16;
|
|
i32 *v32;
|
|
i64 *v64;
|
|
} cmp_int;
|
|
addr_atomic.v = addr;
|
|
cmp_int.v = cmp;
|
|
switch(cmp_size)
|
|
{
|
|
default: Assert(0); cancel = 1; break; /* Invalid futex size */
|
|
case 1: cancel = Atomic8Fetch(addr_atomic.v8) != *cmp_int.v8; break;
|
|
case 2: cancel = Atomic16Fetch(addr_atomic.v16) != *cmp_int.v16; break;
|
|
case 4: cancel = Atomic32Fetch(addr_atomic.v32) != *cmp_int.v32; break;
|
|
case 8: cancel = Atomic64Fetch(addr_atomic.v64) != *cmp_int.v64; break;
|
|
}
|
|
}
|
|
if (!cancel)
|
|
{
|
|
/* Grab futex list from address */
|
|
FutexNeqList *list = 0;
|
|
{
|
|
list = bin->first;
|
|
for (; list; list = list->next)
|
|
{
|
|
if (list->addr == addr) break;
|
|
}
|
|
if (!list)
|
|
{
|
|
if (bin->first_free)
|
|
{
|
|
list = bin->first_free;
|
|
bin->first_free = list->next;
|
|
ZeroStruct(list);
|
|
}
|
|
else
|
|
{
|
|
Arena *perm = PermArena();
|
|
PushAlign(perm, CachelineSize);
|
|
list = PushStruct(perm, FutexNeqList);
|
|
PushAlign(perm, CachelineSize);
|
|
list->addr = addr;
|
|
}
|
|
list->next = bin->first;
|
|
bin->first = list;
|
|
}
|
|
}
|
|
|
|
/* Insert */
|
|
{
|
|
i16 fiber_id = FiberId();
|
|
FiberNeqFutexState *f = FiberNeqFutexStateFromId(fiber_id);
|
|
f->next = list->first;
|
|
list->first = fiber_id;
|
|
}
|
|
}
|
|
}
|
|
UnlockTicketMutex(&bin->tm);
|
|
|
|
/* Suspend */
|
|
if (!cancel)
|
|
{
|
|
SuspendFiber();
|
|
}
|
|
}
|
|
|
|
void FutexWakeNeq(void *addr)
|
|
{
|
|
SharedFutexState *g = &shared_futex_state;
|
|
FutexNeqListBin *bin = &g->neq_bins[RandU64FromSeed((u64)addr) % countof(g->neq_bins)];
|
|
|
|
/* Pull waiting ids */
|
|
i16 first_id = 0;
|
|
{
|
|
LockTicketMutex(&bin->tm);
|
|
{
|
|
FutexNeqList *list = bin->first;
|
|
for (; list; list = list->next)
|
|
{
|
|
if (list->addr == addr) break;
|
|
}
|
|
if (list)
|
|
{
|
|
first_id = list->first;
|
|
/* Free futex list */
|
|
{
|
|
FutexNeqList *prev = list->prev;
|
|
FutexNeqList *next = list->next;
|
|
if (prev)
|
|
{
|
|
prev->next = next;
|
|
}
|
|
else
|
|
{
|
|
bin->first = next;
|
|
}
|
|
if (next)
|
|
{
|
|
next->prev = prev;
|
|
}
|
|
list->next = bin->first_free;
|
|
bin->first_free = list;
|
|
}
|
|
}
|
|
}
|
|
UnlockTicketMutex(&bin->tm);
|
|
}
|
|
|
|
/* Resume fibers */
|
|
if (first_id != 0)
|
|
{
|
|
TempArena scratch = BeginScratchNoConflict();
|
|
i16 *ids = PushDry(scratch.arena, i16);
|
|
i16 ids_count = 0;
|
|
{
|
|
i16 id = first_id;
|
|
while (id != 0)
|
|
{
|
|
FiberNeqFutexState *f = FiberNeqFutexStateFromId(id);
|
|
*PushStructNoZero(scratch.arena, i16) = id;
|
|
++ids_count;
|
|
id = FiberNeqFutexStateFromId(id)->next;
|
|
}
|
|
}
|
|
ResumeFibers(ids_count, ids);
|
|
EndScratch(scratch);
|
|
}
|
|
}
|
|
|
|
////////////////////////////////
|
|
//~ Greater-than-or-equal futex operations
|
|
|
|
void FutexYieldGte(volatile void *addr, void *cmp, u8 cmp_size)
|
|
{
|
|
/* TODO: Actually implement this. Just emulating via neq for now. */
|
|
FutexYieldNeq(addr, cmp, cmp_size);
|
|
}
|
|
|
|
void FutexWakeGte(void *addr)
|
|
{
|
|
/* TODO: Actually implement this. Just emulating via neq for now. */
|
|
FutexWakeNeq(addr);
|
|
}
|