logicaffeine_runtime/config.rs
1//! Scheduler configuration — annotatable knobs with a nice default.
2//!
3//! The default ([`SchedulerConfig::default`]) is the fair, predictable,
4//! fully-deterministic baseline: a FIFO ready queue on a logical clock. Programs
5//! opt into the other disciplines (via a `## Scheduler: <policy>` decorator, wired
6//! in a later phase) or callers build a config with the fluent setters.
7
8/// The ready-task selection discipline. Determinism holds under *every* policy:
9/// `Fifo`/`Lifo`/`RoundRobin`/`Priority` are deterministic by construction, and
10/// `Random` resolves its pick through the seeded `Chooser`, so it is reproducible
11/// under a fixed seed.
12#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
13pub enum SchedulePolicy {
14 /// Deterministic FIFO ready queue — fair, predictable. **The default.**
15 Fifo,
16 /// LIFO (stack) — depth-first; better locality, but can starve siblings.
17 Lifo,
18 /// Round-robin rotation with a cooperative quantum.
19 RoundRobin,
20 /// Seeded-random ready pick — explores interleavings (fuzzing / race-finding).
21 Random,
22 /// Highest task priority first; FIFO within a priority band.
23 Priority,
24}
25
26impl Default for SchedulePolicy {
27 fn default() -> Self {
28 SchedulePolicy::Fifo
29 }
30}
31
32/// How the scheduler's clock advances.
33#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
34pub enum ClockMode {
35 /// Virtual time: `Sleep`/`After` are ordered logically and elapse instantly.
36 /// Deterministic and wall-clock-free — the default, used by tests and TV.
37 Logical,
38 /// Real time: timers map to actual sleeps (production, non-replay runs).
39 Wall,
40}
41
42impl Default for ClockMode {
43 fn default() -> Self {
44 ClockMode::Logical
45 }
46}
47
48/// All scheduler knobs, bundled. Has a `Default` and fluent setters so it is easy
49/// to annotate while still having a sensible baseline.
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
51pub struct SchedulerConfig {
52 /// Ready-task selection discipline (default `Fifo`).
53 pub policy: SchedulePolicy,
54 /// Clock mode (default `Logical`).
55 pub clock: ClockMode,
56 /// Default capacity for channels created without an explicit one
57 /// (default 32 — matches the AOT `mpsc` default).
58 pub default_channel_capacity: usize,
59 /// Cooperative yield interval, in scheduler steps (default 10_000).
60 pub preempt_every: u32,
61 /// M:N worker count; 1 = cooperative single-thread (default 1).
62 pub workers: usize,
63}
64
65impl Default for SchedulerConfig {
66 fn default() -> Self {
67 SchedulerConfig {
68 policy: SchedulePolicy::Fifo,
69 clock: ClockMode::Logical,
70 default_channel_capacity: 32,
71 preempt_every: 10_000,
72 workers: 1,
73 }
74 }
75}
76
77impl SchedulerConfig {
78 /// Set the scheduling policy.
79 pub fn with_policy(mut self, policy: SchedulePolicy) -> Self {
80 self.policy = policy;
81 self
82 }
83 /// Set the clock mode.
84 pub fn with_clock(mut self, clock: ClockMode) -> Self {
85 self.clock = clock;
86 self
87 }
88 /// Set the default channel capacity.
89 pub fn with_channel_capacity(mut self, capacity: usize) -> Self {
90 self.default_channel_capacity = capacity;
91 self
92 }
93 /// Set the worker count (M:N).
94 pub fn with_workers(mut self, workers: usize) -> Self {
95 self.workers = workers;
96 self
97 }
98}
99
100#[cfg(test)]
101mod tests {
102 use super::*;
103
104 #[test]
105 fn policy_default_is_fifo() {
106 assert_eq!(SchedulerConfig::default().policy, SchedulePolicy::Fifo);
107 assert_eq!(SchedulePolicy::default(), SchedulePolicy::Fifo);
108 }
109
110 #[test]
111 fn config_default_is_the_nice_baseline() {
112 let c = SchedulerConfig::default();
113 assert_eq!(c.clock, ClockMode::Logical);
114 assert_eq!(c.default_channel_capacity, 32);
115 assert_eq!(c.workers, 1);
116 }
117
118 #[test]
119 fn builder_overrides_apply() {
120 let c = SchedulerConfig::default()
121 .with_policy(SchedulePolicy::Random)
122 .with_channel_capacity(4)
123 .with_workers(8);
124 assert_eq!(c.policy, SchedulePolicy::Random);
125 assert_eq!(c.default_channel_capacity, 4);
126 assert_eq!(c.workers, 8);
127 assert_eq!(c.clock, ClockMode::Logical, "untouched knobs keep defaults");
128 }
129}