Concurrency & System I/O
Lightweight green tasks via spawn, typed channel message passing, mutual exclusion locks, filesystem I/O, and process execution.
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.
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")
}()
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.
// 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 / Property | Signature | Return | Description & Behavior |
|---|---|---|---|
ch.send | ch.send(val, [timeout_ms]) | NULL | Sends value into channel. If timeout_ms is specified (> 0), aborts with a runtime error if not received within the timeout window. |
ch.try_send | ch.try_send(val) | BOOL | Non-blocking attempt to send. Returns true if delivered immediately (into buffer or waiting task), or false if full/unready without blocking. |
ch.recv | ch.recv([timeout_ms]) | ANY | Receives value from channel. If timeout_ms is specified (> 0), returns null if the timeout expires. (Alias: ch.receive). |
ch.try_recv | ch.try_recv() | ANY | Non-blocking attempt to receive. Returns the received object immediately if available, or null without blocking if channel is empty. |
ch.close | ch.close() | NULL | Closes channel. Equivalent to close(ch). Safe against double closing. |
ch.capacity | ch.capacity | INT | Property returning the buffer capacity integer (0 for unbuffered channels). (Alias: ch.cap). |
// 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()
Mutexes & WaitGroups
For shared mutable state, BramandScript includes native mutex() locks and wait_group() coordination barriers.
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
| Function | Exact Signature | Return | Description |
|---|---|---|---|
spawn | spawn fn() { ... }() | NULL | Grammar keyword: launches function in lightweight concurrent Goroutine. |
chan | chan([capacity]) | CHANNEL | Creates unbuffered (0) or buffered channel for typed message passing. |
close | close(channel) | NULL | Closes channel, notifying receivers of stream termination. |
mutex | mutex() | MUTEX | Creates mutual exclusion lock with .lock() and .unlock(). |
wait_group | wait_group() | WAIT_GROUP | Coordination barrier with .add(n), .done(), and .wait(). |
sleep | sleep(milliseconds) | NULL | Suspends execution of the current thread/task for specified milliseconds. |
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:
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 / Method | Signature | Return | Description |
|---|---|---|---|
new | new([capacity = 1]) | HASH | Creates a counting semaphore with specified capacity (default 1). |
acquire | sem.acquire() | BOOLEAN | Blocks until a permit is available, decrements count, and returns true. |
try_acquire | sem.try_acquire() | BOOLEAN | Attempts non-blocking acquisition. Returns true if acquired, false if full. |
release | sem.release() | BOOLEAN | Releases permit back to semaphore, waking a waiting task. |
available | sem.available() | INTEGER | Returns number of currently available permits. |
with_permit | sem.with_permit(action) | ANY | Executes 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:
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 / Method | Signature | Return | Description |
|---|---|---|---|
new | new([num_workers = 4]) | HASH | Creates a concurrent worker pool with fixed number of worker tasks. |
map | pool.map(items, worker_fn) | ARRAY | Concurrently applies worker_fn(item) across items in parallel; preserves exact input order. |
execute_all | pool.execute_all(jobs_list) | ARRAY | Concurrently executes array of parameterless task functions [fn() { ... }, ...]. |
workers | pool.workers() | INTEGER | Returns configured worker count. |
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.
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
| Method | Signature | Return Type | Description |
|---|---|---|---|
send | act.send(payload) | BOOLEAN | Pushes a message asynchronously to the actor's mailbox; non-blocking. |
ask | act.ask(payload, [timeout_ms = 5000]) | OBJECT | Sends message synchronously and waits for response or timeout error. |
stop | act.stop() | NULL | Signals the actor loop to cleanly terminate and transitions status to "stopped". |
status | act.status() | STRING | Returns current execution status: "running", "stopped", or "failed". |
state | act.state() | OBJECT | Reads current internal private state snapshot under read lock. |
is_alive | act.is_alive() | BOOLEAN | Returns true if the actor status is currently "running". |
restart | act.restart([new_state]) | NULL | Clears error status and restarts actor processing loop with optional new state. |
name | act.name() | STRING | Returns the registered human-readable name, or empty string. |
id | act.id() | STRING | Returns unique process identifier string (e.g. "act_1"). |
last_error | act.last_error() | STRING | NULL | Returns error message or panic cause if status is "failed". |
mailbox_len | act.mailbox_len() | INTEGER | Returns 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:
| Function | Signature | Return Type | Description |
|---|---|---|---|
actor_register | actor_register(name, act) | BOOLEAN | Binds an actor instance to a unique string name in the global registry. |
actor_whereis | actor_whereis(name) | ACTOR | NULL | Resolves registered actor by name across any file or module. |
actor_unregister | actor_unregister(name) | BOOLEAN | Removes 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.
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()
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:
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:
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.
| Method | Signature | Return Type | Description |
|---|---|---|---|
spawn | tg.spawn(callable, ...args) | FUTURE | Schedules a callable function to execute concurrently within this task group. |
wait_all | tg.wait_all() | RESULT | Awaits completion of all tasks in parallel; returns Ok(results_array) or Err(reason). |
wait_any | tg.wait_any() | RESULT | Returns Ok(first_value) from whichever task completes first; returns Err on failure. |
cancel | tg.cancel([reason]) | BOOLEAN | Broadcasts cancellation to all tasks in the group and aborts pending executions. |
is_cancelled | tg.is_cancelled() | BOOLEAN | Returns true if the group has been cancelled. |
count | tg.count() | INTEGER | Returns the number of tasks spawned in the group. |
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
File I/O & Process Execution
BramandScript provides complete system file I/O and process execution capabilities directly in standard library.
// 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
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).
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 / Method | Signature | Return | Description |
|---|---|---|---|
subscribe | s.subscribe(observer) | Subscription | Attaches observer with .request(n), .pause(), .resume(), and .cancel(). |
pull | s.pull(n) | ARRAY | Pulls exactly n items from the stream on demand. |
debounce | s.debounce(ms) | Stream | Suppresses events emitted within ms window until quiet period elapses. |
throttle | s.throttle(ms) | Stream | Ensures at least ms duration between consecutive emissions. |
window | s.window(size) | Stream | Batches incoming items into arrays of fixed size. |
sample | s.sample(ms) | Stream | Emits latest value observed across periodic ms timer interval. |
merge / zip / concat | s1.merge(s2) / .zip / .concat | Stream | Interleaves, pairs pairwise, or sequences multiple streams. |
switch_map | s.switch_map(fn) | Stream | Maps items to inner streams, canceling the active inner stream upon new arrival. |