|
| 1 | +# GC2 table-token full pass and HugeTab admission close |
| 2 | + |
| 3 | +Date: 2026-07-18 |
| 4 | + |
| 5 | +This note records a b1.2.1 implementation tranche. It does not change `plan/` |
| 6 | +and does not declare the live table-rescan cutover complete. The new full-pass |
| 7 | +entry points are deliberately test-only while the production activation/close |
| 8 | +gate is still being designed and validated. |
| 9 | + |
| 10 | +## Problem closed by this tranche |
| 11 | + |
| 12 | +The earlier exact table descriptor and embedded table tokens made one request |
| 13 | +helpable, but they did not prove that a bounded scanner had visited the complete |
| 14 | +physical table universe without a concurrent identity appearing, disappearing, |
| 15 | +or moving. A cursor wrap and the sticky requested-generation maximum are both |
| 16 | +insufficient certificates: |
| 17 | + |
| 18 | +- the requested maximum is only a wake/diagnostic hint and may be raised after |
| 19 | + the token has already become PENDING; |
| 20 | +- the small-arena directory and every stable-TG HugeTab are separate lanes; |
| 21 | +- a publisher can pause at any point around a membership or token LP; and |
| 22 | +- a counted HugeTab reader can complete a `1 -> 2 -> 1` ABA while a remover is |
| 23 | + preparing to consume its apparent sole reader. |
| 24 | + |
| 25 | +This tranche supplies the lower mechanism needed to close those gaps without a |
| 26 | +peer wait. |
| 27 | + |
| 28 | +## Exact topology authority |
| 29 | + |
| 30 | +`LJGC2TableTopology` is an aligned 128-bit authority containing a nonzero |
| 31 | +completed-membership-change epoch and an OPEN/PINNED state. Every bound table |
| 32 | +publishes a post-success change after an insertion, tombstone, transfer, stable |
| 33 | +TG-head/body change, or terminal removal that changes the enumerated universe. |
| 34 | +The epoch never wraps: saturation moves the authority into absorbing PINNED, |
| 35 | +which permanently forbids manufacturing reclamation authority. |
| 36 | + |
| 37 | +The authority is initialized before the small registry and main HugeTab become |
| 38 | +visible. Main, secondary-TG, and registry HugeTabs bind to the same global |
| 39 | +authority before their first membership publication. A transfer involving a |
| 40 | +bound table is accepted only when both source and destination have exactly the |
| 41 | +same authority; bound/unbound and differently bound moves fail closed. |
| 42 | + |
| 43 | +The topology epoch is intentionally not an in-flight writer count. A publisher |
| 44 | +may pause after its membership LP and before the epoch bump. Safety across that |
| 45 | +window is mechanical: |
| 46 | + |
| 47 | +1. a traversable insertion is admitted only with embedded token NONE and a |
| 48 | + clear table descriptor; |
| 49 | +2. a removal or transfer cannot pass while the token is PENDING or the exact |
| 50 | + descriptor is ACTIVE; and |
| 51 | +3. a token publication racing a pass changes the exact descriptor generation, |
| 52 | + which is part of the pass authority. |
| 53 | + |
| 54 | +This avoids an anonymous outstanding-owner counter that a paused thread could |
| 55 | +strand indefinitely. |
| 56 | + |
| 57 | +## Non-ABA HugeTab admission close |
| 58 | + |
| 59 | +The all-ones HugeTab reader encoding (`0xffff`) is now reserved as |
| 60 | +ADMISSION_CLOSED. Ordinary reader admission saturates at `0xfffe`. A destructive |
| 61 | +operation first acquires its one internal certificate, then replaces exactly |
| 62 | +that sole count with CLOSED in one full-slot CAS. All later body-reader, |
| 63 | +mark-reader, sweep, rescue, token, and certificate admissions reject CLOSED. |
| 64 | +Only after the close succeeds may the destructive owner revalidate token, |
| 65 | +descriptor, recovery, root, and deferred-free state and attempt its tombstone. |
| 66 | + |
| 67 | +This closes the counted-reader ABA. If another publisher entered and left before |
| 68 | +the close CAS, its durable PENDING token or still-ACTIVE exact descriptor makes |
| 69 | +the post-close validation fail. No publisher can enter after CLOSED is visible. |
| 70 | +On any veto, reopening clears only the reserved reader encoding, preserves all |
| 71 | +concurrent flags, folds a deferred free when eligible, and emits the normal |
| 72 | +progress wake. |
| 73 | + |
| 74 | +Two additional review findings are part of the protocol: |
| 75 | + |
| 76 | +- A mark cannot return MARK_INTENT behind CLOSED because a delete/transfer |
| 77 | + owner is not a traversal owner. Reader-mark admission instead atomically |
| 78 | + reopens and marks the mapping, returns MARK_SATURATED without a body lease, |
| 79 | + and forces the stale destructive CAS to lose. Metadata-only mark operations |
| 80 | + likewise reopen and durably publish MARK before returning success. |
| 81 | +- Transfer rechecks DEFER_FREE after the close and before destination insert. |
| 82 | + If a concurrent external-free claim added DEFER while the internal lease was |
| 83 | + held, transfer reopens the source and lets the normal fold/wake path retain |
| 84 | + sole free ownership. It never copies an orphaned DEFER entry into the |
| 85 | + destination. |
| 86 | + |
| 87 | +## Bounded two-lane pass |
| 88 | + |
| 89 | +The dormant full-pass helper captures all of the following before scanning: |
| 90 | + |
| 91 | +- the exact topology epoch; |
| 92 | +- an IDLE table-descriptor generation and empty table pointer; |
| 93 | +- the full activation tuple (mark epoch, generation, gate, and state); |
| 94 | +- legacy phase and cycle; |
| 95 | +- a zero incomplete-registry count; and |
| 96 | +- the stable TG registry head used by the Huge lane. |
| 97 | + |
| 98 | +It then walks the global small-arena directory followed by the captured stable |
| 99 | +TG/HugeTab spine. A call consumes no more physical identities than its supplied |
| 100 | +budget, so the pass can resume without waiting for a peer. Ad-hoc diagnostic |
| 101 | +scans cannot share or disturb a live retained pass cursor. Transient snapshots, |
| 102 | +structural inconsistencies, or unavailable SMR admission make the pass restart; |
| 103 | +malformed/saturated authorities pin it fail closed. |
| 104 | + |
| 105 | +After the final Huge identity, the helper performs a complete double |
| 106 | +revalidation of topology, descriptor, activation, phase, cycle, and registry |
| 107 | +completeness. Only a clean result publishes the paired multiword acknowledgement. |
| 108 | +The current acknowledgement is serialized by the existing bounded |
| 109 | +`worker_active` try-claim. It is observational test authority only: it may become |
| 110 | +stale immediately after its validation LP and is not yet used to close a live |
| 111 | +phase or reclaim memory. |
| 112 | + |
| 113 | +## Deterministic evidence |
| 114 | + |
| 115 | +Focused tests cover: |
| 116 | + |
| 117 | +- exact topology increment under contention and absorbing saturation; |
| 118 | +- bound insertion rejection with a preseeded PENDING token; |
| 119 | +- same-authority transfer success and different/bound-unbound rejection; |
| 120 | +- the pre-close reader `1 -> 2 -> 1` ABA with descriptor transfer both before |
| 121 | + and after token publication; |
| 122 | +- a mark racing a successfully installed CLOSED encoding; |
| 123 | +- a deferred external-free claim racing transfer close; |
| 124 | +- incomplete-registry veto and bounded one-identity progress; |
| 125 | +- descriptor races before transfer and after PENDING transfer but before IDLE; |
| 126 | +- PENDING completion and acknowledgement in one clean pass; |
| 127 | +- irrelevance of a saturated sticky requested hint; and |
| 128 | +- phase/activation mutation invalidating an acknowledgement. |
| 129 | + |
| 130 | +The tranche is validated with strict standalone helper builds, repeated focused |
| 131 | +runs, assertion/paranoia configurations, ASan/UBSan, Valgrind, the normal GC2 |
| 132 | +and JIT smoke, and the broader milestone matrix before publication. Platform CI |
| 133 | +remains the release evidence for Linux, macOS, and Windows. |
| 134 | + |
| 135 | +The broad matrix also exposed an older GCC interprocedural object-size false |
| 136 | +positive at the public sweep-boundary function: GCC retained a hypothetical |
| 137 | +null `global_State` path through an out-of-line predicate and diagnosed later |
| 138 | +inlined atomics as zero-sized accesses. A direct null guard now documents that |
| 139 | +boundary and makes the clean `XCFLAGS=-Werror` matrix pass without suppressing |
| 140 | +the warning class. |
| 141 | + |
| 142 | +## Deliberate production boundary and next work |
| 143 | + |
| 144 | +The legacy `table_rescan_pending` counter remains the live conservative |
| 145 | +authority. This tranche does not enable the pass in production and does not |
| 146 | +restore or retain an old-GC fallback. The next coherent cutover must add an |
| 147 | +activation/publisher protocol equivalent to OPEN -> CLOSING -> COMMIT, with |
| 148 | +bounded publisher retry/help and final authority revalidation. Only then can |
| 149 | +phase-close predicates, traversal completion, and token publication migrate as |
| 150 | +one unit from the legacy count. |
| 151 | + |
| 152 | +Before live reclaim authority, the following remain mandatory: |
| 153 | + |
| 154 | +- prove every publisher's ordering against the production close gate; |
| 155 | +- remove any direct legacy token refresh outside the exact descriptor namespace; |
| 156 | +- make production acknowledgement publication/read exact without a blocking |
| 157 | + ownership dependency; |
| 158 | +- either retain and mechanically prove transfer's current source-wrapper |
| 159 | + quiescence contract, or add owner/version discrimination before permitting |
| 160 | + fully arbitrary concurrent transfer: CLOSED itself has no owner generation, |
| 161 | + so a mark may reopen it and a different destructive owner may later recreate |
| 162 | + the same encoding; |
| 163 | +- define bounded fairness between ordinary scanner work and the retained pass; |
| 164 | +- repair or permanently pin on incomplete TG-registry publication; |
| 165 | +- keep every dirty/cycle/generation authority nonwrapping and fail closed; and |
| 166 | +- rerun delayed-helper, same-address reuse, unmap, terminal free, saturation, |
| 167 | + phase-close, sanitizer, and platform stress matrices on the live path. |
| 168 | + |
| 169 | +Physical minor collection remains gated, arbitrary custom `lua_Alloc` remains |
| 170 | +the documented temporary internal-allocator-only exclusion, and the requested |
| 171 | +Lua `atomic(4)` library is still sequenced after the core GC/JIT/FFI cutovers. |
0 commit comments