Files
RetroArch/samples/tasks/wait_timeout
LibretroAdmin 5ba7f8c8cb task_queue: a threaded runner without locks
Where the atomics have compare-and-swap and pointer operations, the
threaded runner takes no lock and waits on no condition variable.

Every task is on a live list from its push until it is retired: any
thread links it at the head, only the checking thread unlinks it, and
find(), retrieve() and the lookups behind wake, cancel and reset walk
it from any thread inside a read epoch. A retired task is cleaned up
and freed once no finder can still hold it, at once when none is
inside, since finders read task->state.

Workers take ready tasks from two bounded MPMC rings: the plain one,
which only the one worker takes from, and the parallel one, which
every worker does; what overflows waits behind them in order. A task
with a 'when' to wait for goes to the timer keeper, whichever idle
worker holds the token, which sleeps until the earliest is due and
hands the token on before it runs anything. Main-thread tasks and
finished ones reach the checking thread through lock-free stacks.
Workers, task_queue_wait() and deinit's bounded wait for the workers
park on eventcounts. Each gather with work in flight and each pass's
end advance one counter that both read, so a handler still sees what
the frame loop wrote before a check, as the frame count a load waits
on, and has read it before the loop writes again.

The volatile and PS2 backends keep the locked runner, as does GCD.
task_queue.c now uses retro_eventcount.c, which the sample builds
compiling it with threads now link.

task_queue_parallel_test floods both rings past what they hold with
every worker held; task_queue_switch_test holds a waker in
retro_eventcount_notify() where the runner takes no lock.
2026-10-30 13:59:17 +01:00
..