Home About Documentation Templates Examples Showcase GitHub
Theme

Scripting & runtime · concurrency

Threads, mutexes, async functions & futures.

Nift v4.5 runs real native work concurrently. Use explicit threads for dedicated work, async functions for pool-scheduled tasks, and mutexes or atomics only when workers intentionally share state.

Native threads

fn(add(a, b)) { return a + b }
t := thread(add, 20, 22)
print(t.join())
print(t.join()) // replayable: 42 again

thread(callable, ...args) accepts a named function or lambda and starts a native worker. join() waits and replays the same result on later calls; worker errors are rethrown at join. done() and status() inspect progress, and hardware_concurrency() reports the host hint. Thread handles own safe teardown: a runtime does not abandon work it created.

Ordinary values and captured closures are copied into an isolated worker context. That makes accidental shared mutation impossible; pass a mutex or atomic handle when sharing is intentional.

Mutex-protected shared state

fn(inc(m, count)) {
    i := 0
    while(i < count) {
        m.lock()
        m.set(m.get() + 1)
        m.unlock()
        i++
    }
    return true
}

counter := mutex(0)
a := thread(inc, counter, 500)
b := thread(inc, counter, 500)
a.join(); b.join()
counter.lock(); print(counter.get()); counter.unlock()

mutex() creates a lock-only handle; mutex(value) also owns a synchronized value. The API is lock(), try_lock(), unlock(), locked(), plus owner-only get()/set(). Locks are non-recursive. Reading the protected value without owning the lock, recursively locking it, or unlocking from another worker is an error.

Async functions and futures

fn[async](double(x)) { return x * 2 }
triple := async (x) => { return x * 3 }

f := double(21)
print(type(f))       // future
print(await f)       // 42
print(await triple(14))

In template syntax the named declaration is @fn[async](double(x)) { ... }. Calling an async function immediately returns a future. Prefix await accepts a future expression directly; a bare await f statement is also valid. Results are replayable, failures propagate at await, and done()/status() inspect state.

Async functions use a bounded native worker pool. A worker awaiting another future helps drain queued work, so nested awaits make progress instead of exhausting a fixed pool. Runtime teardown waits for work created by that runtime. Use thread(...) for an explicit native thread; use async functions for tasks scheduled onto the shared bounded pool.

Old async spellings are not public.

async(work, ...), await(f), and f.await() were superseded before release. Use async function/lambda declarations and prefix await.