Fourth stage of the preemptive context-switching plan, and the biggest. LithosAnanke now genuinely, continuously preempts between Hera, Hermes, and Artemis -- timer-driven, running live for the entire remainder of every boot once the Tripod fleet registers, not a bounded probe. A real design fork was resolved before writing code: the naive approach (the timer ISR calling Stage 2's sk_vm_context_switch() directly) is broken -- Stage 0's trap frame lives on whatever stack was active at interrupt time, and jumping to a different stack via Stage 2's own independent swap mid-handler would abandon that trap frame unresumed, guaranteed corruption on the first tick. Chose the safer of two named options: the ISR only ever sets a flag and returns completely normally through its own full epilogue; the actual switch happens moments later, via Stage 2's already-proven mechanism, at a safe cooperative checkpoint on the mainline (execute_colon_word()'s per-word dispatch loop, checked on literally every word, not throttled to the existing 256-word heartbeat-tuning cadence) -- confirmed with the user that word-level granularity is fine-grained enough given the eventual Zynq FPGA target where a word is a mnemonic. New capsule_vm_switch_signal.c/.h: a purpose-built run-readiness signal, deliberately separate from capsule_vm_physics.c's execution-heat engine (that one's own header documents itself as never touched from interrupt context, by design). Slot table sized with headroom (8) rather than hardcoded to today's 3 participants, so extending participation later is another register() call, not a redesign -- per direct request to leave room for swapping the participant set. Simple linear accumulate-then- threshold for this first cut; a fancier law can replace it later without touching the mechanism around it. heartbeat_tick() gains its one deliberate, documented amendment to this file's own top-half/bottom-half discipline -- the first time this codebase reaches into VM-scheduling state from real ISR context. Registration happens only after all three VMs are fully born, right before the REPL starts -- no critical-section protection yet against being switched away mid-birth-setup. Known, flagged rough edge (not reconciled this pass): MSG-TICK's own idle-pump and this new mechanism can still independently move control between the same VMs; not observed to interact badly in verification, but not fully unified either. Verified interactively at the console on all 3 architectures with continuous background preemption running throughout -- amd64 computed `1 1 + .` -> 2, aarch64 computed `1 1 + dup DUP * . CR` -> 4, both correct, REPL fully responsive, zero fault indicators over sustained runtime. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016UNhH1mhi52i6Qihh7ZV5S
80 lines
3.3 KiB
C
80 lines
3.3 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.
|
||
*/
|
||
|
||
/**
|
||
* 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.
|
||
*/
|
||
|
||
#ifndef STARKERNEL_CAPSULE_VM_SWITCH_SIGNAL_H
|
||
#define STARKERNEL_CAPSULE_VM_SWITCH_SIGNAL_H
|
||
|
||
#ifdef __STARKERNEL__
|
||
|
||
#include "starkernel/vm_uuid.h"
|
||
|
||
/* Register a VM as a switch-signal participant. Returns its slot index,
|
||
* or -1 if the slot table is full. 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). */
|
||
int sk_vm_switch_signal_register(VMUuid vm_id);
|
||
|
||
/* Called from heartbeat_tick() (ISR context) every timer tick. Cheap:
|
||
* iterates only the registered slots (bounded, small). */
|
||
void sk_vm_switch_signal_tick(void);
|
||
|
||
/* Called from the cooperative checkpoint (execute_colon_word(), mainline,
|
||
* once per word dispatch). Returns the VMUuid of a VM that should now be
|
||
* switched to, or vm_uuid_none() if nothing is pending. Clears the
|
||
* pending flag as a side effect -- call at most once per checkpoint. */
|
||
VMUuid sk_vm_switch_signal_take_pending(void);
|
||
|
||
#endif /* __STARKERNEL__ */
|
||
|
||
#endif /* STARKERNEL_CAPSULE_VM_SWITCH_SIGNAL_H */
|