v1.2.2 — 2026-06-07
Low-level synchronisation primitives: mutex, condition variable, and counting semaphore, built directly over POSIX pthreads. No external library required.
macOS note. macOS does not implement unnamed POSIX semaphores (
sem_initalways fails). The semaphore type in this module is therefore implemented with apthread_mutex_t+pthread_cond_t+ counter — identical semantics tosem_t, but portable across Linux and macOS.
Consider higher-level models first. Curry provides actors (
spawn/send!/receive), CSP channels (make-channel/channel-send!/channel-recv!), and Software Transactional Memory (atomically/tvar-read/tvar-write!). Use this module when you specifically need explicit mutex/condvar/semaphore control — e.g. to interoperate with C libraries or to build higher-level synchronisation structures. See the concurrency reference for guidance.
(import (curry sync))Create and initialise a new mutex.
Destroy the mutex and free its memory. Do not use the handle after this call.
Acquire the mutex, blocking until it is available.
Release the mutex.
Try to acquire the mutex without blocking. Returns #t on success, #f if the mutex is already locked.
Returns #t if x is a mutex handle.
Lock mutex, call thunk with no arguments, then unlock. Returns the thunk's result. The unlock is guaranteed even if thunk raises an exception — the exception is re-raised after the mutex is released, so the lock is never left held.
(define mx (make-mutex))
(with-mutex mx (lambda () (display "critical section") (newline)))
(mutex-destroy! mx)
; Exception safety: mx is unlocked even if thunk errors
(guard (e (#t (display "caught\n")))
(with-mutex mx (lambda () (error "boom"))))Create and initialise a new condition variable.
Destroy the condvar and free its memory.
Atomically release mutex and block until cv is signalled. Re-acquires mutex before returning.
Like cond-wait! but with a timeout of seconds (real number). Returns #t if signalled, #f if timed out.
Wake one thread waiting on cv.
Wake all threads waiting on cv.
Returns #t if x is a condvar handle.
(import (curry sync))
(define mx (make-mutex))
(define ready (make-condvar))
(define item #f)
(define producer
(spawn (lambda ()
(let loop ((n 0))
(with-mutex mx
(lambda ()
(set! item n)
(cond-signal! ready)))
(loop (+ n 1))))))
(define consumer
(spawn (lambda ()
(let loop ()
(mutex-lock! mx)
(cond-wait! ready mx)
(display item) (newline)
(mutex-unlock! mx)
(loop)))))Create a counting semaphore with the given initial value (default 0).
Destroy the semaphore and free its memory.
Decrement the semaphore, blocking if the count is zero.
Increment the semaphore, waking a waiting thread if any.
Try to decrement the semaphore without blocking. Returns #t on success, #f if the count is zero.
Return the current count of the semaphore.
Returns #t if x is a semaphore handle.
Handles are plain pair/bytevector values with no GC finalizer. The underlying pthread resource is heap-allocated with malloc. Call the corresponding *-destroy! procedure before releasing a handle to avoid resource leaks.
with-mutex is exception-safe: the mutex is always unlocked, even if the thunk raises. For condition variable and semaphore usage in potentially-raising code, wrap cleanup in dynamic-wind or guard manually.