Skip to content

API Reference

Everything below is exported from the single package cl-cc/runtime. Names are grouped by subsystem, in roughly the order a reader meets them: heap and collector first, then the value codec and frames, then the concurrency primitives, then the observability and platform layers.

Coverage

cl-cc/runtime exports roughly 1480 symbols. This page documents the entry points of each subsystem -- the names you call to get in, plus the accessors and constants needed to use them. Struct accessors, internal-facing hooks and the per-feature tuning variables are not listed individually; the complete export list is the :export clause of src/package.lisp, which is sectioned with the same headings used here.

Filling in the remaining symbols is tracked as follow-up work. Anything documented on this page is API; anything not on this page may still be exported but has not been reviewed for stability.

Heap

make-rt-heap

(make-rt-heap &key young-size old-size). Allocates a fresh managed heap. Sizes are in words. young-size is split evenly into two semi-spaces. With neither argument supplied the sizes are auto-configured from *gc-young-size-words* and *gc-old-size-words*.

rt-heap-ref

Reads one word at an address.

rt-heap-set

Writes one word at an address. Use rt-gc-write-barrier instead when the word being written lives in old space and holds a young-space reference.

rt-heap-object-header

Returns the header word of the object at an address.

rt-heap-set-header

Writes an object's header word. Called by the allocating code immediately after rt-gc-alloc, which leaves the header unwritten on purpose.

make-rt-header

(make-rt-header size type-tag &key gc-bits). Packs size, type tag and GC bits into the one-word compressed header format.

rt-header-size

Extracts the object size in words from a header.

rt-header-type-tag

Extracts the type tag from a header.

rt-header-age

Extracts the survival count used by the promotion policy.

rt-young-addr-p

True when an address falls inside the young generation.

rt-old-addr-p

True when an address falls inside old space.

*gc-young-size-words*

Default young generation size, in words, used when make-rt-heap is called without explicit sizes.

*gc-old-size-words*

Default old generation size, in words.

*gc-tenuring-threshold*

Number of minor collections an object must survive before it is promoted.

+gc-card-size-words+

Card size, in words, for the card table that records old-to-young writes.

Garbage collector

rt-gc-alloc

(rt-gc-alloc heap type-tag size-words). Bump-allocates in young from-space and returns the word address. Triggers a minor collection when from-space is exhausted, and signals an error if the heap is still full afterwards. Does not write the object header.

rt-gc-add-root

(rt-gc-add-root heap root-cell). Registers a cons cell as a root. The collector treats the cell's cdr as the reference and rewrites it in place when the object moves.

rt-gc-remove-root

Removes a previously registered root cell. A root that is never removed keeps its object alive for the lifetime of the heap.

rt-gc-write-barrier

Records a write of a young-space reference into an old-space object, so the next minor collection scans that card.

rt-gc-minor-collect

Runs a minor collection: copies live young objects into to-space, promotes objects past the tenuring threshold, and updates every registered root.

rt-gc-major-collect

Runs a major (whole-heap) collection, including old space.

rt-gc-stats

Returns a plist of counters for a heap: :minor-gc-count, :major-gc-count, :words-collected, :words-promoted, :young-used, :young-total, :old-used, :old-total, :heap-occupancy-pct and :free-list-count.

rt-gc-configure-concurrent-mode

Selects the concurrent-marking configuration: which phases stop the world, the write-barrier mode, and whether mutators assist.

rt-gc-concurrent-assist

Performs a slice of concurrent marking work on the calling thread.

rt-gc-concurrent-sweep

Sweeps old space concurrently with the mutator.

*concurrent-gc-enabled*

Master switch for concurrent collection.

*compacting-gc-enabled*

Enables compaction of old space after a major collection.

Value representation

encode-fixnum

Encodes an integer into the NaN-boxed representation.

decode-fixnum

Recovers the integer from a NaN-boxed fixnum.

encode-double

Encodes a double-float. Doubles are stored as themselves, so this is the identity on the bit pattern.

decode-double

Recovers the double-float.

encode-pointer

Encodes a heap address together with a pointer tag.

decode-pointer

Recovers the heap address from a boxed pointer.

pointer-tag

Returns the pointer tag of a boxed pointer.

encode-char

Encodes a character.

decode-char

Recovers the character.

encode-bool

Encodes a generalised boolean as +val-t+ or +val-nil+.

val-fixnum-p

True when a value is a boxed fixnum. The val-double-p, val-pointer-p, val-nil-p, val-t-p, val-char-p, val-unbound-p, val-object-p, val-cons-p, val-symbol-p, val-function-p and val-string-p predicates follow the same pattern.

+val-nil+

The singleton value for nil. +val-t+ and +val-unbound+ are the other two singletons.

+fixnum-tag+

Tag bits marking a boxed fixnum. +fixnum-mask+ and +fixnum-shift+ complete the fixnum encoding, and +tag-mask+, +addr-mask+, +ptr-base+ and +ptr-mask+ do the same for pointers.

rt-native-integer->value

Boxes a Lisp integer, promoting to the native bignum representation when it does not fit in a fixnum.

rt-native-bignum-add

Adds two native bignums. rt-native-bignum-sub and rt-native-bignum-mul are the other two operations, and rt-native-bignum-to-integer converts back.

Register frames

vm-frame

The call-frame struct: a fixed-size register array plus the stack pointer, program counter, closure and parent-frame slots. vm-frame-registers, vm-frame-sp, vm-frame-pc, vm-frame-closure and vm-frame-return-frame are the accessors, and vm-frame-p is the predicate.

frame-pool-acquire

Takes a frame off the frame pool rather than allocating one. Frames are pooled because a call-heavy program needs one per call and they all have the same shape.

frame-pool-release

Returns a frame to the pool.

initialize-frame-pool

Fills *frame-pool* with +frame-pool-size+ frames.

frame-reg-get

Reads a register from a frame.

frame-reg-set

Writes a register in a frame.

+frame-register-count+

Number of registers in a frame. The +frame-arg-start+, +frame-caller-save-start+, +frame-callee-save-start+, +frame-spill-start+ and +frame-return-reg+ constants divide that register file into the regions the calling convention assigns.

rt-alloc-call-frame

Allocates a call frame on the managed heap rather than from the pool, for frames the collector has to see. rt-free-call-frame releases one.

Runtime primitives

The rt-* functions mirror the Common Lisp operators the compiler emits calls to. They are named after their CL counterparts, take and return runtime values rather than Lisp objects, and are grouped in src/package.lisp under Cons/list, Arrays/vectors, Arithmetic, Bitwise, Comparisons, Math, Strings, Characters, Symbols, Hash tables, CLOS and I/O.

rt-cons

Allocates a cons. rt-car, rt-cdr, rt-rplaca and rt-rplacd are the accessors, and the rest of the list operators (rt-append, rt-reverse, rt-member, rt-nth, rt-assoc, ...) follow the CL names.

rt-make-array

Allocates an array. rt-aref, rt-aset, rt-array-length, rt-array-rank and rt-array-dimensions are the accessors.

rt-typep

Runtime type test. rt-type-of returns the runtime type, and the rt-consp, rt-symbolp, rt-stringp, rt-numberp family are the individual predicates.

rt-make-hash-table

Allocates a hash table; make-hash-table is shadowed in this package for that reason. rt-gethash, rt-sethash, rt-remhash, rt-maphash and rt-hash-count are the operations, and rt-hash-table-weakness reports whether a table holds its keys or values weakly.

rt-boundp

True when a symbol has a global value. rt-fboundp and rt-makunbound are the other two.

Conditions and restarts

The runtime implements its own condition system rather than reusing the host's, because the source language's handler and restart semantics need not match Common Lisp's. See Conditions for the condition types this library itself signals.

rt-signal

Signals a runtime condition through the runtime handler stack.

rt-signal-error

Signals a runtime error.

rt-cerror

Signals a continuable error.

rt-establish-handler

Pushes a handler onto *handler-stack* for the dynamic extent of a call.

rt-establish-restart

Pushes a restart onto *restart-stack*.

rt-find-restart

Looks a restart up by name.

rt-invoke-restart

Transfers control to a restart.

rt-restart-case

Runtime counterpart of restart-case. rt-restart-bind is the counterpart of restart-bind.

Synchronisation

rt-make-mutex

Creates a mutex. rt-with-mutex is the scoped form and the preferred way to use one: it releases the mutex on every exit path, including a non-local one, and -- with :timeout -- runs its body at all only if the lock was actually acquired in time. rt-mutex-lock, rt-mutex-try-lock and rt-mutex-unlock are the manual operations rt-with-mutex is built on, and rt-make-recursive-mutex plus rt-with-recursive-mutex give the reentrant variant.

rt-with-remaining-timeout

(rt-with-remaining-timeout (remaining-fn timeout) &body body). Binds remaining-fn to a function returning the seconds left before timeout elapses, recomputed on every call (or nil, unbounded, when timeout is nil). For writing a retry loop whose wait call needs a shrinking duration each iteration rather than the original timeout re-armed every time -- the shape rt-mutex-lock, rt-semaphore-wait, rt-barrier-wait, rt-rwlock-read-lock/-write-lock, rt-channel-send/-recv and rt-future-await all use internally.

rt-make-rwlock

Creates a reader-writer lock. rt-with-read-lock and rt-with-write-lock are the scoped forms; rt-rwlock-try-read-lock and rt-rwlock-try-write-lock are the non-blocking ones.

rt-make-semaphore

Creates a counting semaphore. rt-semaphore-wait, rt-semaphore-try-wait and rt-semaphore-signal operate on it.

rt-make-barrier

Creates a barrier for a fixed number of participants. rt-barrier-wait blocks until all arrive; rt-barrier-reset reuses it.

rt-make-condition-variable

Creates a condition variable. rt-condition-wait blocks until notified (or :timeout elapses); rt-condition-notify and rt-condition-notify-all wake one or every waiter. rt-condition-wait-until wraps rt-condition-wait in a loop against a predicate, to tolerate spurious wakeups.

rt-make-once

Creates a once-only guard; rt-once-call runs its thunk at most once.

Scheduler and green threads

rt-scheduler-init

Installs a fresh global scheduler, which rt-spawn and rt-scheduler-run then operate on implicitly. Takes no arguments.

rt-make-scheduler

Creates a scheduler value without installing it globally.

rt-spawn

(rt-spawn thunk &key priority). Queues a thunk as a green thread on the global scheduler and returns the thread. priority is :high, :normal or :low, and the scheduler drains the higher queues first.

rt-scheduler-run

Runs queued green threads. With :once it runs exactly one ready task and returns it.

rt-yield

Puts the current green thread back on the ready queue.

rt-sleep-task

Suspends the current green thread until a wall-clock deadline.

rt-current-thread-id

Returns the current green thread's id, or nil outside one.

rt-make-work-stealing-scheduler

Creates a work-stealing scheduler over several workers. rt-work-stealing-submit queues work and rt-work-stealing-run drains it.

Channels, actors, futures

rt-make-channel

(rt-make-channel &key capacity). Creates a CSP channel. Capacity 0 is a rendezvous channel.

rt-channel-send

(rt-channel-send channel value &key timeout). Sends, blocking when the channel is full.

rt-channel-recv

Receives from a channel.

rt-channel-close

Closes a channel.

rt-make-actor

Creates an actor with a mailbox. rt-actor-send posts a message and rt-actor-receive takes the next one; both take :timeout. With :mailbox-limit, rt-actor-send blocks for room rather than growing the mailbox without bound once it holds that many messages; the default nil keeps the mailbox unbounded.

rt-make-future

Creates an unresolved future. rt-future-resolve fulfils it, rt-future-await blocks for the value, rt-future-done-p tests it and rt-future-then chains a continuation.

Software transactional memory

rt-make-tvar

Creates a transactional variable holding an initial value.

rt-read-tvar

Reads a transactional variable, recording the read in the current transaction.

rt-write-tvar

Writes a transactional variable.

rt-atomically

Runs a body as a transaction, retrying on conflict.

(let ((v (rt-make-tvar 0)))
  (rt-atomically (rt-write-tvar v (+ 1 (rt-read-tvar v))))
  (rt-read-tvar v))
;; => 1

rt-retry

Aborts the current transaction and retries it when a read variable changes.

Fibers and effects

rt-make-fiber

Creates a fiber. rt-fiber-spawn creates and schedules one in a single step.

rt-fiber-resume

Resumes a suspended fiber. rt-fiber-yield suspends the running one, and rt-fiber-block and rt-fiber-await are the blocking forms.

rt-run-fibers

Runs scheduled fibers to completion.

rt-fiber-local

Accesses fiber-local storage.

rt-with-handler

Installs handlers for algebraic effects over a body. rt-perform raises an effect, rt-handle dispatches it, and rt-resume continues the computation from the handler. rt-effect-state, rt-effect-error, rt-effect-read and rt-effect-write are the built-in effects.

Lock-free data structures

rt-make-lfstack

Creates a lock-free stack. rt-lfstack-push, rt-lfstack-pop and rt-lfstack-empty-p operate on it.

rt-make-lfqueue

Creates a lock-free queue. rt-lfqueue-push, rt-lfqueue-pop and rt-lfqueue-empty-p operate on it.

rt-make-lfhash-map

Creates a lock-free hash map. rt-lfhash-get, rt-lfhash-cas, rt-lfhash-remove and rt-lfhash-count operate on it.

rt-make-spsc-queue

Creates a single-producer single-consumer ring buffer, the cheapest of the queues when the access pattern allows it. rt-spsc-try-push and rt-spsc-try-pop are the non-blocking operations, rt-spsc-push and rt-spsc-pop the blocking ones.

Safe memory reclamation

Four schemes are provided because they trade reader cost against reclamation latency differently, and a compiler back end picks per workload.

rt-ebr-enter

Enters an epoch-based reclamation critical section. rt-ebr-leave leaves it, rt-with-ebr-critical scopes one to a body, rt-ebr-retire defers a free and rt-ebr-collect reclaims what has become safe.

rt-hp-protect

Publishes a hazard pointer protecting one object. rt-hp-clear drops it, rt-hp-retire defers a free and rt-hp-reclaim frees what is no longer protected. Threads must call rt-hp-register-thread first.

rt-rcu-read-lock

Enters an RCU read-side critical section. rt-with-rcu-read scopes one to a body, rt-rcu-synchronize waits for a grace period, and rt-rcu-assign-pointer and rt-rcu-dereference are the ordered accessors.

rt-qsbr-quiescent

Reports a quiescent state for the calling thread. rt-qsbr-retire defers a free and rt-qsbr-synchronize waits for every registered thread to pass through one.

Memory allocators

make-arena

Creates a bump-pointer arena. arena-alloc allocates from it, arena-reset frees everything at once, and with-arena scopes one to a body. An arena is the right allocator for a compiler pass whose whole working set dies together.

make-object-pool

Creates a fixed-size object pool. pool-acquire and pool-release are the operations.

rt-alloc

Size-class allocator entry point. rt-free returns memory to it and rt-size-class-for reports the class a size falls into.

Observability

rt-make-counter

(rt-make-counter name &key labels). Creates a monotonically increasing counter. rt-counter-increment! advances it.

rt-make-gauge

Creates a gauge. rt-gauge-set! sets its value.

rt-make-histogram

Creates a histogram. rt-histogram-observe! records a sample.

The trailing ! marks the three mutating operations, so a call that changes the registry is distinguishable from a call that only reads it.

rt-register-metric

Adds a metric to the default registry.

rt-metrics-format-prometheus

Renders the registry in the Prometheus text exposition format.

rt-perf-read-counter

Reads a hardware performance counter. rt-perf-enable-counter turns one on and rt-with-perf-counters scopes a measurement to a body. On a platform without counters these signal perf-counters-unsupported. rdtsc and rdtscp read the timestamp counter directly.

rt-otel-start-span

Starts an OpenTelemetry span. rt-otel-end-span closes it, and the exporter serialises spans with cl-json-kit.

rt-start-continuous-profile

Starts continuous profiling at a sample rate. rt-stop-continuous-profile stops it, rt-record-profile-sample records one sample by hand, and rt-export-continuous-profile, rt-continuous-profile-to-otel-json and rt-continuous-profile-to-pprof-json render the result.

rt-deadlock-detect

Runs the wait-for-graph deadlock detector over the current lock set.

Context propagation

rt-with-context

Binds a context over a body. Contexts carry a cancellation signal, a deadline and a value map, and are inherited by green threads created inside them.

rt-context-cancel

Cancels a context. rt-context-cancelled-p tests it.

rt-context-get-deadline

Returns the context deadline, if any.

rt-context-value

Reads a value from the context map. rt-with-context-value binds one.

rt-context-spawn

Spawns a green thread that inherits the current context, and also carries the calling thread's cl-log-kit structured-logging context and span id into it, via capture-log-context/with-captured-log-context -- the same propagation cl-log-kit documents for sb-thread:make-thread, applied here because rt-spawn's queued thunk runs from a different point on the call stack than the spawning call.

Platform and OS

rt-getenv

Reads an environment variable; rt-setenv and rt-unsetenv are the writers. The rest of the OS facade -- rt-run-program, rt-fork, rt-exec, rt-waitpid, rt-exit, rt-getcwd, rt-chdir, rt-sleep, rt-gettime-monotonic -- is in the OS abstraction section of src/package.lisp and sits on sb-posix and sb-ext.

rt-platform

Returns the host platform. rt-platform-darwin-p and rt-platform-linux-p are the two tests the rest of the tree branches on.

rt-set-signal-handler

Installs a handler for an OS signal, named by +rt-sigint+, +rt-sigterm+ and the rest of the +rt-sig*+ constants. rt-with-signal-handler scopes one to a body and rt-process-pending-signals drains the queue at a safe point. See Conditions for the condition types signals become.

mmap-file

Maps a file into memory, returning an rt-mmap-region. with-mmap scopes one to a body, mmap-sync flushes it, mmap-close unmaps it, and mmap-array maps a file as a typed array.

rt-socket

Creates a socket. rt-bind, rt-listen, rt-accept and rt-connect set up a connection, rt-socket-send and rt-socket-recv move bytes, and rt-set-nonblocking with rt-select or rt-epoll-wait drives an event loop.

rt-ffi-load-library

Loads a shared library. rt-define-foreign-function declares an entry point, rt-foreign-funcall calls one, and rt-define-foreign-struct with rt-ffi-struct-field-offset describes foreign layouts.

rt-pin-object

Keeps an object at a fixed address for the duration of a foreign call, so the collector cannot move it out from under C. rt-unpin-object releases it, rt-object-pinned-p tests it, and with-pinned-objects scopes a set of pins to a body.

detect-cpu-cores

Reports the CPU count. detect-numa-topology and memory-tier-info describe the memory hierarchy, and rt-thread-set-affinity pins a thread -- all of it feeding the work-stealing scheduler and NUMA-local GC.

Images

rt-capture-image-state

Captures the runtime state -- registered globals and the schema version -- into an image value.

rt-save-image

Writes an image to a file in the binary format. rt-load-image reads one back and detects corruption. rt-save-core and rt-load-core do the same for a full SBCL core.

rt-restore-image-state

Restores captured state into the running runtime, running every registered restore hook.

rt-image-register-global

Registers a global to be included in captures. rt-image-register-restore-hook adds a hook to run after a restore, and rt-hot-reload drives the reload path that uses both.