Dista · API

dista.executors

A worker's task slots: threads that run tasks while its loop keeps serving.

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.

Structs

Job

struct Job

A task handed to a slot, and what it came back with.

Implements Movable

Fields
object_id: UInt64
task_id: UInt64
kind: UInt8
arrived: UInt64
args: List[UInt8]
many: Int: A TaskMany's number of results; 0 for any other task.
epoch: Int: The worker's head connection it was started under: one finished after the worker rejoined a new head is dropped.
started: UInt64
ended: UInt64
ok: Bool
out: List[UInt8]
error: String

__init__

def __init__(out self, object_id: UInt64 = UInt64(0), task_id: UInt64 = UInt64(0), kind: UInt8 = UInt8(0), arrived: UInt64 = UInt64(0), var args: List[UInt8] = List(), many: Int = Int(0), epoch: Int = Int(0))

Executors

struct Executors[dispatch: def(UInt64, List[UInt8], mut List[UInt8], mut TaskContext) raises thin -> None]

n threads, each running the tasks handed to its slot.

Implements Movable

__init__

def __init__(out self, n: Int, head_addr: String = "", resume_at: String = "", registry_hash: UInt64 = UInt64(0))

__deinit__

def __deinit__(deinit self)

slots

def slots(self) -> Int

running

def running(self) -> Int

Tasks started and not yet taken back.

wake_fd

def wake_fd(self) -> c_int

The descriptor that's readable once a job is done.

free_slot

def free_slot(self) -> Int

An idle slot, or -1 if every one is running a task.

start

def start(mut self, slot: Int, var job: Job)

Hands job to idle slot slot; its executor starts it at once.

take_done

def take_done(mut self) -> List[Job]

The jobs finished since the last call, their slots idle again.

Functions

worker_slots

def worker_slots() -> Int

DISTA_WORKER_SLOTS: tasks a worker runs at once (default 1; 0, one per core).

Aliases

comptime DispatchFn = def(UInt64, List[UInt8], mut List[UInt8], mut TaskContext) raises thin -> None

Runs task task_id on encoded arguments, appending the encoded result; a CtxTask gets the worker's TaskContext.

comptime IDLE = 0
comptime STARTED = 1
comptime DONE = 2
comptime STACK_BYTES = 67108864

Each executor's stack. A task runs where it would on the main thread, whose stack is 8 MiB on Linux; macOS gives other threads only 512 KiB by default. The stack is reserved, not committed: only the pages a task touches cost memory.