Concurrency

BearVM provides two instructions for task-based concurrency: spawn and sync.

Model

Each spawn launches a new OS thread via Zig’s std.Thread API. The spawned thread runs the specified function with the given arguments independently of the caller. sync blocks the calling thread until the spawned thread completes and returns its result.

This is straightforward OS-thread parallelism. There is no thread pool, no work-stealing scheduler, and no green thread runtime.

Spawning a Task

spawn call <func>(<args>) starts a new thread and returns a task handle:

%task1 = spawn call fib(%n1)
%task2 = spawn call fib(%n2)

Both calls return immediately. The two fib invocations run concurrently.

Synchronising

sync <task> blocks until the task finishes and returns its result:

%r1 = sync %task1
%r2 = sync %task2

%res = add %r1, %r2
ret %res

sync must be called for every spawned task. If a task fails, sync propagates the error.

Full Example

The following computes Fibonacci recursively with each pair of sub-problems running in parallel:

@fib(%n: int): int {
entry:
    %cond0 = le %n, 1
    br_if %cond0, base_case, recursive_case

base_case:
    ret %n

recursive_case:
    %n1 = sub %n, 1
    %n2 = sub %n, 2

    %task1 = spawn call fib(%n1)
    %task2 = spawn call fib(%n2)

    %r1 = sync %task1
    %r2 = sync %task2

    %res = add %r1, %r2
    ret %res
}

@main(): int {
entry:
    %n        = const 10
    %task_fib = spawn call fib(%n)
    %result   = sync %task_fib

    %task_print = spawn call puts(%result)
    %r          = sync %task_print

    ret 0
}

Notes

  • Each spawn creates a real OS thread. Spawning a large number of threads simultaneously will behave as the underlying OS dictates.

  • There is no built-in mechanism for shared mutable state or synchronisation primitives beyond spawn and sync.