lib/pool.x
Nested interning pools with region-backed object storage.
Primary API
| Function | Summary |
|---|---|
Pool.close | Closes the innermost open bracket and makes its parent active. |
Pool.current | Returns the borrowed canonical-value pool active on this thread. |
Pool.detach | Removes the innermost open bracket without destroying it. |
Pool.epoch | Returns a counter that changes whenever any pool level is destroyed. |
Pool.intern_new | Returns the canonical value equal to object, installing it in inner. |
Pool.open | Opens a child of the shared String and List canonical-value pool. |
Pool.open_named | Opens and returns a named child of the pool active on this thread. |
Pool.stats | Returns 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
| Function | Summary |
|---|---|
Pool.release | Destroys one pool and returns its parent, or returns NULL for NULL input. |
Pool.retain | Returns an unnamed child of inner, without making it thread-active. |
Pool.retain_named | Returns 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.
| Function | Summary |
|---|---|
Pool.free | Releases a losing or transient allocation from inner’s pool chain. |
Pool.initialize | Creates the process-wide canonical-value root and makes it active here. |
Pool.insert | Installs object as its own canonical value in exactly inner. |
Pool.intern | Returns the canonical value equal to object, installing it when absent. |
Pool.is_permanent | Reports whether the process root owns value as its canonical identity. |
Pool.lookup | Returns the first value equal to key from inner outward, or void. |
Pool.malloc | Allocates object storage owned by inner. |
Pool.own | Proves object safe beyond every pool in inner’s chain. |
Pool.owns | Reports whether this exact level stores key as its canonical identity. |
Pool.promote | Publishes an identity owned by inner in its parent. |
Pool.shutdown | Releases this thread’s open brackets and then the process root. |
Pool.thread_initialize | Installs the existing process root in a newly created worker thread. |
Pool.thread_start | Makes 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
| Type | Kind | Summary |
|---|---|---|
Pool | struct | Holds one level of canonical values and their backing storage. |
PoolStats | struct | Reports 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).