Jade VM-Scheduler & Intrinsic-Grenze¶
Status: Konsolidierter normativer Entwurf
Version: 0.2.0
Datum: 2026-09-05
Geltungsbereich: Tasks, Scheduling, Suspension, I/O-Wakeup, Timer, Engine-Runtime-Konfiguration und Scheduler-Intrinsics
Bezieht sich auf: Runtime-Modell (03), VM Instruction Set (05), Intrinsics & VM API (08), JadeValue/Register-Layout (11), IR (13), Effect Rows & Throw[E]
Ring-Zugehörigkeit: Scheduler Ring 0; JDL-/Stdlib-Abstraktionen darüber Ring 1/2
Host: Zig; konkrete Zig-Strukturen sind nicht Teil des Vertrags
1. Architektur¶
Der Scheduler ist Teil der Jade VM. Er führt M leichtgewichtige Tasks auf N konfigurierbaren Worker-Threads aus.
Zwei Fragen bleiben getrennt:
Task wird wieder lauffähig:
Event oder Zeit
Lauffähige Task bekommt CPU:
Run Queue / Worker / Reduction-Budget
Event-/I/O-Poller Master Clock
│ │
▼ ▼
Event-Wakeup Engine Timer-Wheel
│ │
└─────────┬───────────┘
▼
Runnable
│
▼
Worker / Run Queue
│
▼
VM Loop
1.1 M:N und Work-Stealing¶
Work-Stealing ist die vorgesehene Mehrworker-Strategie. Eine Referenzreihenfolge für Worker ist:
- lokale Run Queue,
- globale Inject-/Wakeup-Queue,
- Work-Stealing,
- Worker parken.
Deque-Typ, Steal-Größe und Victim-Auswahl sind Implementierungsdetails. Die beobachtbare Semantik darf davon nicht abhängen.
1.2 Event-Poller und Clock¶
Der Event-/I/O-Poller übersetzt externe Completions in Scheduler-Wakeups. Die konkrete OS-Anbindung (epoll, io_uring, kqueue, IOCP oder äquivalent) ist Plattformdetail.
Die Master Clock bedient ausschließlich aktive Timer-Wheels. Der Scheduler besitzt keinen globalen Heartbeat. Ohne Arbeit, Events oder aktive Timer darf die Runtime schlafen.
2. Task-Modell¶
Eine Task ist die kleinste vom Scheduler verwaltete, suspendierbare VM-Ausführungseinheit.
Konzeptionell:
Task
id
schedulingState
executionState // IP, Frames, Register
reductions
engineId
waitCondition?
cancellationRequested
outcome?
2.1 Schedulerzustand und Ausgang¶
Schedulerzustand:
Task-Ausgang:
Beides sind getrennte Dimensionen. Completed bedeutet nicht automatisch Erfolg.
stateDiagram-v2
[*] --> Runnable: spawn
Runnable --> Running: dispatch
Running --> Runnable: preemption / yield
Running --> Waiting: event / timer
Waiting --> Runnable: wakeup / cancellation
Running --> Completed: return / throw / trap / cancellation
Completed --> [*]
2.2 Task-Grenze und Throw[E]¶
Ein Throw[E] darf nicht durch den Stack einer anderen Task unwinden.
Eine Join-/Wait-Abstraktion darf den Fehler im wartenden Task wieder lokal als Throw[E] erzeugen. Die konkrete JDL-Task-API wird separat spezifiziert.
Throw[E], Trap und Cancellation bleiben getrennt.
2.3 Task vs. Actor¶
Tasks besitzen keine eingebaute Mailbox und keine Supervision.
Actors, Channels und Mailboxes dürfen generische Wait-/Wakeup-Mechanismen verwenden, gehören aber nicht zum Scheduler-Core.
3. Scheduling und Suspension¶
3.1 Reduction-basierte Fairness¶
Jede laufende Task besitzt ein Reduction-Budget aus ihrer EngineRuntimeConfig.
Bei erschöpftem Budget wird die Task an einem VM-sicheren Preemption-Point gespeichert und erneut als Runnable eingereiht.
Das ist keine willkürliche OS-Präemption in nativen Code. Eine dauerhaft runnable Task darf andere runnable Tasks nicht dauerhaft verdrängen.
3.2 Event-getriebene Suspension¶
Muss eine Operation warten, registriert die Runtime eine Wait-Condition und setzt die aktuelle Task auf Waiting.
Der Scheduler kennt nur die Wait-/Wakeup-Identität, nicht die fachliche Bedeutung des Events.
3.3 Suspension ist kein Result¶
VM-interner Ausführungskontrollfluss muss mindestens unterscheiden können zwischen:
Die konkrete Struktur ist Implementierungsdetail.
Die frühere Form JadeResult.yield() ist kein JDL-Result[T,E] und wird nicht als Fehlersemantik verwendet.
3.4 Transparente Suspension in JDL¶
JDL besitzt keine spezielle async-Funktionsklasse und benötigt für normale Service-Aufrufe keine async/await-Schlüsselwörter.
def loadConfig(path: Path) ->{Fs, Throw[IoError]} Config {
val data = Fs.read(path)
parseConfig(data)
}
Fs.read darf die aktuelle Task suspendieren. Die Funktion bleibt eine normale JDL-Funktion.
Effect Row:
semantische Anforderungen und propagierbare Effekte
Scheduler:
wann die Berechnung weiterlaufen kann
Suspendierbarkeit ist kein Row-Element; es gibt keinen Async-Effekt.
3.5 Explizite Parallelität¶
Suspension erzeugt keine neue Task. Parallele Arbeit wird explizit durch Task-Erzeugung gestartet, konzeptionell über spawn.
// exemplarisch; Oberfläche separat zu spezifizieren
val first = spawn { load("a") }
val b = load("b")
val a = first.join()
Task-Captures unterliegen den normalen Ownership-/Sharing-Regeln der Task-Grenze.
3.6 Cancellation¶
Cancellation ist kooperativer Scheduler-Kontrollfluss und kein Throw[E].
- die Anforderung bleibt sticky,
- wartende Tasks werden geweckt,
- laufende Tasks beobachten sie an definierten Checkpoints,
- es gibt keine willkürliche Unterbrechung beliebigen VM-/FFI-Codes.
Eine höhere Abstraktion darf Cancellation in einen Wert oder Domain-Fehler übersetzen.
4. Engine-Konfiguration und Zeit¶
4.1 Trennung der Ebenen¶
Der Scheduler kennt keine semantische JDL-Engine. Er konsumiert ausschließlich EngineRuntimeConfig.
Tasks speichern ihre engineId.
4.2 EngineRuntimeConfig¶
Scheduler-relevante Konfiguration umfasst mindestens:
Capabilities gehören nicht zur Scheduler-Semantik. Enforcement erfolgt an RuntimeBus-/VM-Grenzen.
Registrierung und Änderungen der Runtime-Konfiguration laufen über den RuntimeBus.
4.3 Engine-lokale Timer-Wheels¶
Nicht jede Engine besitzt ein Timer-Wheel.
typisch zeitgetrieben:
Rendering, periodische Jobs, Health Checks, Simulation
typisch eventgetrieben:
HTTP-Transport, Socket-Completion, viele Actor-/Channel-Wakeups
Ein hierarchisches Timer-Wheel ist die vorgesehene Referenzimplementierung. Anzahl der Ebenen und Slots sind kein Sprachvertrag.
4.4 Overrun-Policies¶
Für periodische Arbeit existieren drei Policies:
Drift nächster Lauf relativ zum tatsächlichen Abschluss; kein Aufstauen
Skip verpasste Intervalle überspringen
Queue verpasste Intervalle nachholen
Drift ist Default, sofern die Engine nichts anderes verlangt.
4.5 Beobachtbarkeit¶
Scheduler und Timer-Wheels sollen mindestens Größen wie folgende bereitstellen:
runnableTasks
waitingTasks
activeWorkers
queuePressure
targetRate
actualRate
handlerDuration
overrunCount
Das konkrete Metrikformat ist nicht normativ.
5. RuntimeBus und Intrinsic-Grenze¶
5.1 Mutationsgrenze¶
Zustandsändernde Scheduler-Operationen laufen ausschließlich über den RuntimeBus.
Typische Operationen:
Mailbox-, Channel- und Actor-Logik liegen darüber.
5.2 Intrinsic-Encoding¶
Die Intrinsic-Grenze ist stabil und versioniert. Diese Scheduler-Spezifikation friert jedoch keine 16-Bit-ID-Bereiche oder Opcode-Nummern ein.
Gemäß VM-Spec bleiben zwei kompatible Lowering-Strategien zulässig:
- compiler-known Builtin/Intrinsic über den normalen Call-Pfad,
- ein generischer
Intrinsic-Opcode mit Subcode.
Konkrete IDs und Encoding gehören in die zentrale VM-/Intrinsic-Spezifikation.
5.3 JadeValue¶
Intrinsics umgehen das aktuelle Grenzmodell nicht.
Gemäß 11-jadevalue-und-register-layout ist JadeValue das Transportformat an Call-, Return-, Intrinsic- und FFI-Grenzen. Innerhalb einer Funktion arbeitet die VM auf dem statisch angelegten Register-Buffer.
Fast Paths sind zulässig, sofern sie dieselbe Semantik und dieselben Verifier-/ABI-Invarianten erhalten.
5.4 Capabilities¶
Capability-Prüfung ist keine Scheduler-Policy.
Runtime-Capabilities werden an RuntimeBus-/VM-Grenzen erzwungen. Der Scheduler erhält nur autorisierte Scheduler-Kommandos.
6. Normative Invarianten¶
S-1 Der Scheduler ist Teil der VM, kein Actor und kein Userland-Service.
S-2 Tasks werden M:N auf Worker-Threads ausgeführt; die Workerzahl ist konfigurierbar.
S-3 Die Task ist die kleinste suspendierbare Scheduler-Einheit; Actors liegen darüber.
S-4 Der Scheduler besitzt keinen globalen Heartbeat.
S-5 Wartende Tasks werden durch Events oder Zeitbedingungen wieder runnable.
S-6 Reduction-basierte Preemption schützt vor unbegrenzter Worker-Monopolisierung.
S-7 Preemption erfolgt nur an VM-definierten sicheren Punkten.
S-8 Suspension ist Runtime-Eigenschaft, keine eigene JDL-Funktionsklasse.
S-9 Für normale suspendierbare Aufrufe gibt es kein Async-Row-Element und keine notwendige async/await-Färbung.
S-10 Neue parallele Ausführung entsteht explizit durch Task-Erzeugung.
S-11 Schedulerzustand und Task-Ausgang sind getrennte Dimensionen.
S-12 Throw[E] unwindet nicht über Task-Grenzen; es wird beim Task-Ausgang materialisiert.
S-13 Cancellation ist weder Throw[E] noch Trap.
S-14 Der Scheduler kennt nur EngineRuntimeConfig, nicht die semantische Engine.
S-15 Timer-Wheels werden durch Engine-Runtime-Konfiguration aktiviert und parametrisiert.
S-16 Scheduler-Zustand wird ausschließlich über den RuntimeBus mutiert.
S-17 Tasks besitzen keine Scheduler-Mailbox; Messaging liegt oberhalb des Scheduler-Cores.
S-18 VM-Suspensionssignale sind keine JDL-Result[T,E]-Werte.
S-19 Intrinsic-Encoding und IDs werden zentral in VM-/Intrinsic-Spezifikationen festgelegt.
S-20 Capability-Enforcement liegt an RuntimeBus-/VM-Grenzen, nicht in der Scheduling-Policy.
7. Bewusst offene Implementierungsdetails¶
Diese Punkte können später entschieden werden, ohne das semantische Modell zu ändern:
| Punkt | Offen |
|---|---|
| Worker-Default | Ableitung aus CPU-/Environment-Ressourcen |
| Work-Stealing | Deque, Steal-Größe, Victim-Auswahl |
| Event-Poller | Plattform-Backend |
| Wait-Registry | interne Datenstruktur |
| Timer-Wheel | Ebenen, Slots, Speicherlayout |
| Master Clock | eigener Thread oder äquivalente Integration |
| Task-Outcome | konkrete Repräsentation / Error-Envelope |
| Task-Handle | Join/Detach/Structured-Concurrency-Lifetime |
| Shutdown | Drain, Graceful Stop, Forced Stop |
| Intrinsic-ABI | ID-Breite, Nummern, Encoding |
| Metrics | genaue Struktur und Export |
Diese Fragen sind keine Blocker für das Scheduler-Grundmodell.
8. Inkrementeller Implementierungsweg¶
V1
ein Worker
eine Run Queue
Task-Zustandsautomat
Reduction-Counter
generisches wait / wake
Event-Poller
V2
mehrere Worker
Work-Stealing
Worker-Parking
V3
Master Clock
optionales Timer-Wheel
Sleep / Deadline / Timeout
V4
EngineRuntimeConfig
Engine-lokale Timer-Wheels
Resource Limits
Overrun-Policies
V5
Metrics
Scheduler-Introspection
Queue-/Fast-Path-Optimierungen
Task-Outcome-Materialisierung für Throw[E], Trap und Cancellation muss spätestens mit Task-Join/Wait integriert werden.
Änderungsprotokoll¶
v0.2.0¶
- D-spezifische Implementierungsdetails entfernt; Host auf Zig aktualisiert.
Effect[R,E,D]und die alte Trennung „imperativer Stil / Effect-Stil“ entfernt.- Effect Rows
->{...}übernommen; Suspendierbarkeit davon ausdrücklich getrennt. - Task-State und Task-Outcome getrennt.
Throw[E]-Materialisierung an Task-Grenzen ergänzt.- Cancellation von
Throw[E]und Trap getrennt. - Task-Mailbox aus dem Scheduler-Core entfernt.
JadeResult.yield()als veraltete Vermischung von VM-Control und JDL-Resultbeseitigt.JadeValueals Intrinsic-Grenzformat berücksichtigt.- Capability-Enforcement vom Scheduler an RuntimeBus-/VM-Grenzen verschoben.
- feste Intrinsic-ID-Bereiche aus dieser Spezifikation entfernt.
- Timer-Wheel-Slotzahlen, Queue-Strukturen und Plattform-I/O zu Implementierungsdetails gemacht.
- Aufbau auf acht kompakte Abschnitte reduziert.