Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

lib/pool.x

Nested interning pools with region-backed object storage.

Primary API

FunctionSummary
Pool.closeCloses the innermost open bracket and makes its parent active.
Pool.currentReturns the borrowed canonical-value pool active on this thread.
Pool.detachRemoves the innermost open bracket without destroying it.
Pool.epochReturns a counter that changes whenever any pool level is destroyed.
Pool.intern_newReturns the canonical value equal to object, installing it in inner.
Pool.openOpens a child of the shared String and List canonical-value pool.
Pool.open_namedOpens and returns a named child of the pool active on this thread.
Pool.statsReturns this level’s canonical counts plus process-wide storage counters.

Pool

Pool.close

void Pool.close(void)

Closes the innermost open bracket and makes its parent active. Canonical Strings, Lists, and transient String buffers owned by that pool are reclaimed unless promoted; ancestor-owned values remain live.

Raises: <bad-state> when no bracket is open. The failure leaves the active pool unchanged.

Source: lib/pool.x:620

Pool.current

Pool Pool.current(void)

Returns the borrowed canonical-value pool active on this thread. The process root is installed lazily on first use, so the result is never NULL. The pool is borrowed: do not release it with Pool.release.

Source: lib/pool.x:550

Pool.detach

Pool Pool.detach(void)

Removes the innermost open bracket without destroying it. Thread keeps the detached pool sealed until join copies its survivors. The returned pool remains owned by the caller until that transfer or an explicit Pool.release.

Raises: <bad-state> when no bracket is open. The failure leaves the active pool unchanged.

Source: lib/pool.x:632

Pool.epoch

unsigned long Pool.epoch(void)

Returns a counter that changes whenever any pool level is destroyed. A cache that borrows canonical identities reads this before trusting an entry: a released level’s addresses can be reused, so identities admitted under an earlier value prove nothing. The counter only advances, so a reader needs no lock to tell that something was released.

Source: lib/pool.x:651

Pool.intern_new

Var Pool.intern_new(Pool inner, Var object, void *alloc)

Returns the canonical value equal to object, installing it in inner. This is Pool.intern for a caller that has already searched the whole chain from inner outward and found nothing, so only the innermost level is probed. The fused Map operation still decides the identity, which keeps one canonical pointer per equal value in inner even when another worker interns the same value first.

Raises: <bad-arg> when inner or alloc is NULL. Map insertion causes propagate and leave the object unregistered.

Source: lib/pool.x:720

Pool.open

Pool Pool.open(void)

Opens a child of the shared String and List canonical-value pool. New canonical misses enter the child, while equal ancestor values retain their existing owner. Promote anything that must outlive the bracket with String.promote or List.promote: unpromoted values are discarded and their identities no longer resolve. Brackets nest. The caller must match this with one Pool.close or detach it for transfer.

Raises: <alloc-fail> while opening the pool.

See: Pool.close, Pool.open_named, List.promote

Source: lib/pool.x:612

Pool.open_named

Pool Pool.open_named(const char *name)

Opens and returns a named child of the pool active on this thread. New canonical misses enter the child; an equal ancestor value keeps its existing owner and lifetime. The caller must match this with one Pool.close or detach it for transfer. The name appears in Scope diagnostics.

Raises: <alloc-fail> while opening the pool.

See: Pool.open, Pool.close

Source: lib/pool.x:597

Pool.stats

PoolStats Pool.stats(Pool inner)

Returns this level’s canonical counts plus process-wide storage counters. A null pool reports depth and per-level counts as zero. The counters are a snapshot; nothing in the result stays live with the pool.

Source: lib/pool.x:934

Advanced and interop API

FunctionSummary
Pool.releaseDestroys one pool and returns its parent, or returns NULL for NULL input.
Pool.retainReturns an unnamed child of inner, without making it thread-active.
Pool.retain_namedReturns a new named child of inner without making it thread-active.

Pool

Pool.release

Pool Pool.release(Pool inner)

Destroys one pool and returns its parent, or returns NULL for NULL input. Promoted slots and large allocations survive under the parent with stable addresses; other identities, the table, and the control Scope become invalid. Children must already be released, and no caller may use inner afterward. Use Pool.close instead to end a bracket opened with Pool.open.

Source: lib/pool.x:522

Pool.retain

Pool Pool.retain(Pool inner)

Returns an unnamed child of inner, without making it thread-active. Ownership, failures, and the comparison with Pool.open follow Pool.retain_named.

Source: lib/pool.x:513

Pool.retain_named

Pool Pool.retain_named(Pool inner, const char *name)

Returns a new named child of inner without making it thread-active. The child owns its control Scope, Map, and mutex and must be released before inner. Use Pool.open_named instead to open a bracket that the canonical String and List operations allocate into.

Raises: <alloc-fail> while building the child. A transfer destroys partial child resources and leaves inner unchanged; native mutex initialization failure aborts.

Source: lib/pool.x:462

Runtime-internal callables

These callables connect runtime translation units. They are documented for source readers but are not supported as user API.

FunctionSummary
Pool.freeReleases a losing or transient allocation from inner’s pool chain.
Pool.initializeCreates the process-wide canonical-value root and makes it active here.
Pool.insertInstalls object as its own canonical value in exactly inner.
Pool.internReturns the canonical value equal to object, installing it when absent.
Pool.is_permanentReports whether the process root owns value as its canonical identity.
Pool.lookupReturns the first value equal to key from inner outward, or void.
Pool.mallocAllocates object storage owned by inner.
Pool.ownProves object safe beyond every pool in inner’s chain.
Pool.ownsReports whether this exact level stores key as its canonical identity.
Pool.promotePublishes an identity owned by inner in its parent.
Pool.shutdownReleases this thread’s open brackets and then the process root.
Pool.thread_initializeInstalls the existing process root in a newly created worker thread.
Pool.thread_startMakes Pool lock from here on, for a process about to start a worker.

Pool

Pool.free

void Pool.free(Pool inner, void *alloc)

Releases a losing or transient allocation from inner’s pool chain. Region slots become immediately reusable; large allocations use Scope.free. A canonical allocation still present in a table must not be freed this way. A null allocation does nothing.

Raises: <bad-arg> when inner is NULL and alloc is not.

Source: lib/pool.x:823

Pool.initialize

void Pool.initialize(void)

Creates the process-wide canonical-value root and makes it active here. x2c_initialize calls this once; a repeat call does nothing.

Raises: <alloc-fail> if the root cannot be constructed. Native mutex initialization failure aborts.

Source: lib/pool.x:560

Pool.insert

void Pool.insert(Pool inner, Var object)

Installs object as its own canonical value in exactly inner. The caller must have ruled out an equal identity in this pool chain; this primitive neither searches ancestors nor takes ownership of separate object storage.

Raises: <bad-arg> when inner is NULL. Map insertion causes propagate and leave the object unregistered.

Source: lib/pool.x:693

Pool.intern

Var Pool.intern(Pool inner, Var object, void *alloc)

Returns the canonical value equal to object, installing it when absent. A miss lands in inner. alloc is the object’s Pool-owned allocation; a hit releases that losing candidate immediately. A failed insertion leaves the candidate owned by the caller so its existing cleanup boundary runs.

Ancestors are checked before the innermost fused Map operation. Pool’s single-canonical-pointer invariant makes that order equivalent to outward shadowing while allowing the innermost table to be probed exactly once. A caller that has already searched the chain uses Pool.intern_new.

Raises: <bad-arg> when inner or alloc is NULL. Map lookup and insertion causes propagate.

Source: lib/pool.x:749

Pool.is_permanent

int Pool.is_permanent(Var value)

Reports whether the process root owns value as its canonical identity. Nothing is promoted, and no open bracket can reclaim a value that answers one.

Source: lib/pool.x:643

Pool.lookup

Var Pool.lookup(Pool inner, Var key)

Returns the first value equal to key from inner outward, or void. The returned identity remains owned by the level where it was found.

Source: lib/pool.x:657

Pool.malloc

void *Pool.malloc(Pool inner, size_t size)

Allocates object storage owned by inner. Requests through 512 bytes use a size-class region; larger requests retain ordinary Scope ownership.

Raises: <bad-arg> when inner is NULL, <size-limit> when a large request overflows Scope storage, or <alloc-fail> when storage cannot be allocated.

Source: lib/pool.x:776

Pool.own

int Pool.own(Pool inner, Var object, void *alloc)

Proves object safe beyond every pool in inner’s chain. It is promoted to the outermost pool when a pool in the chain owns it. alloc is the object’s Pool-owned allocation. Returns one when the value is already outermost or reaches it, and zero when no pool in the chain owns it or a promotion fails. A null inner reports safe, since no pool can then reclaim the value. Promotion is not transactional across levels: an earlier level remains promoted if a later promotion transfers.

Raises: <alloc-fail> when promotion metadata cannot be allocated.

Source: lib/pool.x:915

Pool.owns

int Pool.owns(Pool pool, Var key)

Reports whether this exact level stores key as its canonical identity. Ancestors are not searched, and a null pool reports zero.

Source: lib/pool.x:847

Pool.promote

int Pool.promote(Pool inner, Var object, void *alloc)

Publishes an identity owned by inner in its parent. alloc survives inner’s release without changing its address. Returns zero for a missing owner, root pool, or null allocation.

Raises: <alloc-fail>, <size-limit>, or <invariant> while recording the promotion; that transfer may happen before storage is marked or moved.

Source: lib/pool.x:900

Pool.shutdown

void Pool.shutdown(void)

Releases this thread’s open brackets and then the process root. Call it only after every worker has stopped and no canonical value is still in use. A repeat call after the root is gone does nothing.

Source: lib/pool.x:582

Pool.thread_initialize

void Pool.thread_initialize(void)

Installs the existing process root in a newly created worker thread. The root must already exist; otherwise the process aborts.

Source: lib/pool.x:569

Pool.thread_start

void Pool.thread_start(void)

Makes Pool lock from here on, for a process about to start a worker. Thread.start calls this before pthread_create. It never clears.

Source: lib/pool.x:149

Public types

TypeKindSummary
PoolstructHolds one level of canonical values and their backing storage.
PoolStatsstructReports one pool level’s activity and process-wide storage counters.

Pool

typedef struct Pool { Scope scope, Map table, struct Pool *up, pthread_mutex_t mutex; unsigned child_capacity; size_t interned, promoted, void *blocks, *current[10], *promotions; } *Pool

Holds one level of canonical values and their backing storage. Pools are released from child to parent. Their handles and unpromoted allocations become invalid at release; promoted identities retain their pointers under the parent.

Source: lib/pool.x:38

PoolStats

typedef struct PoolStats { int depth, size_t interned, promoted, allocation_calls, free_calls; size_t requested_bytes, block_allocations, block_reuses, slot_reuses; size_t backing_bytes, active_blocks, active_bytes, depot_blocks, depot_bytes; } PoolStats

Reports one pool level’s activity and process-wide storage counters. The snapshot owns no storage; requested_bytes saturates rather than wraps.

Source: lib/pool.x:48

Design notes

A Pool pairs a control Scope and canonical Map with size-class regions for small objects. Regions reuse losing intern candidates and lease backing blocks from one process-wide depot; large objects retain Scope ownership.

Lookup walks outward. An ancestor hit retains that ancestor’s ownership; only a new identity lands in the requested pool. Release returns empty blocks to the depot and transfers promoted survivors to the parent without changing object pointers. Pools form a stack rather than a tree, preserving one canonical pointer per equal value across the active chain.

String and List allocate into one such stack per thread. Pool.open and Pool.close bracket a level of it, so temporary canonical values are reclaimed together; Pool.current names the level in force. Context is the aggregate that pairs such a bracket with a Scope and with Error and Match state.

Tests and examples

make verify (unittest/test-pool.x).