This commit is contained in:
bQUARKz 2026-02-20 10:43:23 +00:00
parent 49025b1055
commit 70c5e08d66
Signed by: bquarkz
SSH Key Fingerprint: SHA256:Z7dgqoglWwoK6j6u4QC87OveEq74WOhFN+gitsxtkf8
3 changed files with 136 additions and 38 deletions

View File

@ -6,9 +6,11 @@ pub mod vm_init_error;
pub mod object; pub mod object;
pub mod heap; pub mod heap;
pub mod roots; pub mod roots;
pub mod scheduler;
pub use prometeu_hal::{HostContext, HostReturn, NativeInterface, SyscallId}; pub use prometeu_hal::{HostContext, HostReturn, NativeInterface, SyscallId};
pub use virtual_machine::{BudgetReport, LogicalFrameEndingReason, VirtualMachine}; pub use virtual_machine::{BudgetReport, LogicalFrameEndingReason, VirtualMachine};
pub use object::{object_flags, ObjectHeader, ObjectKind}; pub use object::{object_flags, ObjectHeader, ObjectKind};
pub use heap::{Heap, StoredObject}; pub use heap::{Heap, StoredObject};
pub use roots::{RootVisitor, visit_value_for_roots}; pub use roots::{RootVisitor, visit_value_for_roots};
pub use scheduler::Scheduler;

View File

@ -0,0 +1,134 @@
use std::collections::VecDeque;
use prometeu_bytecode::HeapRef;
/// Deterministic cooperative scheduler core.
///
/// Policy:
/// - FIFO for ready coroutines.
/// - Sleeping coroutines are ordered by `wake_tick` and moved to ready when `wake_tick <= current_tick`.
/// - No randomness, no preemption, no context switching here.
#[derive(Debug, Default)]
pub struct Scheduler {
/// Queue of runnable coroutines (FIFO)
ready_queue: VecDeque<HeapRef>,
/// Sleeping list kept sorted by (wake_tick, insertion_order)
sleeping: Vec<SleepEntry>,
/// Currently selected coroutine (purely informational here)
current: Option<HeapRef>,
/// Monotonic counter to preserve deterministic order for same wake_tick
next_seq: u64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
struct SleepEntry {
wake_tick: u64,
seq: u64,
coro: HeapRef,
}
impl Scheduler {
pub fn new() -> Self { Self::default() }
// ---------- Ready queue operations ----------
/// Enqueue a coroutine to the ready FIFO.
pub fn enqueue_ready(&mut self, coro: HeapRef) {
self.ready_queue.push_back(coro);
}
/// Dequeue next ready coroutine (front of FIFO).
pub fn dequeue_next(&mut self) -> Option<HeapRef> {
self.ready_queue.pop_front()
}
pub fn is_ready_empty(&self) -> bool { self.ready_queue.is_empty() }
pub fn ready_len(&self) -> usize { self.ready_queue.len() }
// ---------- Current tracking (no switching here) ----------
pub fn set_current(&mut self, coro: Option<HeapRef>) { self.current = coro; }
pub fn current(&self) -> Option<HeapRef> { self.current }
pub fn clear_current(&mut self) { self.current = None; }
// ---------- Sleeping operations ----------
/// Put a coroutine to sleep until `wake_tick` (inclusive).
/// The sleeping list is kept stably ordered to guarantee determinism.
pub fn sleep_until(&mut self, coro: HeapRef, wake_tick: u64) {
let entry = SleepEntry { wake_tick, seq: self.next_seq, coro };
self.next_seq = self.next_seq.wrapping_add(1);
// Binary search insertion point by wake_tick, then by seq to keep total order deterministic
let idx = match self
.sleeping
.binary_search_by(|e| {
if e.wake_tick == entry.wake_tick {
e.seq.cmp(&entry.seq)
} else {
e.wake_tick.cmp(&entry.wake_tick)
}
}) {
Ok(i) => i, // equal element position; insert after to preserve FIFO among equals
Err(i) => i,
};
self.sleeping.insert(idx, entry);
}
/// Move all sleeping coroutines with `wake_tick <= current_tick` to ready queue (FIFO by wake order).
pub fn wake_ready(&mut self, current_tick: u64) {
// Find split point where wake_tick > current_tick
let split = self
.sleeping
.partition_point(|e| e.wake_tick <= current_tick);
if split == 0 { return; }
let mut ready_slice: Vec<SleepEntry> = self.sleeping.drain(0..split).collect();
// Already in order by (wake_tick, seq); push in that order to preserve determinism
for e in ready_slice.drain(..) {
self.ready_queue.push_back(e.coro);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn hr(id: u32) -> HeapRef { HeapRef(id) }
#[test]
fn fifo_ready_queue_is_deterministic() {
let mut s = Scheduler::new();
s.enqueue_ready(hr(1));
s.enqueue_ready(hr(2));
s.enqueue_ready(hr(3));
assert_eq!(s.ready_len(), 3);
assert_eq!(s.dequeue_next(), Some(hr(1)));
assert_eq!(s.dequeue_next(), Some(hr(2)));
assert_eq!(s.dequeue_next(), Some(hr(3)));
assert_eq!(s.dequeue_next(), None);
}
#[test]
fn sleeping_wake_is_stable_and_fifo_by_insertion() {
let mut s = Scheduler::new();
s.sleep_until(hr(10), 5); // first with wake at 5
s.sleep_until(hr(11), 5); // second with same wake
s.sleep_until(hr(12), 6); // later wake
// Before tick 5: nothing wakes
s.wake_ready(4);
assert!(s.is_ready_empty());
// At tick 5: first two wake in insertion order
s.wake_ready(5);
assert_eq!(s.dequeue_next(), Some(hr(10)));
assert_eq!(s.dequeue_next(), Some(hr(11)));
assert!(s.is_ready_empty());
// At tick 6: last one wakes
s.wake_ready(6);
assert_eq!(s.dequeue_next(), Some(hr(12)));
assert!(s.is_ready_empty());
}
}

View File

@ -1,41 +1,3 @@
# PR-7.2 — Deterministic Scheduler Core
## Briefing
Implement a cooperative deterministic scheduler.
## Target
Scheduler structure inside VM:
* `ready_queue: VecDeque<HeapRef>`
* `sleeping: Vec<HeapRef>` (sorted or scanned by wake_tick)
* `current: Option<HeapRef>`
Policy:
* FIFO for ready coroutines.
* Sleeping coroutines move to ready when `wake_tick <= current_tick`.
No execution switching yet.
## Checklist
* [ ] Scheduler struct exists.
* [ ] Deterministic FIFO behavior.
* [ ] No randomness.
## Tests
* Enqueue 3 coroutines and ensure dequeue order stable.
## Junie Rules
You MAY add scheduler struct.
You MUST NOT implement SPAWN/YIELD/SLEEP yet.
---
# PR-7.3 — SPAWN Instruction # PR-7.3 — SPAWN Instruction
## Briefing ## Briefing