The receiver chooses where a verb runs, what it sees and how it completes, and the verb stays the same.
A database, transaction or snapshot runs a verb on the calling thread, handle.on(executor) blocks on an executor, and handle.deferred(executor) submits it there and answers a Deferred:
db.write(collections, keys, values) # the head, on the calling threaddb.on(executor).write(collections, keys, values) # blocking, on the executorvar call = db.deferred(executor).read(collections, keys, arena) # Deferred[...]print(call.wait().present(0))
On and DeferredOn offer the same verbs with the same arguments, and the blocking verbs are written once, in the BlobsVerbs, StringsVerbs, DocsVerbs, GraphVerbs, PagesVerbs and DatabaseVerbs traits every blocking receiver conforms to.
The verb names follow the C ones: blobs verbs are unprefixed, as write and scan, and the other modalities carry theirs, as strings_scan, docs_gather, graph_read_edges and pages_insert.
Reads see the head of a database, a transaction, or a snapshot; writes join the receiver’s transaction, recording their keys unless it came from txn.unwatched(), and a snapshot’s receiver, which has none, writes at the head. stage, commit, rollback and reset act on the receiver’s transaction and save on its snapshot, and the engine answers Status.UNINITIALIZED for a receiver without one.
A Deferred owns its outcome and outputs until the call is final, and borrows the arena and every input it was handed, which Mojo keeps alive for as long as the Deferred is; freeing the database, the executor or a transaction waits for the calls naming them.
Mojo has no runtime of its own to await on, so wait is the whole interface: it blocks in ustore_outcome_wait until the call is final, then answers the result or raises the failure, as many times as it is asked. status reads the outcome without blocking, and destroying an unfinished Deferred waits for it.
Every output whose length only the call knows, values, keys, offsets, bounds, columns and edges, lands in an Arena you pass, and the verb answers a view over it without copying. Arena(executor) places it where that executor computes, decided once, and Arena(library) makes host pages for the calling thread.
Each call rewinds its arena, so the previous call’s views die and a steady loop allocates once; reading a stale one raises Status.INVALID_ARGUMENT.
To keep a result past the next call, pass that call another arena, and give every deferred call its own.
Scans take their start keys and how many keys every scan keeps, plus an optional config for the end keys, the direction, the patterns or filter, the predicate and the progress handler:
Handlers are compile-time parameters, since a capturing closure cannot cross into C, and their state crosses beside them as a pointer that must outlive the call.
Progress handlers return a value, which Mojo 1.2 is confirmed to pass back on Linux x86-64; Linux AArch64 and macOS rest on the CI matrix.
API
StructOnA database, transaction or snapshot bound to the executor its verbs block on. Binding-only.
struct On[origin: ImmOrigin]
Parameters
originImmOrigin
comptime
The handle and the executor, which this receiver borrows.
StructDeferredOnA database, transaction or snapshot bound to the executor its verbs are submitted to: every verb answers a Deferred at once, borrowing its arena and inputs until final. Binding-only.
struct DeferredOn[origin: ImmOrigin]
The verbs and their arguments are those of the blocking receivers.
Parameters
originImmOrigin
comptime
The handle and the executor, which this receiver borrows.
StructCompletionWhether a verb returns with its final status or once submitted, ustore_completion_t.
struct Completion
The receiver picks it: a handle and handle.on(executor) pass BLOCKING, and
handle.deferred(executor) passes DEFERRED, so no verb takes it as an argument.
Fields
code
c_int
The raw ustore_completion_t value the ABI exchanges.
Values
BLOCKING
Completion(Int32(0))
Return with the final status stored and every output readable.
DEFERRED
Completion(Int32(1))
Return once submitted; the status reads DEFERRED until final.
StructProgressHow a long call reports its advance: the progress and progress_state pair of every verb that takes one. The handler never runs concurrently with itself, nor after its call is final.2
struct Progress
Fields
callback
Address
The handler's C entry point; NULL_ADDRESS skips every report.
state
Address
Forwarded into every report; it must outlive the call.
Called with the units finished, the units in total, and state.
statePointer[T, origin]
Forwarded into every report; it must outlive the call.
StructExecutorThe threads or rings a verb runs on, freed once it and every device arena it made go out of scope.6
struct Executor
Executor(library) owns nothing: every verb handed it runs on the calling thread. Calls on one
executor start in submission order, and a blocking call runs alone.
StructArenaThe memory every output whose length scales with a call lands in, freed when it goes out of scope.4
struct Arena
Made where an executor computes, decided once; nothing is allocated until a call writes into it.
Every call handed it rewinds and refills it, which invalidates the previous call's outputs, so a
steady loop allocates once. One arena serves one call at a time; a second concurrent use is
refused by the engine as CONTENDED, which leaves the holder's results readable.
A result refuses every read with INVALID_ARGUMENT once a later call rewinds its arena, and a
result over a device arena refuses every host read with DEVICE.
Conforms to
Deinitable, Movable
__init__Host pages for calls on the calling thread. Calls ustore_arena_init.
StructDeferredA call submitted to an executor, which owns its outcome and outputs until it is final. Binding-only.4
struct Deferred[C: Call]
status reads DEFERRED until then, without blocking, and wait blocks until the call is
final and answers its result or raises its failure, as many times as it is asked. The result is
the view a blocking call answers, borrowing the same arena. Dropping an unfinished one waits the
same way, so nothing the engine still writes is ever freed under it. The crossing's origins keep
the arena and every borrowed input alive for as long as this is, and freeing the database, the
executor or a transaction waits for the calls naming them.