gizlang:task
Structured tasks. A task runs a closure alongside the rest of the program on the same thread (thread runs one on an OS thread of its own); sleeping, joining and I/O suspend only the calling task. A task never outlives the task that spawned it: when its work returns, its unjoined children are cancelled and awaited. Cancellation reaches a task at its next suspension point as fault 12.
import * as Task from "gizlang:task";Types
Fault
type Fault = { code: i64; }The runtime fault that ended a task: 12 when it was cancelled, 13 when it waited for something that could never happen.
code:i64
Task
type Task<T>A task that runs to a value of type T; await gives the value.
Opaque: create and use it through this module's functions.
Thread
type Thread<T>Work running on an OS thread of its own (thread).
Opaque: create and use it through this module's functions.
Channel
type Channel<T>A bounded first-in, first-out queue of values between tasks. It belongs to the task that made it and closes, dropping what it still holds, when that task ends.
Opaque: create and use it through this module's functions.
Cell
type Cell<T>One value shared by threads (thread), ArcSwap's role: load gives the
latest stored and store replaces it, and neither waits.
Opaque: create and use it through this module's functions.
Lease
type Lease = { generation: i64; until: i64; }A claim fence is monotone even when its wall clock moves backwards. Persistence and authorization remain the caller's responsibility.
generation:i64until:i64
Queue
type Queue<T>An unbounded queue for the tasks of any thread while the task that made it lives: any thread pushes, its reader takes everything at once and may put a batch back in front. A short lock guards it (no suspension inside); a push after close is dropped. Values are shared as on a crossing channel and may not hold Refs.
Opaque: create and use it through this module's functions.
LoadOrder
type LoadOrder = "relaxed" | "acquire" | "seqCst"The memory order of a load, as C11 and Rust name them. seqCst (the default) orders it with every other seqCst
operation; acquire makes the writes released before the value it reads visible; relaxed orders nothing.
StoreOrder
type StoreOrder = "relaxed" | "release" | "seqCst"The memory order of a store: release makes the writes before it visible to an acquire load of its value.
Order
type Order = "relaxed" | "acquire" | "release" | "acqRel" | "seqCst"The memory order of a read-modify-write (fetchAdd, compareExchange).
Atomic
type AtomicOne i64 shared by the tasks of every thread, changed with C11 atomics (sequentially consistent unless an order is
given): no lock, no allocation after create. It uses 8 bytes until the process ends, and copies of it are the same
cell.
Opaque: create and use it through this module's functions.
Words
type WordsA fixed number of i64 words that the tasks of every thread share and change in place, with no lock: a load sees every store made before the store that wrote what it reads (acquire and release), and compareExchange is sequentially consistent. Copies of it are the same words; the last copy to go frees them.
Opaque: create and use it through this module's functions.
Pair
type Pair<A, B> = { first: A; second: B; }The two values of an independent pair of tasks.
Opaque: create and use it through this module's functions.
Functions
spawn
function spawn<F>(work: F): Task<ReturnType<F>>Start work as a task; await gives its result.
join
function join<T>(task: Task<T>): T throws FaultThe task's value, or the fault that ended it. A value is taken once.
parallel
function parallel<F>(count: i64, body: F): Vec<ReturnType<F>>body(i) for every i in 0..count on the process's threads (as many as its CPUs), the results in index order. The body may only compute and fail (no I/O, console or shared state); what it captures is read-only while it runs. A fault in any call is this call's fault.
fill
function fill<F>(total: i64, chunk: i64, body: F): ReturnType<F>One Vec of total items made on the process's threads: body(start) gives the
chunk items from start (the last chunk the rest), and each thread copies
its chunk into place, so the parts are never joined by one serial copy. The
items own nothing (numbers, bools, records of them); a chunk of another
length faults 1. The body is held to what parallel's is.
thread
function thread<F>(work: F): Thread<ReturnType<F>>work(sys) on an OS thread of its own, in a World of its own: its own event
loop, tasks, handles and memory budget, and sys its own System, as a
process's main gets. It runs alongside every task of this thread; what work
captures is shared read-only (reference counts change atomically), and it
cannot reach this thread's Refs or handles (a captured handle is fault 14
there). When the task that started it ends, it cancels still-running work
at its next suspension point and waits for the thread to finish.
cores
function cores(): i64The CPUs this process may use (its affinity): the size of parallel's pool
and of GIZLANG_WORKERS=auto. This observes affinity, CPU admission and the
environment; it cannot certify an environment-independent cached test.
Effects: read.
stackLeft
function stackLeft(): i64The bytes of stack the current task has left before a cyclic function faults with R009. A recursion whose depth its input sets (a SQL function that calls itself) checks it and returns an error instead of faulting.
Effects: read.
stackTop
function stackTop(): u64The address just above the running task's stack, or 0 on its thread's own stack: a collector that scans its callers' frames conservatively (gizvm) reads up to it.
Effects: read.
cancel
function cancel<T>(task: Task<T>): boolAsk the task to stop: fault 12 at its next wait, so code that never waits, such as a loop of arithmetic, runs on. False when it already finished or was cancelled.
sleep
function sleep(milliseconds: i64): boolWait milliseconds ms; true when the wait was not cut short.
Effects: fail, io.
pause
function pause(): boolLet every other ready task run once.
Effects: fail, io.
now
function now(): i64Milliseconds on a monotonic clock.
Effects: fail, io.
nanos
function nanos(): i64Nanoseconds on a monotonic clock: what JavaScript's performance.now and process.hrtime read (not replayed).
Effects: read.
claimLease
function claimLease(previous: Lease, now: i64, duration: i64): Lease throws stringThe lease after PREVIOUS: the next generation, held from NOW for DURATION and never ending before PREVIOUS ends. It fails when a number is out of range.
Effects: fail.
retry
function retry<F>(work: F, attempts: i64): ReturnType<F> throws FaultRun work in a fresh task until it succeeds, at most attempts times;
the last fault when every attempt failed.
within
function within<F>(milliseconds: i64, work: F): ReturnType<F> throws FaultRun work in a fresh task for at most milliseconds; a task still running
then is cancelled, so the result is fault 12 once it waits (cancel): a loop
that never waits is not stopped, and this does not return until it ends.
channel
function channel<T>(capacity: i64): Channel<T>A channel holding at most capacity (at least 1) values.
crossing
function crossing<T>(capacity: i64): Channel<T>A channel between threads: the tasks of any thread (thread) may send,
receive and close while the task that made it lives. A value sent is marked
shared (counts change atomically), so the receiver may keep or change it
(a write copies a buffer someone else still holds); values may not hold Refs.
A task waiting on it is never reported as deadlocked: another thread may
still send or close.
send
function send<T>(queue: Channel<T>, value: T): boolMove value in, waiting while the channel is full; false (and the value is
dropped) when it is closed.
trySend
function trySend<T>(queue: Channel<T>, value: T): boolMove value in without waiting. False drops this value when the channel is
full or closed; it never enqueues a value after returning false.
respond
function respond<T>(queue: Channel<T>, value: T): boolComplete an asynchronous response without waiting. False drops the value when the recipient is full, closed or its owning task has ended. Expired recipients grant no access to a reused channel slot. Ordinary sends retain their stale-handle fault contract.
receive
function receive<T>(queue: Channel<T>): Option<T>The oldest value, waiting for one; none once the channel is closed and empty.
tryReceive
function tryReceive<T>(queue: Channel<T>): Option<T>The oldest value without waiting; none when nothing is queued (or the channel is closed and empty). A Ready set that watches a crossing channel (Ready.channel) drains it with this.
handOff
function handOff<T>(queue: Channel<T>, value: mut T): boolMove VALUE in, waiting while the channel is full, and leave VALUE empty (zero) for the caller to assign again before reading it: a whole record (the JS Heap at each coroutine switch) moves without the copy send makes of a value it borrows. False (and the value dropped) when the channel is closed.
receiveMoved
function receiveMoved<T>(queue: Channel<T>): TThe oldest value, waiting for one, moved out to the caller (matching receive's Option copies it); a fault once the channel is closed and empty.
exchange
function exchange<T>(to: Channel<T>, back: Channel<T>, value: mut T): boolhandOff of VALUE to TO, then receiveMoved from BACK into VALUE: each a copy of its bytes, where the two calls copy a large record field by field several times (4 µs of each JS await for the 4 KB Heap, 10/17). Faults once BACK is closed and empty. False when TO was closed (VALUE was dropped).
close
function close<T>(queue: Channel<T>): boolNo more sends; receivers drain what is queued. False when already closed.
cell
function cell<T>(initial: T): Cell<T>A cell holding initial, for the tasks of any thread while the task that
made it lives. As on a crossing channel, values are marked shared and may not
hold Refs; a writer's store of a new snapshot never waits for readers.
newQueue
function newQueue<T>(): Queue<T>An empty queue between tasks.
atomic
function atomic(initial: i64): AtomicA new atomic holding initial.
Effects: io.
atomicGlobal
function atomicGlobal(name: string): AtomicThe one atomic of name in this process, made with value 0 on first use
(thread-safe): every thread gets the same cell. The lookup takes a lock, so
keep the result and do not call global on a hot path.
Effects: io.
words
function words(count: i64): WordsCOUNT new words, each 0.
Effects: fail, io.
zip
function zip<F, G>(first: F, second: G): Pair<ReturnType<F>, ReturnType<G>> throws FaultStart both jobs together and join both, even when one fails. A task fault stays a Fault; a Result returned by a job stays a Result.
Methods
Thread.join
function Thread.join<T>(self: Thread<T>): T throws FaultThe thread's value, or the fault that ended it; waiting suspends only the calling task. A value is taken once.
Cell.load
function Cell.load<T>(self: Cell<T>): TThe latest value stored (a shared copy: changing it copies what it changes).
Cell.store
function Cell.store<T>(self: Cell<T>, value: T): boolReplace the value; readers that loaded the old one keep it. Like loading, it faults once the task that made the cell has ended.
Cell.close
function Cell.close<T>(self: Cell<T>): boolDrops the value now, not when the task that made the cell ends: a long task that makes cells for data it later retires (a cold segment's columns) gives their memory back. A load or store after it faults. False when already closed.
Queue.push
function Queue.push<T>(self: Queue<T>, value: T): boolVALUE at the end; false (dropped) once the queue is closed.
Queue.take
function Queue.take<T>(self: Queue<T>): Vec<T>Everything queued, oldest first; the queue is empty after.
Queue.putBack
function Queue.putBack<T>(self: Queue<T>, values: Vec<T>): voidVALUES back in front, in their order (a reader that took more than it used).
Queue.shut
function Queue.shut<T>(self: Queue<T>): Vec<T>No more pushes: what is queued, which nobody takes after this.
Atomic.load
function Atomic.load(self: Atomic, order: LoadOrder = "seqCst"): i64The current value.
Effects: io.
Atomic.store
function Atomic.store(self: Atomic, value: i64, order: StoreOrder = "seqCst"): boolReplace the value. Always true.
Effects: io.
Atomic.fetchAdd
function Atomic.fetchAdd(self: Atomic, delta: i64, order: Order = "seqCst"): i64Add delta (wrapping on overflow) and return the value from before.
Effects: io.
Atomic.compareExchange
function Atomic.compareExchange(self: Atomic, expected: i64, desired: i64, order: Order = "seqCst"): boolA failed exchange is a load with ORDER's acquire part.
Effects: io.
Words.count
function Words.count(self: Words): i64How many words there are.
Words.load
function Words.load(self: Words, i: i64): i64The value of word I.
Effects: fail, io.
Words.store
function Words.store(self: Words, i: i64, value: i64): boolSets word I to VALUE. Always true.
Effects: fail, io.
Words.compareExchange
function Words.compareExchange(self: Words, i: i64, expected: i64, desired: i64): boolSets word I to DESIRED only when it holds EXPECTED; true when it did.
Effects: fail, io.