rokdocs
v0.1

rok::core::task

rok::core::task: tasks on the work-stealing executor (stdlib 2.18, refs-lang 7.6) and block_on (2.20.6); time is task::time.

One executor per process, started on first use: N workers on threads that live as long as the process (N = online CPUs or ROK_WORKERS), the blocking lane and the I/O driver. block_on and par::* attach the calling thread as a guest until their future and its spawn_local tasks are done. Without threads (wasm32-wasi, the browser) the guest runs everything itself, waiting in rok_io_wait.

A worker has a local queue (spawn_local, never stolen) and a Chase-Lev deque; guests have neither and push to the shared injector. A worker takes from its local queue, its deque, the injector, then steals; the owner takes FIFO too, since LIFO lets a self-waking task (yield_now) starve the rest of its deque.

A task's state goes IDLE -> SCHEDULED -> RUNNING -> IDLE / DONE; a wake while RUNNING marks it NOTIFIED and it is queued again after the poll.

Idle workers sleep on a flag; a push wakes one flagged worker, which searches until it finds work, and the last searcher to find a task wakes the next (tokio's scheme). No worker spins before sleeping.

I/O (707): a driver thread blocks in kqueue / epoll and unparks the tasks waiting on a ready fd.

Blocking calls (706 §7.6.7): each pool thread has a runtime lane that is marked blocked for the length of a blocking call. A monitor scans the lanes every millisecond; a worker blocked across a whole scan is handed to a spare or new thread, and the blocked thread finishes its poll as an orphan guest, then waits as a spare (10 s, then exits). At most 512 extra threads. A worker with live spawn_local tasks is never handed on.

Cancellation (2.18.4, 707 §7.7.10): Task::cancel sets a flag and wakes the task; each await checks it on entry and after each resume and leaves the body like a return, running finally / using.

Timers: one binary heap behind a Mutex (707 §7.7.5).

ROK_THREAD_PER_CORE=1 (706 §7.6.8) keeps every task on one worker's local queue, so nothing is stolen. ROK_DYNAMIC_WORKERS=1 (707 §7.7.8) wakes only active workers and activates one more when all are busy.

REPL sessions are saved between cells (rok_session_hold): every executor thread is busy from rok_session_enter to rok_session_leave except while asleep, and parks at rok_session_pass (between tasks, holding no lock) while a hold is requested, so no task is mid-poll in a saved session.

Modules

  • time rok::core::task::time - `Duration` / `Instant` (stdlib spec 2.18.6, 3.3.2).

Structs

TaskRef

pub struct TaskRef

A task as the executor sees it (what a Waker holds).

Trait implementations 2
  • unsafe impl Send for TaskRef
  • unsafe impl Sync for TaskRef

Pool

pub struct Pool

The process's work-stealing executor.

  • pub const fn workers(self) -> usize

    Number of pool threads.

Trait implementations 2
  • unsafe impl Send for Pool
  • unsafe impl Sync for Pool

Worker

pub struct Worker

A thread running tasks: a pool thread, or a guest (block_on).

Task

pub struct Task<T>

A spawned task; await it for its result (2.18.2).

  • pub fn is_finished(self) -> bool
  • pub const fn id(self) -> TaskId

    This task's id (what task::id() returns inside it).

  • pub fn cancel(self)

    Ask the task to stop (2.18.4): it is woken, its current await leaves the body (running the finally / using cleanups on the way out) and it finishes without a result. Awaiting it then panics; use try await t.join() to see CancellationError. A task that reaches no cancellation point after the cancel finishes normally, with its result.

  • pub fn is_cancelled(self) -> bool
  • pub const fn join(self) -> Join<T>

    Await the result, throwing CancellationError for a cancelled task.

Trait implementations 3
  • unsafe impl<T: Send> Send for Task<T>
  • impl<T> Future for Task<T>
  • impl<T> Transfer for Task<T>

Join

pub struct Join<T>

Task::join.

  • pub fn poll(ref self, ref ctx: Context) throws (CancellationError) -> Poll<T>

TaskId

pub struct TaskId

Identifies a task for as long as the process runs (ids are never reused): Task::id, task::id(). Not in spec 2.18 (tokio's task::Id).

  • pub const fn as_u64(self) -> u64
Trait implementations 5
  • impl PartialEq for TaskId
  • impl Eq for TaskId
  • impl Hash for TaskId
  • impl Display for TaskId
  • impl Debug for TaskId

CancellationError

pub struct CancellationError

A task was cancelled (2.18.4).

Trait implementations 3
  • impl Display for CancellationError
  • impl Debug for CancellationError
  • impl Error for CancellationError

SelectFuture

pub struct SelectFuture<F>
Trait implementations 1
  • impl<F: Future> Future for SelectFuture<F>

TimeoutError

pub struct TimeoutError

task::timeout ran out.

Trait implementations 3
  • impl Display for TimeoutError
  • impl Debug for TimeoutError
  • impl Error for TimeoutError

IoReady

pub struct IoReady
Trait implementations 1
  • impl Future for IoReady

Blocking

pub struct Blocking<T>
Trait implementations 1
  • impl<T> Future for Blocking<T>

Functions

wake_task

pub fn wake_task(t: TaskRef)

Schedule t to be polled (Waker::wake); safe from any thread.

cancel_requested

pub fn cancel_requested(ctx: Context) -> bool

Is the task polling with ctx cancelled? (The compiler's await checks this: 2.18.4 cancellation points.) A true answer means the caller leaves: the cancelled task (or select proxy) is marked unwinding.

unwinding

pub fn unwinding(ctx: Context) -> bool

Whether a future that just returned Ready with ctx may have left because of a cancel (its value is then a placeholder): the await after a ready poll checks this, not the bare flag, so a value a hand-written future handed over (a message, a guard) is kept when the cancel lands meanwhile - the next cancellation point leaves.

block_on

pub fn block_on<F: Future>(fut: F) -> F::Output

Run fut to completion from synchronous code (2.20.6). It returns once the spawn_local tasks pinned to this thread are done too.

block_on_try

pub fn block_on_try<T

block_on for a throwing async body: its error is thrown here.

host_enter

pub fn host_enter()

This thread works as the host loop's guest worker (host_leave ends it): tasks spawned with spawn_local belong to that worker.

host_leave

pub fn host_leave()

host_run

pub fn host_run(slice: fn() -> i64) -> bool

One turn of the host's loop (inside host_enter): every runnable task and due timer, until none is left; then the wait for sockets and timers, at most slice() ns (< 0: until nothing is left to wait for; 0: none), and what it woke. Whether a socket or timer still waits.

workers

pub fn workers() -> usize

Workers of the executor this thread runs tasks on (1 outside one, and without threads, where the one thread runs every task).

cpu_count

pub fn cpu_count() -> usize

Online CPUs: the executor's pool threads.

id

pub fn id() -> TaskId

The running task's id; panics outside a task (try_id).

try_id

pub fn try_id() -> TaskId | none

The running task's id, or none outside a task (sync code, a spawn_blocking thread).

spawn

pub fn spawn<F: Future + Send>(fut: F) -> Task<F::Output> where F::Output: Send

Run fut as a new task on any worker, concurrently with the spawner (2.18.1). Needs a running executor (inside async fn main / block_on). The task gets its own copy of every collection fut holds, what its coroutine captured included (2.13.9): the spawner keeps the originals.

spawn_local

pub fn spawn_local<F: Future>(fut: F) -> Task<F::Output>

Run fut as a new task on this worker only (it need not be Send). From a task a block_on thread happens to poll, that is one pool worker, and the task starts once that poll ends.

join

pub async fn join<T>(tasks: List<Task<T>>) -> List<T>

Every task's result, in order (2.18.1).

race

pub async fn race<T>(tasks: List<Task<T>>) -> T

The result of whichever task finishes first (2.18.1).

select

pub const fn select<F: Future>(futs: List<F>) -> SelectFuture<F>

The first of futs to complete, with its index: let (i, v) = await task::select([a, b]) (refs-lang 4.12.9 names select; 2.18 gives no signature). Unlike race the futures are not spawned: they run inside the awaiting task, polled in list order (ties go to the lower index). The futures share one type: async { .. } blocks with the same output do (wrap differing outputs, async { Msg::A(await a) }). Once one completes, every other one that has started is polled once more as cancelled (its await leaves through finally / using, as for Task::cancel) and dropped. Panics on an empty list.

sleep

pub async fn sleep(dur: Duration)

Completes once dur has passed since its first poll (2.18.1).

sleep_until

pub async fn sleep_until(when: Instant)

Completes at when.

yield_now

pub async fn yield_now()

Let the other ready tasks run once (2.18.1).

timeout

pub async fn timeout<T>(dur: Duration, task: Task<T>) throws (TimeoutError) -> T

task's result, or TimeoutError once dur has passed since the first poll (2.18.1): try await task::timeout(d, t).

park

pub fn park(key: usize, waker: Waker)

Wake waker at the next unpark(key). The entry goes at that unpark or when the waker's task finishes (a cancelled task's wait).

registrations

pub fn registrations() -> usize

Parked wakers (all keys) plus pending timers of unfinished tasks: a diagnostic (a cancelled task leaves none behind).

unpark

pub fn unpark(key: usize)

Wake every task parked on key (cheap when no task ever parked).

wake_io

pub fn wake_io(fd: i32)

Wake every task waiting on fd's readiness: after a shutdown or a close, which may raise no poller event (stdlib 2.18.1).

readable

pub const fn readable(fd: i32) -> IoReady

Completes once fd may be readable (await task::readable(fd) after a read hit WouldBlock); it may also complete spuriously, so retry the operation in a loop.

writable

pub const fn writable(fd: i32) -> IoReady

readable for writes.

spawn_blocking

pub fn spawn_blocking<T: Send>(f: fn() -> T + Send) -> Blocking<T>

Run f on the executor's blocking lane; the future completes with its result (tokio's spawn_blocking). For syscalls with no readiness to wait on, like regular files under kqueue / epoll. f is queued at once; a cancelled awaiter leaves it to finish in the background and its result is dropped.

Implementations

impl Timeout

  • pub fn poll(ref self, ref ctx: Context) throws (TimeoutError) -> Poll<T>