Skip to content

Commit 2d4e6be

Browse files
committed
gc2: certify full table token passes
Add a saturating topology authority and bounded small/Huge physical pass certificate for the dormant exact table-token plane. Bind every GC2 HugeTab universe before publication and make destructive admission non-ABA with a reserved close encoding. Cover mark/delete, transfer/deferred-free, descriptor, phase, saturation, and full-pass races with deterministic tests. Keep the legacy pending counter authoritative until the production close-gate cutover.
1 parent a3f4cc9 commit 2d4e6be

12 files changed

Lines changed: 1818 additions & 187 deletions
Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
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

Comments
 (0)