lib/pool/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/threads.x:101
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/threads.x:34
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/threads.x:113
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/threads.x:132
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 table 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. Table insertion
causes propagate and leave the object unregistered.
Source: lib/pool/interning.x:131
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/threads.x:93
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/threads.x:78
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/storage.x:534
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/interning.x:234
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/interning.x:225
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, table, 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/interning.x:191
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/storage.x:167
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/threads.x:44
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. Table insertion causes propagate
and leave the object unregistered.
Source: lib/pool/interning.x:153
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 table 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. Table lookup and
insertion causes propagate.
Source: lib/pool/interning.x:92
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/threads.x:124
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/interning.x:43
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/storage.x:83
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.
A thread that loses a concurrent promotion of an equal value still gets
one, though Pool.is_permanent answers zero for its surviving copy.
Raises: <alloc-fail> when promotion metadata cannot be allocated.
Source: lib/pool/storage.x:390
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/interning.x:169
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. When the
parent already holds an equal identity, that one stays canonical and
object only survives. 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/storage.x:373
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/threads.x:63
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/threads.x:53
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/threads.x:20
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. |
PoolTable | struct | Maps each canonical value of one Pool level to itself. |
Pool
typedef struct Pool { Scope scope, PoolTable 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/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 at SIZE_MAX.
Source: lib/pool/pool.x:47
PoolTable
typedef struct PoolTable { Scope scope, Bytes hashes, entries; unsigned used, capacity, mask; } *PoolTable
Maps each canonical value of one Pool level to itself.
A bucket stores the value once, beside its 32-bit hash, so the table costs
12 bytes per bucket where a Map costs 20. It belongs to the level’s
Scope.
Source: lib/pool/pool.x:28
Design notes
Pool owns canonical identity for a stack of nested levels. Lookup walks
outward and a miss lands in the innermost level; release returns empty
blocks to one process-wide depot and moves promoted survivors to the
parent at the same address. Levels have no sibling branches, so the
active chain holds one canonical pointer per equal value. String and
List intern into one such stack per thread.
Tests and examples
make verify (unittest/test-pool.x).