233 lines
8.3 KiB
Nim
233 lines
8.3 KiB
Nim
# SPDX-License-Identifier: LSL-1.0
|
|
# Copyright (c) 2026 Markus Maiwald
|
|
# Stewardship: Self Sovereign Society Foundation
|
|
#
|
|
# This file is part of the Nexus Sovereign Core.
|
|
# See legal/LICENSE_SOVEREIGN.md for license terms.
|
|
|
|
## Rumpk Layer 1: The Reactive Dispatcher (The Tyrant)
|
|
##
|
|
## Implements the Silence Doctrine (SPEC-102).
|
|
## - No Tick.
|
|
## - No Policy.
|
|
## - Only Physics.
|
|
|
|
{.push stackTrace: off, lineTrace: off.}
|
|
|
|
import fiber
|
|
|
|
# We might need to access the Fiber globals from fiber.nim
|
|
# fiber.nim exports current_fiber, but we need to iterate them.
|
|
# Currently kernel.nim manages the specific fibers variables (fiber_ion, fiber_ui, etc.)
|
|
# We need a centralized registry or a way to iterate.
|
|
#
|
|
# For the first pass, we can replicate the logic in kernel.nim which explicitly checks
|
|
# the known fibers, but structured as the Spectrum loop.
|
|
# Or we can make kernel.nim pass the fibers to us.
|
|
#
|
|
# Let's keep it simple and stateless in sched.nim if possible, or have it manage the queue.
|
|
# Since kernel.nim holds the variables, sched.nim should probably define the *Strategy*
|
|
# and kernel.nim calls it, OR sched.nim should import kernel (circular!).
|
|
#
|
|
# Better: fiber.nim holds a linked list of fibers?
|
|
# Or sched.nim is just a helper module that kernel.nim uses.
|
|
|
|
# Let's define the Strategy here.
|
|
|
|
# To avoid circular imports, kernel.nim will likely INCLUDE sched.nim or sched.nim
|
|
# will act on a passed context.
|
|
# BUT, SPEC-102 implies sched.nim *is* the logic.
|
|
#
|
|
# Let's define the Harmonic logic.
|
|
# We need access to `current_fiber` (from fiber.nim) and `get_now_ns` (helper).
|
|
|
|
proc sched_get_now_ns*(): uint64 {.importc: "rumpk_timer_now_ns", cdecl.}
|
|
proc console_write(p: pointer, len: csize_t) {.importc: "hal_console_write", cdecl.}
|
|
proc uart_print_hex(v: uint64) {.importc: "uart_print_hex", cdecl.}
|
|
proc uart_print_hex8(v: uint8) {.importc: "uart_print_hex8", cdecl.}
|
|
|
|
# M4.5: STL emission for budget violations (The Ratchet)
|
|
proc emit_policy_violation*(fiber_id, budget_ns, actual_ns: uint64,
|
|
violation_count: uint32, cause_id: uint64): uint64 {.importc, cdecl.}
|
|
|
|
# Forward declaration — implementation is in THE RATCHET section below
|
|
proc sched_analyze_burst*(f: ptr FiberObject, burst_ns: uint64)
|
|
|
|
var photon_idx, matter_idx, gravity_idx, void_idx: int
|
|
|
|
# Forward declaration for channel data check (provided by kernel/channels)
|
|
proc fiber_can_run_on_channels*(id: uint64, mask: uint64): bool {.importc, cdecl.}
|
|
|
|
proc is_runnable(f: ptr FiberObject, now: uint64): bool =
|
|
if f == nil or f.state.sp == 0: return false # Can only run initialized fibers
|
|
if now < f.sleep_until: return false
|
|
if f.is_blocked:
|
|
if fiber_can_run_on_channels(f.id, f.blocked_on_mask):
|
|
f.is_blocked = false # Latched unblock
|
|
return true
|
|
return false
|
|
return true
|
|
|
|
proc sched_tick_spectrum*(fibers: openArray[ptr FiberObject]): bool =
|
|
let now = sched_get_now_ns()
|
|
|
|
# =========================================================
|
|
# Phase 1: PHOTON (Hard Real-Time / Hardware Driven)
|
|
# =========================================================
|
|
var run_photon = false
|
|
for i in 0..<fibers.len:
|
|
let idx = (photon_idx + i) mod fibers.len
|
|
let f = fibers[idx]
|
|
if f != nil and f.getSpectrum() == Spectrum.Photon:
|
|
if is_runnable(f, now):
|
|
if f != current_fiber:
|
|
photon_idx = (idx + 1) mod fibers.len
|
|
let t0 = sched_get_now_ns()
|
|
switch(f)
|
|
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
|
return true
|
|
else:
|
|
run_photon = true
|
|
if run_photon: return true
|
|
|
|
# =========================================================
|
|
# Phase 2: MATTER (Interactive / Latency Sensitive)
|
|
# =========================================================
|
|
var run_matter = false
|
|
for i in 0..<fibers.len:
|
|
let idx = (matter_idx + i) mod fibers.len
|
|
let f = fibers[idx]
|
|
if f != nil and f.getSpectrum() == Spectrum.Matter:
|
|
if is_runnable(f, now):
|
|
if f != current_fiber:
|
|
matter_idx = (idx + 1) mod fibers.len
|
|
let t0 = sched_get_now_ns()
|
|
switch(f)
|
|
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
|
return true
|
|
else:
|
|
run_matter = true
|
|
if run_matter: return true
|
|
|
|
# =========================================================
|
|
# Phase 3: GRAVITY (Throughput / Background)
|
|
# =========================================================
|
|
var run_gravity = false
|
|
for i in 0..<fibers.len:
|
|
let idx = (gravity_idx + i) mod fibers.len
|
|
let f = fibers[idx]
|
|
if f != nil and f.getSpectrum() == Spectrum.Gravity:
|
|
if is_runnable(f, now):
|
|
if f != current_fiber:
|
|
gravity_idx = (idx + 1) mod fibers.len
|
|
let t0 = sched_get_now_ns()
|
|
switch(f)
|
|
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
|
return true
|
|
else:
|
|
run_gravity = true
|
|
if run_gravity: return true
|
|
|
|
# =========================================================
|
|
# Phase 4: VOID (Scavenger)
|
|
# =========================================================
|
|
for i in 0..<fibers.len:
|
|
let idx = (void_idx + i) mod fibers.len
|
|
let f = fibers[idx]
|
|
if f != nil and f.getSpectrum() == Spectrum.Void:
|
|
if is_runnable(f, now):
|
|
if f != current_fiber:
|
|
void_idx = (idx + 1) mod fibers.len
|
|
let t0 = sched_get_now_ns()
|
|
switch(f)
|
|
sched_analyze_burst(f, sched_get_now_ns() - t0)
|
|
return true
|
|
else:
|
|
return true
|
|
|
|
# =========================================================
|
|
# THE SILENCE
|
|
# =========================================================
|
|
# If we reached here, NO fiber is runnable.
|
|
return false
|
|
|
|
proc sched_get_next_wakeup*(fibers: openArray[ptr FiberObject]): uint64 =
|
|
var min_wakeup: uint64 = 0xFFFFFFFFFFFFFFFF'u64
|
|
let now = sched_get_now_ns()
|
|
|
|
for f in fibers:
|
|
if f != nil and f.sleep_until > now:
|
|
if f.sleep_until < min_wakeup:
|
|
min_wakeup = f.sleep_until
|
|
|
|
return min_wakeup
|
|
|
|
# =========================================================
|
|
# M4.5: Budget Defaults Per Spectrum Tier
|
|
# =========================================================
|
|
|
|
proc default_budget_for_spectrum*(s: Spectrum): uint64 =
|
|
## Return the default budget_ns for a given Spectrum tier
|
|
case s
|
|
of Spectrum.Photon: return 2_000_000'u64 # 2ms — hard real-time
|
|
of Spectrum.Matter: return 10_000_000'u64 # 10ms — interactive
|
|
of Spectrum.Gravity: return 50_000_000'u64 # 50ms — batch
|
|
of Spectrum.Void: return 0'u64 # unlimited — scavenger
|
|
|
|
# =========================================================
|
|
# THE RATCHET (Post-Execution Analysis)
|
|
# =========================================================
|
|
|
|
proc sched_log(msg: string) =
|
|
if msg.len > 0:
|
|
console_write(unsafeAddr msg[0], csize_t(msg.len))
|
|
|
|
proc sched_demote(f: ptr FiberObject) =
|
|
## Demote a fiber to a lower Spectrum tier
|
|
let current = f.getSpectrum()
|
|
case current:
|
|
of Spectrum.Photon:
|
|
f.setSpectrum(Spectrum.Matter)
|
|
console_write(cstring("[Ratchet] DEMOTED fiber to Matter\n"), 34)
|
|
of Spectrum.Matter:
|
|
f.setSpectrum(Spectrum.Gravity)
|
|
console_write(cstring("[Ratchet] DEMOTED fiber to Gravity\n"), 35)
|
|
of Spectrum.Gravity:
|
|
f.setSpectrum(Spectrum.Void)
|
|
console_write(cstring("[Ratchet] DEMOTED fiber to Void\n"), 32)
|
|
of Spectrum.Void:
|
|
discard # Already at the bottom
|
|
|
|
proc sched_analyze_burst*(f: ptr FiberObject, burst_ns: uint64) =
|
|
## Analyze the burst duration of a fiber after it yields/switches
|
|
## Implements the 3-strike rule for budget violations
|
|
if f == nil: return
|
|
|
|
f.last_burst_ns = burst_ns
|
|
|
|
# Update moving average (exponential: 75% old, 25% new)
|
|
if f.avg_burst == 0:
|
|
f.avg_burst = burst_ns
|
|
else:
|
|
f.avg_burst = (f.avg_burst * 3 + burst_ns) div 4
|
|
|
|
# Budget enforcement
|
|
if f.budget_ns > 0 and burst_ns > f.budget_ns:
|
|
f.violations += 1
|
|
discard emit_policy_violation(f.id, f.budget_ns, burst_ns, f.violations, 0)
|
|
console_write(cstring("[Ratchet] Budget violation fiber=0x"), 33)
|
|
uart_print_hex(f.id)
|
|
console_write(cstring(" burst="), 7)
|
|
uart_print_hex(burst_ns)
|
|
console_write(cstring(" budget="), 8)
|
|
uart_print_hex(f.budget_ns)
|
|
console_write(cstring(" strikes="), 9)
|
|
uart_print_hex(uint64(f.violations))
|
|
console_write(cstring("\n"), 1)
|
|
|
|
if f.violations >= 3:
|
|
sched_demote(f)
|
|
f.violations = 0 # Reset after demotion
|
|
|
|
{.pop.}
|