Asynchronous Execution

Concurrency & System I/O

Lightweight green tasks via spawn, typed channel message passing, mutual exclusion locks, filesystem I/O, and process execution.

Concurrency

Lightweight Tasks with spawn

BramandScript provides native concurrency via the spawn keyword. Calling a function or closure with spawn dispatches execution into a lightweight Go goroutine with an isolated stack, safe argument passing, and independent call frames, all sharing a thread-safe globals store.

bramand — Concurrency with spawn
fn worker(id) {
    println("Worker", id, "is running in background")
}

// Spawn named function
spawn worker(1)

// Spawn anonymous closure
spawn fn() {
    println("Anonymous task completed")
}()
Message Passing

Channels & Synchronized Communication

Channels allow concurrent tasks to communicate safely and synchronize execution without explicit locking. Created using the chan([capacity]) builtin, channels support unbuffered and buffered message queues.

bramand — Channel send & receive
// 1. Unbuffered rendezvous channel
let ch = chan()

spawn fn() {
    ch <- 42 // Send value into channel
}()

let result = <-ch // Blocks until received
println("Received:", result)

// 2. Buffered channel & close()
let queue = chan(2)
queue <- "Alpha"
queue <- "Beta"
close(queue)

println(<-queue) // "Alpha"
println(<-queue) // "Beta"

Channel Methods & Non-Blocking Operations

In addition to the arrow operators (ch <- val and <-ch), BramandScript channels provide object-oriented methods supporting timeouts, non-blocking polling, buffer inspection, and lifecycle management:

Method / PropertySignatureReturnDescription & Behavior
ch.sendch.send(val, [timeout_ms])NULLSends value into channel. If timeout_ms is specified (> 0), aborts with a runtime error if not received within the timeout window.
ch.try_sendch.try_send(val)BOOLNon-blocking attempt to send. Returns true if delivered immediately (into buffer or waiting task), or false if full/unready without blocking.
ch.recvch.recv([timeout_ms])ANYReceives value from channel. If timeout_ms is specified (> 0), returns null if the timeout expires. (Alias: ch.receive).
ch.try_recvch.try_recv()ANYNon-blocking attempt to receive. Returns the received object immediately if available, or null without blocking if channel is empty.
ch.closech.close()NULLCloses channel. Equivalent to close(ch). Safe against double closing.
ch.capacitych.capacityINTProperty returning the buffer capacity integer (0 for unbuffered channels). (Alias: ch.cap).
bramand — Non-blocking polling & timeouts
// Allocate a buffered channel of capacity 2
let ch = chan(2)
println("Channel capacity:", ch.capacity) // 2

// 1. Non-blocking try_send
let s1 = ch.try_send("event_A") // true
let s2 = ch.try_send("event_B") // true
let s3 = ch.try_send("event_C") // false (buffer full, does not block)

// 2. Non-blocking try_recv
let item1 = ch.try_recv() // "event_A"

// 3. Receive with timeout (100ms)
let item2 = ch.recv(100) // "event_B"
let item3 = ch.recv(50)  // null (timeout elapsed on empty channel)

// 4. Send with timeout & close
ch.send("final", 200)
println("Drained:", ch.recv()) // "final"
ch.close()
Synchronization

Mutexes & WaitGroups

For shared mutable state, BramandScript includes native mutex() locks and wait_group() coordination barriers.

bramand — Thread safety with mutex & wait_group
let m = mutex()
let wg = wait_group()

let counter = 0
wg.add(2)

spawn fn() {
    m.lock()
    counter += 1
    m.unlock()
    wg.done()
}()

spawn fn() {
    m.lock()
    counter += 1
    m.unlock()
    wg.done()
}()

wg.wait()
println("Safe counter:", counter)

// sleep(ms): Pauses current thread of execution
println("Pausing for 50 milliseconds...")
sleep(50)
println("Resumed execution cleanly")

Concurrency & Synchronization Reference

FunctionExact SignatureReturnDescription
spawnspawn fn() { ... }()NULLGrammar keyword: launches function in lightweight concurrent Goroutine.
chanchan([capacity])CHANNELCreates unbuffered (0) or buffered channel for typed message passing.
closeclose(channel)NULLCloses channel, notifying receivers of stream termination.
mutexmutex()MUTEXCreates mutual exclusion lock with .lock() and .unlock().
wait_groupwait_group()WAIT_GROUPCoordination barrier with .add(n), .done(), and .wait().
sleepsleep(milliseconds)NULLSuspends execution of the current thread/task for specified milliseconds.
Enterprise Concurrency

Semaphores & Worker Pools (std/semaphore & std/pool)

For large-scale backend systems, microservices, and high-throughput data processing, BramandScript provides battle-tested concurrency primitives built directly on top of lightweight tasks and buffered channels:

1. Counting Semaphore (std/semaphore)

The counting semaphore bounds the number of concurrent operations accessing a constrained resource (e.g. database connections, rate-limited APIs, or disk writes). It provides non-blocking try_acquire() and exception-safe with_permit() wrappers using defer:

bmd — Counting Semaphore Rate Limiting
import "std/semaphore" as sem_mod

// Allow at most 3 concurrent operations
let sem = sem_mod.new(3)

// Acquire permit, execute task, and automatically release via defer
let result = sem.with_permit(fn() {
    println("Active permits:", sem.capacity() - sem.available())
    return "operation completed safely"
})
println("Result:", result)
println("All permits released. Available:", sem.available()) // 3
Function / MethodSignatureReturnDescription
newnew([capacity = 1])HASHCreates a counting semaphore with specified capacity (default 1).
acquiresem.acquire()BOOLEANBlocks until a permit is available, decrements count, and returns true.
try_acquiresem.try_acquire()BOOLEANAttempts non-blocking acquisition. Returns true if acquired, false if full.
releasesem.release()BOOLEANReleases permit back to semaphore, waking a waiting task.
availablesem.available()INTEGERReturns number of currently available permits.
with_permitsem.with_permit(action)ANYExecutes action() under an acquired permit with guaranteed deferred release.

2. High-Throughput Worker Pool (std/pool)

Worker pools distribute work across a fixed pool of lightweight background worker tasks, preventing resource exhaustion while guaranteeing that results from parallel map() preserve their exact input index ordering:

bmd — Order-Preserving Parallel Worker Pool
import "std/pool" as pool_mod

// Create pool with 4 concurrent background worker tasks
let pool = pool_mod.new(4)
let endpoints = [
    "https://api.internal/users",
    "https://api.internal/orders",
    "https://api.internal/billing",
    "https://api.internal/inventory"
]

// Parallel map runs across workers and returns array in identical input order:
let summaries = pool.map(endpoints, fn(url) {
    println("Worker processing:", url)
    return { "endpoint": url, "status": "processed" }
})

println("Summary count:", len(summaries))
println("First summary:", summaries[0]["endpoint"]) // Guaranteed to match endpoints[0]
Function / MethodSignatureReturnDescription
newnew([num_workers = 4])HASHCreates a concurrent worker pool with fixed number of worker tasks.
mappool.map(items, worker_fn)ARRAYConcurrently applies worker_fn(item) across items in parallel; preserves exact input order.
execute_allpool.execute_all(jobs_list)ARRAYConcurrently executes array of parameterless task functions [fn() { ... }, ...].
workerspool.workers()INTEGERReturns configured worker count.
Actor Model Fault Tolerance

Lightweight Actors & Supervision Trees (actor & std/actor)

BramandScript provides a first-class, Erlang/OTP-inspired Lightweight Actor primitive. Each actor encapsulates its own private mutable state, processes incoming messages sequentially from an asynchronous FIFO mailbox, and completely isolates execution errors and panics from calling threads.

1. Actor Architecture & Lifecycle

Actors run independently in lightweight VM goroutines. When a message is sent to an actor via act.send(msg), it is appended to the actor's non-blocking mailbox. If a synchronous reply is required, act.ask(msg, timeout_ms) delivers the message with a private one-shot channel and awaits the actor's response with dead-letter protection.

bramand — State-mutating actor with request-reply
import "std/actor" as act_mod

// Define an actor with private counter state
let counter = actor(fn(msg, state) {
    if msg["type"] == "inc" {
        return state + msg["value"]
    }
    if msg["type"] == "get" {
        return state
    }
    return state
}, 100, "metrics_counter")

// Asynchronous fire-and-forget message
counter.send({ "type": "inc", "value": 50 })

// Synchronous request-reply with timeout (in ms)
let current = counter.ask({ "type": "get" }, 1000)
println("Current counter:", current) // 150

// Inspect mailbox queue depth & state
println("Mailbox depth:", counter.mailbox_len())
println("Actor status:", counter.status()) // "running"
counter.stop()

2. Actor Instance Methods Reference

MethodSignatureReturn TypeDescription
sendact.send(payload)BOOLEANPushes a message asynchronously to the actor's mailbox; non-blocking.
askact.ask(payload, [timeout_ms = 5000])OBJECTSends message synchronously and waits for response or timeout error.
stopact.stop()NULLSignals the actor loop to cleanly terminate and transitions status to "stopped".
statusact.status()STRINGReturns current execution status: "running", "stopped", or "failed".
stateact.state()OBJECTReads current internal private state snapshot under read lock.
is_aliveact.is_alive()BOOLEANReturns true if the actor status is currently "running".
restartact.restart([new_state])NULLClears error status and restarts actor processing loop with optional new state.
nameact.name()STRINGReturns the registered human-readable name, or empty string.
idact.id()STRINGReturns unique process identifier string (e.g. "act_1").
last_erroract.last_error()STRING | NULLReturns error message or panic cause if status is "failed".
mailbox_lenact.mailbox_len()INTEGERReturns number of pending messages waiting in the FIFO mailbox buffer.

3. Global Process Registry

Named actors can register into a thread-safe global registry, enabling transparent inter-module process discovery:

FunctionSignatureReturn TypeDescription
actor_registeractor_register(name, act)BOOLEANBinds an actor instance to a unique string name in the global registry.
actor_whereisactor_whereis(name)ACTOR | NULLResolves registered actor by name across any file or module.
actor_unregisteractor_unregister(name)BOOLEANRemoves the name binding from the registry.

4. Fault-Tolerant Supervision Trees (std/actor/supervisor.bmd)

Supervisors continuously monitor worker actors and implement automatic failure recovery according to declarative restart strategies:

  • one_for_one: Only the crashed child actor is terminated and restarted. Other children continue uninterrupted.
  • one_for_all: If any child crashes, all other children supervised by the tree are restarted simultaneously.
  • rest_for_one: If a child crashes, that child and all children started after it in configuration order are restarted.
bramand — Fault-Tolerant Supervisor with Auto-Recovery
import "std/actor" as act_mod

let sup = act_mod.new_supervisor({
    "strategy": "one_for_one",
    "max_restarts": 5,
    "time_window": 10000,
    "children": [
        {
            "id": "database_worker",
            "start": fn() {
                return actor(fn(msg, state) {
                    if msg == "crash" {
                        panic("database connection dropped")
                    }
                    return state + 1
                }, 0)
            }
        }
    ]
})

// Send messages directly through the supervisor
sup.send("database_worker", "crash") // Triggers worker crash

// Background monitor automatically detects crash and restarts child
sleep(50)
let worker = sup.get_child("database_worker")
println("Worker recovered status:", worker.status()) // "running"

sup.stop()
Async & Structured Concurrency

Uncolored Async/Await & Structured Task Groups

BramandScript implements uncolored asynchronous coroutines. Unlike traditional languages where async functions virally mandate that all callers become async, BramandScript's await expression is completely uncolored: it can await Future objects, Channel receivers, or raw synchronous values directly with zero overhead.

1. Uncolored Coroutines (async fn & await)

An async fn executes asynchronously in a dedicated VM thread frame, returning an immediately resolvable Future:

bramand — Async functions and uncolored await
let fetch_user = async fn(id) {
    sleep(10)
    return { "id": id, "name": "Bramand Developer" }
}

// Coroutine call returns a Future
let future = fetch_user(101)
println("Is future done:", future.is_done()) // false

// Await unwraps the resolved value
let user = await future
println("User Name:", user["name"]) // "Bramand Developer"

// Uncolored: awaiting regular non-future values returns them instantly
let instant = await 42
println("Instant value:", instant) // 42

2. Cooperative Multitasking (yield)

Compute-heavy loops can voluntarily yield control to the scheduler using the yield keyword, triggering a cooperative execution switch:

bramand — Cooperative time-slicing
let compute_stream = fn(n) {
    let i = 0
    while (i < n) {
        // Voluntarily yield execution time slice to other concurrent fibers
        yield i
        i = i + 1
    }
}

3. Structured Task Groups (task_group() & std/concurrency)

Structured concurrency guarantees that concurrent subtasks never leak beyond their lexical scope. A task_group() coordinates parallel tasks with collective awaiting (wait_all, wait_any) and automatic cascade cancellation: if any spawned task panics or fails, all sibling tasks in the group are immediately cancelled.

MethodSignatureReturn TypeDescription
spawntg.spawn(callable, ...args)FUTURESchedules a callable function to execute concurrently within this task group.
wait_alltg.wait_all()RESULTAwaits completion of all tasks in parallel; returns Ok(results_array) or Err(reason).
wait_anytg.wait_any()RESULTReturns Ok(first_value) from whichever task completes first; returns Err on failure.
canceltg.cancel([reason])BOOLEANBroadcasts cancellation to all tasks in the group and aborts pending executions.
is_cancelledtg.is_cancelled()BOOLEANReturns true if the group has been cancelled.
counttg.count()INTEGERReturns the number of tasks spawned in the group.
bramand — Task group parallel execution & cascade cancellation
import "std/concurrency" as conc

let tg = conc.task_group()

// Spawn multiple concurrent branches
tg.spawn(fn() {
    sleep(20)
    return "profile_data"
})

tg.spawn(fn() {
    sleep(15)
    return "orders_data"
})

// Parallel wait-all: executes both tasks in parallel
let res = tg.wait_all()
if res.is_ok() {
    let data = res.unwrap()
    println("Aggregated:", data[0], data[1])
}

// Cascade Cancellation Demonstration
let faulty_tg = conc.task_group()
faulty_tg.spawn(fn() {
    panic("database query timeout")
})
faulty_tg.spawn(fn() {
    sleep(500)
    return "never_reached"
})

let fail_res = faulty_tg.wait_all()
println("Failure caught:", fail_res.is_err()) // true
System I/O

File I/O & Process Execution

BramandScript provides complete system file I/O and process execution capabilities directly in standard library.

bramand — File I/O & subprocess execution
// File Operations
write_file("data.txt", "First line\n")
append_file("data.txt", "Second line\n")
let content = read_file("data.txt")
println(content)
delete_file("data.txt")

// Process execution via exec()
let res = exec("git", ["status", "-s"])
println("Git code:", res.code)
println("Git output:", res.stdout)

// Environment Variables
set_env("APP_ENV", "production")
println("Environment:", env("APP_ENV"))

// Terminal Raw Mode & Real-Time Keyboard Input
term_raw(true) // Disable line buffering & echo
let key = read_key(100) // Non-blocking wait up to 100ms
if key == "up" || key == "w" {
    println("Steered Up!")
}
term_raw(false) // Restore terminal mode
Day 32 Standard Reactive Pipelines

Native Reactive Event Streams with Backpressure (std/stream)

BramandScript provides native push/pull reactive event streams with backpressure demand signaling, high-frequency time-windowed operators (debounce, throttle, window, sample), and stream multiplexing combinators (merge, zip, concat, switch_map).

bmd — Reactive Stream Pipeline & Demand Signaling
import "std/stream" as stream;

// 1. Pipeline Transformation: Map, Filter, Window
let s = stream.from_array([1, 2, 3, 4, 5, 6, 7, 8])
    .filter(fn(x) { return x % 2 == 0; })
    .map(fn(x) { return x * 10; })
    .window(2);

let batches = s.collect();
println("Batches:", batches); // [[20, 40], [60, 80]]

// 2. Controlled Backpressure Demand Pull
let source = stream.from_array([100, 200, 300, 400]);
let sub = source.subscribe({
    "next": fn(val) { println("Received:", val); },
    "complete": fn() { println("Stream complete."); }
});

// Explicit demand pull: request 2 items
sub.request(2);
sub.pause();
println("Subscription active:", sub.is_active());
sub.resume();

// 3. Stream Combinators (Zip & Merge)
let s1 = stream.from_array(["A", "B", "C"]);
let s2 = stream.from_array([1, 2, 3]);
let paired = s1.zip(s2).collect();
println("Zipped pairs:", paired); // [["A", 1], ["B", 2], ["C", 3]]
Operator / MethodSignatureReturnDescription
subscribes.subscribe(observer)SubscriptionAttaches observer with .request(n), .pause(), .resume(), and .cancel().
pulls.pull(n)ARRAYPulls exactly n items from the stream on demand.
debounces.debounce(ms)StreamSuppresses events emitted within ms window until quiet period elapses.
throttles.throttle(ms)StreamEnsures at least ms duration between consecutive emissions.
windows.window(size)StreamBatches incoming items into arrays of fixed size.
samples.sample(ms)StreamEmits latest value observed across periodic ms timer interval.
merge / zip / concats1.merge(s2) / .zip / .concatStreamInterleaves, pairs pairwise, or sequences multiple streams.
switch_maps.switch_map(fn)StreamMaps items to inner streams, canceling the active inner stream upon new arrival.