Stage O-A: the Tripod is an observer -- outside the compudynamic comparison, acting on events (FABRIC-3.9.0 §2, 3.9.8)
Observer flag on switch-signal slots, set by the kernel for the Tripod (is_fleet_foundation, hoisted; Hera in kernel_main). The decision: an observer in progress or holding an event keeps the CPU; observers with events (mail, Hera's console input) go ahead of participants, oldest wait stamp first; participants compared by work heat as before. Observers are never charged (O-2) nor floored. Uncommitted heat-share charge discarded. Evidence, byte-identical amd64/aarch64/riscv64: G interleave unchanged; Hestia's mail runs before any worker output (HHHABB...); a line typed while two workers run is read and executed mid-stream (...ABAB1 2 + . / 3 ok / ABAB...) -- console responsive under load. Relay 3, dict_hash unchanged. Busy-wait DOE deliberately not run (deadlocks by design until O-C). FABRIC-3.9.0 records rulings and Captain Bob's messaging note (§7). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
04dbc3a5d3
commit
6e933dac38
@@ -154,3 +154,41 @@ C-4 (α)'s starvation: under load, the console, USB and storage wait until every
|
||||
4. **O-4:** device servicing under load: (c) accept for the relaunch and (b) interrupt-driven
|
||||
later (proposed), or (a) treat the device-poll interval as a Hera event.
|
||||
5. **Discard the uncommitted absolute-heat-share charge** (proposed), or keep it as a safety net.
|
||||
|
||||
**RULED 2026-09-24, Captain Bob: "all as proposed."** O-1 through O-4 and the discard (5) all go
|
||||
as proposed above.
|
||||
|
||||
## 7. Raised 2026-09-24: messaging should flow on its own (OPEN, not yet designed)
|
||||
|
||||
**Captain Bob, verbatim:** *"Another potential defect is perhaps in messaging itself. We
|
||||
shouldn't have to actually specifically decide to send a message … that should be autonomous by
|
||||
the operating system itself … The messages should just flow. … However, I would still have a
|
||||
manual method of sending and receiving messages … I should be able to create channels manually,
|
||||
I should be able to destroy channels manually, I should be able to inject messages into the
|
||||
queue, all of that should be manual, but also able to operate autonomously."*
|
||||
|
||||
**What exists today** (traced, not proposed):
|
||||
- **The kernel side is complete in C:** `sk_hermes_channel_create/destroy/subscribe/unsubscribe/
|
||||
is_member/member_count`, `sk_hermes_publish`, `sk_hermes_send_one`, and the negotiation calls
|
||||
(`channel_request/respond/close/open_policy`), in `kernel_hermes.c`.
|
||||
- **Only `SEND` reaches FORTH.** There are also the fixed-purpose `KH-BLK-ATTACH-SEND` and
|
||||
`KH-ELEVATE-SEND`. There is no FORTH word to create or destroy a channel, subscribe, publish,
|
||||
or inspect a queue.
|
||||
- **Every message today is sent because some code decided to send it:** the console relay,
|
||||
BLK-ATTACH, `SEND` in a job. Nothing is routed by the OS on its own.
|
||||
|
||||
**Two parts, which separate cleanly:**
|
||||
- **Manual.** FORTH words over the existing C API: create/destroy a channel, subscribe and
|
||||
unsubscribe, publish, inject a message into a queue, and inspect queues. Mechanical: mostly
|
||||
exposure, not new mechanism. The ACL decides who may use them (`ACL.4th`, never C).
|
||||
- **Autonomous.** What should flow without anyone deciding, and on what trigger, is a real design
|
||||
question. Candidates to discuss:
|
||||
- events the OS already knows about (a VM born, killed, parked idle; a device attached);
|
||||
- results a VM leaves on its stack or in a known place, routed to whoever subscribed;
|
||||
- heat/physics events routed to observers, which fits "observers act on events".
|
||||
|
||||
Not designed. It needs Captain Bob's framing first.
|
||||
|
||||
**Relation to the O stages:** independent of O-B to O-D. The event-driven DoE driver (O-C) is one
|
||||
concrete case: a worker's completion notice is today a `SEND` in the job text, and could become an
|
||||
OS event ("worker parked idle after its job") if autonomous flow lands first.
|
||||
|
||||
+48
-2
@@ -22,7 +22,7 @@ Evidence is named **before** each stage runs.
|
||||
|
||||
| Stage | Change | Evidence (named in advance) |
|
||||
|---|---|---|
|
||||
| **O-A** | Observer role (`FABRIC-3.9.0.md` §2.1). The absolute-heat-share charge is discarded (ruling 5). Observers are left out of the decision's candidates, the work-heat charge and the floor. (Also the absolute charge, if O-2 is ruled so.) Observers with events go ahead at clean boundaries, among themselves by wait stamp (§2.4, per O-1). | The standard sequence. A probe (reverted after capture) shows the decision never returning an observer as a heat candidate, and an observer with mail switched to at the next participant checkpoint. **The busy-wait DoE driver, unchanged, run as a cfg-0 trial with rows on for a fixed window:** Hera's share of rows falls from about ½ (the stopped relaunch's tail) to near 0 while both workers have work. That is the measured symptom this design exists to fix. |
|
||||
| **O-A** | Observer role (`FABRIC-3.9.0.md` §2.1). The absolute-heat-share charge is discarded (ruling 5). Observers are left out of the decision's candidates, the work-heat charge and the floor, and (O-2) the absolute charge. Observers with events go ahead at clean boundaries, among themselves by wait stamp (§2.4, per O-1). | *(Corrected before starting.)* The standard sequence, with the G interleave unchanged. **Console responsive under load:** `1 2 + .` typed while `wa`/`wb` run prints `3` in the middle of the A/B stream. **Observer precedence:** a message to Hestia sent in the same line as the two worker jobs runs before any `A`/`B`. **Not run: the busy-wait DoE driver.** Under O-1, Hera running `DOE` is an observer acting on an event, and nothing displaces her, so `MU-WAIT-ALL` would wait forever. The originally named evidence ("Hera's share falls on the unchanged busy-wait driver") contradicts the design. `DOE` stays unrun until O-C replaces the driver. |
|
||||
| **O-B** | Hera's own mail drained at `sk_vm_idle_point()`, with the readline `n > 0` gate; the timer-paced pump drain removed (§2.5). | The standard sequence, plus: a message sent to Hera while she sits idle at the prompt is acted on **without waiting for the idle beat**. A probe records the delay in beats from admission to drain; it must be well under `SK_IDLE_BEAT_INTERVAL`. The relay and BLK-ATTACH flows are unchanged. |
|
||||
| **O-C** | An event-driven DoE driver (§3): `DOE` starts trial 1 and returns to the prompt; `MU-DONE` counts, ends trials and starts the next; `MU-WAIT-ALL` is deleted. Capsule only. | A 1-rep smoke campaign on amd64 runs all 6 cells to `MU-CAMPAIGN: complete`, with every trial `fail=0` and N/N `KILL … dead`. Hera's rows during trials are only event handling. The prompt answers typed input while trials run. Then the standard sequence on all three ISAs. |
|
||||
| **O-D** | The DoE relaunch: `12345 3 S" <isa>" DOE`, amd64, then aarch64, then riscv64, with the ring extracted between ISAs and `artemis.img` **not** reset in between. | A fresh time estimate from O-C's measured row rate, **before** launch. Then per ISA: 18 `MU-TRIAL` markers, `fail=0`, `MU-CAMPAIGN: complete`. |
|
||||
@@ -37,4 +37,50 @@ Evidence is named **before** each stage runs.
|
||||
|
||||
## 2. Execution log
|
||||
|
||||
*(empty: no stage started)*
|
||||
- [x] **Stage O-A — the observer role (`FABRIC-3.9.0.md` §2).** 2026-09-24. Captain Bob: *"all as
|
||||
proposed, go ahead with O-A"* (O-1 to O-4 and the discard, all as proposed).
|
||||
- **Discarded:** the uncommitted absolute-heat-share charge (`git checkout` of
|
||||
`capsule_vm_physics.c/.h`), per ruling 5.
|
||||
- **`capsule_vm_switch_signal.c/.h`:**
|
||||
- Each slot carries an `observer` flag, with `sk_vm_switch_signal_set_observer()` and
|
||||
`is_observer()`.
|
||||
- `sk_vm_switch_decide()` now works in tiers:
|
||||
1. An observer that is itself in progress (a live non-IDLE frame) or holding an event
|
||||
keeps the CPU.
|
||||
2. Otherwise, an observer with an event, meaning mail (or console input, for Hera),
|
||||
goes ahead of every participant. Among observers, the oldest wait stamp wins.
|
||||
3. Otherwise, participants are compared by work heat exactly as in FABRIC-3.8.
|
||||
Observers are never heat candidates.
|
||||
- `note_work_gained()` never floors an observer and leaves observers out of the floor
|
||||
set. The running VM counts only if it is a participant.
|
||||
- **`capsule_birth.c`:** `is_fleet_foundation` is hoisted to function scope. Registration
|
||||
marks the Tripod as observers from that same birth-time fact.
|
||||
- **`kernel_main.c`:** Hera is marked an observer where she registers.
|
||||
- **`vm_runtime.c`:** observers are not charged at all (O-2).
|
||||
- Nothing a VM executes can set or clear the role.
|
||||
|
||||
**Evidence, as corrected before starting (§1).** Ordinary words only, typed at
|
||||
`[zuse@Hera] ok>`, byte-identical on all three ISAs (`logs/20260924-084904/amd64/`,
|
||||
`-090107/aarch64/`, `-091424/riscv64/`):
|
||||
- **G interleave unchanged:** A = 40, B = 40, 48 runs.
|
||||
- **Observer precedence:** `S" LA" S" wa" SEND . S" LB" S" wb" SEND . S" 72 EMIT 72 EMIT
|
||||
72 EMIT" S" Hestia" SEND .` gives `HHHABBAABBAA…`. Hestia's mail ran before any
|
||||
participant output once Hera went idle.
|
||||
- **Console responsive under load:** with `wa`/`wb` on 200 markers × 20,000-iteration
|
||||
loops, `1 2 + .` typed mid-run appears as `…ABABAB1 2 + .` / `3 ok` / `ok> ABABBA…`.
|
||||
Hera, an observer with console input, got the CPU at the next worker boundary, ran the
|
||||
line, and the workers resumed. This is the C-4 (α) starvation recorded at C-b, fixed.
|
||||
*(The first amd64 attempt at this check used loops that finished before the line was
|
||||
typed, so it proved nothing. It was re-run with the heavy loops, and the script was fixed
|
||||
for the other two ISAs.)*
|
||||
|
||||
**Acceptance** (same boots):
|
||||
- All reach `[zuse@Hera] ok>` with zero `UNKNOWN WORD`, faults and frame corruption.
|
||||
- The `dict_hash` set is unchanged from Stage I (word_count 531, M7.1a
|
||||
`0x572daac83b797332`), since no words were added or removed.
|
||||
- Mint → WIREBIND → `USE` → `1 2 + .` → `3`.
|
||||
- Idle log growth: 0 bytes.
|
||||
- Clean build on all three plus hosted, no new warnings.
|
||||
|
||||
**Not run, by design:** the busy-wait DoE driver, which would deadlock under O-1 (§1).
|
||||
Don't run `DOE` until O-C.
|
||||
|
||||
+14
-14
@@ -1,5 +1,5 @@
|
||||
# Capsule Block Manifest — Auto-generated
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-24T12:13:16Z -->
|
||||
<!-- Generated by mkcapsule --manifest 2026-09-24T13:13:49Z -->
|
||||
<!-- DO NOT EDIT — re-run mkcapsule --manifest to refresh. -->
|
||||
<!-- Hand-written justifications and immutability notes live -->
|
||||
<!-- in MANIFEST.md alongside this auto-generated index. -->
|
||||
@@ -25,7 +25,7 @@
|
||||
| `init-l8-volatile.4th` | 4810, 4811, 4812, 4813 | `0x98caabbbd92abac4` | yes |
|
||||
| `init.4th` | 2049, 2050, 2057 | `0xea038ba684c53443` | yes |
|
||||
| `lib.4th` | 4050 | `0x4b216635c359ef73` | yes |
|
||||
| `multiuser-doe.4th` | 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055 | `0x97cd087a36d60cdc` | yes |
|
||||
| `multiuser-doe.4th` | 5044, 5045, 5046, 5047, 5048, 5049, 5050, 5051, 5052, 5053, 5054, 5055 | `0x2b268decf6065736` | yes |
|
||||
| `per-isa-doe.4th` | 5117, 5119 | `0xab13edecabfd78db` | yes |
|
||||
| `sdk.4th` | 5109, 5110, 5111, 5112, 5113, 5114, 5115 | `0x008fdbbb62c94a3a` | yes |
|
||||
| `turtle.4th` | 5100, 5101, 5102, 5103, 5104, 5105, 5106, 5107, 5108 | `0x4d470418ca543365` | yes |
|
||||
@@ -334,18 +334,18 @@
|
||||
| 5001 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
|
||||
| 5002 | `fabric.4th` | `0x9d9489cbeca4099b` | ok |
|
||||
| 5043 | `workload-calib1.4th` | `0x3b9f2d17b554fabc` | ok |
|
||||
| 5044 | `multiuser-doe.4th` | `0x97cd087a36d60cdc` | ok |
|
||||
| 5045 | `multiuser-doe.4th` | `0x97cd087a36d60cdc` | ok |
|
||||
| 5046 | `multiuser-doe.4th` | `0x97cd087a36d60cdc` | ok |
|
||||
| 5047 | `multiuser-doe.4th` | `0x97cd087a36d60cdc` | ok |
|
||||
| 5048 | `multiuser-doe.4th` | `0x97cd087a36d60cdc` | ok |
|
||||
| 5049 | `multiuser-doe.4th` | `0x97cd087a36d60cdc` | ok |
|
||||
| 5050 | `multiuser-doe.4th` | `0x97cd087a36d60cdc` | ok |
|
||||
| 5051 | `multiuser-doe.4th` | `0x97cd087a36d60cdc` | ok |
|
||||
| 5052 | `multiuser-doe.4th` | `0x97cd087a36d60cdc` | ok |
|
||||
| 5053 | `multiuser-doe.4th` | `0x97cd087a36d60cdc` | ok |
|
||||
| 5054 | `multiuser-doe.4th` | `0x97cd087a36d60cdc` | ok |
|
||||
| 5055 | `multiuser-doe.4th` | `0x97cd087a36d60cdc` | ok |
|
||||
| 5044 | `multiuser-doe.4th` | `0x2b268decf6065736` | ok |
|
||||
| 5045 | `multiuser-doe.4th` | `0x2b268decf6065736` | ok |
|
||||
| 5046 | `multiuser-doe.4th` | `0x2b268decf6065736` | ok |
|
||||
| 5047 | `multiuser-doe.4th` | `0x2b268decf6065736` | ok |
|
||||
| 5048 | `multiuser-doe.4th` | `0x2b268decf6065736` | ok |
|
||||
| 5049 | `multiuser-doe.4th` | `0x2b268decf6065736` | ok |
|
||||
| 5050 | `multiuser-doe.4th` | `0x2b268decf6065736` | ok |
|
||||
| 5051 | `multiuser-doe.4th` | `0x2b268decf6065736` | ok |
|
||||
| 5052 | `multiuser-doe.4th` | `0x2b268decf6065736` | ok |
|
||||
| 5053 | `multiuser-doe.4th` | `0x2b268decf6065736` | ok |
|
||||
| 5054 | `multiuser-doe.4th` | `0x2b268decf6065736` | ok |
|
||||
| 5055 | `multiuser-doe.4th` | `0x2b268decf6065736` | ok |
|
||||
| 5056 | `workload-5-lite.4th` | `0x05c651e140ef667b` | ok |
|
||||
| 5057 | `workload-5-lite.4th` | `0x05c651e140ef667b` | ok |
|
||||
| 5058 | `workload-1-lite.4th` | `0xbc13567fea25063a` | ok |
|
||||
|
||||
@@ -116,6 +116,17 @@ int sk_vm_switch_signal_unregister(VMUuid vm_id);
|
||||
* mail is delivered on their own stacks, by a switch). */
|
||||
int sk_vm_switch_signal_is_participant(VMUuid vm_id);
|
||||
|
||||
/* FABRIC-3.9.0.md §2 (Stage O-A, 2026-09-24): the observer role. The
|
||||
* Tripod (Hera, Artemis, Hestia) is marked by the kernel at registration --
|
||||
* never by anything a VM executes. Observers are outside the compudynamic
|
||||
* comparison (never heat candidates, never charged, never in a floor) and
|
||||
* act on events: at a clean boundary an observer with mail (or, for Hera,
|
||||
* console input) goes ahead of every participant, and an observer acting
|
||||
* on an event is not displaced (ruling O-1). is_observer() is 0 for an
|
||||
* unregistered VM. */
|
||||
void sk_vm_switch_signal_set_observer(VMUuid vm_id);
|
||||
int sk_vm_switch_signal_is_observer(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);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -835,20 +835,24 @@ CapsuleRunResult capsule_birth_baby(
|
||||
* session_set_pinned() failures are soft-fail the same way (logged,
|
||||
* non-fatal) -- same reasoning §H.12 step 4 already established for
|
||||
* Hera. */
|
||||
/* FABRIC-3.6.md Phase 4 (Stage E), 2026-09-22: "Hermes" retired
|
||||
* from this OR-chain -- FABRIC-3.5.md SXXXIV.4's own ruling
|
||||
* named this exact moment ("removed in Stage E, with the strip,
|
||||
* not with the Tripod change"). The chain temporarily held four
|
||||
* names (Hera, Hermes, Artemis, Hestia) through Phases 1-3,
|
||||
* deliberately, per that same ruling; three names now.
|
||||
* FABRIC-3.9.0.md §2.1 (Stage O-A, 2026-09-24): hoisted out of the
|
||||
* Stadium block below so the switch-signal registration further down
|
||||
* reads the SAME birth-time structural fact (the Tripod members are
|
||||
* observers) instead of a second copy of this test. */
|
||||
const int is_fleet_foundation =
|
||||
vm_name_prefix_eq_nocase(capsule_name, "Hera") ||
|
||||
vm_name_prefix_eq_nocase(capsule_name, "Artemis") ||
|
||||
vm_name_prefix_eq_nocase(capsule_name, "Hestia");
|
||||
{
|
||||
StadiumPatronHeader vm_patron;
|
||||
uint8_t *raw = (uint8_t *)&vm_patron;
|
||||
size_t i;
|
||||
/* FABRIC-3.6.md Phase 4 (Stage E), 2026-09-22: "Hermes" retired
|
||||
* from this OR-chain -- FABRIC-3.5.md SXXXIV.4's own ruling
|
||||
* named this exact moment ("removed in Stage E, with the strip,
|
||||
* not with the Tripod change"). The chain temporarily held four
|
||||
* names (Hera, Hermes, Artemis, Hestia) through Phases 1-3,
|
||||
* deliberately, per that same ruling; three names now. */
|
||||
int is_fleet_foundation =
|
||||
vm_name_prefix_eq_nocase(capsule_name, "Hera") ||
|
||||
vm_name_prefix_eq_nocase(capsule_name, "Artemis") ||
|
||||
vm_name_prefix_eq_nocase(capsule_name, "Hestia");
|
||||
|
||||
for (i = 0; i < sizeof(vm_patron); i++) raw[i] = 0;
|
||||
vm_patron.identity = 0;
|
||||
@@ -932,6 +936,10 @@ CapsuleRunResult capsule_birth_baby(
|
||||
if (sk_vm_switch_signal_register(vm_id) < 0) {
|
||||
log_message(LOG_WARN, "capsule_birth_baby: switch-signal table full -- %s won't be switched to",
|
||||
capsule_name);
|
||||
} else if (is_fleet_foundation) {
|
||||
/* FABRIC-3.9.0.md §2.1 (Stage O-A): the Tripod observes the
|
||||
* compudynamics and acts on events; it does not participate. */
|
||||
sk_vm_switch_signal_set_observer(vm_id);
|
||||
}
|
||||
|
||||
capsule_parity_log_birth(vm_id, cap->capsule_id, cap->content_hash, dict_hash);
|
||||
|
||||
@@ -68,6 +68,11 @@ typedef struct {
|
||||
uint64_t parked_stamp; /* wait stamp taken when this VM last stopped
|
||||
* running (sk_vm_switch_signal_note_parked());
|
||||
* 0 = never parked. See sk_vm_switch_decide(). */
|
||||
int observer; /* FABRIC-3.9.0.md §2 (Stage O-A, 2026-09-24):
|
||||
* 1 for the Tripod (Hera, Artemis, Hestia) --
|
||||
* outside the compudynamic comparison, acting on
|
||||
* events; 0 for every participant. Set by the
|
||||
* kernel at registration, never by a VM. */
|
||||
} sk_switch_slot_entry_t;
|
||||
|
||||
static sk_switch_slot_entry_t *g_slots = (sk_switch_slot_entry_t *)0;
|
||||
@@ -129,6 +134,7 @@ int sk_vm_switch_signal_boot_init(void) {
|
||||
for (i = 0; i < max_vm_count; i++) {
|
||||
slots[i].vm_id = vm_uuid_none();
|
||||
slots[i].parked_stamp = 0;
|
||||
slots[i].observer = 0;
|
||||
}
|
||||
|
||||
g_slots = slots;
|
||||
@@ -151,9 +157,20 @@ int sk_vm_switch_signal_register(VMUuid vm_id) {
|
||||
if (g_slot_count >= g_slot_capacity) return -1;
|
||||
g_slots[g_slot_count].vm_id = vm_id;
|
||||
g_slots[g_slot_count].parked_stamp = 0;
|
||||
g_slots[g_slot_count].observer = 0;
|
||||
return g_slot_count++;
|
||||
}
|
||||
|
||||
void sk_vm_switch_signal_set_observer(VMUuid vm_id) {
|
||||
int slot = slot_for_vm_id(vm_id);
|
||||
if (slot >= 0) g_slots[slot].observer = 1;
|
||||
}
|
||||
|
||||
int sk_vm_switch_signal_is_observer(VMUuid vm_id) {
|
||||
int slot = slot_for_vm_id(vm_id);
|
||||
return slot >= 0 && g_slots[slot].observer;
|
||||
}
|
||||
|
||||
int sk_vm_switch_signal_unregister(VMUuid vm_id) {
|
||||
int slot = slot_for_vm_id(vm_id);
|
||||
if (slot < 0) return -1;
|
||||
@@ -210,13 +227,12 @@ static int candidate_standing(VM *v, VM *mama, uint64_t parked_stamp,
|
||||
eligible = 1;
|
||||
stamp = m->wait_stamp;
|
||||
}
|
||||
(void)mama; /* Stage O-A: Hera is an observer, never a candidate here */
|
||||
if (v->native_stack_saved_sp != 0) { /* parked on its own stack */
|
||||
const VMFrameStack *fs = &v->frames;
|
||||
int in_progress = fs->count > 0 &&
|
||||
fs->entries[fs->count - 1].kind != VM_FRAME_IDLE;
|
||||
/* §3.13.5 / ruling C-5: pending console input is Hera's mail. */
|
||||
int console_input = (v == mama) && sk_console_key_available();
|
||||
if (in_progress || console_input) {
|
||||
if (in_progress) {
|
||||
eligible = 1;
|
||||
if (parked_stamp != 0 && parked_stamp < stamp) stamp = parked_stamp;
|
||||
}
|
||||
@@ -230,17 +246,72 @@ static int candidate_standing(VM *v, VM *mama, uint64_t parked_stamp,
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* FABRIC-3.9.0.md §2.3 (Stage O-A): an observer's events -- pending mail,
|
||||
* and for Hera pending console input. Returns 1 with the event's wait
|
||||
* stamp (oldest mail, or when Hera last parked for console input). */
|
||||
static int observer_event(VM *v, VM *mama, uint64_t parked_stamp, uint64_t *stamp_out) {
|
||||
SkHermesMessage *m = sk_hermes_pending_peek(v->stadium_vm_id);
|
||||
uint64_t stamp = UINT64_MAX;
|
||||
int ev = 0;
|
||||
|
||||
if (m) { ev = 1; stamp = m->wait_stamp; }
|
||||
if (v == mama && sk_console_key_available()) {
|
||||
ev = 1;
|
||||
if (parked_stamp != 0 && parked_stamp < stamp) stamp = parked_stamp;
|
||||
}
|
||||
*stamp_out = stamp;
|
||||
return ev;
|
||||
}
|
||||
|
||||
/* An observer acting on an event: a live frame on its stack (a line or a
|
||||
* drained message being interpreted). Nothing displaces it (§2.4). */
|
||||
static int observer_in_progress(const VM *v) {
|
||||
return v->frames.count > 0 &&
|
||||
v->frames.entries[v->frames.count - 1].kind != VM_FRAME_IDLE;
|
||||
}
|
||||
|
||||
VM *sk_vm_switch_decide(VM *self, int self_competes) {
|
||||
VM *mama = (VM *)sk_get_mama_vm();
|
||||
VM *best = (VM *)0;
|
||||
uint64_t best_heat = 0, best_stamp = 0;
|
||||
int i;
|
||||
int i, self_obs = self && sk_vm_switch_signal_is_observer(self->stadium_vm_id);
|
||||
|
||||
/* FABRIC-3.9.0.md §2.4, ruling O-1 (Stage O-A): an observer acting on
|
||||
* an event, or holding one, keeps the CPU -- observers respond to what
|
||||
* happens; they are not compared with anyone. */
|
||||
if (self_obs) {
|
||||
uint64_t s;
|
||||
if (observer_in_progress(self) || observer_event(self, mama, 0, &s)) return (VM *)0;
|
||||
}
|
||||
|
||||
/* Observers with events go ahead of every participant, at this clean
|
||||
* boundary; among themselves, the one waiting longest (unique stamps,
|
||||
* so still independent of walk order). No heat involved. */
|
||||
for (i = 0; i < g_slot_count; i++) {
|
||||
VMRegistryEntry e;
|
||||
VM *v;
|
||||
uint64_t stamp;
|
||||
|
||||
if (!g_slots[i].observer) continue;
|
||||
if (self && vm_uuid_equal(g_slots[i].vm_id, self->stadium_vm_id)) continue;
|
||||
if (capsule_vm_registry_get(g_slots[i].vm_id, &e) != 0) continue;
|
||||
v = (VM *)e.vm_ptr;
|
||||
if (!v || v == self || e.pending_reap) continue;
|
||||
if (e.state == VM_STATE_DEAD || e.state == VM_STATE_STILLBORN) continue;
|
||||
if (v->native_stack_top == 0) continue;
|
||||
if (!observer_event(v, mama, g_slots[i].parked_stamp, &stamp)) continue;
|
||||
if (!best || stamp < best_stamp) { best = v; best_stamp = stamp; }
|
||||
}
|
||||
if (best) return best;
|
||||
|
||||
/* Participants: the FABRIC-3.8 compudynamics, unchanged -- observers
|
||||
* are never heat candidates. */
|
||||
for (i = 0; i < g_slot_count; i++) {
|
||||
VMRegistryEntry e;
|
||||
VM *v;
|
||||
uint64_t heat, stamp;
|
||||
|
||||
if (g_slots[i].observer) continue;
|
||||
if (self && vm_uuid_equal(g_slots[i].vm_id, self->stadium_vm_id)) continue;
|
||||
if (capsule_vm_registry_get(g_slots[i].vm_id, &e) != 0) continue;
|
||||
v = (VM *)e.vm_ptr;
|
||||
@@ -263,7 +334,8 @@ VM *sk_vm_switch_decide(VM *self, int self_competes) {
|
||||
/* A running VM that still has work keeps the CPU unless the best
|
||||
* candidate is strictly cooler: a tie with the incumbent is "no reason
|
||||
* to move," not a coin toss (ruling C-3). */
|
||||
if (best && self_competes && !(best_heat < vm_physics_work_heat_of(self->stadium_vm_id))) {
|
||||
if (best && self_competes && !self_obs &&
|
||||
!(best_heat < vm_physics_work_heat_of(self->stadium_vm_id))) {
|
||||
return (VM *)0;
|
||||
}
|
||||
return best;
|
||||
@@ -281,6 +353,10 @@ void sk_vm_switch_signal_note_work_gained(VMUuid vm_id) {
|
||||
if (capsule_vm_registry_get(vm_id, &e) != 0 || !e.vm_ptr) return;
|
||||
v = (VM *)e.vm_ptr;
|
||||
|
||||
/* Stage O-A (FABRIC-3.9.0.md §2.2): observers have no work heat to
|
||||
* floor, and count in nobody's floor. */
|
||||
if (sk_vm_switch_signal_is_observer(vm_id)) return;
|
||||
|
||||
/* Only a VM that had NO work gains some here: not the one running, and
|
||||
* not one parked mid-task (either already had work, and keeps its own
|
||||
* work heat). */
|
||||
@@ -288,9 +364,10 @@ void sk_vm_switch_signal_note_work_gained(VMUuid vm_id) {
|
||||
if (v->native_stack_saved_sp != 0 && v->frames.count > 0 &&
|
||||
v->frames.entries[v->frames.count - 1].kind != VM_FRAME_IDLE) return;
|
||||
|
||||
/* The floor: the lowest work heat among the VMs that have work right
|
||||
* now -- the one running, and every other eligible participant. */
|
||||
if (current) {
|
||||
/* The floor: the lowest work heat among the participants that have
|
||||
* work right now -- the one running (if it is a participant), and every
|
||||
* other eligible participant. */
|
||||
if (current && !sk_vm_switch_signal_is_observer(current->stadium_vm_id)) {
|
||||
floor_q48 = vm_physics_work_heat_of(current->stadium_vm_id);
|
||||
have = 1;
|
||||
}
|
||||
@@ -298,6 +375,7 @@ void sk_vm_switch_signal_note_work_gained(VMUuid vm_id) {
|
||||
VMRegistryEntry o;
|
||||
VM *ov;
|
||||
uint64_t heat, stamp;
|
||||
if (g_slots[i].observer) continue;
|
||||
if (vm_uuid_equal(g_slots[i].vm_id, vm_id)) continue;
|
||||
if (capsule_vm_registry_get(g_slots[i].vm_id, &o) != 0) continue;
|
||||
ov = (VM *)o.vm_ptr;
|
||||
|
||||
@@ -1266,6 +1266,9 @@ static void kernel_main_deep(BootInfo *boot_info) {
|
||||
VMRegistryEntry hera_entry;
|
||||
if (capsule_vm_registry_get(vm_uuid_hera(), &hera_entry) == 0) {
|
||||
sk_vm_switch_signal_register(hera_entry.vm_id);
|
||||
/* FABRIC-3.9.0.md §2.1 (Stage O-A): Hera is an observer, like
|
||||
* the rest of the Tripod (whose role capsule_birth_baby() sets). */
|
||||
sk_vm_switch_signal_set_observer(hera_entry.vm_id);
|
||||
/* Seed the switch mechanism's own "who is running" tracker
|
||||
* (FABRIC-3.md §XXVIII Stage 3 follow-on, 2026-09-14) -- Hera
|
||||
* is genuinely the one running here, before any switch has
|
||||
|
||||
@@ -150,7 +150,13 @@ void vm_tick(VM* vm)
|
||||
* vm_physics_touch()'s dispatch law. Kernel-side only: nothing a VM
|
||||
* executes can ask for, skip, or give back a charge. */
|
||||
if (vm == sk_vm_switch_current_vm()) {
|
||||
vm_physics_charge(vm->stadium_vm_id);
|
||||
/* FABRIC-3.9.0.md §2.2, ruling O-2 (Stage O-A): observers -- the
|
||||
* Tripod, the fleet's heat bath -- are not charged, neither work
|
||||
* heat nor absolute heat. Participants pull absolute heat off them
|
||||
* through their own charges, and retire returns it, so fleet K
|
||||
* stays conserved. */
|
||||
if (!sk_vm_switch_signal_is_observer(vm->stadium_vm_id))
|
||||
vm_physics_charge(vm->stadium_vm_id);
|
||||
/* Stage C-b (§3.13.3): the running VM's heat just changed, so the
|
||||
* next owned checkpoint re-decides who runs. */
|
||||
sk_vm_switch_signal_note_beat();
|
||||
|
||||
Reference in New Issue
Block a user