Skip to main content

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}