With DISTA_WORKER_SLOTS above 1, a worker runs up to that many tasks at
once, each on a thread of its own, while its event loop stays on the main
thread: it keeps reading the head, serving fetches, fetching inputs and
reporting results while tasks run. The executors only run the task: the loop
builds each one's arguments before handing it over, and takes its result
back, storing and reporting it as a worker with one slot does. So nothing an
executor touches is shared with the loop but the job it was handed.
The hand-off is one job per slot under a mutex. The loop fills an idle
slot's job, then marks it started and wakes the executors (a condition
variable); slot i's executor runs it, marks it done and writes a byte to a
pipe the loop polls, which wakes the loop. The loop alone moves a slot from
idle to started and from done back to idle, so it knows which slots are free
without the lock. The mutex orders the job's fields between the two threads.
An executor that runs a CtxTask gives it a TaskContext of its own: a
client per slot, so two tasks never share one connection.
A thread body can't raise or capture (see the threads tin), so an executor
catches its task's error and hands it back as the job's.
Leaving with tasks still running (a drain past its deadline, a head gone in
local mode) can't wait for them, since a task can't be interrupted: the
executors are detached instead, and their shared state is left for the
process's exit to reclaim.