The piece FABRIC-3.5.md SXXXIII.6 calls "what remains genuinely hard,"
built and proven first per its own recommendation. Added
src/starkernel/vm/kernel_hermes.c (wired into Makefile.starkernel's
LOADER_EXTRA_SRCS -- this repo lists vm/*.c files explicitly, no glob)
and sk_hermes_alloc()'s declaration in kernel_hermes.h.
Checks stadium_reservoir_peek(vm_id) >= SK_HERMES_Q_SLOT before
touching the reservoir at all -- refusal this way needs no rollback,
since nothing was pulled -- with an explicit rollback path
(stadium_reservoir_push) kept defensively for the pull-then-short case,
though nothing in this single-core kernel is expected to reach it.
SK_HERMES_Q_SLOT = Q48_ONE / SK_HERMES_MSG_MAX (2048), deliberately
simpler than messaging.4th's own formula, which reserves a Q.1/3 floor
for COMMON-CH's own Stadium heat -- kernel-Hermes has no such object
(SXXXIII.4/SXXXIII.5's flat membership list carries no heat of its
own), so there is nothing left for that floor to protect.
Self-test in kernel_main.c, same diagnostic-only synthetic-VM pattern
as the existing Stadium quota grant self-test (lo=3, distinct from
that test's lo=1): reads back the actual granted reservoir rather than
assuming a number, derives expected_n from it, allocates to refusal,
and checks the refusal lands at exactly expected_n, the reservoir
doesn't move on the refused attempt (rollback proven, not assumed),
and the final reservoir is exactly reservoir0 minus got_n times
Q_SLOT.
Three-arch boot clean: amd64/aarch64/riscv64 all reach [zuse@Hera] ok>,
zero UNKNOWN WORD, dict_hash unmoved from task 2.1 (pure C, no FORTH
touched). All three print identical self-test arithmetic: reservoir0=
65536 Q_SLOT=2048 expected_n=32 got_n=32 reservoir_after=0. No compiler
warnings.
Noted, not fixed: Q_SLOT's divisor and SK_HERMES_MSG_MAX are the same
32, so reservoir and arena exhaustion land at exactly the same count by
construction -- this test can't distinguish which refusal reason
fired, only that refusal is correct and rolls back correctly.
Deliberately not evidence for stadium_conserved(): allocating alone
(no release yet, task 2.3) leaves pulled heat held off the Stadium
floor, so the two-term check would correctly read false right now if
run mid-hold. That's expected, not a bug -- Stage B (task 2.7) is
defined as "before and after the alloc/free cycle," not "continuously
during." This task's self-test checks reservoir arithmetic directly
instead.
Authorized by Captain Bob ("Yes continue").
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
177 lines
7.8 KiB
C
177 lines
7.8 KiB
C
/*
|
||
StarForth — Steady-State Virtual Machine Runtime
|
||
|
||
Copyright (c) 2023–2025 Robert A. James
|
||
All rights reserved.
|
||
|
||
This file is part of the StarForth project.
|
||
|
||
Licensed under the StarForth License, Version 1.0 (the "License");
|
||
you may not use this file except in compliance with the License.
|
||
|
||
You may obtain a copy of the License at:
|
||
https://github.com/star.4th@proton.me/StarForth/LICENSE.txt
|
||
|
||
This software is provided "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||
express or implied, including but not limited to the warranties of
|
||
merchantability, fitness for a particular purpose, and noninfringement.
|
||
|
||
See the License for the specific language governing permissions and
|
||
limitations under the License.
|
||
|
||
*/
|
||
|
||
/**
|
||
* kernel_hermes.h - Kernel-resident Hermes: message and membership
|
||
* structures (FABRIC-3.6.md task 2.1, item 28; design: FABRIC-3.5.md
|
||
* SIII/SXXXIII/SXXXIV).
|
||
*
|
||
* Phase 2, task 2.1 ONLY: type definitions, wired to nothing, drawing no
|
||
* heat. No allocator, no send/deliver/reap logic, no registration
|
||
* anywhere -- those are tasks 2.2 onward, each its own commit. This file
|
||
* existing and compiling changes no VM's dictionary and no runtime
|
||
* behaviour; that is deliberate (FABRIC-3.5.md SXXII.4: Phase 2 structures
|
||
* come first and prove nothing until the allocator is built on top, SXL.4
|
||
* item 41).
|
||
*
|
||
* SkHermesMessage mirrors capsules/common/messaging.4th's live MSG-CELLS
|
||
* layout (9 cells: MSG-TYPE@/FROM@/TO@/PADDR@/PLEN@/STADIUM-CELL@/SEQ@/
|
||
* CH@/ORIG-TYPE@) field-for-field, per FABRIC-3.5.md SXXXIII.4 item 1 --
|
||
* "roughly half the file is accessors that become struct fields." The
|
||
* Stadium-cell field is the heat coupling itself: a message's heat is not
|
||
* a field of its own, it IS the Stadium cell it occupies (SXL.4's
|
||
* consumption model, SXXXIX.4's per-VM invariant) -- there is deliberately
|
||
* no separate heat field here to keep that single-source-of-truth.
|
||
*
|
||
* SkHermesMembership is the "one broadcast membership list" SXXXIII.4
|
||
* item 3 and SXXXIII.5 recommend in place of messaging.4th's 28-word
|
||
* channel abstraction (CH-REQUEST/ACCEPT/CONFIRM/CLOSE/MINT-ID and the
|
||
* CH-NEGOTIATING/OPEN/CLOSING state machine) -- traced to have exactly one
|
||
* live caller, CH-ADD-MBR, everything else channel-shaped is unexercised.
|
||
* Whether kernel-Hermes ever adds negotiation on top is item 27, an open
|
||
* Phase 3 ruling (FABRIC-3.6.md B1) -- this structure does not answer
|
||
* that question, it only holds a flat list, which is correct either way.
|
||
*/
|
||
|
||
#ifndef STARKERNEL_VM_KERNEL_HERMES_H
|
||
#define STARKERNEL_VM_KERNEL_HERMES_H
|
||
|
||
#ifdef __STARKERNEL__
|
||
|
||
#include <stddef.h>
|
||
#include <stdint.h>
|
||
#include "starkernel/vm_uuid.h" /* VMUuid */
|
||
#include "starkernel/q48_16.h" /* Q48_ONE */
|
||
|
||
/*
|
||
* SK_HERMES_MSG_MAX / SK_HERMES_MEMBER_MAX - sizing. Mirrors
|
||
* messaging.4th's own MSG-MAX (32) and MBR-MAX (64) as a starting point --
|
||
* kernel-Hermes is a single central pool rather than N per-VM arenas, so
|
||
* these may need revisiting once real traffic exists to size against. Not
|
||
* a ruling, just where the FORTH precedent already was.
|
||
*/
|
||
#define SK_HERMES_MSG_MAX 32
|
||
#define SK_HERMES_MEMBER_MAX 64
|
||
|
||
/*
|
||
* SK_HERMES_Q_SLOT - per-message admission heat, task 2.2 (item 28).
|
||
* messaging.4th:44-46 derives Q.SLOT as the reservoir remaining after
|
||
* COMMON-CH's own Q.1/3 floor, split across MSG-MAX + (CH-MAX-1) slots --
|
||
* a floor that exists to reserve heat for the one live channel object
|
||
* itself. Kernel-Hermes has no such object: SXXXIII.4 item 3 and
|
||
* SXXXIII.5 replace the channel abstraction with a flat membership list
|
||
* that carries no Stadium heat of its own, so there is nothing left for a
|
||
* floor to protect. Deliberately simpler here rather than carrying the
|
||
* old formula's now-unmotivated term forward: reservoir split evenly
|
||
* across message slots only.
|
||
*/
|
||
#define SK_HERMES_Q_SLOT ((uint64_t)Q48_ONE / SK_HERMES_MSG_MAX)
|
||
|
||
/*
|
||
* SkHermesMessage - one message slot, field-for-field mirror of
|
||
* messaging.4th's 9-cell MSG layout.
|
||
*
|
||
* @field type Message type code (SPAWN/PAUSE/RESUME/KILL-style
|
||
* codes are Category A/dead per task 0.1/0.4; live
|
||
* types today are CONSOLE-CMD-EVENT(7),
|
||
* ELEVATE-REQUEST(8), BLK-ATTACH-EVENT(9), messaging.4th
|
||
* reserved sentinels MSG-NACKED(253)/MSG-DELIVERED(255)).
|
||
* @field from Sending VM (mirrors MSG-FROM@).
|
||
* @field to Target VM (mirrors MSG-TO@).
|
||
* @field payload_addr Out-of-line payload address (mirrors MSG-PADDR@).
|
||
* FABRIC-3.5.md SXLIII.6.1 (item 44, still open): a
|
||
* payload above INPUT_BUFFER_SIZE-1 (1024) bytes cannot
|
||
* be drained in one interpret call -- bound or chunk it
|
||
* before Phase 3, not here.
|
||
* @field payload_len Payload length in bytes (mirrors MSG-PLEN@).
|
||
* @field stadium_cell Index into the Stadium cell array this message's
|
||
* heat currently occupies, or a sentinel meaning "none"
|
||
* (mirrors MSG-STADIUM-CELL@) -- the heat coupling
|
||
* itself; see this file's own top comment.
|
||
* @field seq Monotonic send sequence (mirrors MSG-SEQ@).
|
||
* @field channel Broadcast/channel marker; unused today (mirrors
|
||
* MSG-CH@) -- item 27 territory, not decided here.
|
||
* @field orig_type Original type before a NACK/redeliver rewrite
|
||
* (mirrors MSG-ORIG-TYPE@).
|
||
* @field in_use Free-list occupancy flag for task 2.2's allocator.
|
||
* Not present in the FORTH layout (which uses
|
||
* MSG-TYPE@ 0<> as its own live/free test) -- kept
|
||
* explicit here rather than overloading `type == 0`,
|
||
* since kernel-Hermes's held/pulled/returned/consumed
|
||
* ledger (task 2.4, SXL.4) needs an unambiguous
|
||
* occupancy bit independent of the type field's value.
|
||
*/
|
||
typedef struct {
|
||
uint32_t type;
|
||
VMUuid from;
|
||
VMUuid to;
|
||
void *payload_addr;
|
||
uint32_t payload_len;
|
||
int32_t stadium_cell;
|
||
uint32_t seq;
|
||
uint32_t channel;
|
||
uint32_t orig_type;
|
||
int in_use;
|
||
} SkHermesMessage;
|
||
|
||
/*
|
||
* SkHermesMembership - one flat broadcast membership list: every VM that
|
||
* has joined, no per-member state beyond identity. Replaces the 28-word
|
||
* channel abstraction; see this file's own top comment for why.
|
||
*
|
||
* @field members Member VM identities, valid for indices < count.
|
||
* @field count Number of valid entries in members[].
|
||
*/
|
||
typedef struct {
|
||
VMUuid members[SK_HERMES_MEMBER_MAX];
|
||
size_t count;
|
||
} SkHermesMembership;
|
||
|
||
/*
|
||
* sk_hermes_alloc - Heat-coupled allocate (task 2.2, item 28): pull
|
||
* SK_HERMES_Q_SLOT from vm_id's own Stadium reservoir and claim a free
|
||
* message slot. Refuses cleanly, touching neither the reservoir nor the
|
||
* arena, if either is unavailable -- "roll back on refusal" is satisfied
|
||
* by never pulling until affordability is confirmed, not by pulling then
|
||
* undoing (though the rollback path exists too, for the pull-then-fail
|
||
* case the check-first ordering is not expected to reach).
|
||
*
|
||
* Wired to nothing outside this file's own self-test (kernel_main.c) as
|
||
* of task 2.2 -- no FORTH word, no capsule interaction, no protocol logic
|
||
* (send/deliver/reap are later tasks). This function existing and being
|
||
* exercised by a synthetic-VM self-test does not change any real VM's
|
||
* dictionary or Stadium state.
|
||
*
|
||
* @param vm_id Caller whose reservoir is charged.
|
||
* @param out_msg On success, set to the claimed slot. Untouched on
|
||
* refusal.
|
||
* @return 0 on success, -1 on refusal (insufficient reservoir or no free
|
||
* slot -- task 2.2 does not distinguish the two in the return
|
||
* value; both leave all state exactly as it was).
|
||
*/
|
||
int sk_hermes_alloc(VMUuid vm_id, SkHermesMessage **out_msg);
|
||
|
||
#endif /* __STARKERNEL__ */
|
||
|
||
#endif /* STARKERNEL_VM_KERNEL_HERMES_H */
|