Files
LithosAnanake/include/starkernel/capsule_vm_switch_signal.h
T
Robert Allan JamesandClaude Opus 5.5 dd23c3aa45 Stage I: strip dead code -- pump drain loop, SWITCH-MARK-WORK, global interpret depth, native_switch_back_to (FABRIC-3.8 §4 row I)
Each shown dead three ways (FABRIC-3.5 §XXII): the pump loop by its
condition, by construction (birth cap == switch table capacity, every birth
registers) and at run time (probe silent through boot, G interleave, an
8-worker DoE trial and the relay); SWITCH-MARK-WORK by callers, capsules
(source and baked), experiments/tools/logs; the depth counter and back_to
field by having no readers.

Kept: Hera's own pump drain (last timer-paced delivery, Hera only) and the
always-0 readiness CSV columns (a DoE schema call). Acceptance on amd64/
aarch64/riscv64: G interleave unchanged, cfg-0 trial fail=0 2/2 dead,
relay 3, idle growth 0; dict_hash set changed by the removed word
(word_count 531) and identical across ISAs. FABRIC-3.8 stages A-I done.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-24 06:23:41 -04:00

190 lines
9.2 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*
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.
*/
/**
* capsule_vm_switch_signal.h - New, purpose-built "who runs next" signal
* for preemptive context switching (FABRIC-3.md §XXVIII, Stage 3,
* 2026-09-13).
*
* Deliberately NOT a repurposing of capsule_vm_physics.c's execution-heat
* engine -- that measures word-level dispatch fairness over millions of
* executions on a different timescale, and its own header explicitly
* documents it as never touched from interrupt context (unlocked, by
* design). This is a different physical quantity: instant-by-instant
* run-readiness, consulted from real ISR context (heartbeat_tick()) every
* timer tick.
*
* Concurrency discipline mirrors heartbeat.c's own §21.1-sanctioned
* pattern for heartbeat_next_period_ns(): single-writer-ISR (tick()) /
* single-reader-mainline (take_pending(), called from the cooperative
* checkpoint in execute_colon_word()), no lock, because nothing on this
* single hart is concurrent with the ISR while it runs.
*
* NOT truly interrupt-driven register/stack swapping (that was
* considered and rejected for this stage -- see FABRIC-3.md §XXVIII
* Stage 3 for why): the ISR only ever sets a flag. The actual switch
* (Stage 2's already-proven sk_vm_context_switch()) happens later, at a
* safe cooperative checkpoint on the mainline, once per word dispatch.
*
* Slot table is sized with headroom, not hardcoded to exactly today's 3
* participants (Hera/Hermes/Artemis) -- extending participation later
* (Stage 4+) is another sk_vm_switch_signal_register() call, not a
* redesign.
*
* FABRIC-3.6.md task 3.1 (2026-09-21, ruled B2 / FABRIC-3.5.md §XLV.2): the
* slot table is no longer a fixed SK_SWITCH_MAX_SLOTS=16 compile-time array.
* It is kmalloc'd at boot by sk_vm_switch_signal_boot_init(), sized from
* stadium_max_vm_count() -- the same RAM-derived population bound Stadium
* and session.c already use (session_boot_init() is the direct precedent
* mirrored here). Every switch-signal participant is a Stadium VM, so
* reusing that bound directly (rather than re-deriving a separate RAM
* budget) needs no new sizing formula.
*/
#ifndef STARKERNEL_CAPSULE_VM_SWITCH_SIGNAL_H
#define STARKERNEL_CAPSULE_VM_SWITCH_SIGNAL_H
#ifdef __STARKERNEL__
#include <stdint.h>
#include "starkernel/vm_uuid.h"
struct VM;
/* Boot-time allocation (FABRIC-3.6.md task 3.1, 2026-09-21): kmalloc's the
* slot table to stadium_max_vm_count() entries. Must run after
* stadium_boot_init() (that bound is 0, and this fails, until Stadium has
* computed it) and before the first sk_vm_switch_signal_register() call.
* Soft failure -- returns -1 and leaves the table unallocated (capacity 0,
* so register() below simply refuses every registration) rather than
* halting boot, same posture as stadium_boot_init()/session_boot_init().
* Idempotent-unsafe: calling twice leaks the first allocation, so callers
* must call it exactly once. */
int sk_vm_switch_signal_boot_init(void);
/* Register a VM as a switch-signal participant. Returns its slot index,
* or -1 if the slot table is full (or sk_vm_switch_signal_boot_init() was
* never called / failed). Call once per participating VM, after that VM is
* fully born (never mid-birth -- this stage has no critical-section
* protection against being switched away mid-setup). FABRIC-3.8.md §3.6
* (Stage E): capsule_birth_baby() is now the one caller for every born VM
* (Hera, never born through it, is registered in kernel_main.c).
* Idempotent: registering an already-registered VM returns its slot. */
int sk_vm_switch_signal_register(VMUuid vm_id);
/* Remove a switch-signal participant (FABRIC-3.md §XXVIII Stage 4,
* 2026-09-14) -- Tripod VMs never need this (they live forever), but
* WIREBIND-birthed identity VMs cycle through attach/detach repeatedly
* and must free their slot for reuse, or the bounded table exhausts
* after sk_vm_switch_signal_slot_capacity() attach/detach cycles. Compacts the table
* (small, bounded, mutated only at attach/detach -- not a hot path).
* Clears a pending switch targeting this VM, if any, so the checkpoint
* never attempts to switch into a no-longer-registered participant.
* No-op (returns -1) if vm_id was never registered. */
int sk_vm_switch_signal_unregister(VMUuid vm_id);
/*
* FABRIC-3.8.md §3.13 (Stage C-b, 2026-09-23): the ISR half of this module
* (sk_vm_switch_signal_tick() from heartbeat_tick(), readiness accrual
* against a fixed 50-tick threshold, a pending flag) is removed -- the
* timer no longer decides anything. This module's own header comment above
* (not a repurposing of VM-physics heat) is reversed by Captain Bob's
* §3.11 ruling: the decision below IS a VM-physics heat comparison, made
* workable at switch granularity by Stage C-a's per-beat charge.
*/
/* 1 if vm_id is a registered participant (the idle pump skips these: their
* mail is delivered on their own stacks, by a switch). */
int sk_vm_switch_signal_is_participant(VMUuid vm_id);
/* A fresh, unique wait stamp (mainline, single writer). kernel_hermes.c
* stamps each admitted message with one. */
uint64_t sk_vm_switch_signal_next_stamp(void);
/* vm_id just stopped running (sk_vm_context_switch(), for `from`): record
* when, for the "waiting longest" tie-break. */
void sk_vm_switch_signal_note_parked(VMUuid vm_id);
/* Physical events after which the decision may differ: the running VM
* produced a beat (vm_tick(), right after its Stage C-a charge), or mail
* was admitted (sk_hermes_send_one()). take_decision_due() consumes the
* flag at the next owned checkpoint. */
void sk_vm_switch_signal_note_beat(void);
void sk_vm_switch_signal_note_mail(void);
int sk_vm_switch_signal_take_decision_due(void);
/*
* sk_vm_switch_decide - who runs next, by physical comparison (§3.13.3).
*
* Among registered participants other than `self` that have work -- a
* pending kernel-Hermes message, or parked on their own stack mid-task
* (top frame not VM_FRAME_IDLE), or (Hera only) pending console input --
* returns the one with the lowest work heat (vm_physics_work_heat_of(): heat
* earned by its own per-beat charges -- ruling C-4 (β), FABRIC-3.8.md
* §3.13.7; Hera's structural endowment doesn't count); among equally cool
* candidates, the one waiting longest (unique wait stamps). With
* self_competes set (the caller still has work), returns a candidate only
* if it is strictly cooler than `self`. Returns NULL when nobody should
* run instead. A pure function of physical state: it keeps nothing
* between calls, and its answer does not depend on table walk order. Not
* a scheduler -- no queue, no priority list, nothing on a clock.
*/
struct VM *sk_vm_switch_decide(struct VM *self, int self_competes);
/* Stage H, "floor on contact" (Captain Bob, 2026-09-23): vm_id just gained
* work (kernel_hermes.c: its queue went from empty to one message). If it
* had none before -- not running, not parked mid-task -- raise its work
* heat to the lowest work heat among the VMs that have work now (the one
* running and every eligible participant). A new or long-idle VM thus
* joins the busy set level with its floor, rather than at 0 and running
* alone until it "caught up" with VMs that have worked since boot. */
void sk_vm_switch_signal_note_work_gained(VMUuid vm_id);
/* Reap one participant marked pending_reap (a WIREBIND identity whose
* device detached while it was parked), if any. Call only at a safe point
* (checkpoint, trampoline, idle point) -- same contract the old Stage 3
* checkpoint's inline reap had. */
void sk_vm_switch_reap_pending(void);
/* Call once, immediately before a decided switch executes -- feeds the DoE
* CSV counters below. */
void sk_vm_switch_signal_note_switch_performed(VMUuid target_id);
/* DoE CSV read-only exposure (FABRIC-3.md §XXVIII Stage 3 follow-on,
* 2026-09-13). Stage C-b: ticks_since_switch now counts beats of the
* running VM since the last switch (§3.13.4), not ISR ticks; the per-VM
* readiness getters are gone with the readiness itself. */
uint64_t sk_vm_switch_signal_switch_count(void); /* cumulative, since boot */
uint32_t sk_vm_switch_signal_ticks_since_switch(void);
int sk_vm_switch_signal_current_slot(void); /* -1 = none/unregistered */
int sk_vm_switch_signal_slot_count(void);
/* FABRIC-3.6.md task 3.1 (2026-09-21): the table's boot-time-computed
* capacity (0 if sk_vm_switch_signal_boot_init() was never called or
* failed) -- the dynamic replacement for the old compile-time
* SK_SWITCH_MAX_SLOTS=16. */
int sk_vm_switch_signal_slot_capacity(void);
#endif /* __STARKERNEL__ */
#endif /* STARKERNEL_CAPSULE_VM_SWITCH_SIGNAL_H */