Skip to content

Repository files navigation

cl-concurrent-kit

CI License: MIT Documentation

A dependency-free, SBCL-only concurrency toolkit. It wraps sb-thread into the primitives a portability layer such as bordeaux-threads would offer (threads, locks, condition variables, semaphores), then builds the concurrency shapes familiar from modern languages on top: promises/futures with .then()-style combinators, CSP channels with select, a fixed-size executor with bounded queues and observability, structured-concurrency scopes with cooperative cancellation, a preemptive WITH-TIMEOUT for bounding an arbitrary body, countdown latches and cyclic barriers, and a reactive stream layer of CHANNEL-* operators (map/filter/merge/zip and the rest) built on top of channels.

Full documentation is published at https://nerima-lisp.github.io/cl-concurrent-kit/. The source for that site lives in docs/src/.

Quick Start

(asdf:load-system "cl-concurrent-kit")

(cl-concurrent-kit:with-task-scope (scope)
  (let ((f (cl-concurrent-kit:spawn scope (lambda () (+ 1 2)))))
    (cl-concurrent-kit:await f)))
;; => 3

;; PROMISE-THEN composes promises by continuation, never by blocking:
(cl-concurrent-kit:await
 (cl-concurrent-kit:promise-then (cl-concurrent-kit:future (+ 1 2))
                                  (lambda (value) (* value 10))))
;; => 30

Install

# flake.nix
inputs.cl-concurrent-kit = {
  url = "github:nerima-lisp/cl-concurrent-kit/v0.4.2";
  inputs.nixpkgs.follows = "nixpkgs";
};

Note the pinned tag. Consumers inside this org must pin a release tag rather than follow the default branch.

Documentation

Development

nix develop          # SBCL with CL_SOURCE_REGISTRY already set
nix run .#test       # run the test suite
nix build .#coverage # an sb-cover HTML report as the build's $out
nix run .#benchmark -- 1000000  # report each primitive's round-trip overhead
nix flake check      # tests + coverage + formatting + docs, the same gate CI uses
nix fmt              # format Nix sources (treefmt)

Outside Nix, sbcl --script run-coverage.lisp [output-dir] writes the same HTML report plus an lcov.info next to it, for tooling that reads LCOV directly; nix flake check's coverage-lcov check runs the same script and additionally fails the build if any source file falls under this project's own coverage bar, rather than only rendering a number someone has to remember to look at.

sb-cover instruments code as it runs, so a file's own IN-PACKAGE form, its DEFSTRUCT slot-default initializers, and any DEFMACRO's body all run once at compile/macroexpansion time -- before sb-cover starts recording -- and show up as "not executed" no matter how many times their effect (a fully-covered DEFINE-CONDITION, a channel actually being locked) is exercised elsewhere. Chasing 100% on the expression column there would mean chasing the instrumentation, not the behavior; the branch column is the one worth holding at 100%.

Tests live in t/ and run under cl-weave, the org's test framework.

nix develop -c sbcl --script benchmarks/run-benchmarks.lisp reports each primitive's own round-trip overhead (channel send/recv, promise deliver/await, executor submit/await, scope spawn/await, ...) using cl-weave's benchmark, the same tool the org's other repositories use, so the numbers are directly comparable across them. Argument parsing is cl-cli: -- --only channel runs only the benchmarks whose name contains channel, and a trailing number (-- 0.1) scales every benchmark's own iteration count by that factor, for a quick pass instead of the full suite.

Contributing

See the org-wide CONTRIBUTING guide and the package standard.

Support

See SUPPORT.

License

MIT. See LICENSE.

About

Dependency-free, SBCL-only concurrency toolkit built directly on sb-thread

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages