Stage C-b: compudynamic switch decision, idle point, timer path removed (FABRIC-3.8 §3.13.3-§3.13.5)
Who runs next is now sk_vm_switch_decide(): among participants with work (pending kernel-Hermes mail, parked mid-task, or Hera with console input), the coolest by VM-physics heat; ties with the incumbent keep the incumbent, ties among candidates go to the longest waiting (unique wait stamps). No scheduler state, no timer: heartbeat_tick() no longer touches switching, readiness/threshold/has_work/take_pending removed. Decided at owned word checkpoints (colon words and, new, between top-level words) after a beat or new mail, at the trampoline between messages, and at Hera's idle point in the console poll loops. The idle pump skips participants (G3 closed for them): the WIREBIND relay is delivered by a real switch to rajames~user. Probe (restored away): relay via switch Hera->rajames~user->Hera on all three ISAs, parked saved_sp inside own stack, walk-order disagreements 0 over tens of millions of decisions, 0 bytes idle log growth. Acceptance: dict_hash unchanged, relay 3, no faults. Rulings: C-3, C-5 as proposed; C-4 taken as (alpha), consequence recorded. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
7c3736841d
commit
06bcab504d
@@ -68,6 +68,8 @@
|
||||
#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
|
||||
@@ -97,44 +99,74 @@ int sk_vm_switch_signal_register(VMUuid vm_id);
|
||||
* No-op (returns -1) if vm_id was never registered. */
|
||||
int sk_vm_switch_signal_unregister(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);
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* 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);
|
||||
/* 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);
|
||||
|
||||
/* Call once, from the same checkpoint, immediately after a switch
|
||||
* sk_vm_switch_signal_take_pending() requested actually executes (not if
|
||||
* the target turned out invalid/self) -- feeds the DoE CSV counters
|
||||
* below. Also resets `target_id`'s own readiness/has_work directly
|
||||
* (Stage 3 follow-on correction, 2026-09-14) -- see the .c file's own doc
|
||||
* comment on why this can't be left to tick()'s current-slot polling. */
|
||||
/* 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 VM-physics heat; 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);
|
||||
|
||||
/* 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);
|
||||
|
||||
/* Message-arrival eligibility hook (FABRIC-3.md §XXVIII Stage 3 follow-on,
|
||||
* 2026-09-14): mark that VM `vm_id` was just sent a message (the FORTH-side
|
||||
* MSG-SEND hook in capsules/common/messaging.4th calls this via the new
|
||||
* SWITCH-MARK-WORK primitive, mainline, single-writer). Consulted by
|
||||
* sk_vm_switch_signal_tick()'s own readiness->pending decision so a VM
|
||||
* with nothing recently sent to it never becomes a switch target purely by
|
||||
* sitting idle long enough -- closes the wasteful (but, since the Stage 3
|
||||
* stack-ownership fix, no longer corrupting) trampoline-bounce cycle for
|
||||
* an idle participant. No-op for an unregistered vm_id. */
|
||||
/* SWITCH-MARK-WORK's body. Stage C-b: no flag to set any more (the queue
|
||||
* is the eligibility); only asks for a fresh decision. */
|
||||
void sk_vm_switch_signal_mark_work(VMUuid vm_id);
|
||||
|
||||
/* DoE CSV read-only exposure (FABRIC-3.md §XXVIII Stage 3 follow-on,
|
||||
* 2026-09-13) -- all of this state already existed for the switch
|
||||
* decision itself; these just make it observable. */
|
||||
* 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);
|
||||
uint32_t sk_vm_switch_signal_readiness(int slot); /* 0 if slot out of range */
|
||||
uint32_t sk_vm_switch_signal_readiness_of(VMUuid vm_id); /* 0 if not registered */
|
||||
|
||||
/* 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
|
||||
|
||||
Reference in New Issue
Block a user