62 / Concurrency

Semaphore

A Semaphore from std/sync limits the number of concurrent accesses to a resource. sem_new(n) creates a semaphore with n permits, sem_acquire takes a permit (blocking if none available), and sem_release returns one.

62-semaphore.nxSource →
// Semaphore — limiting concurrent access to a resource
// Semáforo — limitar acceso concurrente a un recurso

import "std/sync"

// Stateful handles (Semaphore/WaitGroup/mutex_new()) are SAFE as global
// initializers — they run single-threaded before main (since v0.24.27).
// Never lazy-init them behind a READY flag: that's a data race (see LLM.md).
// ES: los handles con estado son SEGUROS como inicializadores globales —
// corren single-threaded antes de main. Nunca lazy-init detrás de un flag.
var sem: Semaphore = sem_new(2)
var done: WaitGroup = wg_new()

fn access_resource() -> int {
    sem_acquire(sem)
    print("acquired (count: " + int_to_string(sem_count(sem)) + ")")
    sleep(50)
    sem_release(sem)
    print("released")
    wg_done(done)
    return 0
}

fn main() -> int {
    // Allow at most 2 concurrent accesses
    wg_add(done, 5)

    var i: int = 0
    while i < 5 {
        thread_spawn(access_resource)
        i = i + 1
    }

    wg_wait(done)
    print("all done")
    return 0
}
Illustrative outputstdout
acquired (count: 1)
acquired (count: 0)
released
released
acquired (count: 1)
acquired (count: 0)
released
released
acquired (count: 1)
released
all done

How it works

sem_new(2) creates a semaphore with 2 permits. At most 2 threads can hold a permit simultaneously. When a third thread calls sem_acquire, it blocks until one of the first two calls sem_release.

Five threads compete for the semaphore. sem_count returns the number of remaining permits, which drops to 0 when both are taken. After sleeping 50 milliseconds (simulating work), each thread releases its permit and signals the WaitGroup.

This pattern is useful for rate-limiting access to external resources like database connections, file descriptors, or API endpoints where you want controlled concurrency rather than full mutual exclusion.