Mojo · Quickstart

Quickstart

from forkunion import Library, Pool, BroadcastJoin, … 4.0.0-rc.1 · d927445f

ForkUnion is dead-simple to use! There is no nested parallelism, exception handling, or “future promises”; they are banned. A pool runs one dispatch at a time and refuses any other with the contended status, including one from inside its own callbacks. The thread pool itself has a few core operations:

  • spawn to initialize worker threads, and
  • for_threads to launch a blocking callback on all threads.

Higher-level APIs for index-addressable tasks are also available:

  • for_n - for individual evenly-sized tasks,
  • for_n_dynamic - for individual unevenly-sized tasks,
  • for_slices - for contiguous runs of evenly-sized tasks, one per worker.

For additional flow control and tuning, following helpers are available:

  • nap - to park workers on the CPU’s address monitor, waking with no syscall,
  • sleep(microseconds) - to block workers in the kernel for long idle periods, with 0 waiting for the next dispatch,
  • terminate - to kill the threads before the destructor is called,
  • unsafe_for_threads - to broadcast a callback without blocking, returning a generation token,
  • unsafe_join - to block until the completion of a broadcasted generation,
  • is_complete - to poll a generation token for completion without blocking.

Every dispatch is identified by a generation token. On caller_exclusive_k pools you can dispatch, overlap your own work, poll is_complete, and join - the classic poll-then-join pattern. On caller_inclusive_k pools the calling thread owes one slice of the work, which only runs inside unsafe_join, so completion can’t be reached by polling alone. The same rule shapes the RAII guard returned by for_threads and, in C++, the for_n family: the workers start at the guard’s construction on every pool, and on inclusive pools the caller’s own slice runs at join or destruction.

On caller_exclusive_k pools thread 0 may also fork from inside its own slice, at any depth: such a fork waits for every other slice of the one enclosing it, runs on all threads, and returns complete. So unsafe_for_threads can hand a whole multi-step call to thread 0, which forks the rest of the pool as it goes, while the caller only polls a status word the call stores at its end. That is how one manager thread keeps several exclusive pools busy with no queue and no extra thread. A join on such a pool waits for whatever dispatch runs, since the call may end before its submitter stores the token.

In C++ it provides NUMA extensions. That includes the colocated_pool analog of the flat_pool and the domain_allocator_t for allocating memory in a specific memory domain. Those are out-of-the-box compatible with the higher-level APIs. Most interestingly, for Big Data applications, a higher-level distributed_pool class addresses and balances the work across all compute domains.

domain_allocator_t is backed by linux_numa_allocator_t, freebsd_numa_allocator_t, or windows_numa_allocator_t.

To integrate into your Mojo project, let Pixi build the package from source — there is no channel to add and no linker flag to pass:

[workspace]
preview = ["pixi-build"]

[dependencies]
forkunion = {git = "https://github.com/ashvardanian/ForkUnion", tag = "v4.0.0-rc.1"}

Vendoring works too and needs no manifest: Mojo resolves a source package ahead of a compiled one, so mojo run -I <path-to>/mojo is enough once libforkunion.so is on the loader path.

Then import and use in your code:

from forkunion import ComputeDomain, Library, Pool, SyncMutPointer, TasksRange, ThreadInDomain, Topology

@fieldwise_init
struct Output(ImplicitlyCopyable, TrivialRegisterPassable):
    """The output array every kernel below writes into."""

    var values: SyncMutPointer[Int64]

def double(task: Int, at: ThreadInDomain, mut output: Output):
    output.values.at(task) = Int64(task * 2)

def announce(thread_index: Int, compute_domain_index: Int, mut output: Output):
    print(t"thread {thread_index} on compute domain {compute_domain_index}")

def sweep(tasks: TasksRange, at: ThreadInDomain, mut output: Output):
    for task in tasks:
        output.values.at(task) += 1

def main() raises:
    var library = Library()
    var topology = Topology(library)
    var pool = Pool(topology, threads=4, name="demo")

    var results = List[Int64](length=1000, fill=0)
    var output = Output(SyncMutPointer(results.unsafe_ptr().unsafe_origin_cast[MutUntrackedOrigin]()))

    pool.for_threads[announce](output)          # once per worker, OpenMP-style parallel
    pool.for_n[double](1000, output)            # evenly split, OpenMP-style parallel for
    pool.for_n_dynamic[double](1000, output)    # claimed as threads free up, for uneven work
    pool.for_slices[sweep](1000, output)        # one contiguous run per worker
    print(t"results[7] = {results[7]}")         # 15: doubled to 14, then swept once

The callback is a compile-time parameter and the state it needs is a separate runtime argument. Mojo cannot hand a capturing closure to C at all, so a callback carries no captures — the scratch is the only channel, and its type and origin are inferred from the argument, leaving a call site to name only the work. Fallible calls raise Error and nothing else, and a call raises if and only if it can fail - a refusal is never folded into an Optional the caller has to interpret.

The non-blocking dispatch is a context manager, so the join cannot be forgotten and the pool cannot be torn down while its workers are still running:

with pool.unsafe_for_threads[work](scratch) as dispatch:
    while not dispatch.is_complete(): # exclusive pools only
        prepare_next_batch()

The Topology handle reports the hardware topology, to size and place work:

var topology = Topology(library)
for index in range(topology.compute_domains_count()):
    var domain = ComputeDomain(index)
    var cores = topology.logical_cores_count_in(domain)
    var level = topology.compute_level_in(domain)
    print(t"domain {index}: {cores} cores, level {level}, allocate on {topology.local_memory_of(domain)}")

Handles release themselves, so there is no defer to forget — though Mojo destroys a value after its last use rather than at the end of the scope, which is earlier than a Zig defer fires.

Unlike max.algorithm.parallelize, which takes every hardware thread and offers no way to ask for fewer, ForkUnion’s pool width is an argument. On a machine you are sharing, that is the difference between a speedup and a slowdown.

API

StructLibraryA reference to the loaded core, cheap to copy and shared by everything it creates.3
struct Library

Every handle carries one, which is what keeps the symbols resolved for as long as anything can still call into them. It is a loader reference, not a topology reference - the C API is explicit that a pool never retains the topology it spawned from.

Fields

sharedArcPointer[_Loaded]The handle and its symbols, refcounted so the last holder closes the handle.

Conforms to

Copyable, Deinitable, ImplicitlyCopyable, Movable
__init__Opens the shared object and resolves it, which every handle then shares.
def __init__(out self)

Raises

With LIBRARY_MISSING, when nothing is on the loader path or its major version differs.
symbolsThe resolved entry points, borrowed rather than copied on every call.
def symbols(self) -> ref[self.shared.symbols] Symbols

Returns

A reference that stays valid for as long as this Library is held.
versionThe loaded library's own version, which is also the cheapest check that loading worked.
def version(self) -> Tuple[Int, Int, Int]

Returns

The major, minor, and patch numbers the shared object reports for itself.
StructPoolA spawned pool. Released when the last reference to it goes out of use.17
struct Pool

Every query re-asks the C core rather than caching, so the answers stay correct across a terminate followed by a fresh spawn with a different width or exclusivity.

Fields

libraryLibraryThe loaded core, held so its symbol table outlives every call made through the handle.
handlePointer[NoneType, MutUntrackedOrigin]The opaque fu_pool_t every call below hands back to the core.

Conforms to

Deinitable, Movable
__init__Spawns threads workers across the whole machine.
def __init__(
    out self,
    topology: Topology,
    threads: Int,
    exclusivity: CallerExclusivity = CallerExclusivity.INCLUSIVE,
    name: StringSpan[ImmStaticOrigin] = StringSpan(""),
    allowed: Capabilities = Capabilities.ALL
)
def __init__(out self, *, library: Library, handle: Pointer[NoneType, MutUntrackedOrigin])

Adopts a handle that is already spawned, which is how on hands one back.

Parameters

topologyTopologyThe machine description; read only during this call, and freeable afterwards.
threadsIntWorker count above zero, counting the caller itself on an inclusive pool.
exclusivityCallerExclusivityWhether the dispatching thread runs a slice or only coordinates.
nameStringSpan[ImmStaticOrigin]What the workers answer to in a debugger; an empty name leaves them unnamed.
allowedCapabilitiesThe spin hints and placement facilities the spawn may pick from, before masking.
libraryLibraryThe loaded core the handle was created through, and is deleted through.
handlePointer[NoneType, MutUntrackedOrigin]An fu_pool_t whose lifetime this pool takes over.

Raises

Error if the pool cannot be allocated, is already spawned, or the OS refuses the threads.
__deinit__Stops the workers before freeing the pool, so nothing is still running at the delete.
def __deinit__(deinit self)
onSpawns threads workers pinned to one compute domain.
def on(
    topology: Topology,
    domain: ComputeDomain,
    threads: Int,
    exclusivity: CallerExclusivity = CallerExclusivity.INCLUSIVE,
    name: StringSpan[ImmStaticOrigin] = StringSpan(""),
    allowed: Capabilities = Capabilities.ALL
) -> Self

Parameters

topologyTopologyThe machine description; read only during this call, and freeable afterwards.
domainComputeDomainThe one domain the workers and their domain-local allocations stay on.
threadsIntWorker count above zero, counting the caller itself on an inclusive pool.
exclusivityCallerExclusivityWhether the dispatching thread runs a slice or only coordinates.
nameStringSpan[ImmStaticOrigin]What the workers answer to in a debugger; an empty name leaves them unnamed.
allowedCapabilitiesThe spin hints and placement facilities the spawn may pick from, before masking.

Returns

A pool whose workers never leave domain, so one per domain can be driven side by side.

Raises

Error if the domain is out of range, the pool is already spawned, or the OS refuses the threads.
threads_countWorkers in the pool, including the caller on an inclusive pool.
def threads_count(self) -> Int

Returns

The width the last spawn settled on, so a re-spawn at a different width changes it.

Raises

Error if the pool was never spawned.
threads_count_inWorkers the pool placed in one compute domain.
def threads_count_in(self, domain: ComputeDomain) -> Int

Parameters

domainComputeDomainA dense compute-domain index, below compute_domains_count and not bounds-checked.

Returns

That domain's share alone, which need not be an equal split across the domains.

Raises

Error if the pool was never spawned.
compute_domains_countCompute domains this pool spans, which may be fewer than the machine's.
def compute_domains_count(self) -> Int

Returns

One on a flat pool or a domain-pinned one, more once NUMA nodes or QoS tiers are crossed.

Raises

Error if the pool was never spawned.
locate_thread_inA global thread index expressed as a local one inside a compute domain.
def locate_thread_in(self, global_thread_index: Int, domain: ComputeDomain) -> Int

Parameters

global_thread_indexIntA thread index as a dispatch reports it, spanning the whole pool.
domainComputeDomainThe domain to rebase into, below compute_domains_count and not bounds-checked.

Returns

That thread's position among the domain's own workers, where zero is the domain's first.

Raises

Error if the pool was never spawned.
caller_exclusivityQueried live, so it stays correct across a terminate and a re-spawn.
def caller_exclusivity(self) -> CallerExclusivity

Returns

INCLUSIVE when the dispatching thread owes a slice, EXCLUSIVE when it only coordinates.

Raises

Error if the pool was never spawned.
capabilitiesThe requested allow-mask narrowed by what the machine offers; masking never adds.
def capabilities(self) -> Capabilities

Returns

The bits that survived, which the waiter and the placement strategy were chosen from.

Raises

Error if the pool was never spawned.
napParks the workers on the CPU's address monitor; the next dispatch wakes them.
def nap(self)

Not thread-safe: call it between task batches, never during a dispatch.

sleepBlocks the workers in the kernel; the next dispatch wakes them.
def sleep(self, microseconds: Int = Int(0))

Not thread-safe: call it between task batches, never during a dispatch.

Parameters

microsecondsIntThe longest a worker stays blocked before re-checking; 0 means no limit.
terminateStops the workers but keeps the handle, so the pool can be spawned again.
def terminate(self)

Not thread-safe: call it only when no dispatch is in flight, never as a sync point.

for_threadsOne call per worker, which is where per-thread scratch is set up or torn down.
def for_threads[Scratch: AnyType, scratch_origin: MutOrigin, //, work: def(Int, Int, mut Scratch) thin -> None](
    self,
    ref[scratch_origin] scratch: Scratch
)

The callback takes the thread and compute-domain indices; the C API supplies no task index here, so none is invented.

Parameters

ScratchAnyTypecomptimeThe scratch type, inferred from the argument.
scratch_originMutOrigincomptimeThe scratch's origin, inferred with it.
workdef(Int, Int, mut Scratch) thin -> NonecomptimeThe callback, taking a pool-wide thread index, a compute-domain index, and the scratch.
scratchScratchThe state the callback reads and writes, borrowed for the whole call.

Raises

Error if the pool was never spawned, or the core refuses the dispatch.
for_nSplits n tasks into equal contiguous chunks and blocks until every one has finished.
def for_n[Scratch: AnyType, scratch_origin: MutOrigin, //, work: def(Int, ThreadInDomain, mut Scratch) thin -> None](
    self,
    n: Int,
    ref[scratch_origin] scratch: Scratch
)

Parameters

ScratchAnyTypecomptimeThe scratch type, inferred from the argument.
scratch_originMutOrigincomptimeThe scratch's origin, inferred with it.
workdef(Int, ThreadInDomain, mut Scratch) thin -> NonecomptimeThe callback, taking a task index, a ThreadInDomain, and the scratch.
nIntHow many tasks to split across the pool, one callback invocation each.
scratchScratchThe state the callback reads and writes, borrowed for the whole call.

Raises

Error if the pool was never spawned, or the core refuses the dispatch.
for_n_dynamicThe same, but tasks are claimed as threads free up, for work of uneven cost.
def for_n_dynamic[Scratch: AnyType, scratch_origin: MutOrigin, //, work: def(Int, ThreadInDomain, mut Scratch) thin -> None](
    self,
    n: Int,
    ref[scratch_origin] scratch: Scratch
)

Parameters

ScratchAnyTypecomptimeThe scratch type, inferred from the argument.
scratch_originMutOrigincomptimeThe scratch's origin, inferred with it.
workdef(Int, ThreadInDomain, mut Scratch) thin -> NonecomptimeThe callback, taking a task index, a ThreadInDomain, and the scratch.
nIntHow many tasks to hand out, no worker taking a second before it finishes its first.
scratchScratchThe state the callback reads and writes, borrowed for the whole call.

Raises

Error if the pool was never spawned, or the core refuses the dispatch.
for_slicesOne contiguous run per worker, for vectorized or per-slice-setup work.
def for_slices[Scratch: AnyType, scratch_origin: MutOrigin, //, work: def(TasksRange, ThreadInDomain, mut Scratch) thin -> None](
    self,
    n: Int,
    ref[scratch_origin] scratch: Scratch
)

Every worker is called exactly once; an idle one receives a range with count == 0.

Parameters

ScratchAnyTypecomptimeThe scratch type, inferred from the argument.
scratch_originMutOrigincomptimeThe scratch's origin, inferred with it.
workdef(TasksRange, ThreadInDomain, mut Scratch) thin -> NonecomptimeThe callback, taking a TasksRange, a ThreadInDomain, and the scratch.
nIntHow many tasks to divide into one run per worker, however few workers there are.
scratchScratchThe state the callback reads and writes, borrowed for the whole call.

Raises

Error if the pool was never spawned, or the core refuses the dispatch.
unsafe_for_threadsDispatches on every worker without blocking, returning a guard that joins on exit.
def unsafe_for_threads[Scratch: AnyType, pool_origin: ImmOrigin, scratch_origin: MutOrigin, //, work: def(Int, Int, mut Scratch) thin -> None](
    ref[pool_origin] self,
    ref[scratch_origin] scratch: Scratch
) -> BroadcastJoin[pool_origin, scratch_origin]

Use it as a context manager. The workers start here; on an inclusive pool the caller owes its own slice, which runs at the join, so only an exclusive pool can be polled before it.

Parameters

ScratchAnyTypecomptimeThe scratch type, inferred from the argument.
pool_originImmOrigincomptimeThis pool's borrow, inferred from self and carried on into the guard.
scratch_originMutOrigincomptimeThe scratch's origin, inferred with the scratch and carried on too.
workdef(Int, Int, mut Scratch) thin -> NonecomptimeThe callback, taking a pool-wide thread index, a compute-domain index, and the scratch.
scratchScratchThe state the callback reads and writes, borrowed until the join rather than the return.

Returns

A guard naming the in-flight generation, which joins when its with block ends.

Raises

Error if the pool was never spawned, or the core refuses the dispatch.
StructBroadcastJoinOne in-flight generation, joined when the with block ends.3
struct BroadcastJoin[pool_origin: ImmOrigin, scratch_origin: MutOrigin]

Both origins are load-bearing, because Mojo destroys a value after its last named use and the dispatch outlives that point.

Parameters

pool_originImmOrigincomptimeThe pool's borrow, without which it would be torn down while its workers were still running - and a terminate mid-flight trips an assertion inside the core.
scratch_originMutOrigincomptimeThe scratch's borrow, keeping the storage the workers write through alive until the join, which a raw context pointer cannot express.

Fields

poolPointer[Pool, pool_origin]The dispatching pool, reached at the join and at every poll.
generationIntThe token the core stamped on this dispatch, which the poll and the join both name.

Conforms to

Copyable, Deinitable, ImplicitlyCopyable, Movable
__enter__Hands back the guard itself, so the block can poll as well as read the generation.
def __enter__(self) -> Self

Returns

This same guard, so is_complete stays reachable from inside the block.
__exit__Joins the generation, running the caller's own slice first on an inclusive pool.
def __exit__(mut self)
is_completeA non-blocking poll; meaningful on exclusive pools, where the caller owes no slice.
def is_complete(self) -> Bool

Returns

True once every worker has left this generation; on an inclusive pool, never before the join.

Raises

Error if the pool was never spawned.
StructErrorKindWhy a call into the C core failed.2
struct ErrorKind

Fields

codeInt32The raw fu_status_t value, kept even when this build does not name it.

Values

SUCCESSErrorKind(Int32(0))The call completed.
UNKNOWNErrorKind(Int32(-1))No reason was reported, or one this build does not name.
BAD_ALLOCErrorKind(Int32(-2))An allocation or mapping failed; a smaller request may succeed.
CAPACITY_EXHAUSTEDErrorKind(Int32(-3))A fixed ceiling was reached, so a smaller request will not help either.
INVALID_ARGUMENTErrorKind(Int32(-4))An argument was malformed, out of range, or would overflow a byte count.
CONFIG_MISMATCHErrorKind(Int32(-5))The handles or the pool kind cannot serve this call together.
ALREADY_SPAWNEDErrorKind(Int32(-6))The pool is already spawned; terminate it first.
NOT_SPAWNEDErrorKind(Int32(-7))The pool was never spawned.
THREAD_REFUSEDErrorKind(Int32(-8))The OS declined to create a thread - a resource limit, or permissions.
TOPOLOGY_UNAVAILABLEErrorKind(Int32(-9))The machine could not be described.
PERMISSION_DENIEDErrorKind(Int32(-10))A privileged operation was declined.
UNSUPPORTEDErrorKind(Int32(-11))This build or this machine has no such facility.
LIBRARY_MISSINGErrorKind(Int32(-12))Nothing to dlopen, or what loaded is a different major version.
SYMBOL_MISSINGErrorKind(Int32(-13))The library loaded but does not export a symbol the binding needs.
CONTENDEDErrorKind(Int32(-14))The pool is running another dispatch: another caller's, or the one this call runs inside.

Conforms to

Copyable, Deinitable, Equatable, ImplicitlyCopyable, Movable, RegisterPassable, TrivialRegisterPassable, Writable
write_toSpells the failure out in prose, so a code this build does not name still reads.
def write_to(self, mut writer: T)

Parameters

writerTmutThe sink the reason is appended to.
ofLifts a raw fu_status_t, keeping an unnamed one rather than guessing.
def of(status: Int32) -> Self

Parameters

statusInt32Whatever the C call returned, named by this build or not.

Returns

The matching kind, or one carrying the unrecognized code verbatim.
StructErrorWhat went wrong reaching the C core, and which symbol or argument it was.1
struct Error

Fields

kindErrorKindWhich class of failure the C core reported.
detailStaticStringThe symbol or argument it was about, for a reader rather than for a branch.

Conforms to

Copyable, Deinitable, ImplicitlyCopyable, Movable, Writable
write_toWrites the reason and the detail together, prefixed so the source is unambiguous.
def write_to(self, mut writer: T)

Parameters

writerTmutThe sink the message is appended to.
StructCallerExclusivityWhether the thread that dispatches also executes, or only coordinates.1
struct CallerExclusivity

Fields

identifierc_intThe raw fu_caller_exclusivity_t value the C API exchanges.

Values

INCLUSIVECallerExclusivity(Int32(0))The calling thread owes one slice of the work, which runs inside the join.
EXCLUSIVECallerExclusivity(Int32(1))The calling thread only coordinates, so a dispatch can be polled before it is joined.

Conforms to

Copyable, Deinitable, Equatable, ImplicitlyCopyable, Movable, RegisterPassable, TrivialRegisterPassable, Writable
write_toNames which of the two policies this is.
def write_to(self, mut writer: T)

Parameters

writerTmutThe sink the name is appended to.
StructCapabilitiesThe bit set of spin hints and placement facilities a build or a machine offers.3
struct Capabilities

One bit per facility, and two accessors ask two questions of the same bit. comptime_capabilities reports whether the code was built with a path; runtime_capabilities reports whether the machine offers it now. Neither implies the other.

Fields

bitsUInt32One bit per facility, laid out as the C fu_capability_t enumerators are.

Values

NONECapabilities(UInt32(0))Nothing detected, or nothing compiled in.
X86_PAUSECapabilities(UInt32(1))The PAUSE spin hint, on every x86 since the Pentium 4.
X86_TPAUSECapabilities(UInt32(2))TPAUSE sleeps the core until a deadline. Needs the WAITPKG feature.
ARM64_YIELDCapabilities(UInt32(4))The YIELD hint, on every AArch64.
ARM64_WFETCapabilities(UInt32(8))WFET sleeps the core until a deadline or an event. Needs FEAT_WFxT.
RISCV64_PAUSECapabilities(UInt32(16))The PAUSE spin hint, from the Zihintpause extension.
RISCV64_WRSCapabilities(UInt32(32))WRS.STO sleeps the hart until a reservation breaks. Needs the Zawrs extension.
OS_THREADSCapabilities(UInt32(64))Own the raw OS thread handle instead of a std::thread.
TOPOLOGYCapabilities(UInt32(128))Enumerate this machine's cores, compute domains, and memory domains.
PLACE_THREADS_BY_AFFINITYCapabilities(UInt32(256))Bind a thread to a set of cores, choosing where it runs.
PLACE_THREADS_BY_CORE_CLASSCapabilities(UInt32(512))Steer a thread onto a class of core at creation.
RESCHEDULE_THREADS_BY_CLASSCapabilities(UInt32(1024))Reclass a thread's scheduler to sleep or wake it.
PLACE_MEMORY_ON_DOMAINCapabilities(UInt32(2048))Place a buffer's pages on a chosen memory domain.
PLACE_HUGE_PAGES_ON_DOMAINCapabilities(UInt32(4096))Place larger-than-base pages on a chosen memory domain.
HUGE_TRANSPARENT_PAGESCapabilities(UInt32(8192))The kernel promotes base pages to huge pages on its own.
COLOCATE_POOLS_ON_DOMAINCapabilities(UInt32(16384))The domain-aware colocated and distributed pools are compiled.
X86_CLDEMOTECapabilities(UInt32(32768))CLDEMOTE moves a just-written line toward the shared LLC.
ARM64_DC_CVACCapabilities(UInt32(65536))DC CVAC cleans a dirty line to the coherency point.
RISCV64_ZICBOMCapabilities(UInt32(131072))User-mode Zicbom cache-block management, attested through hwprobe.
X86_CMPCCXADDCapabilities(UInt32(262144))CMPCCXADD, the conditional atomic add.
X86_RAOINTCapabilities(UInt32(524288))RAO-INT aadd and its family, executed at the shared cache.
ARM64_LSECapabilities(UInt32(1048576))Armv8.1 FEAT_LSE: one instruction per read-modify-write.
ARM64_RCPCCapabilities(UInt32(2097152))Armv8.3 FEAT_LRCPC: LDAPR acquires, cheaper than LDAR's.
RISCV64_ZACASCapabilities(UInt32(4194304))RISC-V Zacas, the amocas compare-and-swap.
RISCV64_ATOMICCapabilities(UInt32(8388608))The RISC-V A extension's lr, sc and amo words.
ARM64_PRFMCapabilities(UInt32(16777216))PRFM, AArch64's prefetch hint.
RISCV64_ZICBOPCapabilities(UInt32(33554432))RISC-V Zicbop, the prefetch.w hint.
RISCV64_ZABHACapabilities(UInt32(67108864))RISC-V Zabha, byte and halfword atomics.
ANY_YIELDCapabilities(UInt32(63))Every busy-wait waiter bit, to enumerate the ones a machine offers.
ALLCapabilities(UInt32(4294967295))All-ones allow-mask: hand to a pool to disable capability filtering.

Conforms to

Copyable, Deinitable, Equatable, ImplicitlyCopyable, Movable, RegisterPassable, TrivialRegisterPassable
__contains__Whether every bit of other is set here.
def __contains__(self, other: Self) -> Bool

Parameters

otherSelfThe facilities being asked about, one bit each.

Returns

True only when all of them are present, so a multi-bit mask asks for all of them.
__and__The intersection, to keep only what a build and a machine both offer.
def __and__(self, other: Self) -> Self

Parameters

otherSelfThe facilities to keep.

Returns

A set holding only the bits both sides had.
__or__The union, to build an allow-mask out of the named facilities.
def __or__(self, other: Self) -> Self

Parameters

otherSelfThe facilities to add.

Returns

A set holding every bit either side had.
StructTasksRangeA half-open [first, first + count) run of task indices - the "what work" of a slice dispatch.4
struct TasksRange

Iterable, so a callback reads for task in range rather than rebuilding the bounds. An idle thread receives a range with count == 0, which every dispatch still calls exactly once.

Fields

firstIntThe first task index in the run.
countIntHow many tasks the run covers; zero means an idle thread.

Conforms to

Copyable, Deinitable, Equatable, ImplicitlyCopyable, Movable, RegisterPassable, SizedRaising, TrivialRegisterPassable
__len__How many tasks the run covers.
def __len__(self) -> Int

Returns

The count, which is zero on the thread a dispatch left idle.
__iter__Walks the task indices, so a callback loops over the run rather than rebuilding it.
def __iter__(self) -> _SequentialRange

Returns

A range over the half-open span, empty for an idle thread.
endOne past the last task index, so first..end() is the half-open span.
def end(self) -> Int

Returns

The exclusive upper bound, which equals first when the run is empty.
is_emptyWhether the run covers no tasks at all.
def is_empty(self) -> Bool

Returns

True on the idle thread every dispatch still calls exactly once.
StructIndexedSplitSplits a range of tasks into fair-sized runs, minimizing the spread across threads.2
struct IndexedSplit

The first tasks % threads runs get one extra task; the rest get the floor. Mirrors the C++ indexed_split. See https://lemire.me/blog/2025/05/22/dividing-an-array-into-fair-sized-chunks/

Fields

quotientIntThe floor of tasks over threads, which every run is at least this long.
remainderIntHow many of the leading runs take one task more than the floor.

Conforms to

Copyable, Deinitable, ImplicitlyCopyable, Movable, RegisterPassable, TrivialRegisterPassable
__init__Precomputes the split once, leaving get free of division.
def __init__(tasks_count: Int, threads_count: Int) -> Self

Parameters

tasks_countIntHow many tasks are shared out; zero is allowed and leaves every run empty.
threads_countIntHow many runs to cut, which must be positive.
getThe run owned by thread_index.
def get(self, thread_index: Int) -> TasksRange

Parameters

thread_indexIntWhich of the runs to describe, counted from zero.

Returns

That thread's span, one task longer for the first remainder of them.
StructSyncConstPointerA read-only view letting one immutable buffer be read by every worker across a C callback.1
struct SyncConstPointer[T: AnyType]

The caller guarantees the pointee outlives the parallel region and is not mutated while shared.

Parameters

TAnyTypecomptimeThe element type, read unchanged by every worker for the whole region.

Fields

pointerPointer[T, ImmUntrackedOrigin]The first element, its origin erased so the view survives the C callback boundary.

Conforms to

Copyable, Deinitable, ImplicitlyCopyable, Movable, RegisterPassable, TrivialRegisterPassable
atThe element at index; the caller owns bounds checking.
def at(self, index: Int) -> ref[ImmUntrackedOrigin] T

Parameters

indexIntAn offset in elements, not in bytes.

Returns

A read-only reference into the caller's buffer, live as long as that buffer is.
StructSyncMutPointerA writable view letting workers write disjoint slots of one buffer across a C callback.1
struct SyncMutPointer[T: AnyType]

The caller guarantees each worker touches disjoint indices and the pointee outlives the region.

Parameters

TAnyTypecomptimeThe element type, whose slots the workers write one apiece.

Fields

pointerPointer[T, MutUntrackedOrigin]The first element, its origin erased so the view survives the C callback boundary.

Conforms to

Copyable, Deinitable, ImplicitlyCopyable, Movable, RegisterPassable, TrivialRegisterPassable
atA reference to the element at index; the caller owns disjointness and bounds.
def at(self, index: Int) -> ref[MutUntrackedOrigin] T

Parameters

indexIntAn offset in elements, not in bytes.

Returns

A writable reference into the caller's buffer, unsynchronized against every other one.

Functions

versionThe loaded library's own version.
def version(library: Library) -> Tuple[Int, Int, Int]

Parameters

libraryLibraryThe loaded core to report on, whose major version the loader already matched.

Returns

Major, minor, and patch, as the shared object reports them.
comptime_capabilitiesWhat this build of the library was compiled to be able to do.
def comptime_capabilities(library: Library) -> Capabilities

Parameters

libraryLibraryThe loaded core whose build is being asked about.

Returns

The compiled-in mask, to be tested against the Capabilities constants.
runtime_capabilitiesWhat this machine actually offers, which is not implied by the above in either direction.
def runtime_capabilities(library: Library) -> Capabilities

Parameters

libraryLibraryAny loaded core; the answer describes the machine rather than the build.

Returns

The detected mask, to be tested against the same Capabilities constants.
name_capabilitiesRenders a mask as a comma-separated name list such as "arm64_yield,arm64_wfet".
def name_capabilities(library: Library, capabilities: Capabilities) -> String

Parameters

libraryLibraryThe loaded core that holds the naming table.
capabilitiesCapabilitiesA mask from either accessor; a bit this build cannot name is skipped.

Returns

The names in ascending bit order, or an empty string for an empty mask.

Raises

Error if the core refuses the rendering, which a buffer it cannot write does with INVALID_ARGUMENT.
comptime_capabilities_stringThe compiled-in capabilities, named.
def comptime_capabilities_string(library: Library) -> String

Parameters

libraryLibraryThe loaded core whose build is being named.

Returns

The comma-separated names of the compiled-in mask.

Raises

Error if the core refuses the rendering, which a buffer it cannot write does with INVALID_ARGUMENT.
runtime_capabilities_stringThe capabilities this machine offers, named.
def runtime_capabilities_string(library: Library) -> String

Parameters

libraryLibraryAny loaded core; the names describe the machine rather than the build.

Returns

The comma-separated names of the detected mask.

Raises

Error if the core refuses the rendering, which a buffer it cannot write does with INVALID_ARGUMENT.
bytes_for_elementsBytes occupied by count elements of element_bytes each, refusing a product that wraps.
def bytes_for_elements(count: Int, element_bytes: Int) -> Int

Parameters

countIntHow many elements the buffer holds.
element_bytesIntThe width of one element.

Returns

The exact product, because the overflowing case raises rather than truncating.

Raises

With INVALID_ARGUMENT, when the product would exceed Int.MAX.

Constants

DEFAULT_ALIGNMENTTwo cache lines, because most x86 parts prefetch in pairs, matching the C++ default_alignment_k.
comptime DEFAULT_ALIGNMENT = 128