isolate
import isolate
High-throughput parallel concurrency backed by a pool of operating-system isolate threads.
Architectural Model: Shared-Nothing Isolates
Modern multi-core computing demands concurrency models that scale linearly with CPU core count while safeguarding applications against data races and memory corruption. Zuri addresses this by adopting a Shared-Nothing Isolate architecture (similar to the Actor model and Dart/Erlang isolates) rather than shared-memory green threads.
In Zuri, every isolate thread executes within its own self-contained
Virtual Machine (VM) instance. Each isolate possesses an independent
generational garbage-collected heap, nursery allocator, call frame
stack, register array, and global module namespace. Isolates run
concurrently across physical CPU cores without sharing heap pointers or
mutable structures with other isolates or the main thread.
Inter-isolate communication is accomplished strictly across defined
boundary barriers. When values, closures, or data structures are
transmitted via spawn(), join(), or Channel queues, Zuri traverses
the object graph and constructs an isomorphic copy on the destination
isolate’s heap. This transfer barrier faithfully preserves cyclic
graphs, shared object identity within the payload, and class prototypes
without sharing mutable memory across thread boundaries.
Memory Footprint & Task Density
High-concurrency systems frequently face a dilemma between heavy operating system threads and complex coroutine runtimes. Zuri reconciles high task density with OS-level multi-core execution through a two-tier scheduling design:
Instead of allocating a dedicated OS thread or a dedicated runtime stack
for every dispatched task, calling isolate.spawn() creates a
lightweight Task descriptor (the callee and its arguments, already
captured into a heap-independent snapshot) and pushes it onto a shared,
mutex-guarded queue.
- Isolate Pooling & Reuse: A fixed pool of isolate OS
threads, configured via
configure()and defaulting to the system’s available CPU core count is pre-warmed at startup. These isolate threads continuously drain the task queue, executing tasks sequentially within their long-lived isolate contexts. - Small, Fixed-Size Task Descriptors: Because a queued task is a captured value snapshot rather than a suspended OS thread, applications can enqueue large task bursts (e.g. 100,000+ jobs) without paying for a dedicated stack or heap per task. The fixed part of a task (its queue slot plus its join handle) is a few hundred bytes; a task’s actual total footprint on top of that scales with whatever the callee’s own arguments and captured state need to carry across the boundary, same as any other value transfer described above.
Garbage Collection & Scalability Under Load
A frequent performance bottleneck in multi-threaded runtimes is garbage collection cross-talk. When multiple threads share a single global heap, an allocation spike in one thread can trigger global Stop-The-World (STW) pauses, lock contention on shared allocation arenas, or heavy write-barrier synchronization that degrades throughput across all CPU cores.
Zuri eliminates GC interference through complete heap segregation:
- Local Generational Collections: Every isolate isolate manages its own nursery and old-generation heaps. Young-generation minor collections and major mark-sweep cycles execute entirely on the local isolate thread. - Zero Stop-The-World Pauses Across Threads: A garbage collection cycle on Isolate A never halts, pauses, or synchronizes with Isolate B or the main thread. Compute-heavy, allocation-intensive workloads (such as parallel tree building, matrix algebra, and image rendering) achieve near-linear multi-core speedup without GC contention.
Data Safety & Race Conditions
Concurrency bugs in shared-memory architectures such as data races, torn reads, and lock inversion deadlocks often arise from uncontrolled concurrent access to mutable heap memory.
- Data-Race Free by Construction: Because memory is physically partitioned into isolated heaps, two isolates can never concurrently mutate the same object in memory. Data races on heap objects are fundamentally impossible, eliminating the need for mutex locks or synchronized wrappers around user data structures. - Snapshot Closure Semantics: When a closure is passed to an isolate, its captured local variables and referenced global definitions are snapshot at dispatch time. The isolate receives an isolated clone of the captured state; subsequent mutations performed by the caller never affect the isolate, and isolate mutations never leak back to the caller.
Comparison with Go Goroutines
Both Go and Zuri provide lightweight concurrency abstractions, but they optimize for different runtime trade-offs:
Go utilizes Communicating Sequential Processes (CSP) built on M:N green threads multiplexed across OS threads with a single shared global heap. Zuri utilizes Thread-Pooled Shared-Nothing Isolates with deep-copy transfer barriers.
| Feature | Go Goroutines | Zuri Isolates |
|---|---|---|
| Concurrency Paradigm | M:N green threads on a single shared heap | Thread-pooled shared-nothing isolates / actors |
| Memory Safety | Shared memory; data races possible without mutexes | Data-race free by design via physical isolation |
| GC Impact | Shared global GC cycles and allocator write barriers | Independent per-isolate GC; zero STW cross-talk |
| Task Density | ~2 KB per goroutine stack | a few hundred bytes of fixed queue/handle overhead per task; pooled thread reuse |
| Communication | Shared-memory pointer channels or locks | Deep-copied value graphs or moved native resources |
| Primary Strengths | Ultra-high-concurrency non-blocking I/O multiplexing | Compute-heavy parallel processing & task pipelines |
Values That Can Cross Isolate Boundaries
Zuri’s value transfer engine supports virtually all language data types across isolate boundaries, including:
- Primitives:
nil,bool,number,bigint,string,bytes, andrange. - Collections:listanddict(including nested, multidimensional, and cyclic structures). - Object-Oriented Structures: Class instances, class declarations (preserving static fields, superclasses, and method tables), and bound methods. - Executable Code: Functions, closures (capturing upvalues), anonymous lambdas, and top-level scripts. - Modules: imported modules and module bindings, including one a closure captured.
- Native Handles:
Ptrhandles (moved linearly),Channelhandles, andIsolatehandles.
A module is reloaded on the receiving isolate rather than copied, so the recipient gets that isolate’s own instance of it, with its own top-level state.
Non-Transferable Types
Attempting to transmit an active operating system file handle
(file(...)) across a isolate boundary raises an IsolateError.
Native Resources & Ownership Transfer
Operating system resources encapsulated within native pointers
(Ptr)—such as database connections, network sockets, or compression
streams—cannot be safely duplicated across threads.
Zuri enforces linear ownership transfer (move semantics) for Ptr
values: - When a Ptr is sent through spawn(), join(), or a
Channel, ownership is transferred to the recipient, and the sender’s
handle is permanently invalidated.
- Subsequent attempts to read or use an invalidated
Ptrwill raise an error. - In contrast, synchronization primitives likeChannelandIsolatehandles are thread-safe and may be freely shared among multiple isolates.
Failure Isolation & Fault Tolerance
A software defect or unhandled exception within one isolate is strictly contained and will never terminate peer isolates, corrupt the pool, or crash the host process.
- Structured Error Propagation: Raised exceptions in a
isolate task are captured and re-raised as
IsolateError(complete with original source file and line stack traces) whenjoin()ortry_join()is invoked. - Automatic Isolate Recycling: In the rare event of an unrecoverable isolate panic, the affected isolate thread is safely retired and replaced with a fresh isolate, maintaining full pool capacity. - Un-Joined Failure Diagnostics: If an isolate fails and its handle is dropped without being joined, a diagnostic warning is emitted to ensure errors are never silently swallowed.
Cancellation Semantics
Applications can cancel running or queued isolates via
Isolate.cancel().
- Preemptive Blocking Interruption: If an isolate is
blocked on a channel operation (
Channel.send(),Channel.recv()), an isolate join (Isolate.join()), or a multiplexer (select(),wait_any(),wait_all()),cancel()interrupts the blocking call promptly with aIsolateCancelledError. - Cooperative Compute Polling: During active computational loops, code can cooperatively pollisolate.is_cancelled()to gracefully terminate execution.
Structured Concurrency (isolate.scope())
Unbounded background concurrency can lead to orphan tasks, leaked
resources, and missed errors if spawned isolates outlive their intended
context. Zuri provides Structured Concurrency through
isolate.scope(body) and the Scope handle.
- Lifetime Bounded to Lexical Scope: Any isolate spawned
via
s.spawn()within ascope()block is guaranteed to complete beforescope()returns. - Fail-Fast Automatic Cancellation: If the scope’sbodyraises an exception or if any child isolate fails, all other sibling isolates in that scope are automatically sent a cancellation signal (cancel()), preventing wasted computation. - Clean Teardown & Error Prioritization:scope()waits for all cancelled siblings to finish shutting down before propagating the root failure to the caller.
Examples
Basic Task Spawning
import isolate
def calculate_primes(limit) {
var count = 0
iter var n = 2; n <= limit; n++ {
var is_prime = true
iter var d = 2; d * d <= n; d++ {
if n % d == 0 {
is_prime = false
break
}
}
if is_prime count++
}
return count
}
var task = isolate.spawn(calculate_primes, 100_000)
echo "Primes found: ${task.join()}"
Parallel Batch Mapping
import isolate
def square(n) {
return n * n
}
var numbers = [1, 2, 3, 4, 5, 6, 7, 8]
var results = isolate.map(square, numbers)
echo "Squares: ${results}"
Structured Concurrency (scope)
import isolate
var total = isolate.scope(def(s) {
var w1 = s.spawn(def() { return 10 * 2 })
var w2 = s.spawn(def() { return 20 * 2 })
return w1.join() + w2.join()
})
echo "Total: ${total}"
Publish-Subscribe (Broadcast)
import isolate
var hub = isolate.broadcast()
var sub1 = hub.subscribe()
var sub2 = hub.subscribe()
isolate.spawn(def(c) { echo "Isolate 1: ${c.recv()}" }, sub1)
isolate.spawn(def(c) { echo "Isolate 2: ${c.recv()}" }, sub2)
hub.send("Hello Subscribers!")
hub.close()
The isolate API
Every public name in isolate, wherever it is declared. Each links to
the page that documents it.
| Name | Kind | Summary |
|---|---|---|
isolate.Broadcast | class | A one-to-many publish-subscribe message distribution hub. |
isolate.Channel | class | A thread-safe, multi-producer multi-consumer (MPMC) message queue. |
isolate.Isolate | class | A handle to an isolate, returned by spawn(). |
isolate.IsolateCancelledError | class | Raised from Isolate.join(), Channel.send(), Channel.recv(), isolate.wait_any(), isolate.wait_all(),… |
isolate.IsolateError | class | Raised when an isolate’s function raises an uncaught error, a Channel operation fails, or a value cannot… |
isolate.IsolateTimeoutError | class | Raised when Isolate.join(), Channel.send(), or Channel.recv() is given a timeout and it elapses… |
isolate.Scope | class | A structured concurrency supervisor and task nursery. |
isolate.active_count | function | |
isolate.broadcast | function | Creates a new one-to-many Broadcast distribution hub. |
isolate.channel | function | Creates a new thread-safe Channel. |
isolate.configure | function | Sets how many isolate threads the pool may run at once. |
isolate.cpu_count | function | |
isolate.is_cancelled | function | Checks whether the currently running isolate has been asked to stop via its handle’s cancel(). |
isolate.is_shutdown | function | |
isolate.map | function | Runs fn once per item in items, in parallel across the isolate pool, and returns the results in the same… |
isolate.pool_size | function | How many isolate threads the pool may run at once. |
isolate.queued_count | function | |
isolate.scope | function | Executes body(s) within a structured concurrency scope. |
isolate.select | function | Multiplexes across multiple channels, blocking until at least one channel is ready to deliver a value or is… |
isolate.shutdown | function | Stops the pool from accepting any further spawn() calls, then blocks until every isolate already spawned;… |
isolate.spawn | function | |
isolate.spawn_named | function | Same as spawn(), but gives the isolate a name; purely a debugging label, read back via Isolate.name()… |
isolate.started_count | function | How many isolate threads the pool has started so far. |
isolate.wait_all | function | Blocks until every one of isolates has finished, then returns their results in the same order as isolates. |
isolate.wait_any | function | Blocks until at least one of isolates finishes, and returns that one. |
Submodules
| Module | Reached as | Summary |
|---|---|---|
isolate.broadcast | isolate.broadcast.* | One-to-many (fan-out) publish-subscribe message distribution for isolate isolates. |
isolate.channel | isolate.channel.* | Thread-safe, multi-producer multi-consumer (MPMC) communication queues for passing values and messages across… |
isolate.error | isolate.error.* | IsolateError, kept in its own leaf module (no imports of its own) so both index and channel can depend… |
Functions
spawn()
isolate.spawn(fn: function, ...args: list)
spawn_named()
isolate.spawn_named(name: string, fn: function, ...args: list) -> Isolate
Same as spawn(), but gives the isolate a name; purely a debugging
label, read back via Isolate.name() and included in the “unobserved
failure” warning (see the module docs’ “Failure isolation” section) if
this one fails and is never join()ed or try_join()ed.
Parameters
name(string)fn(function) — a closure, bound method, or class method.args(...any)
Returns Isolate
Raises TypeError if fn is not callable
Raises IsolateError if fn is a native function or class,
fn/args cannot cross an isolate boundary, or the pool has been
shutdown()
scope()
isolate.scope(body: function)
Executes body(s) within a structured concurrency scope.
isolate.scope() establishes a structured task boundary, guaranteeing
that all isolates spawned via s.spawn() or s.spawn_named() finish
before scope() returns.
Lifecycle & Error Semantics
- Completion Guarantee:
scope()blocks untilbody(s)returns and all child isolates have terminated. 2. Automatic Cancellation: Ifbodyraises an exception or if any child isolate fails, all other still-running children in the scope are automatically sent a cancellation signal (cancel()). 3. Graceful Teardown:scope()waits for all cancelled isolates to cleanly finish their teardown before returning or re-raising. 4. Error Priority: Ifbodyraised an exception,body’s exception is re-raised. Otherwise, the earliest child failure (excluding secondaryIsolateCancelledErrorcascades) is re-raised.
import isolate
var result = isolate.scope(def(s) {
var task1 = s.spawn(def() { return 100 })
var task2 = s.spawn(def() { return 200 })
return task1.join() + task2.join()
})
echo "Total: ${result}" # 300
Parameters
body(function) — A callback functiondef(s)that receives a freshScope.
Returns — any: The return value of body(s).
Raises IsolateError if a child isolate failed and body did not
raise.
Raises any: Any exception raised by body(s).
configure()
isolate.configure(threads: int)
Sets how many isolate threads the pool may run at once. Has no effect if the pool has already started; its size is fixed once the first isolate runs.
The size is a ceiling. A thread is started only when an isolate is waiting and every thread already started is busy, so a pool sized for peak load costs only the threads the load actually needed.
Parameters
threads(int)
Returns — bool: true if applied, false if the pool had already
started.
Raises ValueError if threads is less than 1
pool_size()
isolate.pool_size() -> int
How many isolate threads the pool may run at once. Starts the pool, with
whatever size configure() set or the machine’s CPU count otherwise, if
it has not started yet.
Returns int
started_count()
isolate.started_count() -> int
How many isolate threads the pool has started so far. Threads start as
isolates need them, so this grows with the most isolates ever waiting at
once and never passes pool_size().
Returns int
cpu_count()
isolate.cpu_count()
Returns — int: the number of logical CPUs this machine reports.
shutdown()
isolate.shutdown(timeout: ?number)
Stops the pool from accepting any further spawn() calls, then blocks
until every isolate already spawned; queued or already running; has
finished. Nothing in flight is abandoned.
Permanent: once called, every spawn() for the rest of the process
raises IsolateError, even if this call itself later times out.
Parameters
timeout(number) — seconds to wait before giving up. Waits indefinitely if omitted.
Raises IsolateTimeoutError if timeout elapses with isolates
still in flight
active_count()
isolate.active_count()
Returns — int: isolates an isolate is actively running right now.
Does not include ones still queued_count().
queued_count()
isolate.queued_count()
Returns — int: isolates spawned but not yet picked up by a isolate.
is_shutdown()
isolate.is_shutdown()
Returns — bool: true if shutdown() has been called.
wait_any()
isolate.wait_any(isolates: list, timeout: ?number)
Blocks until at least one of isolates finishes, and returns that one.
Useful for reacting to whichever of several tasks completes first,
rather than joining them one at a time in a fixed order.
Parameters
isolates([Isolate]) — must not be empty.timeout(number) — seconds to wait before giving up. Waits indefinitely if omitted.
Returns — Isolate: whichever one finished first.
Raises ValueError if isolates is empty
Raises IsolateTimeoutError if timeout elapses before any of them
finish
Raises IsolateCancelledError if the CALLING isolate is cancelled
while blocked here
wait_all()
isolate.wait_all(isolates: list, timeout: ?number) -> list
Blocks until every one of isolates has finished, then returns their
results in the same order as isolates.
Parameters
isolates([Isolate]) — may be empty (returns[]).timeout(number) — seconds to wait for all of them combined, not per isolate. Waits indefinitely if omitted.
Returns list
Raises IsolateError from the first one (in isolates order) whose
function raised, once every one of them has finished
Raises IsolateTimeoutError if timeout elapses before all of them
finish
Raises IsolateCancelledError if the CALLING isolate is cancelled
while blocked here
map()
isolate.map(fn: function, items: list, timeout: ?number) -> list
Runs fn once per item in items, in parallel across the isolate pool,
and returns the results in the same order as items. Equivalent to
spawning an isolate per item and passing the handles to wait_all().
Parameters
fn(function) — a closure, bound method, or class method.items(list)timeout(number) — seconds to wait for the whole batch combined. Waits indefinitely if omitted.
Returns list
Raises IsolateError from the first item (in items order) whose
call raised, once every call has finished
Raises IsolateTimeoutError if timeout elapses before every call
finishes
Raises IsolateCancelledError if the CALLING isolate is cancelled
while blocked here
is_cancelled()
isolate.is_cancelled() -> bool
Checks whether the currently running isolate has been asked to stop via
its handle’s cancel(). Meant to be polled periodically by long-running
or looping isolate code so it can return early.
Calling this outside an isolate (from the main script, or from another
thread) always returns false.
Returns bool
Classes
Isolate
class isolate.Isolate
A handle to an isolate, returned by spawn().
Constructor
isolate.Isolate(ptr)
Isolate.join()
isolate.Isolate.join(timeout: ?number) -> any
Blocks until the isolate finishes and returns its result. May be called more than once; later calls return immediately with the same result.
Parameters
timeout(number) — seconds to wait before giving up. Waits indefinitely if omitted.
Returns any
Raises IsolateError if the isolate’s function raised an uncaught
error
Raises IsolateTimeoutError if timeout elapses first
Raises IsolateCancelledError if the CALLING isolate (not this one)
is cancelled while blocked here
Isolate.cancel()
isolate.Isolate.cancel()
Requests that the isolate stop. Has no effect on an isolate that has already finished.
If the isolate is currently blocked in Channel.send()/ recv(),
Isolate.join(), wait_any(), wait_all(), or select(), that call
is interrupted within roughly 50ms and raises IsolateCancelledError.
Otherwise; plain, non-blocking code; cancellation is cooperative: it
does not interrupt code that’s already running. The isolate’s own
function must check isolate.is_cancelled() and return on its own.
Isolate.is_cancelled()
isolate.Isolate.is_cancelled()
Returns — bool: true if cancel() has been called on this
isolate.
Isolate.name()
isolate.Isolate.name()
Returns — ?string: the name given via spawn_named(), or nil if
this isolate was started with plain spawn().
Isolate.try_join()
isolate.Isolate.try_join() -> any
Returns the isolate’s result, or nil if it has not finished yet. Does
not block.
Returns any
Raises IsolateError if the isolate’s function raised an uncaught
error
Note: an isolate that returns
nilis indistinguishable from one still running. Useis_done()to tell them apart.
Isolate.is_done()
isolate.Isolate.is_done()
Returns — bool: true if the isolate has finished.
Isolate.status()
isolate.Isolate.status()
Unlike try_join(), never marks a failure as “observed”; safe to poll
purely to check progress without silencing the warning a failure that’s
never actually join()ed/try_join()ed would otherwise get (see the
module docs’ “Failure isolation” section).
Returns — string: 'pending' while still running, 'done' once
finished successfully, or 'error' once finished with an uncaught
error.
Isolate.handle()
isolate.Isolate.handle()
This isolate’s underlying native handle, needed by wait_any() to
operate on a plain list of Isolates from outside the class. Not meant
for direct use.
Scope
class isolate.Scope
A structured concurrency supervisor and task nursery.
Scope manages the lifecycle of a set of concurrent child isolates
spawned within a isolate.scope() block. It guarantees that:
- Lifetime Bounding: All isolates spawned via
s.spawn()ors.spawn_named()are tracked by the scope and cannot outlive the enclosingscope(body)block. 2. Fail-Fast Error Handling: If any child isolate raises an unhandled error (or if thescopebody itself fails), all remaining sibling isolates are automatically cancelled (cancel()), preventing wasted computation. 3. Deterministic Teardown:scope()waits for all active and cancelled children to complete execution before re-raising the root failure or returning the body’s result.
Scope instances cannot be constructed directly; they are created and
passed to the callback function by isolate.scope(body).
Examples
Parallel Task Aggregation
import isolate
var summary = isolate.scope(@(s) {
var task_a = s.spawn(@() { return "Task A complete" })
var task_b = s.spawn(@() { return "Task B complete" })
return "${task_a.join()} & ${task_b.join()}"
})
echo summary # "Task A complete & Task B complete"
Fail-Fast Child Cancellation
import isolate
catch {
isolate.scope(def(s) {
# Spawn an isolate that fails quickly
s.spawn(def() {
raise "critical error in child"
})
# Sibling isolate is automatically cancelled as soon as child 1 fails
s.spawn(def() {
iter var i = 0; i < 1_000_000; i++ {
if isolate.is_cancelled() break
}
})
})
} as err {
echo "Scope caught child failure: ${err.message}"
}
Constructor
isolate.Scope()
Scope.spawn()
isolate.Scope.spawn(fn: function, ...args: list)
Spawns a concurrent isolate task tracked by this scope.
The spawned isolate is bound to this scope’s lifecycle:
isolate.scope() will not return until this isolate has completed. If
this isolate fails, all sibling isolates in the scope are automatically
cancelled.
isolate.scope(def(s) {
var w = s.spawn(def(x, y) { return x + y }, 10, 20)
echo "Result: ${w.join()}" # 30
})
Parameters
fn(function) — A callable function, closure, or method to execute.args(...any) — Arguments to pass tofn.
Returns — Isolate: A handle to the spawned isolate.
Raises TypeError if fn is not callable.
Raises IsolateError if fn is a native function or class, if
fn/args cannot cross isolate boundaries, or if the isolate pool has
shut down.
Scope.spawn_named()
isolate.Scope.spawn_named(name: string, fn: function, ...args: list)
Spawns a named concurrent isolate task tracked by this scope.
Operates identically to Scope.spawn(), but assigns an identifier
name used in diagnostic error messages and debugging traces.
isolate.scope(def(s) {
var w = s.spawn_named("fetch-user", fetch_user, user_id)
return w.join()
})
Parameters
name(string) — A human-readable identifier for the isolate.fn(function) — A callable function, closure, or method to execute.args(...any) — Arguments to pass tofn.
Returns — Isolate: A handle to the spawned isolate.
Raises TypeError if fn is not callable.
Raises IsolateError if fn is a native function or class, if
fn/args cannot cross isolate boundaries, or if the isolate pool has
shut down.
Scope.children()
isolate.Scope.children()
Returns all Isolate handles spawned within this scope.
isolate.scope(def(s) {
s.spawn(task1)
s.spawn(task2)
for child in s.children() {
echo "Child isolate active"
}
})
Returns — list: A list of Isolate instances in the order they were
spawned.
2026, Richard Ore and Zuri contributors