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
@@ -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__ */
|
||||
|
||||
Reference in New Issue
Block a user