carrier-composition Specification¶
Purpose¶
Define how Evictable composes two carriers into one exclusively owned storage hierarchy and moves values between promoted and evicted residency without changing their logical identity.
Terminology¶
| Term | Meaning |
|---|---|
| primary tier | The owned Carrier instance that holds the hierarchy's complete values while promoted and whose exact class performs normal value access and operation execution. |
| secondary tier | The distinct owned mutable Carrier instance used to receive and retain the hierarchy's complete values during eviction and whose exact class defines the evicted-storage and reverse-move boundary; while the hierarchy is promoted, this tier may be empty. |
| promoted | Residency state in which complete values are available through the primary tier. |
| evicted | Residency state in which complete values reside in the secondary tier and primary access is unavailable. |
| exclusive ownership | The relationship granting one composite privileged control over a child carrier's mutation, movement, release, and replacement while retained external aliases may read live storage but cannot perform those controlled actions. |
| transition | An evict() or promote() move that changes residency without changing logical values. |
Requirements¶
Requirement: Evictable construction validates a complete two-tier hierarchy¶
Evictable(primary, secondary) SHALL return a promoted hierarchy and take
exclusive ownership of both supplied tiers. primary names the live carrier
whose current values initialize the hierarchy and whose exact class performs
normal operation dispatch. secondary names a distinct live mutable carrier
that can receive evicted values.
Both arguments SHALL be Carrier instances, SHALL be distinct objects, SHALL be
unreleased and unowned, and SHALL have identical dtype descriptor identities.
The secondary SHALL be publicly mutable. The primary SHALL contain at least one
element. A non-Carrier SHALL fail with TypeError; identical tiers or an empty
primary SHALL fail with ValueError; a released, owned, or immutable required
tier SHALL fail with RuntimeError; mismatched dtypes SHALL fail with
TypeError.
Both directions between the exact tier classes SHALL have a registered move implementation. If either direction is unavailable, construction SHALL fail before returning a hierarchy.
Scenario: Construct a promoted hierarchy¶
- WHEN a live non-empty primary and a distinct live mutable secondary have the same dtype and support moves in both directions
- THEN construction returns a promoted Evictable that owns both tiers and reports the primary's size and dtype
Scenario: Reject mismatched tier dtypes¶
- WHEN the primary and secondary dtypes are not the same descriptor object
- THEN construction fails with
TypeErrorand neither tier becomes owned
Requirement: Construction failure leaves ownership unchanged¶
Evictable construction SHALL validate ordinary tier preconditions before claiming ownership. If claiming the secondary fails after the primary was claimed, or if per-instance capability finalization fails after both were claimed, construction SHALL relinquish every claim it made. No failed construction SHALL leave a supplied tier owned by an unreachable hierarchy.
Scenario: Fail while claiming the secondary¶
- WHEN primary ownership succeeds and secondary ownership cannot be completed
- THEN construction fails and the primary is returned to its previous unowned state
Scenario: Fail while freezing hierarchy capabilities¶
- WHEN the hierarchy's dependent capability generation fails
- THEN construction returns no Evictable and both supplied tiers are unowned afterwards
Requirement: The hierarchy exposes current tier identities¶
primary SHALL return the current primary-tier carrier and secondary SHALL
return the current secondary-tier carrier. These properties SHALL retain exact
carrier objects rather than copies. A transition MAY replace a tier with fresh
storage of the same exact class; after replacement, the property SHALL return
the replacement.
size() SHALL return the hierarchy's logical physical-slot count while live,
and dtype() SHALL return the dtype identity captured from the initial tiers.
is_evicted() SHALL return False after construction and promotion and True
after successful eviction.
Scenario: Inspect initial tiers¶
- WHEN an Evictable has just been constructed
- THEN
primaryandsecondaryare the supplied objects andis_evicted()isFalse
Requirement: Child ownership makes retained aliases externally read-only¶
While a tier is owned by an Evictable, tier.is_owned() SHALL return True.
A retained alias MAY read the tier while its storage remains live, but public
mutation, scatter, direct version increment, release, and direct move SHALL
fail with RuntimeError. An intrinsically mutable owned tier SHALL report
is_mutable() == False to external callers.
The Evictable owner SHALL have privileged access sufficient to mutate, move, release, or replace its children through the composite interface. That access SHALL not make the tier publicly mutable to another thread or retained alias. Ownership SHALL compose: an Evictable used as a tier of another hierarchy SHALL be protected by the same rules.
Scenario: Read but do not mutate an owned tier alias¶
- WHEN a caller retains a live primary alias after constructing an Evictable
- THEN the alias can read its values but direct write or release fails with
RuntimeError
Scenario: Mutate through the owner¶
- WHEN the outer Evictable is mutable and promoted
- THEN an indexed write through the Evictable succeeds even though the primary alias reports not mutable
Requirement: Composite mutability is captured from the primary¶
At construction, Evictable SHALL capture whether public mutation was permitted
by the primary. is_mutable() on the hierarchy SHALL report that captured
intrinsic policy subject to any ownership of the hierarchy itself. The
secondary's requirement to be mutable SHALL not make an immutable-primary
hierarchy mutable.
Promotion SHALL preserve the hierarchy's captured mutability. When an immutable hierarchy needs fresh primary storage, the transition MAY use mutable storage internally to complete the move, but public mutation through the restored hierarchy SHALL remain unavailable.
Scenario: Preserve an immutable hierarchy¶
- WHEN an Evictable was constructed from an immutable primary and later evicted and promoted
- THEN the hierarchy still reports
is_mutable() == Falseand rejects public writes
Requirement: Promoted state is required for public data access and execution¶
While promoted, get_value, indexed reads, permitted writes, scatter, and
operation dispatch SHALL act through the primary tier. While evicted, those
interfaces SHALL fail with RuntimeError directing the caller to promote().
is_evicted(), dtype(), structural storage support, structural operation
capabilities, promote(), and release SHALL remain available in the evicted
state.
Evictable SHALL not expose a DLPack buffer in either residency state; a DLPack
request SHALL fail with BufferError.
Scenario: Block access while evicted¶
- WHEN a hierarchy is evicted and a caller reads, writes, scatters, or dispatches an operation
- THEN the call fails with
RuntimeErrorrequiring promotion
Requirement: Eviction moves complete storage into the secondary tier¶
evict() SHALL return None. On a live promoted hierarchy, it SHALL move the
complete physical storage from primary to secondary through the registered
move for their exact classes, using lowered execution that creates no autograd
node.
If the current secondary is live and large enough, eviction SHALL use it as the destination. Otherwise it SHALL allocate a fresh mutable secondary of the hierarchy's size and dtype. After success, the hierarchy SHALL be evicted, the secondary property SHALL identify the destination containing all values, and the source primary storage SHALL be released by the move lifecycle. A replaced secondary SHALL be released and relinquished.
Calling evict() while already evicted SHALL be an idempotent no-op returning
None.
Scenario: Evict into existing secondary storage¶
- WHEN the live secondary has at least the hierarchy size
- THEN eviction moves all values into that tier and marks the hierarchy evicted
Scenario: Provision an undersized secondary¶
- WHEN the current secondary is too small
- THEN eviction uses a fresh same-class secondary of sufficient size and makes it the owned secondary after success
Requirement: Promotion restores fresh primary-class storage¶
promote() SHALL return None. On a live evicted hierarchy, it SHALL move the
complete physical storage from secondary to primary through the registered
reverse move, using lowered execution that creates no autograd node.
If the current primary remains live and large enough, promotion MAY use it as the destination. Otherwise it SHALL allocate fresh primary storage of the hierarchy's size, dtype, and internal transition mutability. After success, the hierarchy SHALL be promoted, the primary property SHALL identify the complete destination, and replaced primary storage SHALL be released and relinquished.
Calling promote() while already promoted SHALL be an idempotent no-op
returning None.
Scenario: Promote an evicted hierarchy¶
- WHEN a live hierarchy is evicted
- THEN promotion restores all values into storage of the original exact primary class and marks the hierarchy promoted
Requirement: Residency transitions are failure-atomic¶
Each transition SHALL resolve the applicable move implementation when that transition begins. If allocation, move dispatch, lowered execution, or result validation fails, the hierarchy SHALL preserve its previous residency state, current tier properties, logical values, ownership, size, dtype, mutability, and visible version. Fresh temporary destinations SHALL be released and relinquished.
A move result whose carrier is not the selected destination SHALL make the
transition fail with RuntimeError without committing the new state. A failed
transition SHALL remain retryable after its cause is corrected.
Scenario: Fail eviction without changing residency¶
- WHEN an eviction move raises after a temporary destination was allocated
- THEN the hierarchy remains promoted with its original tiers and values, the temporary destination is disposed, and a later eviction may retry
Scenario: Fail promotion without changing residency¶
- WHEN a promotion move raises
- THEN the hierarchy remains evicted with its secondary values intact and a later promotion may retry
Requirement: Transitions preserve logical version identity¶
Eviction and promotion SHALL be storage-only transitions and SHALL not advance
the Evictable carrier's visible version. A successful public logical write
or scatter through the wrapper SHALL advance the wrapper version as defined by
carrier-storage. Moving to a new tier object SHALL not reset or substitute
the wrapper's version authority.
Scenario: Round-trip residency without mutation¶
- WHEN a hierarchy evicts and promotes with no public value write
- THEN its values and visible version equal those before the round trip
Requirement: Evictable factories preserve the hierarchy pattern¶
The value and allocation factories defined by carrier-storage SHALL return
fresh promoted Evictable hierarchies using the receiver's exact primary and
secondary carrier kinds. The primary SHALL contain the materialized values or
requested allocation; the mutable secondary SHALL initially have size zero.
A dtype override SHALL have to be supported by both tier implementations.
Scenario: Allocate a fresh hierarchy¶
- WHEN
allocate_likesucceeds on an Evictable - THEN the result is promoted with requested primary storage and a lazy zero-size secondary of the matching dtype
Requirement: Evictable results retain composition ownership¶
An operation result or gradient restored through carrier-dispatch SHALL be
an Evictable that exclusively owns its new child storage. Retained aliases to
that result's child carriers SHALL therefore receive the same read-only
ownership protection as construction inputs. A layout-only result that reuses
the original primary SHALL reuse the original Evictable rather than attempting
to give the same child a second owner.
Scenario: Own an allocating result¶
- WHEN an Evictable operation allocates fresh primary result storage
- THEN the restored result hierarchy claims that storage and exposes no publicly mutable child alias
Requirement: Release ends the whole hierarchy lifecycle¶
release() on a live unowned Evictable SHALL release both child tiers through
owner access, mark the hierarchy released, and return None. It SHALL be
idempotent. After release, size() SHALL return zero, is_released() SHALL be
True, and eviction, promotion, value access, mutation, scatter, and dispatch
SHALL fail with RuntimeError.
When an unreleased Evictable is destroyed without explicit release, it SHALL relinquish ownership of each surviving child without releasing that child's storage, allowing retained tier aliases to become publicly usable according to their intrinsic mutability. A child already released by a residency transition SHALL remain released.
Scenario: Release both tiers¶
- WHEN a caller releases a live hierarchy
- THEN both current tiers are released and no later residency transition succeeds
Scenario: Destroy without release¶
- WHEN an unreleased hierarchy becomes unreachable while retained live tier aliases remain
- THEN those surviving tiers are no longer owned and their storage was not released merely by destruction of the wrapper