Concurrency Overview

Planned — design subject to change

This feature is designed but not yet implemented. The syntax and behavior described here represent the target specification.

Model

Tape uses an actor model for concurrency:

  • Threads are declared as named worker pools
  • Communication is via typed messages (copy by default)
  • No shared mutable state between threads
  • No locks, no mutexes, no data races

Thread declaration

tape
thread ImageProcessor {
    @pool(2, 8);  // min 2, max 8 threads

    fn resize(img: Image, width: i64, height: i64) -> Image {
        // runs on a pool thread
        return gfx.resize(img, width, height);
    }

    fn blur(img: Image, radius: f64) -> Image {
        return gfx.blur(img, radius);
    }
}

Calling patterns

UI → Thread (non-blocking)

tape
// from UI thread — returns immediately, callback fires when done
ImageProcessor.resize(photo, 800, 600) -> on_resize_done;

fn on_resize_done(result: Image) {
    display.set_image(result);
}

Thread → Thread (blocking)

tape
// from another thread — blocks until result is ready
let processed = ImageProcessor.blur(img, 2.0);

Data passing

All data is copied when sent to a thread. For large data, use readonly to enable zero-copy:

tape
fn process(readonly data: []u8) -> []u8 {
    // data is frozen — cannot be mutated
    // zero-copy transfer (no clone)
}

No shared state

Each thread has its own isolated state. The only way to communicate is through function calls (messages). This eliminates data races by design.

Last modified: