cl-concurrent-kit¶
A dependency-free, SBCL-only concurrency toolkit.
Common Lisp has no standard concurrency library, and bordeaux-threads exists
to paper over the differences between implementations' native thread APIs.
This project takes the opposite bet, in line with nerima-lisp's coding
standard:
target SBCL only, wrap sb-thread directly, and spend the effort that
portability would have cost on a richer set of concurrency shapes instead.
Layers¶
cl-concurrent-kit is five layers, each built only on the ones below it:
- Primitives (
src/primitives.lisp) -- threads, locks, condition variables, semaphores, and atomic counters. The vocabulary a portability layer such as bordeaux-threads would offer, minus the portability. - Promise / future (
src/promise.lisp) -- a write-once result cell (PROMISE), a JS/Rust-styleFUTUREmacro that spawns a thread to settle one, andPROMISE-THENfor composing promises by continuation-passing instead of by blocking. - Channel (
src/channel.lisp, a preallocated ring buffer) -- a Go-style CSP channel, buffered or unbuffered (true rendezvous), plusSELECT(src/select.lisp) for waiting on several of them at once. - Executor (
src/executor.lisp) -- a fixed-size worker pool, Java'sExecutorService, built onPROMISEfor its results and its own preallocated ring buffer for its work queue. - Structured concurrency (
src/scope-state.lisp,src/scope.lisp) --WITH-TASK-SCOPE, a Kotlin/Swift/Python-trio-style nursery that guarantees every taskSPAWNed inside it has finished (or an optional:TIMEOUThas elapsed) before the scope returns, and that a failed task's condition always resurfaces.
Three further pieces build on those five rather than adding a strictly new layer of their own:
- Preemptive timeouts (
src/timeout.lisp) --WITH-TIMEOUT, the one deadline here that bounds an arbitrary body rather than a wait this library implements itself, by interrupting the running thread. The counterpart to structured concurrency's deliberately cooperative cancellation, not a replacement for it. - Countdown latches and barriers (
src/latch.lisp) --COUNTDOWN-LATCHandBARRIER, both able to take an optionalWITH-TASK-SCOPE:SCOPEso a blocked wait unblocks on cancellation the same wayAWAITdoes. - Reactive streams (
src/stream.lispand friends) -- Rx-styleCHANNEL-*operators (CHANNEL-MAP,CHANNEL-MERGE,CHANNEL-DEBOUNCE, and around thirty more) that compose channels into pipelines, each stage run viaSPAWNor an executor rather than a hand-written read/transform/write loop.
See Core concepts for how they fit together, Recipes
for worked examples, and Architecture for the
implementation decisions behind SELECT and the unbuffered channel
rendezvous.