starkernel: item 4.2 -- Hermes native on the Stadium (complete)
Migrates Hermes's message/channel lifecycle onto the Stadium's unified heat/capacity economy: MSG-ALLOC/FREE-NODE and CH-ALLOC/FREE-NODE now route entirely through stadium_admit()/stadium_evict(), replacing the old local free-list + independent heat-field mechanism. Eight kernel-only STADIUM-* FORTH primitives (ADMIT, EVICT, RES@, RES-PULL, RES-PUSH, HEAT@, HEAT!, WORD-HEAT), VM.stadium_vm_id threaded through all three vm_core.c dispatch sites (replacing item 4.1's hardcoded vm_uuid_hera()), and the stadium_owner[idx] fix so evict-credit lands in the VM that actually admitted a patron, not whoever owned cell 0. This session's own contribution, on top of that pre-existing implementation: found and fixed two bugs blocking the item's own K≡1.0 conservation self-check (HERMES-K was reading 0, not 65536): - Q.SLOT admission-heat fix (capsules/hermes/init.4th): MSG-SEND/ CH-ACCEPT admitted with Q.1 (the entire fleet-wide "1.0" unit) per item, a leftover from before the Stadium migration when each message/channel had its own unconstrained heat field. Instantly drained the shared, finite reservoir. - Reservoir floor for word-execution admission (stadium_words.c): stadium_word_dispatch() (item 4.1) pulls STADIUM_WORD_HEAT_QUANTUM on every word dispatch, not just first admission -- exhausts a VM's entire reservoir in ~32 dispatches, starving any application-level economy sharing that VM's reservoir before it gets a chance to pull anything. word_dispatch_pull() now clamps word-execution's own pulls to leave a Q48_ONE/3 floor (same fair-share figure COMMON-CH's own floor already uses); application-level pulls are unaffected. - STADIUM-WORD-HEAT primitive + stadium_words_resident_heat(): the floor deliberately leaves word-execution residents holding real heat, invisible to HERMES-K's original formula (MSG+CH+reservoir, no term for word patrons). Adding this term closes K to exactly 65536 on all three architectures. Also rules on two open scope questions in FABRIC.md: MBR-ALLOC/ MBR-FREE-NODE stay off the Stadium (membership records have no heat field, never did -- the acceptance bullet's inclusion of them was a completeness gesture predating a check of the actual layout), and records the effort number (12 implementation files, +759/-120 lines). Verified: all three architectures boot clean, full self-test passes, Stadium conservation closes exactly (resident_sum + reservoir = Q48_ONE) at both the C/Stadium level and the FORTH-level HERMES-K check. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
0a7f144367
commit
5a28458b21
@@ -3344,9 +3344,10 @@ document and committing that amendment as its own item.*
|
||||
> item never touches reservoir on the donor side), `test-vm reservoir=65536` (a fresh
|
||||
> `Q48_ONE`, as ruled). `dict_hash` identical across all three and unchanged from item 4.1's
|
||||
> baseline (`0x3d4e1daf289da94f`), confirming this item added no dictionary word.
|
||||
- [ ] **4.2 — Hermes native on the Stadium.** The proving ground; produces the effort
|
||||
- [x] **4.2 — Hermes native on the Stadium.** The proving ground; produces the effort
|
||||
number. *Refs:* §10. **Unblocked 2026-08-05** — item 4.1a closed; `stadium_grant_quota()`
|
||||
exists and is wired into every baby VM's birth.
|
||||
exists and is wired into every baby VM's birth. **Complete 2026-08-07** — all `Done when`
|
||||
bullets satisfied; see the effort number and MBR-scoping ruling below.
|
||||
|
||||
> **Two rulings taken before work starts, 2026-08-05:**
|
||||
> 1. **`stadium_owner[idx]` fix folded into this item's scope**, by explicit Captain Bob
|
||||
@@ -3492,8 +3493,83 @@ document and committing that amendment as its own item.*
|
||||
> the active log threshold and never appears in the serial log, a separate dead end closed
|
||||
> along the way) is the only viable method for this kernel today.
|
||||
>
|
||||
> **Blocker found 2026-08-06, ruled and fixed 2026-08-07 — item 4.1 and item 4.2 silently
|
||||
> share one finite per-VM reservoir, and word-execution admission alone can exhaust it
|
||||
> before any application-level allocation runs. This failed the K≡1.0 `Done when` bullet
|
||||
> below and was not a code bug to just patch — it was a design question spanning both
|
||||
> items, reported for a ruling rather than resolved unilaterally (§25.0 rule 3). Captain
|
||||
> Bob ruled option 4 below (reserve a floor); implementation and result are at the end of
|
||||
> this note.**
|
||||
>
|
||||
> After the `Q.SLOT` admission-heat fix (below) closed the original `MSG-SEND`/`CH-ACCEPT`
|
||||
> over-admission bug, `HERMES-K` still read `0` instead of `65536`. Three prints in one
|
||||
> boot discriminated the cause: `stadium_reservoir_peek(Hermes)` reads `65536` immediately
|
||||
> after `BIRTH` (the one-time grant, item 4.1a, is fine) but is already `0` — and
|
||||
> `COMMON-CH`'s own heat is already `0` — immediately after `CD-INIT` finishes, before
|
||||
> `HERMES-MSG-TEST`/`MSG-DELIVER-ALL`/anything else in the self-test runs. So this is not a
|
||||
> Stadium cell getting silently reassigned out from under `COMMON-CH` after the fact
|
||||
> (aliasing); `COMMON-INIT`'s own `CH-ALLOC` call, partway through `CD-INIT`, never got
|
||||
> funded in the first place.
|
||||
>
|
||||
> Root cause: `stadium_word_dispatch()` (item 4.1, `stadium_words.c:111`) pulls
|
||||
> `STADIUM_WORD_HEAT_QUANTUM` (2048) from the dispatching VM's reservoir on **every single
|
||||
> word dispatch**, not just the first time a word is admitted — the "already resident"
|
||||
> branch (line 142) does `h->heat += stadium_reservoir_pull(vm_id, STADIUM_WORD_HEAT_QUANTUM)`
|
||||
> unconditionally, every call. `CD-INIT`'s own `MSG-INIT-FREE`/`CH-INIT-FREE`/
|
||||
> `MBR-INIT-FREE` loops alone dispatch several hundred words (32 + 16 + 64 iterations, each
|
||||
> several words deep) before `COMMON-INIT` ever runs. At 2048 per dispatch, a VM's entire
|
||||
> 65536 reservoir is exhausted by roughly 32 total word dispatches — trivially reached
|
||||
> within `CD-INIT`'s first loop, let alone the rest of Hermes's boot. The boot log's own
|
||||
> `promotions=145` figure (Hermes's dict-check diagnostics) makes this arithmetic visible
|
||||
> directly: 145 × 2048 = 296,960, about 4.5× her entire conserved share, from
|
||||
> word-execution tracking alone. This applies to any VM doing non-trivial work, not
|
||||
> something specific to Hermes or to messages/channels — Hera's own reservoir has read `0`
|
||||
> in every log this entire session, for the same reason, just never surfaced as a problem
|
||||
> because nothing previously tried to spend Hera's reservoir on anything else.
|
||||
>
|
||||
> Options, no ranking, not decided here:
|
||||
> 1. **Separate reservoirs per VM** — one for word-execution tracking (item 4.1), one for
|
||||
> application-level use (item 4.2 and whatever comes after it). Most invasive: splits
|
||||
> `stadium_quotas[slot].reservoir` or the one-time grant itself, touches item 4.1's
|
||||
> already-shipped design and its recorded DoE baseline.
|
||||
> 2. **Exempt certain VMs from word-execution admission entirely** — e.g., only Hera (or
|
||||
> only VMs with no item-4.2-style application economy) get word-heat tracking. Requires
|
||||
> a new per-VM-class distinction that doesn't exist today.
|
||||
> 3. **Re-scope `STADIUM_WORD_HEAT_QUANTUM`** — smaller, or charged per-unique-word instead
|
||||
> of per-dispatch. Touches a Kconfig default that already feeds item 4.1's recorded DoE
|
||||
> measurements; re-tuning it here could invalidate that baseline.
|
||||
> 4. **Reserve a floor within the shared reservoir** that word-execution admission cannot
|
||||
> dip below, mirroring `COMMON-CH`'s own `Q.1/3` floor pattern but at the reservoir
|
||||
> level instead of a single resident. New mechanism, not yet designed.
|
||||
>
|
||||
> **Ruling, 2026-08-07: option 4.** Implemented as `word_dispatch_pull()`
|
||||
> (`stadium_words.c`), a static helper wrapping `stadium_reservoir_pull()` for
|
||||
> `stadium_word_dispatch()`'s two call sites only (both the already-resident re-heat pull
|
||||
> and the not-yet-resident starter-grant pull) — clamped so a pull never takes the
|
||||
> reservoir below `Q48_ONE / 3`, the same "VM-COUNT=3 fair share" figure `COMMON-CH`'s own
|
||||
> floor already uses, not a new invented number. Application-level pulls
|
||||
> (`stadium_reservoir_pull()` called directly, e.g. via `STADIUM-RES-PULL`) are untouched —
|
||||
> only word-execution admission respects the ceiling on its own consumption. Verified: the
|
||||
> eviction-credit demo now shows a real transfer (`resident_sum` −1612, `reservoir` +1612,
|
||||
> exactly, when `COMMON-CH` is evicted) instead of the prior `0`→`0` no-op, and the
|
||||
> Stadium's own conservation line closes exactly on every boot, every architecture:
|
||||
> `resident_sum=43691 reservoir=21845 sum=65536`.
|
||||
>
|
||||
> This alone brought `HERMES-K` from `0` to `43002` — real, but not exact, because
|
||||
> `HERMES-K`'s formula (`MSG-TOTAL-HEAT CH-TOTAL-HEAT + STADIUM-RES@ +`) has no term for
|
||||
> word-execution residents' heat, which the floor now deliberately leaves nonzero. Second
|
||||
> ruling, same date: **add that term.** New accessor `stadium_words_resident_heat(vm_id)`
|
||||
> (`stadium_words.c`) sums heat over only a VM's own word-execution residents (walking its
|
||||
> `word_slots` map, not `stadium_resident_sum()`'s full ownership scan, which would double-
|
||||
> count messages/channels already in `MSG-TOTAL-HEAT`/`CH-TOTAL-HEAT`), exposed as an
|
||||
> eighth `STADIUM-*` primitive, `STADIUM-WORD-HEAT ( -- heat )`, same implicit-self
|
||||
> discipline as the other seven. `HERMES-K` becomes
|
||||
> `MSG-TOTAL-HEAT CH-TOTAL-HEAT + STADIUM-RES@ + STADIUM-WORD-HEAT + ;`. Confirmed on all
|
||||
> three architectures: `HERMES-K` prints exactly `65536`, K≡1.0, closing the item's
|
||||
> headline invariant.
|
||||
>
|
||||
> *Done when:*
|
||||
> - The seven `STADIUM-*` FORTH primitives exist, are kernel-only (not in the shared/
|
||||
> - The eight `STADIUM-*` FORTH primitives exist, are kernel-only (not in the shared/
|
||||
> vendored word set), and are exercised by at least one Hermes word each.
|
||||
> - `stadium_owner[idx]` is written correctly on both the free-list-pop and
|
||||
> eviction-fallback paths in `stadium_admit()`, verified by a resident cell's
|
||||
@@ -3505,23 +3581,64 @@ document and committing that amendment as its own item.*
|
||||
> `vm_uuid_hera()` — verified by a Hermes-dispatched word's heat landing in Hermes's
|
||||
> own reservoir, not Hera's, with both VMs' conservation checks closing independently.
|
||||
> - Hermes's message and channel lifecycle (`MSG-ALLOC`/`MSG-FREE-NODE`, `CH-ALLOC`/
|
||||
> `CH-FREE-NODE`, `MBR-ALLOC`/`MBR-FREE-NODE`) run entirely through Stadium admission/
|
||||
> eviction — no parallel free list, no parallel heat field. Per §11, this is atomic:
|
||||
> `MSG-HEAT@/!`, `MSG-COOL-ONE`, `MSG-COOL-ALL`, `CH-HEAT@/!`, `CH-COOL-ALL`,
|
||||
> `CH-TOTAL-HEAT`, `MSG-TOTAL-HEAT` either come out in this same change or are rewritten
|
||||
> to read/write the Stadium cell instead of a local field — never both mechanisms live
|
||||
> at once.
|
||||
> `CH-FREE-NODE`) run entirely through Stadium admission/eviction — no parallel free
|
||||
> list, no parallel heat field. Per §11, this is atomic: `MSG-HEAT@/!`, `MSG-COOL-ONE`,
|
||||
> `MSG-COOL-ALL`, `CH-HEAT@/!`, `CH-COOL-ALL`, `CH-TOTAL-HEAT`, `MSG-TOTAL-HEAT` either
|
||||
> come out in this same change or are rewritten to read/write the Stadium cell instead
|
||||
> of a local field — never both mechanisms live at once. **`MBR-ALLOC`/`MBR-FREE-NODE`
|
||||
> ruled out of scope, 2026-08-07 — see below.**
|
||||
>
|
||||
> **Ruling, 2026-08-07: `MBR-ALLOC`/`MBR-FREE-NODE` stay on their own free list, not
|
||||
> migrated onto the Stadium.** This bullet originally named them alongside `MSG-*`/`CH-*`.
|
||||
> Checked the actual record layout (`capsules/hermes/init.4th`): an MBR record has exactly
|
||||
> two fields, `MBR-NEXT@` (link) and `MBR-VM@` (owning VM id) — a pure channel-membership
|
||||
> relationship, no heat field, never had one. The bullet's own stated purpose is "no
|
||||
> parallel free list, no parallel heat field" — for MBR, "no parallel heat field" is
|
||||
> already true vacuously, since none exists to be parallel to. Forcing MBR records through
|
||||
> `stadium_admit()`/`stadium_evict()` would mean inventing a heat/mass/behaviour for
|
||||
> something structurally without either, spending Stadium cells and reservoir budget on
|
||||
> records the item's actual design goal (a conserved, evictable-under-pressure heat
|
||||
> economy) has no reason to govern — "does VM X belong to channel Y" is not a quantity
|
||||
> that cools, competes for capacity, or needs eviction pressure. Their original inclusion
|
||||
> in this bullet reads as a completeness gesture written before the field layout was
|
||||
> checked, not a deliberate requirement. `MBR-ALLOC`/`MBR-FREE-NODE`'s own free list
|
||||
> (`capsules/hermes/init.4th`, unchanged this item) is correct as-is.
|
||||
> - Blocks 4110–4113 (Artemis) are untouched, per `HERMES.md`'s block-map lock. Any new or
|
||||
> changed Hermes block is verified with `mkcapsule --lint` before commit, per
|
||||
> `experiments/bare_metal/README.md`.
|
||||
> - The POST suite (regression gate per §10) passes.
|
||||
> - **The effort number is recorded explicitly** — per §10, "what Hermes costs is the
|
||||
> multiplier for everything else." Report at minimum: wall-clock/session time spent,
|
||||
> lines changed (FORTH + the seven-primitive C surface, split out), and file count
|
||||
> lines changed (FORTH + the eight-primitive C surface, split out), and file count
|
||||
> touched, so 4.3/4.4 can be estimated from a real data point rather than guessed.
|
||||
> - All three architectures boot to `ok>`/`zuse)ok>` with logs under `logs/`, and
|
||||
> Hermes's own conservation check (K≡1.0 across messages + channels + reservoir) closes
|
||||
> exactly, reported the same way item 4.1 reported `resident_sum`/`reservoir`/`sum`.
|
||||
>
|
||||
> **Effort number, reported 2026-08-07:**
|
||||
> - **Session time.** This conversation's own boot-log timestamps span roughly 10 hours
|
||||
> elapsed (`logs/20260806-153504` through `logs/20260807-013712`), covering: the amd64
|
||||
> GOT-indirect-addressing corruption investigation and fix (unrelated to Stadium logic,
|
||||
> committed separately as `0a7f144`), the item-4.2 acceptance-status survey against this
|
||||
> punch-list entry, the `Q.SLOT` admission-heat fix, the word-execution reservoir-floor
|
||||
> fix, and the `STADIUM-WORD-HEAT` addition that closed K≡1.0. This does **not** include
|
||||
> whatever time the original seven-primitive implementation and capsule migration
|
||||
> (already in place when this session's survey began) cost in an earlier session — no
|
||||
> visibility into that, not estimated rather than guessed.
|
||||
> - **Lines changed, split FORTH vs. C surface** (`git diff --stat`, this session's
|
||||
> contribution only — the pre-existing implementation's own diff is included since it
|
||||
> was still uncommitted when measured, but its authorship/timing is the caveat above):
|
||||
> - FORTH (`capsules/hermes/init.4th`): +116 / −45 (161 changed), 1 file.
|
||||
> - C, the eight-primitive `STADIUM-*` surface + Stadium core (`mama_forth_words.c`,
|
||||
> `stadium.c`, `stadium_words.c`, `stadium.h`, `stadium_words.h`, `vm.h`): +511 / −69
|
||||
> (580 changed), 6 files.
|
||||
> - C, other wiring (`capsule_birth.c`, `sk_vm_bootstrap.c`, `vm_core.c`,
|
||||
> `dictionary_management.c`): +13 / −6 (19 changed), 4 files.
|
||||
> - Self-test scaffolding (`kernel_main.c`, diagnostic-only, not production code):
|
||||
> +119 / −0, 1 file.
|
||||
> - **Total: 12 implementation files, +759 / −120 (879 lines changed).**
|
||||
> - **File count:** 12 implementation files (13 including this write-up in `FABRIC.md`
|
||||
> itself).
|
||||
- [ ] **4.3 — Console.** Settles 1.11 as part of the work. *Refs:* §17.5.
|
||||
|
||||
> **Note, 2026-08-05: Captain Bob wants a discussion before any work starts on this item.**
|
||||
|
||||
+40
-32
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-05T17:10:15Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-08-07T05:36:57Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
@@ -13,7 +13,7 @@
|
||||
| `common:msg.4th` | 4055 | `0xa99c5bcd3877f80e` |
|
||||
| `doe-campaign.4th` | 4060, 4061, 4062, 4063, 4064, 4065 | `0x3d4549142d91ec20` |
|
||||
| `doe.4th` | 2100, 2101, 2102, 2103, 2104, 2105, 2106, 2107 | `0xb6ecf5374e8ee77c` |
|
||||
| `hermes:init.4th` | 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4114, 4115, 4116, 4117, 4118, 4119, 4120, 4121, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, 4151, 4152, 4153 | `0xbb77f3ac463f507f` |
|
||||
| `hermes:init.4th` | 4100, 4101, 4102, 4103, 4104, 4105, 4106, 4107, 4108, 4109, 4114, 4115, 4116, 4117, 4118, 4119, 4120, 4121, 4142, 4143, 4144, 4145, 4146, 4147, 4148, 4149, 4150, 4151, 4152, 4153, 4154, 4155, 4156, 4157, 4158, 4159, 4175, 4176 | `0x85c7b311d1e5bf97` |
|
||||
| `init-0.4th` | 2200, 2201 | `0xd0a9550baf786bb3` |
|
||||
| `init-1.4th` | 4406, 4415, 4425, 4435 | `0x63e251adb0a03613` |
|
||||
| `init-2.4th` | 4506, 4515, 4525, 4535, 4545 | `0xf113b3d0bcccae47` |
|
||||
@@ -109,28 +109,28 @@
|
||||
| 4063 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
|
||||
| 4064 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
|
||||
| 4065 | `doe-campaign.4th` | `0x3d4549142d91ec20` | ok |
|
||||
| 4100 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4101 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4102 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4103 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4104 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4105 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4106 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4107 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4108 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4109 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4100 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4101 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4102 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4103 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4104 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4105 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4106 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4107 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4108 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4109 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4110 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4111 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4112 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4113 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4114 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4115 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4116 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4117 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4118 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4119 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4120 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4121 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4114 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4115 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4116 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4117 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4118 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4119 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4120 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4121 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4122 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4123 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4124 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
@@ -151,18 +151,24 @@
|
||||
| 4139 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4140 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4141 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4142 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4143 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4144 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4145 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4146 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4147 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4148 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4149 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4150 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4151 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4152 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4153 | `hermes:init.4th` | `0xbb77f3ac463f507f` | ok |
|
||||
| 4142 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4143 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4144 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4145 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4146 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4147 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4148 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4149 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4150 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4151 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4152 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4153 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4154 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4155 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4156 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4157 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4158 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4159 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4160 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4161 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4162 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
@@ -178,6 +184,8 @@
|
||||
| 4172 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4173 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4174 | `artemis:init.4th` | `0xc9e92cd18f4c7b49` | ok |
|
||||
| 4175 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4176 | `hermes:init.4th` | `0x85c7b311d1e5bf97` | ok |
|
||||
| 4300 | `process.4th` | `0x781afc1dbd0294f7` | ok |
|
||||
| 4301 | `process.4th` | `0x781afc1dbd0294f7` | ok |
|
||||
| 4406 | `init-1.4th` | `0x63e251adb0a03613` | ok |
|
||||
|
||||
+116
-45
@@ -15,16 +15,29 @@ Block 4100
|
||||
64 CONSTANT MBR-MAX
|
||||
65208 CONSTANT Q-DECAY
|
||||
255 CONSTANT MSG-DELIVERED
|
||||
Block 4155
|
||||
( item 4.2 -- StadiumBehaviour tags, match stadium.h's enum )
|
||||
0 CONSTANT SB-MIGRATE
|
||||
1 CONSTANT SB-DELIVER
|
||||
2 CONSTANT SB-EXPIRE
|
||||
3 CONSTANT SB-COOL
|
||||
-1 CONSTANT STADIUM-NONE
|
||||
( item 4.2 -- per-item admission heat for MSG-SEND/CH-ACCEPT. )
|
||||
( Remaining reservoir after COMMON-CH's Q.1/3 floor, split )
|
||||
( evenly across MSG-MAX messages + non-COMMON CH-MAX-1 slots. )
|
||||
Q.1 Q.1 3 / - MSG-MAX CH-MAX 1- + / CONSTANT Q.SLOT
|
||||
Block 4101
|
||||
( Hermes v1 — arenas and free-list roots )
|
||||
( Hermes v1 -- arenas. item 4.2: heat/capacity via Stadium; )
|
||||
( MBR keeps its own free list (not part of heat economy). )
|
||||
CREATE MSG-ARENA MSG-MAX MSG-CELLS * CELLS ALLOT
|
||||
CREATE CH-ARENA CH-MAX CH-CELLS * CELLS ALLOT
|
||||
CREATE MBR-ARENA MBR-MAX MBR-CELLS * CELLS ALLOT
|
||||
VARIABLE MSG-FREE-HEAD
|
||||
VARIABLE CH-FREE-HEAD
|
||||
VARIABLE MSG-ALLOC-SLOT
|
||||
VARIABLE CH-ALLOC-SLOT
|
||||
VARIABLE MBR-FREE-HEAD
|
||||
VARIABLE MSG-SEQ
|
||||
VARIABLE CH-ACTIVE
|
||||
VARIABLE COMMON-CH
|
||||
Block 4142
|
||||
( Hermes v1 — VM name routing table )
|
||||
8 CONSTANT VM-MAX
|
||||
@@ -39,23 +52,19 @@ CREATE VM-NAME-LENS VM-MAX CELLS ALLOT
|
||||
S" Hermes" 1 VM-NAME-REG
|
||||
S" Artemis" 2 VM-NAME-REG ;
|
||||
Block 4102
|
||||
( Hermes v1 — MSG-INIT-FREE CH-INIT-FREE )
|
||||
( Hermes v1 -- INIT-FREE. item 4.2: stamps STADIUM-NONE into )
|
||||
( each slot's stadium-cell field (msg off 5, ch off 3). )
|
||||
( Raw offsets: accessors aren't defined yet in file order. )
|
||||
: MSG-INIT-FREE ( -- )
|
||||
MSG-MAX 1- 0 DO
|
||||
I MSG-CELLS * CELLS MSG-ARENA +
|
||||
I 1+ MSG-CELLS * CELLS MSG-ARENA + SWAP !
|
||||
LOOP
|
||||
0 MSG-MAX 1- MSG-CELLS * CELLS MSG-ARENA + !
|
||||
MSG-ARENA MSG-FREE-HEAD ! ;
|
||||
MSG-MAX 0 DO
|
||||
STADIUM-NONE I MSG-CELLS * CELLS MSG-ARENA + 5 CELLS + !
|
||||
LOOP ;
|
||||
: CH-INIT-FREE ( -- )
|
||||
CH-MAX 1- 0 DO
|
||||
I CH-CELLS * CELLS CH-ARENA +
|
||||
I 1+ CH-CELLS * CELLS CH-ARENA + SWAP !
|
||||
LOOP
|
||||
0 CH-MAX 1- CH-CELLS * CELLS CH-ARENA + !
|
||||
CH-ARENA CH-FREE-HEAD ! ;
|
||||
CH-MAX 0 DO
|
||||
STADIUM-NONE I CH-CELLS * CELLS CH-ARENA + 3 CELLS + !
|
||||
LOOP ;
|
||||
Block 4103
|
||||
( Hermes v1 — MBR-INIT-FREE MSG alloc/free )
|
||||
( Hermes v1 -- MBR-INIT-FREE (member free list, untouched) )
|
||||
: MBR-INIT-FREE ( -- )
|
||||
MBR-MAX 1- 0 DO
|
||||
I MBR-CELLS * CELLS MBR-ARENA +
|
||||
@@ -63,26 +72,76 @@ Block 4103
|
||||
LOOP
|
||||
0 MBR-MAX 1- MBR-CELLS * CELLS MBR-ARENA + !
|
||||
MBR-ARENA MBR-FREE-HEAD ! ;
|
||||
: MSG-ALLOC ( -- addr|0 )
|
||||
MSG-FREE-HEAD @ DUP 0= IF EXIT THEN
|
||||
DUP @ MSG-FREE-HEAD !
|
||||
DUP MSG-CELLS CELLS 0 FILL ;
|
||||
Block 4156
|
||||
( item 4.2 -- MSG-ALLOC: finds a free slot (TYPE=0) first, )
|
||||
( admits into Stadium only once confirmed free (no leak). )
|
||||
: MSG-FIND-FREE-SLOT ( -- addr|0 )
|
||||
MSG-ARENA MSG-MAX 0 DO
|
||||
DUP @ 0= IF UNLOOP EXIT THEN
|
||||
MSG-CELLS CELLS +
|
||||
LOOP DROP 0 ;
|
||||
Block 4175
|
||||
( item 4.2 -- MSG-ALLOC: pulls heat from reservoir first, )
|
||||
( admits (identity=idx, heat=pulled), rolls back on refusal. )
|
||||
: MSG-ALLOC ( heat -- addr|0 )
|
||||
MSG-FIND-FREE-SLOT DUP 0= IF SWAP DROP EXIT THEN
|
||||
MSG-ALLOC-SLOT !
|
||||
STADIUM-RES-PULL
|
||||
MSG-ALLOC-SLOT @ MSG-ARENA - MSG-CELLS CELLS /
|
||||
SWAP DUP >R
|
||||
SB-DELIVER STADIUM-ADMIT
|
||||
DUP STADIUM-NONE = IF
|
||||
DROP R> STADIUM-RES-PUSH 0 EXIT
|
||||
THEN
|
||||
R> DROP
|
||||
MSG-ALLOC-SLOT @ MSG-CELLS CELLS 0 FILL
|
||||
MSG-ALLOC-SLOT @ 5 CELLS + !
|
||||
MSG-ALLOC-SLOT @ ;
|
||||
Block 4157
|
||||
( item 4.2 -- MSG-FREE-NODE: evict from Stadium, clear field )
|
||||
: MSG-FREE-NODE ( addr -- )
|
||||
MSG-FREE-HEAD @ OVER ! MSG-FREE-HEAD ! ;
|
||||
DUP 5 CELLS + @ STADIUM-EVICT DROP
|
||||
DUP 5 CELLS + STADIUM-NONE SWAP !
|
||||
DROP ;
|
||||
Block 4104
|
||||
( Hermes v1 — CH MBR alloc/free )
|
||||
: CH-ALLOC ( -- addr|0 )
|
||||
CH-FREE-HEAD @ DUP 0= IF EXIT THEN
|
||||
DUP @ CH-FREE-HEAD !
|
||||
DUP CH-CELLS CELLS 0 FILL ;
|
||||
: CH-FREE-NODE ( addr -- )
|
||||
CH-FREE-HEAD @ OVER ! CH-FREE-HEAD ! ;
|
||||
( Hermes v1 -- MBR alloc/free (unchanged; not heat economy) )
|
||||
: MBR-ALLOC ( -- addr|0 )
|
||||
MBR-FREE-HEAD @ DUP 0= IF EXIT THEN
|
||||
DUP @ MBR-FREE-HEAD !
|
||||
DUP MBR-CELLS CELLS 0 FILL ;
|
||||
: MBR-FREE-NODE ( addr -- )
|
||||
MBR-FREE-HEAD @ OVER ! MBR-FREE-HEAD ! ;
|
||||
Block 4158
|
||||
( item 4.2 -- CH-FIND-FREE-SLOT. No TYPE field, so freeness )
|
||||
( is stadium-cell = STADIUM-NONE. CH-ALLOC -> block 4176. )
|
||||
: CH-FIND-FREE-SLOT ( -- addr|0 )
|
||||
CH-ARENA CH-MAX 0 DO
|
||||
DUP 3 CELLS + @ STADIUM-NONE = IF UNLOOP EXIT THEN
|
||||
CH-CELLS CELLS +
|
||||
LOOP DROP 0 ;
|
||||
Block 4176
|
||||
( item 4.2 -- CH-ALLOC: pulls heat from reservoir first, )
|
||||
( admits (identity=idx, heat=pulled), rolls back on refusal. )
|
||||
: CH-ALLOC ( heat -- addr|0 )
|
||||
CH-FIND-FREE-SLOT DUP 0= IF SWAP DROP EXIT THEN
|
||||
CH-ALLOC-SLOT !
|
||||
STADIUM-RES-PULL
|
||||
CH-ALLOC-SLOT @ CH-ARENA - CH-CELLS CELLS /
|
||||
SWAP DUP >R
|
||||
SB-COOL STADIUM-ADMIT
|
||||
DUP STADIUM-NONE = IF
|
||||
DROP R> STADIUM-RES-PUSH 0 EXIT
|
||||
THEN
|
||||
R> DROP
|
||||
CH-ALLOC-SLOT @ CH-CELLS CELLS 0 FILL
|
||||
CH-ALLOC-SLOT @ 3 CELLS + !
|
||||
CH-ALLOC-SLOT @ ;
|
||||
Block 4159
|
||||
( item 4.2 -- CH-FREE-NODE: evict from Stadium, clear field )
|
||||
: CH-FREE-NODE ( addr -- )
|
||||
DUP 3 CELLS + @ STADIUM-EVICT DROP
|
||||
DUP 3 CELLS + STADIUM-NONE SWAP !
|
||||
DROP ;
|
||||
Block 4105
|
||||
( Hermes v1 — message field accessors )
|
||||
: MSG-TYPE@ ( m -- n ) @ ;
|
||||
@@ -95,8 +154,9 @@ Block 4105
|
||||
: MSG-PADDR! ( a m -- ) 3 CELLS + ! ;
|
||||
: MSG-PLEN@ ( m -- u ) 4 CELLS + @ ;
|
||||
: MSG-PLEN! ( u m -- ) 4 CELLS + ! ;
|
||||
: MSG-HEAT@ ( m -- q ) 5 CELLS + @ ;
|
||||
: MSG-HEAT! ( q m -- ) 5 CELLS + ! ;
|
||||
( item 4.2: offset 5 = Stadium cell idx. MSG-HEAT@/! -> 4154. )
|
||||
: MSG-STADIUM-CELL@ ( m -- cell ) 5 CELLS + @ ;
|
||||
: MSG-STADIUM-CELL! ( cell m -- ) 5 CELLS + ! ;
|
||||
: MSG-SEQ@ ( m -- n ) 6 CELLS + @ ;
|
||||
: MSG-SEQ! ( n m -- ) 6 CELLS + ! ;
|
||||
Block 4143
|
||||
@@ -114,14 +174,22 @@ Block 4106
|
||||
: CH-OWNER! ( n c -- ) 1 CELLS + ! ;
|
||||
: CH-STATE@ ( c -- n ) 2 CELLS + @ ;
|
||||
: CH-STATE! ( n c -- ) 2 CELLS + ! ;
|
||||
: CH-HEAT@ ( c -- q ) 3 CELLS + @ ;
|
||||
: CH-HEAT! ( q c -- ) 3 CELLS + ! ;
|
||||
( item 4.2: offset 3 = Stadium cell index. CH-HEAT@/! -> 4154. )
|
||||
: CH-STADIUM-CELL@ ( c -- cell ) 3 CELLS + @ ;
|
||||
: CH-STADIUM-CELL! ( cell c -- ) 3 CELLS + ! ;
|
||||
: CH-MBRS@ ( c -- a ) 4 CELLS + @ ;
|
||||
: CH-MBRS! ( a c -- ) 4 CELLS + ! ;
|
||||
: CH-NEXT@ ( c -- a ) 5 CELLS + @ ;
|
||||
: CH-NEXT! ( a c -- ) 5 CELLS + ! ;
|
||||
: MBR-NEXT@ ( m -- a ) @ ;
|
||||
: MBR-VM@ ( m -- n ) 1 CELLS + @ ;
|
||||
Block 4154
|
||||
( item 4.2 -- composed MSG/CH-HEAT@/!, same names/stacks, )
|
||||
( new bodies routed via Stadium. Callers need no changes. )
|
||||
: MSG-HEAT@ ( m -- q ) MSG-STADIUM-CELL@ STADIUM-HEAT@ ;
|
||||
: MSG-HEAT! ( q m -- ) MSG-STADIUM-CELL@ STADIUM-HEAT! ;
|
||||
: CH-HEAT@ ( c -- q ) CH-STADIUM-CELL@ STADIUM-HEAT@ ;
|
||||
: CH-HEAT! ( q c -- ) CH-STADIUM-CELL@ STADIUM-HEAT! ;
|
||||
Block 4107
|
||||
( Hermes v1 — deliver )
|
||||
VARIABLE MSG-LAST-MSG
|
||||
@@ -134,17 +202,19 @@ VARIABLE MSG-LAST-MSG
|
||||
DUP MSG-PADDR@ OVER MSG-PLEN@
|
||||
ROT MSG-TO@ IDX>NAME VM-EXEC ;
|
||||
Block 4144
|
||||
( Hermes v1 — MSG-SEND )
|
||||
( Hermes v1 -- MSG-SEND. item 4.2: heat -> MSG-ALLOC's )
|
||||
( admission directly (zero-heat would lose eviction-fallback )
|
||||
( density comparisons), not set afterward as before. )
|
||||
: MSG-SEND ( type from to paddr plen ch -- )
|
||||
MSG-ALLOC DUP 0= IF 2DROP 2DROP 2DROP DROP EXIT THEN
|
||||
Q.SLOT MSG-ALLOC DUP 0= IF 2DROP 2DROP 2DROP DROP EXIT THEN
|
||||
>R
|
||||
MSG-SEQ @ 1+ DUP MSG-SEQ ! R@ MSG-SEQ!
|
||||
R@ MSG-CH!
|
||||
R@ MSG-PLEN! R@ MSG-PADDR!
|
||||
R@ MSG-TO! R@ MSG-FROM! DUP R@ MSG-TYPE! R@ MSG-ORIG-TYPE!
|
||||
Q.1 R@ MSG-HEAT! R> DROP ;
|
||||
R> DROP ;
|
||||
Block 4108
|
||||
( Hermes v1 — message cooling )
|
||||
( Hermes v1 — MSG-COOL-ONE/ALL: linear decay per tick )
|
||||
VARIABLE MSG-SCAN
|
||||
: MSG-COOL-ONE ( m -- )
|
||||
DUP MSG-HEAT@ Q-DECAY Q.* SWAP MSG-HEAT! ;
|
||||
@@ -236,17 +306,17 @@ Block 4115
|
||||
CH-SCAN !
|
||||
REPEAT ;
|
||||
Block 4116
|
||||
( Hermes v1 — COMMON channel + HERMES-TICK )
|
||||
( COMMON floor = Q.1/3: Hermes's fair share, VM-COUNT=3 )
|
||||
VARIABLE COMMON-CH
|
||||
( Hermes v1 -- COMMON + HERMES-TICK. floor=Q.1/3, VM-COUNT=3 )
|
||||
( COMMON-CH VARIABLE now in block 4101; see CH-REAP-SAFE 4115 )
|
||||
: COMMON-INIT ( -- )
|
||||
CH-ALLOC DUP COMMON-CH !
|
||||
Q.1 3 / CH-ALLOC DUP COMMON-CH !
|
||||
0 OVER CH-ID! 0 OVER CH-OWNER!
|
||||
CH-OPEN OVER CH-STATE!
|
||||
Q.1 3 / OVER CH-HEAT!
|
||||
0 OVER CH-MBRS!
|
||||
CH-ACTIVE @ OVER CH-NEXT!
|
||||
CH-ACTIVE ! ;
|
||||
( item 4.2: floor-refresh now reservoir-constrained, may no-op )
|
||||
( under pressure (was unconstrained write before). Watch log. )
|
||||
: HERMES-TICK ( -- )
|
||||
MSG-DELIVER-ALL MSG-REDELIVER-NACKED
|
||||
MSG-COOL-ALL MSG-REAP
|
||||
@@ -254,8 +324,10 @@ VARIABLE COMMON-CH
|
||||
Q.1 3 / COMMON-CH @ CH-HEAT! ;
|
||||
Block 4147
|
||||
( Hermes v1 — HERMES-K + WELCOME )
|
||||
( item 4.2: +STADIUM-WORD-HEAT so word patrons count -- 25.7 )
|
||||
: HERMES-K ( -- q48 )
|
||||
MSG-TOTAL-HEAT CH-TOTAL-HEAT + ;
|
||||
MSG-TOTAL-HEAT CH-TOTAL-HEAT + STADIUM-RES@ +
|
||||
STADIUM-WORD-HEAT + ;
|
||||
: WELCOME ( -- ) LOG-INFO" Hermes: loaded" ;
|
||||
WELCOME
|
||||
Block 4117
|
||||
@@ -294,11 +366,10 @@ Block 4119
|
||||
Block 4148
|
||||
( Hermes v1 — channel ops: accept confirm close )
|
||||
: CH-ACCEPT ( -- ch|0 )
|
||||
CH-ALLOC DUP 0= IF EXIT THEN
|
||||
Q.SLOT CH-ALLOC DUP 0= IF EXIT THEN
|
||||
1 CH-MINT-ID OVER CH-ID!
|
||||
1 OVER CH-OWNER!
|
||||
CH-NEGOTIATING OVER CH-STATE!
|
||||
Q.1 OVER CH-HEAT!
|
||||
0 OVER CH-MBRS!
|
||||
CH-ACTIVE @ OVER CH-NEXT!
|
||||
DUP CH-ACTIVE ! ;
|
||||
|
||||
Binary file not shown.
@@ -57,3 +57,39 @@ tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_
|
||||
56,560000,10000,0,0,211,103,32,4096,1620,0,0,0,65536,0,0,65536,0,0
|
||||
57,570000,10000,0,0,193,110,33,4096,1712,0,0,0,65536,0,0,65536,0,0
|
||||
58,580000,10000,0,0,190,5,67,4096,1775,0,0,0,65536,0,0,65536,0,0
|
||||
1,10000,18446744073708981616,0,0,117,4,4,4096,0,0,4895412794951728869,10,65536,0,0,65536,0,0
|
||||
2,20000,10000,0,0,78,10,7,4096,0,0,0,11,65536,0,0,65536,0,0
|
||||
3,30000,10000,0,0,101,13,7,4096,0,0,0,13,65536,0,0,65536,0,0
|
||||
4,40000,10000,0,0,63,19,8,4096,0,0,0,14,65536,0,0,65536,0,0
|
||||
5,50000,10000,0,0,92,25,8,4096,2,0,0,16,65536,0,0,65536,0,0
|
||||
6,60000,10000,0,0,64,27,8,4096,2,0,0,18,65536,0,0,65536,0,0
|
||||
7,70000,10000,0,0,228,32,8,4096,210,0,0,24,65536,0,0,65536,0,0
|
||||
8,80000,10000,0,0,256,34,9,4096,466,0,0,31,65536,0,0,65536,0,0
|
||||
9,90000,10000,0,0,256,36,10,4096,722,0,0,37,65536,0,0,65536,0,0
|
||||
10,100000,10000,0,0,256,36,11,4096,978,0,0,43,65536,0,0,65536,0,0
|
||||
11,110000,10000,0,0,256,36,11,4096,1234,0,0,50,65536,0,0,65536,0,0
|
||||
12,120000,10000,0,0,256,36,12,4096,1490,0,0,56,65536,0,0,65536,0,0
|
||||
13,130000,10000,0,0,256,37,12,4096,1746,0,0,63,65536,0,0,65536,0,0
|
||||
14,140000,10000,0,0,230,39,12,4096,1949,0,0,69,65536,0,0,65536,0,0
|
||||
15,150000,10000,0,0,256,39,13,4096,2204,0,0,76,65536,0,0,65536,0,0
|
||||
16,160000,10000,0,0,256,39,14,4096,2459,0,0,82,65536,0,0,65536,0,0
|
||||
17,170000,10000,0,0,256,39,15,4096,2713,0,0,89,65536,0,0,65536,0,0
|
||||
18,180000,10000,0,0,256,39,15,4096,2969,0,0,95,65536,0,0,65536,0,0
|
||||
19,190000,10000,0,0,256,39,16,4096,3224,0,0,102,65536,0,1,65536,0,0
|
||||
20,200000,10000,0,0,256,41,17,4096,3480,0,0,109,65536,0,1,65536,0,0
|
||||
21,210000,10000,0,0,256,41,17,4096,3736,0,0,117,65536,0,1,65536,0,0
|
||||
22,220000,10000,0,0,256,41,18,4096,3992,0,0,123,65536,0,1,65536,0,0
|
||||
23,230000,10000,0,0,256,41,19,4096,4096,0,0,130,65536,0,1,65536,0,0
|
||||
24,240000,10000,0,0,256,41,20,4096,4096,0,0,136,65536,0,1,65536,0,0
|
||||
25,250000,10000,0,0,256,43,20,2048,2048,0,0,143,65536,0,1,65536,0,0
|
||||
26,260000,10000,0,0,256,45,21,2048,2048,0,0,149,65536,0,1,65536,0,0
|
||||
27,270000,10000,0,0,256,45,22,1024,1024,0,0,156,65536,0,1,65536,0,0
|
||||
28,280000,10000,0,0,256,45,23,1024,1024,0,0,162,65536,0,1,65536,0,0
|
||||
29,290000,10000,0,0,256,45,24,512,512,0,0,169,65536,0,1,65536,0,0
|
||||
30,300000,10000,0,0,256,45,24,512,512,0,0,175,65536,0,1,65536,0,0
|
||||
31,310000,10000,0,0,256,45,25,256,256,0,0,182,65536,0,1,65536,0,0
|
||||
32,320000,10000,0,0,255,45,26,256,256,0,0,188,65536,0,1,65536,0,0
|
||||
33,330000,10000,0,0,256,45,27,256,256,0,0,195,65536,0,1,65536,0,0
|
||||
34,340000,10000,0,0,256,45,27,256,256,0,0,201,65536,0,1,65536,0,0
|
||||
35,350000,10000,0,0,256,45,28,256,256,0,0,208,65536,0,1,65536,0,0
|
||||
36,360000,10000,0,0,256,45,29,256,256,0,0,214,65536,0,1,65536,0,0
|
||||
|
||||
|
@@ -57,3 +57,39 @@ tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_
|
||||
56,560000,10000,0,0,211,103,32,4096,1620,0,0,0,65536,0,0,65536,0,0
|
||||
57,570000,10000,0,0,193,110,33,4096,1712,0,0,0,65536,0,0,65536,0,0
|
||||
58,580000,10000,0,0,190,5,67,4096,1775,0,0,0,65536,0,0,65536,0,0
|
||||
1,10000,18446744073708981616,0,0,117,4,4,4096,0,0,4895412794951728869,8,65536,0,0,65536,0,0
|
||||
2,20000,10000,0,0,78,10,7,4096,0,0,0,9,65536,0,0,65536,0,0
|
||||
3,30000,10000,0,0,101,13,7,4096,0,0,0,11,65536,0,0,65536,0,0
|
||||
4,40000,10000,0,0,63,19,8,4096,0,0,0,12,65536,0,0,65536,0,0
|
||||
5,50000,10000,0,0,92,25,8,4096,2,0,0,14,65536,0,0,65536,0,0
|
||||
6,60000,10000,0,0,64,27,8,4096,2,0,0,15,65536,0,0,65536,0,0
|
||||
7,70000,10000,0,0,228,32,8,4096,210,0,0,21,65536,0,0,65536,0,0
|
||||
8,80000,10000,0,0,256,34,9,4096,466,0,0,28,65536,0,0,65536,0,0
|
||||
9,90000,10000,0,0,256,36,10,4096,722,0,0,34,65536,0,0,65536,0,0
|
||||
10,100000,10000,0,0,256,36,11,4096,978,0,0,40,65536,0,0,65536,0,0
|
||||
11,110000,10000,0,0,256,36,11,4096,1234,0,0,46,65536,0,0,65536,0,0
|
||||
12,120000,10000,0,0,256,36,12,4096,1490,0,0,52,65536,0,0,65536,0,0
|
||||
13,130000,10000,0,0,256,37,12,4096,1746,0,0,59,65536,0,0,65536,0,0
|
||||
14,140000,10000,0,0,230,39,12,4096,1949,0,0,65,65536,0,0,65536,0,0
|
||||
15,150000,10000,0,0,256,39,13,4096,2204,0,0,71,65536,0,0,65536,0,0
|
||||
16,160000,10000,0,0,256,39,14,4096,2459,0,0,77,65536,0,0,65536,0,0
|
||||
17,170000,10000,0,0,256,39,15,4096,2713,0,0,83,65536,0,0,65536,0,0
|
||||
18,180000,10000,0,0,256,39,15,4096,2969,0,0,90,65536,0,0,65536,0,0
|
||||
19,190000,10000,0,0,256,39,16,4096,3224,0,0,96,65536,0,1,65536,0,0
|
||||
20,200000,10000,0,0,256,41,17,4096,3480,0,0,102,65536,0,1,65536,0,0
|
||||
21,210000,10000,0,0,256,41,17,4096,3736,0,0,108,65536,0,1,65536,0,0
|
||||
22,220000,10000,0,0,256,41,18,4096,3992,0,0,116,65536,0,1,65536,0,0
|
||||
23,230000,10000,0,0,256,41,19,4096,4096,0,0,123,65536,0,1,65536,0,0
|
||||
24,240000,10000,0,0,256,41,20,4096,4096,0,0,129,65536,0,1,65536,0,0
|
||||
25,250000,10000,0,0,256,43,20,2048,2048,0,0,135,65536,0,1,65536,0,0
|
||||
26,260000,10000,0,0,256,45,21,2048,2048,0,0,142,65536,0,1,65536,0,0
|
||||
27,270000,10000,0,0,256,45,22,1024,1024,0,0,148,65536,0,1,65536,0,0
|
||||
28,280000,10000,0,0,256,45,23,1024,1024,0,0,154,65536,0,1,65536,0,0
|
||||
29,290000,10000,0,0,256,45,24,512,512,0,0,161,65536,0,1,65536,0,0
|
||||
30,300000,10000,0,0,256,45,24,512,512,0,0,167,65536,0,1,65536,0,0
|
||||
31,310000,10000,0,0,256,45,25,256,256,0,0,173,65536,0,1,65536,0,0
|
||||
32,320000,10000,0,0,255,45,26,256,256,0,0,179,65536,0,1,65536,0,0
|
||||
33,330000,10000,0,0,256,45,27,256,256,0,0,185,65536,0,1,65536,0,0
|
||||
34,340000,10000,0,0,256,45,27,256,256,0,0,192,65536,0,1,65536,0,0
|
||||
35,350000,10000,0,0,256,45,28,256,256,0,0,198,65536,0,1,65536,0,0
|
||||
36,360000,10000,0,0,256,45,29,256,256,0,0,204,65536,0,1,65536,0,0
|
||||
|
||||
|
@@ -57,3 +57,39 @@ tick_number,elapsed_ns,tick_interval_ns,cache_hits_delta,bucket_hits_delta,word_
|
||||
56,560000,10000,0,0,211,103,32,4096,1620,0,0,0,65536,0,0,65536,0,0
|
||||
57,570000,10000,0,0,193,110,33,4096,1712,0,0,0,65536,0,0,65536,0,0
|
||||
58,580000,10000,0,0,190,5,67,4096,1775,0,0,0,65536,0,0,65536,0,0
|
||||
1,10000,18446744073708981616,0,0,117,4,4,4096,0,0,4895412794951728869,8,65536,0,0,65536,0,0
|
||||
2,20000,10000,0,0,78,10,7,4096,0,0,0,10,65536,0,0,65536,0,0
|
||||
3,30000,10000,0,0,101,13,7,4096,0,0,0,11,65536,0,0,65536,0,0
|
||||
4,40000,10000,0,0,63,19,8,4096,0,0,0,12,65536,0,0,65536,0,0
|
||||
5,50000,10000,0,0,92,25,8,4096,2,0,0,14,65536,0,0,65536,0,0
|
||||
6,60000,10000,0,0,64,27,8,4096,2,0,0,15,65536,0,0,65536,0,0
|
||||
7,70000,10000,0,0,228,32,8,4096,210,0,0,23,65536,0,0,65536,0,0
|
||||
8,80000,10000,0,0,256,34,9,4096,466,0,0,30,65536,0,0,65536,0,0
|
||||
9,90000,10000,0,0,256,36,10,4096,722,0,0,37,65536,0,0,65536,0,0
|
||||
10,100000,10000,0,0,256,36,11,4096,978,0,0,45,65536,0,0,65536,0,0
|
||||
11,110000,10000,0,0,256,36,11,4096,1234,0,0,52,65536,0,0,65536,0,0
|
||||
12,120000,10000,0,0,256,36,12,4096,1490,0,0,59,65536,0,0,65536,0,0
|
||||
13,130000,10000,0,0,256,37,12,4096,1746,0,0,67,65536,0,0,65536,0,0
|
||||
14,140000,10000,0,0,230,39,12,4096,1949,0,0,74,65536,0,0,65536,0,0
|
||||
15,150000,10000,0,0,256,39,13,4096,2204,0,0,81,65536,0,0,65536,0,0
|
||||
16,160000,10000,0,0,256,39,14,4096,2459,0,0,89,65536,0,0,65536,0,0
|
||||
17,170000,10000,0,0,256,39,15,4096,2713,0,0,96,65536,0,0,65536,0,0
|
||||
18,180000,10000,0,0,256,39,15,4096,2969,0,0,103,65536,0,0,65536,0,0
|
||||
19,190000,10000,0,0,256,39,16,4096,3224,0,0,115,65536,0,1,65536,0,0
|
||||
20,200000,10000,0,0,256,41,17,4096,3480,0,0,122,65536,0,1,65536,0,0
|
||||
21,210000,10000,0,0,256,41,17,4096,3736,0,0,129,65536,0,1,65536,0,0
|
||||
22,220000,10000,0,0,256,41,18,4096,3992,0,0,137,65536,0,1,65536,0,0
|
||||
23,230000,10000,0,0,256,41,19,4096,4096,0,0,144,65536,0,1,65536,0,0
|
||||
24,240000,10000,0,0,256,41,20,4096,4096,0,0,151,65536,0,1,65536,0,0
|
||||
25,250000,10000,0,0,256,43,20,2048,2048,0,0,159,65536,0,1,65536,0,0
|
||||
26,260000,10000,0,0,256,45,21,2048,2048,0,0,166,65536,0,1,65536,0,0
|
||||
27,270000,10000,0,0,256,45,22,1024,1024,0,0,174,65536,0,1,65536,0,0
|
||||
28,280000,10000,0,0,256,45,23,1024,1024,0,0,181,65536,0,1,65536,0,0
|
||||
29,290000,10000,0,0,256,45,24,512,512,0,0,188,65536,0,1,65536,0,0
|
||||
30,300000,10000,0,0,256,45,24,512,512,0,0,196,65536,0,1,65536,0,0
|
||||
31,310000,10000,0,0,256,45,25,256,256,0,0,204,65536,0,1,65536,0,0
|
||||
32,320000,10000,0,0,255,45,26,256,256,0,0,211,65536,0,1,65536,0,0
|
||||
33,330000,10000,0,0,256,45,27,256,256,0,0,222,65536,0,1,65536,0,0
|
||||
34,340000,10000,0,0,256,45,27,256,256,0,0,229,65536,0,1,65536,0,0
|
||||
35,350000,10000,0,0,256,45,28,256,256,0,0,236,65536,0,1,65536,0,0
|
||||
36,360000,10000,0,0,256,45,29,256,256,0,0,244,65536,0,1,65536,0,0
|
||||
|
||||
|
@@ -307,6 +307,33 @@ void stadium_reservoir_push(VMUuid vm_id, uint64_t amount);
|
||||
*/
|
||||
uint64_t stadium_reservoir_peek(VMUuid vm_id);
|
||||
|
||||
/*
|
||||
* stadium_quota_slot_for_vm - Read-only: vm_id's quota slot index (0 to
|
||||
* STADIUM_MAX_VM_COUNT-1), for callers outside stadium.c that need to key
|
||||
* their own per-VM state the same way stadium.c's internal arrays already
|
||||
* do (FABRIC.md §25.5 item 4.2 -- stadium_words.c's word_id -> cell_index
|
||||
* map needs this to stop colliding across VMs; word_id is scoped per-VM,
|
||||
* not globally unique, so a single shared map aliases different VMs' words
|
||||
* onto each other's Stadium cells and reservoirs).
|
||||
*
|
||||
* @param vm_id VM to look up.
|
||||
* @return Quota slot index, or -1 if vm_id holds no quota.
|
||||
*/
|
||||
int stadium_quota_slot_for_vm(VMUuid vm_id);
|
||||
|
||||
/*
|
||||
* stadium_resident_sum - Read-only: sum of heat across every cell currently
|
||||
* resident AND owned by vm_id's own quota (FABRIC.md §25.5 item 4.2 --
|
||||
* boot diagnostics need this filtered per-VM once a second VM holds a
|
||||
* quota; summing every resident cell regardless of owner, as the pre-4.2
|
||||
* diagnostic did, mixes two VMs' conservation totals together).
|
||||
* Returns 0 for an unknown vm_id, same convention as stadium_reservoir_peek().
|
||||
*
|
||||
* @param vm_id Owning VM's id.
|
||||
* @return Sum of resident heat owned by vm_id (Q48.16), or 0 if vm_id has no quota.
|
||||
*/
|
||||
uint64_t stadium_resident_sum(VMUuid vm_id);
|
||||
|
||||
/*
|
||||
* stadium_evict - Reap the patron header at cell_index (FABRIC.md §17.2:
|
||||
* "reap means leaves the floor, not destroyed"). Dispatches its behaviour
|
||||
@@ -430,6 +457,43 @@ size_t stadium_admit(VMUuid vm_id, const StadiumPatronHeader *candidate);
|
||||
*/
|
||||
int stadium_grant_quota(VMUuid new_vm_id, VMUuid from_vm_id);
|
||||
|
||||
/*
|
||||
* stadium_cell_heat_get - Read a resident cell's own heat (FABRIC.md item
|
||||
* 4.2's fourth ruling). Requires cell_index to be resident AND owned by
|
||||
* vm_id's quota -- returns 0 otherwise (out of range, not resident, or
|
||||
* belongs to a different VM), same ambiguity-with-a-genuine-zero already
|
||||
* accepted by stadium_reservoir_peek()'s doc: callers that need to
|
||||
* distinguish "refused" from "actually zero" must already know the cell is
|
||||
* theirs (e.g. from their own resident-cell tracking), same contract as
|
||||
* every other implicit-self primitive here.
|
||||
*
|
||||
* @param vm_id Calling VM's own identity.
|
||||
* @param cell_index Index of the resident patron header to read.
|
||||
* @return The cell's current heat (Q48.16), or 0 if refused.
|
||||
*/
|
||||
uint64_t stadium_cell_heat_get(VMUuid vm_id, size_t cell_index);
|
||||
|
||||
/*
|
||||
* stadium_cell_heat_set - Write a resident cell's own heat, reconciling the
|
||||
* reservoir delta atomically (FABRIC.md item 4.2's fourth ruling). Same
|
||||
* ownership requirement as stadium_cell_heat_get(). If new_heat is higher
|
||||
* than the cell's current heat, pulls the exact difference from vm_id's own
|
||||
* reservoir first -- refuses (returns -1, no mutation) if the reservoir
|
||||
* cannot cover the full increase, never a partial credit that would invent
|
||||
* heat. If new_heat is lower, pushes the exact difference back to the
|
||||
* reservoir after writing. Equal is a no-op success. This is the only
|
||||
* sanctioned way to change a resident cell's heat post-admission -- doing
|
||||
* the reservoir accounting here, not leaving it to the FORTH caller, is the
|
||||
* whole reason this primitive exists rather than a raw field poke.
|
||||
*
|
||||
* @param vm_id Calling VM's own identity.
|
||||
* @param cell_index Index of the resident patron header to write.
|
||||
* @param new_heat The heat value to set (Q48.16).
|
||||
* @return 0 on success, -1 if refused (not owned/resident, or insufficient
|
||||
* reservoir for an increase).
|
||||
*/
|
||||
int stadium_cell_heat_set(VMUuid vm_id, size_t cell_index, uint64_t new_heat);
|
||||
|
||||
#endif /* __STARKERNEL__ */
|
||||
|
||||
#endif /* STARKERNEL_VM_STADIUM_H */
|
||||
@@ -48,9 +48,17 @@
|
||||
|
||||
/*
|
||||
* stadium_words_init - Zeroes the word_id -> cell_index map (DICTIONARY_SIZE
|
||||
* entries, static -- no allocation). Must be called after stadium_boot_init()
|
||||
* and stadium_birth_hera(), before any word ever dispatches. Safe to call
|
||||
* again (re-zeroes); nothing does today.
|
||||
* entries per VM quota slot, STADIUM_MAX_VM_COUNT slots, static -- no
|
||||
* allocation). Must be called after stadium_boot_init() and
|
||||
* stadium_birth_hera(), before any word ever dispatches. Safe to call again
|
||||
* (re-zeroes for every slot); nothing does today.
|
||||
*
|
||||
* item 4.2 (FABRIC.md §25.5): the map is keyed by quota slot, not just
|
||||
* word_id -- word_id is assigned per-VM (vm->next_word_id), not globally
|
||||
* unique, so a single shared word_id -> cell_index map aliased different
|
||||
* VMs' words onto each other's Stadium cells and reservoirs the moment a
|
||||
* second VM (Hermes) held a quota. One system-wide init call still covers
|
||||
* every slot; no per-VM init call is needed.
|
||||
*/
|
||||
void stadium_words_init(void);
|
||||
|
||||
@@ -65,25 +73,34 @@ void stadium_words_init(void);
|
||||
* (fraction of the cell's own current heat, scaled by elapsed_ticks since
|
||||
* this word's own last touch -- STADIUM_WORD_COOL_RATE_Q48) crediting the
|
||||
* cooled amount back to vm_id's reservoir, then pulls
|
||||
* STADIUM_WORD_HEAT_QUANTUM from the reservoir into the cell (both clamped
|
||||
* to what the reservoir actually holds).
|
||||
* STADIUM_WORD_HEAT_QUANTUM from the reservoir into the cell -- clamped to
|
||||
* what the reservoir actually holds AND to a floor of Q48_ONE / 3 that
|
||||
* word-execution admission alone may never dip the reservoir below
|
||||
* (FABRIC.md §25.7, Captain Bob's ruling 2026-08-06: this pull fires on
|
||||
* EVERY dispatch, not just first admission, and without a floor exhausts a
|
||||
* VM's entire reservoir in ~32 dispatches, starving any application-level
|
||||
* economy -- e.g. item 4.2's Hermes -- sharing the same VM's reservoir).
|
||||
* Application-level pulls (stadium_reservoir_pull() called directly) are
|
||||
* not subject to this floor.
|
||||
*
|
||||
* If word_id is not resident (or the map's entry is stale -- self-healing
|
||||
* check against the cell's discriminator bit and identity, covers both a
|
||||
* prior eviction and a FORGET/redefine word_id reuse this function did not
|
||||
* itself clear): attempts Option B starter-grant admission -- pulls
|
||||
* STADIUM_WORD_HEAT_QUANTUM from the reservoir, builds an unpinned COOL
|
||||
* candidate, calls stadium_admit(). On refusal, pushes the pulled quantum
|
||||
* back (rollback, preserves conservation across the failed attempt). On
|
||||
* success, records the mapping and increments the promotion counter.
|
||||
* STADIUM_WORD_HEAT_QUANTUM from the reservoir (same floor as above),
|
||||
* builds an unpinned COOL candidate, calls stadium_admit(). On refusal,
|
||||
* pushes the pulled quantum back (rollback, preserves conservation across
|
||||
* the failed attempt). On success, records the mapping and increments the
|
||||
* promotion counter.
|
||||
*
|
||||
* No-op if word_id == WORD_ID_INVALID, word_id >= DICTIONARY_SIZE, or the
|
||||
* word layer has not been initialized.
|
||||
* No-op if word_id == WORD_ID_INVALID, word_id >= DICTIONARY_SIZE, or vm_id
|
||||
* holds no Stadium quota.
|
||||
*
|
||||
* @param vm_id Owning VM. Hardcoded to vm_uuid_hera() at every
|
||||
* call site today -- Tripod is pruned to Hera alone
|
||||
* (item 0.1), and she is the only VM with a Stadium
|
||||
* quota. Revisit when item 4.2 restores Hermes.
|
||||
* @param vm_id Owning VM -- vm->stadium_vm_id at every call site.
|
||||
* Scopes the word_id -> cell_index lookup to this
|
||||
* VM's own quota slot (item 4.2, FABRIC.md §25.5) so
|
||||
* two VMs' independently-numbered word_ids cannot
|
||||
* alias onto each other's cells/reservoirs.
|
||||
* @param word_id The dispatching DictEntry's stable word_id.
|
||||
* @param heartbeat_ticks Current vm->heartbeat.tick_count (virtual tick,
|
||||
* never wall-clock -- same convention as every other
|
||||
@@ -99,12 +116,34 @@ void stadium_word_dispatch(VMUuid vm_id, uint32_t word_id, uint64_t heartbeat_ti
|
||||
* its stale heat (same failure class as the 2026-08-02 block_words.c
|
||||
* aliasing bug). Evicts the cell if word_id is resident (crediting its heat
|
||||
* back to the reservoir via stadium_evict()'s own credit path) and clears
|
||||
* the map entry. No-op if word_id is not resident, out of range, or the
|
||||
* word layer is not initialized.
|
||||
* the map entry. No-op if word_id is not resident, out of range, or vm_id
|
||||
* holds no Stadium quota.
|
||||
*
|
||||
* @param vm_id Owning VM -- vm->stadium_vm_id (item 4.2, FABRIC.md §25.5:
|
||||
* scopes the lookup to this VM's own word_slots, same
|
||||
* reason stadium_word_dispatch() takes it).
|
||||
* @param word_id The DictEntry's word_id, about to be recycled.
|
||||
*/
|
||||
void stadium_word_forget(uint32_t word_id);
|
||||
void stadium_word_forget(VMUuid vm_id, uint32_t word_id);
|
||||
|
||||
/*
|
||||
* stadium_words_resident_heat - Sum of heat held by vm_id's own
|
||||
* word-execution residents only (item 4.1's cells, tracked in this file's
|
||||
* own word_slots map) -- NOT messages/channels/other application residents,
|
||||
* which stadium_resident_sum() (stadium.h, item 4.2) mixes in alongside
|
||||
* everything else a VM owns. Exists so a VM's own application-level
|
||||
* conservation check (e.g. Hermes's HERMES-K, FABRIC.md §25.7, Captain
|
||||
* Bob's ruling 2026-08-06) can add this as an explicit term instead of
|
||||
* silently omitting word-execution heat it has no other way to see.
|
||||
*
|
||||
* Walks all DICTIONARY_SIZE word_slots for vm_id's quota slot; each
|
||||
* resident entry contributes its cell's current heat, verified live against
|
||||
* the discriminator bitmap (same self-healing pattern as
|
||||
* resolve_resident_cell() -- a stale map entry contributes 0, not garbage).
|
||||
*
|
||||
* @param vm_id The VM whose word-execution residents to sum.
|
||||
*/
|
||||
uint64_t stadium_words_resident_heat(VMUuid vm_id);
|
||||
|
||||
/*
|
||||
* stadium_words_stats - Promotion/eviction counters (same shape as the old
|
||||
@@ -113,9 +152,10 @@ void stadium_word_forget(uint32_t word_id);
|
||||
* admission. Eviction = this word's cell was reaped by another admission's
|
||||
* eviction fallback (stadium_admit()'s density comparison), detected
|
||||
* lazily via the self-healing stale check in stadium_word_dispatch(), or
|
||||
* explicitly via stadium_word_forget().
|
||||
* explicitly via stadium_word_forget(). Scoped to vm_id's own quota slot
|
||||
* (item 4.2) -- counters are no longer system-wide.
|
||||
*/
|
||||
void stadium_words_stats(uint64_t *promotions, uint64_t *evictions);
|
||||
void stadium_words_stats(VMUuid vm_id, uint64_t *promotions, uint64_t *evictions);
|
||||
|
||||
/*
|
||||
* stadium_words_print_boot_diagnostics - Console output satisfying item
|
||||
@@ -123,11 +163,9 @@ void stadium_words_stats(uint64_t *promotions, uint64_t *evictions);
|
||||
* acceptance line. Prints promotions/evictions, then
|
||||
* Σ(resident heat) + reservoir against Q48_ONE as a conservation check --
|
||||
* not required by the acceptance text, but the mechanism proves nothing if
|
||||
* this silently doesn't hold. The heat sum is taken over ALL resident
|
||||
* Stadium cells, not scoped by owner -- correct only because vm_id is the
|
||||
* sole VM with any Stadium quota today (item 0.1's Hera-only pruning); the
|
||||
* per-cell owner byte is private to stadium.c and has no public accessor.
|
||||
* Revisit the scoping when item 4.2 restores Hermes.
|
||||
* this silently doesn't hold. The heat sum is scoped to vm_id's own quota
|
||||
* (stadium_resident_sum(), item 4.2, FABRIC.md §25.5) so two VMs' checks
|
||||
* close independently instead of mixing both VMs' resident heat together.
|
||||
*
|
||||
* @param vm_id The VM whose reservoir to read (vm_uuid_hera() today).
|
||||
*/
|
||||
|
||||
@@ -358,6 +358,8 @@ typedef enum
|
||||
} vm_mode_t;
|
||||
|
||||
#ifdef __STARKERNEL__
|
||||
#include "starkernel/vm_uuid.h" /* VMUuid -- item 4.2, VM.stadium_vm_id */
|
||||
|
||||
/* Saved interpreter state for VM lifecycle calls (BIRTH, VM-EXEC, START).
|
||||
* Dynamically allocated; grows on demand via sf_realloc. */
|
||||
typedef struct {
|
||||
@@ -529,6 +531,17 @@ typedef struct VM
|
||||
int call_stack_cap; /**< Allocated capacity in entries */
|
||||
int call_stack_max; /**< High-water mark depth (DoE metric) */
|
||||
/** @} */
|
||||
|
||||
/** @name Stadium Identity (item 4.2, FABRIC.md §25.5)
|
||||
* @{
|
||||
*/
|
||||
VMUuid stadium_vm_id; /**< This VM's own identity, set once at birth from
|
||||
* VMRegistryEntry.vm_id. Lets vm_core.c's
|
||||
* stadium_word_dispatch() call sites attribute
|
||||
* word-execution heat to the VM that actually
|
||||
* ran the word, instead of item 4.1's hardcoded
|
||||
* vm_uuid_hera(). */
|
||||
/** @} */
|
||||
#endif
|
||||
} VM;
|
||||
|
||||
|
||||
Binary file not shown.
@@ -135,7 +135,7 @@ void vm_dictionary_untrack_entry(VM *vm, DictEntry *entry) {
|
||||
* is recycled below -- otherwise the next word assigned this same id
|
||||
* would alias onto the forgotten word's stale cell (same failure class
|
||||
* as the 2026-08-02 block_words.c aliasing bug). */
|
||||
stadium_word_forget(word_id);
|
||||
stadium_word_forget(vm->stadium_vm_id, word_id);
|
||||
#endif
|
||||
|
||||
if (vm->word_id_map[word_id] == entry) {
|
||||
|
||||
@@ -475,6 +475,11 @@ CapsuleRunResult capsule_birth_baby(
|
||||
return CAPSULE_RUN_ERR_STILLBORN;
|
||||
}
|
||||
|
||||
/* item 4.2: set before the IDENTITY exec below, so any word this baby
|
||||
* dispatches during her own init capsule already attributes heat to her
|
||||
* own reservoir, not vm_uuid_hera()'s (item 4.1's hardcoded default). */
|
||||
((VM *)new_vm)->stadium_vm_id = vm_id;
|
||||
|
||||
const uint8_t *payload = capsule_get_payload(cap, arena);
|
||||
if (!payload) {
|
||||
entry->state = VM_STATE_STILLBORN;
|
||||
|
||||
@@ -44,6 +44,8 @@
|
||||
#include "starkernel/capsule_loader.h"
|
||||
#include "starkernel/capsule_vm_physics.h"
|
||||
#include "starkernel/vm/vm_internal.h"
|
||||
#include "starkernel/vm/stadium.h" /* item 4.2 -- STADIUM-* words */
|
||||
#include "starkernel/vm/stadium_words.h" /* item 4.2 -- stadium_words_resident_heat() */
|
||||
#include "starkernel/repl.h"
|
||||
#include "starkernel/capsule_generated.h"
|
||||
#include "starkernel/console.h"
|
||||
@@ -1156,16 +1158,205 @@ void register_mama_forth_words(VM *vm)
|
||||
vocabulary_word_definitions(vm);
|
||||
}
|
||||
|
||||
/* ============================================================================
|
||||
* Stadium Words (FABRIC.md punch list item 4.2)
|
||||
*
|
||||
* The entire C surface item 4.2 is permitted to add, per HERMES.md's
|
||||
* language constraint: all eight operate on the CALLING VM's own identity
|
||||
* (vm->stadium_vm_id) implicitly, never a FORTH-supplied vm-id. A
|
||||
* stack-passed vm-id could only ever be the caller's own (redundant) or
|
||||
* another VM's (stadium_admit()/stadium_evict() would refuse it via quota
|
||||
* isolation, except STADIUM-RES-PUSH, which has no such guard and would be
|
||||
* an outright heat-forgery primitive against another VM's reservoir).
|
||||
* Conservation is the invariant this item is verified against, so implicit
|
||||
* self is not an optimization -- it's the only version that can't break it.
|
||||
*
|
||||
* STADIUM-ADMIT does not itself pull the candidate's heat from the
|
||||
* reservoir -- that's STADIUM-RES-PULL's job, composed in FORTH by the
|
||||
* caller (e.g. a rewritten MSG-ALLOC): pull first, admit with the pulled
|
||||
* amount, and STADIUM-RES-PUSH it back if admission refuses. Mirrors
|
||||
* stadium_words.c's C-side Option B starter-grant pattern, but the
|
||||
* composition itself lives in StarForth, not here, per HERMES.md.
|
||||
* ============================================================================ */
|
||||
|
||||
/**
|
||||
* @brief STADIUM-ADMIT ( identity heat behaviour -- cell | -1 )
|
||||
* Admits a mass-1 patron into the calling VM's own Stadium quota.
|
||||
* `behaviour` must be a valid StadiumBehaviour tag (0..3); anything else
|
||||
* refuses without calling stadium_admit() at all. `contains` is always
|
||||
* explicitly STADIUM_CONTAINS_NONE -- stadium_admit()'s own doc warns a
|
||||
* zero-initialized `contains` reads as "contains Hera" (index 0) and
|
||||
* permanently blocks eviction, so this is never left to a zero-fill.
|
||||
*/
|
||||
static void mama_word_stadium_admit(VM *vm)
|
||||
{
|
||||
cell_t behaviour_cell, heat_cell, identity_cell;
|
||||
StadiumPatronHeader candidate;
|
||||
uint8_t *raw = (uint8_t *)&candidate;
|
||||
size_t i;
|
||||
size_t idx;
|
||||
|
||||
if (vm->dsp < 2) { vm->error = 1; return; }
|
||||
|
||||
behaviour_cell = vm_pop(vm);
|
||||
heat_cell = vm_pop(vm);
|
||||
identity_cell = vm_pop(vm);
|
||||
|
||||
if (behaviour_cell < STADIUM_BEHAVIOUR_MIGRATE || behaviour_cell > STADIUM_BEHAVIOUR_COOL) {
|
||||
vm_push(vm, (cell_t)-1);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(candidate); i++) raw[i] = 0;
|
||||
candidate.identity = (uint64_t)identity_cell;
|
||||
candidate.heat = (uint64_t)heat_cell;
|
||||
candidate.ttl = 0;
|
||||
candidate.link = 0;
|
||||
candidate.contains = STADIUM_CONTAINS_NONE;
|
||||
candidate.mass = 1;
|
||||
candidate.flags = 0;
|
||||
candidate.behaviour = (uint8_t)behaviour_cell;
|
||||
|
||||
idx = stadium_admit(vm->stadium_vm_id, &candidate);
|
||||
vm_push(vm, (idx == STADIUM_CELL_NONE) ? (cell_t)-1 : (cell_t)idx);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief STADIUM-EVICT ( cell -- flag )
|
||||
* Reaps the patron header at `cell`. flag is FORTH true (-1) on success,
|
||||
* false (0) if refused (out of range, not resident, pinned, or contains-
|
||||
* gated) -- stadium_evict()'s own refusal set, unchanged here.
|
||||
*/
|
||||
static void mama_word_stadium_evict(VM *vm)
|
||||
{
|
||||
cell_t cell_cell;
|
||||
|
||||
if (vm->dsp < 0) { vm->error = 1; return; }
|
||||
|
||||
cell_cell = vm_pop(vm);
|
||||
if (cell_cell < 0) {
|
||||
vm_push(vm, (cell_t)0);
|
||||
return;
|
||||
}
|
||||
vm_push(vm, (stadium_evict((size_t)cell_cell) == 0) ? (cell_t)-1 : (cell_t)0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief STADIUM-RES@ ( -- heat )
|
||||
* Read-only peek at the calling VM's own reservoir balance (Q48.16).
|
||||
*/
|
||||
static void mama_word_stadium_res_fetch(VM *vm)
|
||||
{
|
||||
vm_push(vm, (cell_t)stadium_reservoir_peek(vm->stadium_vm_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief STADIUM-WORD-HEAT ( -- heat )
|
||||
* Sum of heat held by the calling VM's own word-execution residents
|
||||
* (item 4.1's cells) -- the term a VM's own application-level conservation
|
||||
* check (e.g. Hermes's HERMES-K) needs to close exactly, since word patrons
|
||||
* are otherwise invisible to FORTH (FABRIC.md §25.7, ruling 2026-08-06).
|
||||
*/
|
||||
static void mama_word_stadium_word_heat(VM *vm)
|
||||
{
|
||||
vm_push(vm, (cell_t)stadium_words_resident_heat(vm->stadium_vm_id));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief STADIUM-RES-PULL ( qty -- heat )
|
||||
* Pulls up to `qty` (Q48.16) from the calling VM's own reservoir. Returns
|
||||
* the amount actually pulled, which may be less than requested -- never
|
||||
* negative, never invents heat, mirrors stadium_reservoir_pull()'s own
|
||||
* clamping exactly.
|
||||
*/
|
||||
static void mama_word_stadium_res_pull(VM *vm)
|
||||
{
|
||||
cell_t qty_cell;
|
||||
|
||||
if (vm->dsp < 0) { vm->error = 1; return; }
|
||||
|
||||
qty_cell = vm_pop(vm);
|
||||
if (qty_cell < 0) {
|
||||
vm_push(vm, (cell_t)0);
|
||||
return;
|
||||
}
|
||||
vm_push(vm, (cell_t)stadium_reservoir_pull(vm->stadium_vm_id, (uint64_t)qty_cell));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief STADIUM-RES-PUSH ( heat -- )
|
||||
* Credits `heat` (Q48.16) back into the calling VM's own reservoir -- the
|
||||
* other half of every reservoir transfer (cooling, refused-admission
|
||||
* rollback, or a departing patron's remaining heat after eviction).
|
||||
*/
|
||||
static void mama_word_stadium_res_push(VM *vm)
|
||||
{
|
||||
cell_t heat_cell;
|
||||
|
||||
if (vm->dsp < 0) { vm->error = 1; return; }
|
||||
|
||||
heat_cell = vm_pop(vm);
|
||||
if (heat_cell < 0) return;
|
||||
stadium_reservoir_push(vm->stadium_vm_id, (uint64_t)heat_cell);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief STADIUM-HEAT@ ( cell -- heat )
|
||||
* Reads a resident cell's own heat. Requires the cell to be resident and
|
||||
* owned by the calling VM's own quota -- returns 0 otherwise (out of range,
|
||||
* not resident, or belongs to a different VM).
|
||||
*/
|
||||
static void mama_word_stadium_heat_fetch(VM *vm)
|
||||
{
|
||||
cell_t cell_cell;
|
||||
|
||||
if (vm->dsp < 0) { vm->error = 1; return; }
|
||||
|
||||
cell_cell = vm_pop(vm);
|
||||
if (cell_cell < 0) {
|
||||
vm_push(vm, (cell_t)0);
|
||||
return;
|
||||
}
|
||||
vm_push(vm, (cell_t)stadium_cell_heat_get(vm->stadium_vm_id, (size_t)cell_cell));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief STADIUM-HEAT! ( new-heat cell -- )
|
||||
* Writes a resident cell's own heat, reconciling the reservoir delta
|
||||
* atomically in C (pulls on an increase, refusing silently if the
|
||||
* calling VM's reservoir can't cover it; pushes back on a decrease).
|
||||
* Requires the cell to be resident and owned by the calling VM's own
|
||||
* quota -- silently refused otherwise, same as every other write here.
|
||||
*/
|
||||
static void mama_word_stadium_heat_store(VM *vm)
|
||||
{
|
||||
cell_t cell_cell, new_heat_cell;
|
||||
|
||||
if (vm->dsp < 1) { vm->error = 1; return; }
|
||||
|
||||
cell_cell = vm_pop(vm);
|
||||
new_heat_cell = vm_pop(vm);
|
||||
if (cell_cell < 0 || new_heat_cell < 0) return;
|
||||
(void)stadium_cell_heat_set(vm->stadium_vm_id, (size_t)cell_cell, (uint64_t)new_heat_cell);
|
||||
}
|
||||
|
||||
/**
|
||||
* register_child_vm_words - Register the minimal word set needed by child VMs.
|
||||
*
|
||||
* Child VMs are not bootstrapped through sk_vm_bootstrap_parity, so they
|
||||
* do not get register_mama_forth_words(). They only need STOP (self-halt)
|
||||
* and EXEC (load a capsule). Keeping the registrations here — in the same
|
||||
* translation unit as the word functions — avoids cross-TU function-pointer
|
||||
* loads that produce R_X86_64_REX_GOTPCRELX relocations; those are not
|
||||
* relaxed by the PE32+ linker, causing the function code bytes to be read
|
||||
* as the pointer value instead of the actual address.
|
||||
* and EXEC (load a capsule) -- plus, as of item 4.2, the eight STADIUM-*
|
||||
* primitives Hermes needs to migrate her message/channel lifecycle onto the
|
||||
* Stadium. Keeping the registrations here — in the same translation unit
|
||||
* as the word functions — avoids cross-TU function-pointer loads that
|
||||
* produce R_X86_64_REX_GOTPCRELX relocations; those are not relaxed by the
|
||||
* PE32+ linker, causing the function code bytes to be read as the pointer
|
||||
* value instead of the actual address.
|
||||
*
|
||||
* Deliberately NOT added to register_mama_forth_words(): that would put
|
||||
* these words in Hera's own dictionary too and move dict_hash off item
|
||||
* 4.1's baseline (0x3d4e1daf289da94f) -- a deliberate baseline change to
|
||||
* state this item does not make as a side effect.
|
||||
*/
|
||||
void register_child_vm_words(VM *vm)
|
||||
{
|
||||
@@ -1173,6 +1364,14 @@ void register_child_vm_words(VM *vm)
|
||||
register_word(vm, "EXEC", mama_word_exec);
|
||||
register_word(vm, "VM-EXEC", mama_word_vm_exec);
|
||||
register_word(vm, "VM-CALL", mama_word_vm_call);
|
||||
register_word(vm, "STADIUM-ADMIT", mama_word_stadium_admit);
|
||||
register_word(vm, "STADIUM-EVICT", mama_word_stadium_evict);
|
||||
register_word(vm, "STADIUM-RES@", mama_word_stadium_res_fetch);
|
||||
register_word(vm, "STADIUM-RES-PULL", mama_word_stadium_res_pull);
|
||||
register_word(vm, "STADIUM-RES-PUSH", mama_word_stadium_res_push);
|
||||
register_word(vm, "STADIUM-HEAT@", mama_word_stadium_heat_fetch);
|
||||
register_word(vm, "STADIUM-HEAT!", mama_word_stadium_heat_store);
|
||||
register_word(vm, "STADIUM-WORD-HEAT", mama_word_stadium_word_heat);
|
||||
}
|
||||
|
||||
#endif /* __STARKERNEL__ */
|
||||
|
||||
@@ -165,6 +165,30 @@ static void print_uint(const char *label, uint64_t value) {
|
||||
console_println(buf);
|
||||
}
|
||||
|
||||
#ifdef STARFORTH_ENABLE_VM
|
||||
/**
|
||||
* @brief Verify a known word is still reachable in a VM's dictionary.
|
||||
*
|
||||
* Diagnostic-only: confirms @c vm_find_word() can still walk the chain
|
||||
* from @c vm->latest to a word defined early in Hermes's capsule
|
||||
* (MSG-COOL-ALL, block 4108). Added after item 4.2's self-test bisection
|
||||
* found an amd64-only in-place corruption of an existing dictionary entry
|
||||
* during MSG-DELIVER-ALL -- here/latest stay unchanged (no reallocation),
|
||||
* so a lookup is a cheap signal. NOTE: an earlier version of this comment
|
||||
* attributed the corruption to GDB perturbing execution timing; that is
|
||||
* unconfirmed and more likely just a parity/dict-hash boot-gate failure
|
||||
* triggered by the debugger session itself (a software breakpoint's 0xCC
|
||||
* patch landing in memory the loader then overwrote) -- don't propagate
|
||||
* "timing-sensitive" as an established finding.
|
||||
*/
|
||||
static void hermes_dict_check(VM *hermes_vm, const char *checkpoint) {
|
||||
DictEntry *e = vm_find_word(hermes_vm, "MSG-COOL-ALL", 12);
|
||||
console_puts(" Dict-check ");
|
||||
console_puts(checkpoint);
|
||||
console_println(e ? ": OK" : ": FAIL (MSG-COOL-ALL unreachable)");
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Print a boot-information summary from the UEFI memory map to the console.
|
||||
*
|
||||
@@ -648,6 +672,101 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
}
|
||||
}
|
||||
|
||||
/* item 4.2 self-test: a REAL birth (not synthetic, unlike 4.1a's --
|
||||
* this exercises capsules/hermes/init.4th's actual migrated code),
|
||||
* exercised then killed again so the resting boot state stays
|
||||
* Hera-alone, per item 0.1's intent. Diagnostic only -- production
|
||||
* boot still never auto-births Hermes (init.4th's BIRTH stays
|
||||
* commented out). */
|
||||
console_println("Hermes 4.2 migration self-test: birthing...");
|
||||
vm_interpret(mama, "S\" Hermes\" BIRTH");
|
||||
{
|
||||
VMRegistryEntry entry;
|
||||
if (capsule_vm_find_by_name_nocase("Hermes", &entry) == 0 &&
|
||||
entry.state == VM_STATE_LIVE) {
|
||||
VM *hermes_vm = (VM *)entry.vm_ptr;
|
||||
console_println("Hermes 4.2 self-test: exercising migrated words...");
|
||||
hermes_dict_check(hermes_vm, "at self-test start");
|
||||
hermes_vm->error = 0;
|
||||
vm_interpret(hermes_vm, "CD-INIT");
|
||||
print_uint(" DBG err after CD-INIT=", (uint64_t)hermes_vm->error);
|
||||
hermes_dict_check(hermes_vm, "after CD-INIT");
|
||||
hermes_vm->error = 0;
|
||||
vm_interpret(hermes_vm, "HERMES-MSG-TEST . CR");
|
||||
print_uint(" DBG err after MSG-TEST=", (uint64_t)hermes_vm->error);
|
||||
hermes_dict_check(hermes_vm, "after MSG-TEST");
|
||||
hermes_vm->error = 0;
|
||||
vm_interpret(hermes_vm, "HERMES-STATUS");
|
||||
print_uint(" DBG err after STATUS=", (uint64_t)hermes_vm->error);
|
||||
hermes_dict_check(hermes_vm, "after STATUS");
|
||||
hermes_vm->error = 0;
|
||||
vm_interpret(hermes_vm, "MSG-DELIVER-ALL");
|
||||
print_uint(" DBG err after DELIVER-ALL=", (uint64_t)hermes_vm->error);
|
||||
hermes_dict_check(hermes_vm, "after DELIVER-ALL");
|
||||
hermes_vm->error = 0;
|
||||
vm_interpret(hermes_vm, "MSG-REDELIVER-NACKED");
|
||||
print_uint(" DBG err after REDELIVER=", (uint64_t)hermes_vm->error);
|
||||
hermes_vm->error = 0;
|
||||
vm_interpret(hermes_vm, "MSG-COOL-ALL");
|
||||
print_uint(" DBG err after MSG-COOL-ALL=", (uint64_t)hermes_vm->error);
|
||||
hermes_vm->error = 0;
|
||||
vm_interpret(hermes_vm, "MSG-REAP");
|
||||
print_uint(" DBG err after MSG-REAP=", (uint64_t)hermes_vm->error);
|
||||
hermes_vm->error = 0;
|
||||
vm_interpret(hermes_vm, "CH-COOL-ALL CH-REAP-SAFE");
|
||||
print_uint(" DBG err after CH-COOL/REAP=", (uint64_t)hermes_vm->error);
|
||||
hermes_vm->error = 0;
|
||||
vm_interpret(hermes_vm, "Q.1 3 / COMMON-CH @ CH-HEAT!");
|
||||
print_uint(" DBG err after CH-HEAT!=", (uint64_t)hermes_vm->error);
|
||||
hermes_vm->error = 0;
|
||||
vm_interpret(hermes_vm, "HERMES-STATUS");
|
||||
print_uint(" DBG err after TICK=", (uint64_t)hermes_vm->error);
|
||||
hermes_vm->error = 0;
|
||||
console_puts("Hermes 4.2 self-test: HERMES-K=");
|
||||
vm_interpret(hermes_vm, "HERMES-K .");
|
||||
console_println("");
|
||||
print_uint(" DBG err after HERMES-K=", (uint64_t)hermes_vm->error);
|
||||
/* Side-by-side with the Stadium's own view, same checkpoint --
|
||||
* if these two disagree, HERMES-K's FORTH-side arena scan is
|
||||
* seeing different residents than stadium_resident_sum()'s
|
||||
* ownership+bitmap view (a real, separate finding, not the
|
||||
* Q.SLOT admission-heat fix's job to explain). */
|
||||
print_uint(" DBG stadium_resident_sum(Hermes)=", stadium_resident_sum(entry.vm_id));
|
||||
print_uint(" DBG stadium_reservoir_peek(Hermes)=", stadium_reservoir_peek(entry.vm_id));
|
||||
/* item 4.2 Done-when: "a resident cell's evict-credit landing
|
||||
* in the correct VM's reservoir ... not just asserted from
|
||||
* reading the code" -- explicitly evict the common channel
|
||||
* (CH-FREE-NODE -> STADIUM-EVICT), a known resident from
|
||||
* CD-INIT's own COMMON-INIT, same credit path stadium_admit()'s
|
||||
* density-fallback eviction uses. resident_sum dropping and
|
||||
* reservoir rising by the same amount is the proof: the freed
|
||||
* cell's heat landed back in Hermes's own reservoir, not lost
|
||||
* or credited to Hera. */
|
||||
console_println("Hermes 4.2 self-test: before eviction:");
|
||||
print_uint(" Hermes resident_sum=", stadium_resident_sum(entry.vm_id));
|
||||
print_uint(" Hermes reservoir=", stadium_reservoir_peek(entry.vm_id));
|
||||
console_println("Hermes 4.2 self-test: forcing an explicit eviction (COMMON-CH)...");
|
||||
hermes_vm->error = 0;
|
||||
vm_interpret(hermes_vm, "COMMON-CH @ . CR");
|
||||
print_uint(" DBG err after COMMON-CH@=", (uint64_t)hermes_vm->error);
|
||||
hermes_vm->error = 0;
|
||||
vm_interpret(hermes_vm, "COMMON-CH @ CH-FREE-NODE");
|
||||
print_uint(" DBG err after CH-FREE-NODE=", (uint64_t)hermes_vm->error);
|
||||
console_println("Hermes 4.2 self-test: after eviction:");
|
||||
print_uint(" Hermes resident_sum=", stadium_resident_sum(entry.vm_id));
|
||||
print_uint(" Hermes reservoir=", stadium_reservoir_peek(entry.vm_id));
|
||||
/* item 4.2 Done-when: both VMs' conservation checks close
|
||||
* independently -- Hermes's own resident+reservoir sum first,
|
||||
* then Hera's again (unaffected by Hermes's activity above). */
|
||||
stadium_words_print_boot_diagnostics(entry.vm_id);
|
||||
vm_interpret(mama, "S\" Hermes\" KILL");
|
||||
console_println("Hermes 4.2 self-test: killed, resting state restored");
|
||||
stadium_words_print_boot_diagnostics(vm_uuid_hera());
|
||||
} else {
|
||||
console_println("Hermes 4.2 self-test: birth registry lookup FAILED");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Runtime --doe flag: inject "EXEC-DOE BYE" if requested via boot args.
|
||||
* Checked before SK_STARTUP_FORTH so a runtime --doe takes precedence.
|
||||
|
||||
@@ -271,6 +271,7 @@ int sk_vm_bootstrap_parity(ParityPacket *out) {
|
||||
* capsule_birth_baby() which never calls vm_init_with_host(). */
|
||||
capsule_vm_hooks_register();
|
||||
capsule_vm_registry_init(vm); /* establishes [Hera] console prefix */
|
||||
vm->stadium_vm_id = vm_uuid_hera(); /* item 4.2 */
|
||||
vm_physics_init(vm_uuid_hera()); /* Hera: the fleet's root, seeded Q48_ONE */
|
||||
capsule_run_log_init();
|
||||
register_mama_forth_words(vm); /* BIRTH KILL START STOP USE + capsule words */
|
||||
|
||||
@@ -358,6 +358,10 @@ size_t stadium_admit(VMUuid vm_id, const StadiumPatronHeader *candidate) {
|
||||
stadium_quotas[slot].free_head = link_to_size(stadium_cell_array[idx].header.link);
|
||||
stadium_cell_array[idx].header = *candidate;
|
||||
bitmap_set(idx);
|
||||
/* Item 4.2 fix (§25.7): record ownership so stadium_evict()'s
|
||||
* reservoir credit and free-list return land on the VM that actually
|
||||
* admitted this patron, not whatever owner[idx] held at boot. */
|
||||
stadium_owner[idx] = (uint8_t)slot;
|
||||
return idx;
|
||||
}
|
||||
|
||||
@@ -395,6 +399,11 @@ size_t stadium_admit(VMUuid vm_id, const StadiumPatronHeader *candidate) {
|
||||
stadium_quotas[slot].free_head = link_to_size(stadium_cell_array[idx].header.link);
|
||||
stadium_cell_array[idx].header = *candidate;
|
||||
bitmap_set(idx);
|
||||
/* Same fix as the free-list-pop path above -- stadium_evict() just wrote
|
||||
* owner[idx] = slot as part of reaping least_dense_index, so this is
|
||||
* currently a no-op in practice, but it must not be assumed to stay a
|
||||
* no-op: this is the correctness statement, not a redundant write. */
|
||||
stadium_owner[idx] = (uint8_t)slot;
|
||||
return idx;
|
||||
}
|
||||
|
||||
@@ -453,6 +462,45 @@ int stadium_grant_quota(VMUuid new_vm_id, VMUuid from_vm_id) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Shared by stadium_cell_heat_get()/_set(): resident AND owned by vm_id's
|
||||
* own quota slot. Returns the quota slot on success, -1 on any refusal. */
|
||||
static int owned_resident_slot(VMUuid vm_id, size_t cell_index) {
|
||||
int slot = quota_slot_for_vm(vm_id);
|
||||
if (slot < 0) return -1;
|
||||
if (cell_index >= stadium_ncells) return -1;
|
||||
if (!bitmap_get(cell_index)) return -1;
|
||||
if (stadium_owner[cell_index] != (uint8_t)slot) return -1;
|
||||
return slot;
|
||||
}
|
||||
|
||||
uint64_t stadium_cell_heat_get(VMUuid vm_id, size_t cell_index) {
|
||||
if (owned_resident_slot(vm_id, cell_index) < 0) return 0;
|
||||
return stadium_cell_array[cell_index].header.heat;
|
||||
}
|
||||
|
||||
int stadium_cell_heat_set(VMUuid vm_id, size_t cell_index, uint64_t new_heat) {
|
||||
int slot = owned_resident_slot(vm_id, cell_index);
|
||||
uint64_t old_heat, delta, pulled;
|
||||
|
||||
if (slot < 0) return -1;
|
||||
|
||||
old_heat = stadium_cell_array[cell_index].header.heat;
|
||||
if (new_heat == old_heat) return 0;
|
||||
|
||||
if (new_heat > old_heat) {
|
||||
delta = new_heat - old_heat;
|
||||
pulled = (delta > stadium_quotas[slot].reservoir) ? stadium_quotas[slot].reservoir : delta;
|
||||
if (pulled < delta) return -1; /* insufficient -- no partial credit, no mutation */
|
||||
stadium_quotas[slot].reservoir -= pulled;
|
||||
} else {
|
||||
delta = old_heat - new_heat;
|
||||
stadium_quotas[slot].reservoir += delta;
|
||||
}
|
||||
|
||||
stadium_cell_array[cell_index].header.heat = new_heat;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t stadium_reservoir_pull(VMUuid vm_id, uint64_t amount) {
|
||||
int slot = quota_slot_for_vm(vm_id);
|
||||
uint64_t pulled;
|
||||
@@ -478,6 +526,25 @@ uint64_t stadium_reservoir_peek(VMUuid vm_id) {
|
||||
return stadium_quotas[slot].reservoir;
|
||||
}
|
||||
|
||||
int stadium_quota_slot_for_vm(VMUuid vm_id) {
|
||||
return quota_slot_for_vm(vm_id);
|
||||
}
|
||||
|
||||
uint64_t stadium_resident_sum(VMUuid vm_id) {
|
||||
int slot = quota_slot_for_vm(vm_id);
|
||||
uint64_t sum = 0;
|
||||
size_t i;
|
||||
|
||||
if (slot < 0) return 0;
|
||||
|
||||
for (i = 0; i < stadium_ncells; i++) {
|
||||
if (!bitmap_get(i)) continue;
|
||||
if (stadium_owner[i] != (uint8_t)slot) continue;
|
||||
sum += stadium_cell_array[i].header.heat;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
/*
|
||||
* FABRIC.md item 3.6 / item 4.1: see stadium.h's doc. Idempotent via the
|
||||
* item-3.1 discriminator bitmap -- if cell 0 already reads as resident,
|
||||
|
||||
@@ -40,25 +40,37 @@
|
||||
* 2026-08-05: no DictEntry field). `last_decay_tick` is this layer's own
|
||||
* bookkeeping, separate from DictEntry.physics.last_decay_tick -- that field
|
||||
* belongs to execution_heat's decay, which item 4.1 does not touch.
|
||||
*
|
||||
* item 4.2 fix (FABRIC.md §25.5): keyed by [quota slot][word_id], not just
|
||||
* word_id. word_id is assigned per-VM (vm->next_word_id in
|
||||
* dictionary_management.c), not globally unique -- a single shared
|
||||
* word_id -> cell_index map let two VMs' independently-numbered word_ids
|
||||
* (e.g. both VMs' own "DUP") alias onto the same slot, so one VM's dispatch
|
||||
* could cool/heat-pump a cell it did not own and credit/debit the wrong
|
||||
* VM's reservoir. Exposed only because item 4.2 restored a second VM
|
||||
* (Hermes) with her own dictionary; invisible with Hera alone.
|
||||
*/
|
||||
typedef struct {
|
||||
size_t cell_index; /* STADIUM_CELL_NONE if not resident */
|
||||
uint64_t last_decay_tick;
|
||||
} StadiumWordSlot;
|
||||
|
||||
static StadiumWordSlot word_slots[DICTIONARY_SIZE];
|
||||
static StadiumWordSlot word_slots[STADIUM_MAX_VM_COUNT][DICTIONARY_SIZE];
|
||||
static int words_initialized = 0;
|
||||
static uint64_t stat_promotions = 0;
|
||||
static uint64_t stat_evictions = 0;
|
||||
static uint64_t stat_promotions[STADIUM_MAX_VM_COUNT];
|
||||
static uint64_t stat_evictions[STADIUM_MAX_VM_COUNT];
|
||||
|
||||
void stadium_words_init(void) {
|
||||
int slot;
|
||||
uint32_t i;
|
||||
for (i = 0; i < DICTIONARY_SIZE; i++) {
|
||||
word_slots[i].cell_index = STADIUM_CELL_NONE;
|
||||
word_slots[i].last_decay_tick = 0;
|
||||
for (slot = 0; slot < STADIUM_MAX_VM_COUNT; slot++) {
|
||||
for (i = 0; i < DICTIONARY_SIZE; i++) {
|
||||
word_slots[slot][i].cell_index = STADIUM_CELL_NONE;
|
||||
word_slots[slot][i].last_decay_tick = 0;
|
||||
}
|
||||
stat_promotions[slot] = 0;
|
||||
stat_evictions[slot] = 0;
|
||||
}
|
||||
stat_promotions = 0;
|
||||
stat_evictions = 0;
|
||||
words_initialized = 1;
|
||||
}
|
||||
|
||||
@@ -68,6 +80,29 @@ static int cell_is_resident(size_t idx) {
|
||||
return (bm[idx / 8u] >> (idx % 8u)) & 1u;
|
||||
}
|
||||
|
||||
/*
|
||||
* word_dispatch_pull - Reservoir pull for word-execution admission, clamped
|
||||
* to leave a floor for application-level use (FABRIC.md §25.5/§25.7,
|
||||
* Captain Bob's ruling 2026-08-06). Without this, stadium_word_dispatch()
|
||||
* pulling STADIUM_WORD_HEAT_QUANTUM on every dispatch -- not just the first
|
||||
* admission of a given word -- exhausts a VM's entire reservoir within
|
||||
* roughly 32 total dispatches (65536 / 2048), starving any item-4.2-style
|
||||
* application economy sharing the same VM's reservoir before it gets a
|
||||
* chance to pull anything. The floor is Q48_ONE / 3, the same "VM-COUNT=3
|
||||
* fair share" reasoning capsules/hermes/init.4th's COMMON-CH floor already
|
||||
* uses -- not a new invented number. Application-level pulls
|
||||
* (stadium_reservoir_pull() called directly, e.g. via STADIUM-RES-PULL) are
|
||||
* NOT floored -- only word-execution admission respects this ceiling on
|
||||
* its own consumption.
|
||||
*/
|
||||
static uint64_t word_dispatch_pull(VMUuid vm_id, uint64_t want) {
|
||||
uint64_t available = stadium_reservoir_peek(vm_id);
|
||||
uint64_t floor = Q48_ONE / 3;
|
||||
uint64_t pullable = (available > floor) ? (available - floor) : 0;
|
||||
uint64_t capped = (want < pullable) ? want : pullable;
|
||||
return stadium_reservoir_pull(vm_id, capped);
|
||||
}
|
||||
|
||||
/*
|
||||
* resolve_resident_cell - Self-healing lookup (advisor-flagged reverse
|
||||
* coherence gap): the map may claim word_id is resident at a cell that was
|
||||
@@ -77,36 +112,59 @@ static int cell_is_resident(size_t idx) {
|
||||
* no new coupling from stadium.c into this file. A stale mapping is cleared
|
||||
* and counted as an eviction on discovery.
|
||||
*/
|
||||
static size_t resolve_resident_cell(uint32_t word_id) {
|
||||
size_t cell = word_slots[word_id].cell_index;
|
||||
static size_t resolve_resident_cell(int slot, uint32_t word_id) {
|
||||
size_t cell = word_slots[slot][word_id].cell_index;
|
||||
StadiumCell *cells;
|
||||
|
||||
if (cell == STADIUM_CELL_NONE) return STADIUM_CELL_NONE;
|
||||
if (cell >= stadium_cell_count() || !cell_is_resident(cell)) {
|
||||
word_slots[word_id].cell_index = STADIUM_CELL_NONE;
|
||||
stat_evictions++;
|
||||
word_slots[slot][word_id].cell_index = STADIUM_CELL_NONE;
|
||||
stat_evictions[slot]++;
|
||||
return STADIUM_CELL_NONE;
|
||||
}
|
||||
cells = stadium_cells();
|
||||
if (cells[cell].header.identity != (uint64_t)word_id) {
|
||||
word_slots[word_id].cell_index = STADIUM_CELL_NONE;
|
||||
stat_evictions++;
|
||||
word_slots[slot][word_id].cell_index = STADIUM_CELL_NONE;
|
||||
stat_evictions[slot]++;
|
||||
return STADIUM_CELL_NONE;
|
||||
}
|
||||
return cell;
|
||||
}
|
||||
|
||||
uint64_t stadium_words_resident_heat(VMUuid vm_id) {
|
||||
int slot;
|
||||
uint32_t i;
|
||||
uint64_t sum = 0;
|
||||
StadiumCell *cells;
|
||||
|
||||
if (!words_initialized) return 0;
|
||||
slot = stadium_quota_slot_for_vm(vm_id);
|
||||
if (slot < 0) return 0;
|
||||
|
||||
cells = stadium_cells();
|
||||
for (i = 0; i < DICTIONARY_SIZE; i++) {
|
||||
size_t cell = resolve_resident_cell(slot, i);
|
||||
if (cell == STADIUM_CELL_NONE) continue;
|
||||
sum += cells[cell].header.heat;
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
void stadium_word_dispatch(VMUuid vm_id, uint32_t word_id, uint64_t heartbeat_ticks) {
|
||||
int slot;
|
||||
size_t cell;
|
||||
|
||||
if (!words_initialized) return;
|
||||
if (word_id == WORD_ID_INVALID || word_id >= DICTIONARY_SIZE) return;
|
||||
|
||||
cell = resolve_resident_cell(word_id);
|
||||
slot = stadium_quota_slot_for_vm(vm_id);
|
||||
if (slot < 0) return;
|
||||
|
||||
cell = resolve_resident_cell(slot, word_id);
|
||||
|
||||
if (cell != STADIUM_CELL_NONE) {
|
||||
StadiumPatronHeader *h = &stadium_cells()[cell].header;
|
||||
uint64_t elapsed = heartbeat_ticks - word_slots[word_id].last_decay_tick;
|
||||
uint64_t elapsed = heartbeat_ticks - word_slots[slot][word_id].last_decay_tick;
|
||||
|
||||
if (elapsed > 0) {
|
||||
/* Redirected Loop #3 (§17.7): a FRACTION of the cell's own
|
||||
@@ -120,17 +178,17 @@ void stadium_word_dispatch(VMUuid vm_id, uint32_t word_id, uint64_t heartbeat_ti
|
||||
h->heat -= cooled;
|
||||
stadium_reservoir_push(vm_id, cooled);
|
||||
}
|
||||
word_slots[word_id].last_decay_tick = heartbeat_ticks;
|
||||
word_slots[slot][word_id].last_decay_tick = heartbeat_ticks;
|
||||
}
|
||||
|
||||
h->heat += stadium_reservoir_pull(vm_id, (uint64_t)STADIUM_WORD_HEAT_QUANTUM);
|
||||
h->heat += word_dispatch_pull(vm_id, (uint64_t)STADIUM_WORD_HEAT_QUANTUM);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Not resident: Option B starter-grant admission (§17.7). execution_heat
|
||||
* plays no role -- density is decided entirely by the pulled quantum. */
|
||||
{
|
||||
uint64_t pulled = stadium_reservoir_pull(vm_id, (uint64_t)STADIUM_WORD_HEAT_QUANTUM);
|
||||
uint64_t pulled = word_dispatch_pull(vm_id, (uint64_t)STADIUM_WORD_HEAT_QUANTUM);
|
||||
StadiumPatronHeader candidate;
|
||||
uint8_t *raw = (uint8_t *)&candidate;
|
||||
size_t i;
|
||||
@@ -152,30 +210,36 @@ void stadium_word_dispatch(VMUuid vm_id, uint32_t word_id, uint64_t heartbeat_ti
|
||||
return;
|
||||
}
|
||||
|
||||
word_slots[word_id].cell_index = idx;
|
||||
word_slots[word_id].last_decay_tick = heartbeat_ticks;
|
||||
stat_promotions++;
|
||||
word_slots[slot][word_id].cell_index = idx;
|
||||
word_slots[slot][word_id].last_decay_tick = heartbeat_ticks;
|
||||
stat_promotions[slot]++;
|
||||
}
|
||||
}
|
||||
|
||||
void stadium_word_forget(uint32_t word_id) {
|
||||
void stadium_word_forget(VMUuid vm_id, uint32_t word_id) {
|
||||
int slot;
|
||||
size_t cell;
|
||||
|
||||
if (!words_initialized) return;
|
||||
if (word_id == WORD_ID_INVALID || word_id >= DICTIONARY_SIZE) return;
|
||||
|
||||
cell = resolve_resident_cell(word_id);
|
||||
slot = stadium_quota_slot_for_vm(vm_id);
|
||||
if (slot < 0) return;
|
||||
|
||||
cell = resolve_resident_cell(slot, word_id);
|
||||
if (cell == STADIUM_CELL_NONE) return;
|
||||
|
||||
if (stadium_evict(cell) == 0) {
|
||||
word_slots[word_id].cell_index = STADIUM_CELL_NONE;
|
||||
stat_evictions++;
|
||||
word_slots[slot][word_id].cell_index = STADIUM_CELL_NONE;
|
||||
stat_evictions[slot]++;
|
||||
}
|
||||
}
|
||||
|
||||
void stadium_words_stats(uint64_t *promotions, uint64_t *evictions) {
|
||||
if (promotions) *promotions = stat_promotions;
|
||||
if (evictions) *evictions = stat_evictions;
|
||||
void stadium_words_stats(VMUuid vm_id, uint64_t *promotions, uint64_t *evictions) {
|
||||
int slot = stadium_quota_slot_for_vm(vm_id);
|
||||
|
||||
if (promotions) *promotions = (slot >= 0) ? stat_promotions[slot] : 0;
|
||||
if (evictions) *evictions = (slot >= 0) ? stat_evictions[slot] : 0;
|
||||
}
|
||||
|
||||
/* Freestanding: no libc printf. Prints an unsigned decimal, no leading
|
||||
@@ -197,19 +261,16 @@ static void console_put_u64(uint64_t v) {
|
||||
|
||||
void stadium_words_print_boot_diagnostics(VMUuid vm_id) {
|
||||
uint64_t promotions = 0, evictions = 0;
|
||||
uint64_t resident_sum = 0;
|
||||
uint64_t resident_sum;
|
||||
uint64_t reservoir;
|
||||
size_t ncells = stadium_cell_count();
|
||||
size_t i;
|
||||
|
||||
stadium_words_stats(&promotions, &evictions);
|
||||
stadium_words_stats(vm_id, &promotions, &evictions);
|
||||
|
||||
for (i = 0; i < ncells; i++) {
|
||||
if (cell_is_resident(i)) {
|
||||
resident_sum += stadium_cells()[i].header.heat;
|
||||
}
|
||||
}
|
||||
reservoir = stadium_reservoir_peek(vm_id);
|
||||
/* item 4.2 fix (FABRIC.md §25.5): filtered per-VM -- with two VMs
|
||||
* holding quotas, summing every resident cell regardless of owner
|
||||
* (the pre-4.2 behavior) mixed both VMs' conservation totals together. */
|
||||
resident_sum = stadium_resident_sum(vm_id);
|
||||
reservoir = stadium_reservoir_peek(vm_id);
|
||||
|
||||
console_puts("Stadium words: promotions=");
|
||||
console_put_u64(promotions);
|
||||
|
||||
@@ -684,9 +684,10 @@ void execute_colon_word(VM* vm)
|
||||
|
||||
/* item 4.1, FABRIC.md §17.7: feed the Stadium's independent
|
||||
* conserved heat wire. execution_heat above is untouched by
|
||||
* this call. vm_uuid_hera() is hardcoded here -- Tripod is
|
||||
* pruned to Hera alone (item 0.1); revisit at item 4.2. */
|
||||
stadium_word_dispatch(vm_uuid_hera(), w->word_id, vm->heartbeat.tick_count);
|
||||
* this call. item 4.2: dispatching VM's own identity, not the
|
||||
* item-4.1 hardcoded vm_uuid_hera() -- refused harmlessly by
|
||||
* stadium_admit() for any VM without a granted quota. */
|
||||
stadium_word_dispatch(vm->stadium_vm_id, w->word_id, vm->heartbeat.tick_count);
|
||||
|
||||
uint32_t word_id = w->word_id;
|
||||
if (word_id < DICTIONARY_SIZE)
|
||||
@@ -881,7 +882,7 @@ void vm_interpret_word(VM* vm, const char* word_str, size_t len)
|
||||
entry->physics.last_decay_ns = lookup_ns;
|
||||
|
||||
physics_execution_heat_increment(entry);
|
||||
stadium_word_dispatch(vm_uuid_hera(), entry->word_id, vm->heartbeat.tick_count);
|
||||
stadium_word_dispatch(vm->stadium_vm_id, entry->word_id, vm->heartbeat.tick_count);
|
||||
if (canon && canon != entry)
|
||||
{
|
||||
/* Apply decay to canonical entry as well */
|
||||
@@ -892,7 +893,7 @@ void vm_interpret_word(VM* vm, const char* word_str, size_t len)
|
||||
canon->physics.last_decay_ns = lookup_ns;
|
||||
|
||||
physics_execution_heat_increment(canon);
|
||||
stadium_word_dispatch(vm_uuid_hera(), canon->word_id, vm->heartbeat.tick_count);
|
||||
stadium_word_dispatch(vm->stadium_vm_id, canon->word_id, vm->heartbeat.tick_count);
|
||||
physics_metadata_touch(canon, canon->execution_heat, lookup_ns);
|
||||
}
|
||||
sf_mutex_unlock(&vm->dict_lock);
|
||||
|
||||
Reference in New Issue
Block a user