BRAIDGROUP
RESEARCH & DEV
19. Documentation

Concurrency

Green Threads with spawn

Braid provides concurrency through lightweight green threads. The spawnkeyword starts a new concurrent task that runs a block of code in its own execution context. These are managed by the BraidVM scheduler, not the OS.

fn main() {
    spawn {
        print("hello from a green thread!");
    };
    print("hello from main");
    // both prints execute concurrently
}

Multiple Spawns

You can spawn multiple concurrent tasks. The scheduler interleaves their execution cooperatively.

fn main() {
    spawn {
        let i = 0;
        while i < 3 {
            print("task a: " + i);
            i = i + 1;
        }
    };
    spawn {
        let i = 0;
        while i < 3 {
            print("task b: " + i);
            i = i + 1;
        }
    };
    spawn {
        let i = 0;
        while i < 3 {
            print("task c: " + i);
            i = i + 1;
        }
    };
}

Channels for Communication

Braid provides channels for communicating between concurrent tasks. Channels allow sending values from one task to another in a thread-safe manner.

fn main() {
    let ch = channel();

    spawn {
        ch.send(42);
    };

    let val = ch.recv();
    print("received: " + val);
}

Channel Example: Producer-Consumer

A common pattern is one task producing values and another consuming them over a channel.

fn producer(ch: channel, count: int) {
    let i = 0;
    while i < count {
        ch.send(i);
        i = i + 1;
    }
    ch.close();
}

fn consumer(ch: channel) {
    while true {
        let val = ch.recv();
        if val == nil {
            break;
        }
        print("got: " + val);
    }
}

fn main() {
    let ch = channel();
    spawn { producer(ch, 5); };
    spawn { consumer(ch); };
}

Buffered Channels

Channels can be created with a buffer size. A buffered channel allows sending without blocking until the buffer is full.

fn main() {
    let ch = channel(10);

    spawn {
        let i = 0;
        while i < 10 {
            ch.send(i * i);
            i = i + 1;
        }
        ch.close();
    };

    let sum = 0;
    while true {
        let val = ch.recv();
        if val == nil {
            break;
        }
        sum = sum + val;
    }
    print("sum: " + sum);
}

Waiting for Tasks

Use a channel to synchronize task completion. The main task can wait until all spawned tasks signal they are done.

fn main() {
    let done = channel();

    let i = 0;
    while i < 4 {
        let id = i;
        spawn {
            print("task " + id + " starting");
            // simulate work
            let j = 0;
            while j < 1000000 {
                j = j + 1;
            }
            print("task " + id + " done");
            done.send(true);
        };
        i = i + 1;
    }

    // wait for all 4 tasks
    let count = 0;
    while count < 4 {
        done.recv();
        count = count + 1;
    }
    print("all tasks complete");
}

Spawn and Async Interop

The spawn block works alongside async functions. You can spawn a task that calls async functions internally.

async fn fetch_data(url: string) -&gt; string {
    // perform async network call
    return "response data";
}

fn main() {
    spawn {
        let data = await fetch_data("https://example.com");
        print("fetched: " + data);
    };
    print("main continues while fetch runs");
}