# dista.executors

> A module of Dista's API.

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`

```mojo
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__`

```mojo
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`

```mojo
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__`

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

#### `__deinit__`

```mojo
def __deinit__(deinit self)
```

#### `slots`

```mojo
def slots(self) -> Int
```

#### `running`

```mojo
def running(self) -> Int
```

Tasks started and not yet taken back.

#### `wake_fd`

```mojo
def wake_fd(self) -> c_int
```

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

#### `free_slot`

```mojo
def free_slot(self) -> Int
```

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

#### `start`

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

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

#### `take_done`

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

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

## Functions

### `worker_slots`

```mojo
def worker_slots() -> Int
```

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

## Aliases

### `DispatchFn`

```mojo
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`.

### `IDLE`

```mojo
comptime IDLE = 0
```

### `STARTED`

```mojo
comptime STARTED = 1
```

### `DONE`

```mojo
comptime DONE = 2
```

### `STACK_BYTES`

```mojo
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.
