fix(hermes): one delivery path -- a VM takes its own messages on its own turn, never on another's stack
FABRIC-3.5 SXXXII, closing FABRIC-3 SXXIX bug 2 (SXXXVI). A send marks the receiver for the switch signal; the VM's own loop on its native stack takes its messages and yields; Hera's idle beat makes the pending switch instead of running other VMs' messages on her stack; every VM gets switcher turns from birth; the drain refuses any call outside the VM's own context (the test: it held every relayed line before, none now). Found on the way: amd64's first-entry frame left rsp 8 bytes off the SysV entry alignment (#GP on the first interpreter run on a VM's own stack), and a VM yielding from its loop was marked SWITCHED_OUT, which stopped console relay after one message and would have made KILL refuse it. Relay of four lines to a WIREBIND identity on all three ISAs, five hot-plugged identities, and standard acceptance (POST 1052, hash 0x08873e0f44b7cb2a) all pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
b7e8e4b058
commit
029a8ed9cf
@@ -346,6 +346,8 @@ void capsule_vm_force_reap(VMUuid vm_id) {
|
||||
* capsule_vm_kill()'s own unconditional rule. */
|
||||
if (vm_uuid_is_hera(entry->vm_id)) return;
|
||||
|
||||
(void)sk_vm_switch_signal_unregister(entry->vm_id); /* no turns for the dead */
|
||||
|
||||
VM *vm = (VM *)entry->vm_ptr;
|
||||
|
||||
/* Deliberately NOT capsule_vm_kill()'s VM_STATE_SWITCHED_OUT refusal
|
||||
@@ -467,6 +469,8 @@ int capsule_vm_kill(const char *name) {
|
||||
vm_id = entry->vm_id;
|
||||
vm = (VM *)entry->vm_ptr;
|
||||
|
||||
(void)sk_vm_switch_signal_unregister(vm_id); /* no turns for the dead */
|
||||
|
||||
/* FABRIC-2.md SS B, VM-COOL: reap this VM's own Stadium patron cell for
|
||||
* real, dispatching COOL. Refusal (already naturally reclaimed by
|
||||
* unrelated quota pressure, or never admitted) is silently tolerated --
|
||||
@@ -724,6 +728,11 @@ CapsuleRunResult capsule_birth_baby(
|
||||
((VM *)new_vm)->native_stack_paddr = ns.paddr;
|
||||
((VM *)new_vm)->native_stack_guard_vaddr = ns.guard_vaddr;
|
||||
((VM *)new_vm)->native_stack_top = ns.stack_top;
|
||||
/* One path (2026-10-14): a VM that can be switched to is one
|
||||
* the switcher can give a turn, so that a message sent to it
|
||||
* is taken by the VM itself, on its own stack (switch.c) --
|
||||
* every VM, not only the Tripod and WIREBIND identities. */
|
||||
(void)sk_vm_switch_signal_register(vm_id);
|
||||
} else {
|
||||
log_message(LOG_WARN, "capsule_birth_baby: native stack allocation failed for %s",
|
||||
capsule_name);
|
||||
|
||||
@@ -132,6 +132,11 @@ int sk_vm_switch_signal_boot_init(void) {
|
||||
}
|
||||
|
||||
int sk_vm_switch_signal_register(VMUuid vm_id) {
|
||||
/* Idempotent (2026-10-14): every VM is registered at its birth now
|
||||
* (capsule_birth.c), and the Tripod and WIREBIND callers that predate
|
||||
* that still register their own -- the second call finds the first. */
|
||||
int slot = slot_for_vm_id(vm_id);
|
||||
if (slot >= 0) return slot;
|
||||
if (g_slot_count >= g_slot_capacity) return -1;
|
||||
g_slots[g_slot_count].vm_id = vm_id;
|
||||
g_slots[g_slot_count].readiness = 0;
|
||||
|
||||
Reference in New Issue
Block a user