pr7.2
This commit is contained in:
parent
49025b1055
commit
70c5e08d66
@ -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;
|
||||||
|
|||||||
134
crates/console/prometeu-vm/src/scheduler.rs
Normal file
134
crates/console/prometeu-vm/src/scheduler.rs
Normal 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user