Skip to main content

scheduler/
scheduler.rs

1mod clock;
2mod executor;
3mod test_scheduler;
4#[cfg(test)]
5mod tests;
6
7pub use clock::*;
8pub use executor::*;
9pub use test_scheduler::*;
10
11use async_task::Runnable;
12use futures::channel::oneshot;
13use std::{
14    future::Future,
15    panic::Location,
16    pin::Pin,
17    sync::Arc,
18    task::{Context, Poll},
19    time::Duration,
20};
21
22/// Task priority for background tasks.
23///
24/// Higher priority tasks are more likely to be scheduled before lower priority tasks,
25/// but this is not a strict guarantee - the scheduler may interleave tasks of different
26/// priorities to prevent starvation.
27#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
28#[repr(u8)]
29pub enum Priority {
30    /// Realtime priority
31    ///
32    /// Spawning a task with this priority will spin it off on a separate thread dedicated just to that task. Only use for audio.
33    RealtimeAudio,
34    /// High priority - use for tasks critical to user experience/responsiveness.
35    High,
36    /// Medium priority - suitable for most use cases.
37    #[default]
38    Medium,
39    /// Low priority - use for background work that can be deprioritized.
40    Low,
41}
42
43impl Priority {
44    /// Returns the relative probability weight for this priority level.
45    /// Used by schedulers to determine task selection probability.
46    pub const fn weight(self) -> u32 {
47        match self {
48            Priority::High => 60,
49            Priority::Medium => 30,
50            Priority::Low => 10,
51            // realtime priorities are not considered for probability scheduling
52            Priority::RealtimeAudio => 0,
53        }
54    }
55}
56
57/// Metadata attached to runnables for debugging and profiling.
58#[derive(Clone)]
59pub struct RunnableMeta {
60    /// The source location where the task was spawned.
61    pub location: &'static Location<'static>,
62}
63
64impl std::fmt::Debug for RunnableMeta {
65    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66        f.debug_struct("RunnableMeta")
67            .field("location", &self.location)
68            .finish()
69    }
70}
71
72pub trait Scheduler: Send + Sync {
73    /// Block until the given future completes or timeout occurs.
74    ///
75    /// Returns `true` if the future completed, `false` if it timed out.
76    /// The future is passed as a pinned mutable reference so the caller
77    /// retains ownership and can continue polling or return it on timeout.
78    fn block(
79        &self,
80        session_id: Option<SessionId>,
81        future: Pin<&mut dyn Future<Output = ()>>,
82        timeout: Option<Duration>,
83    ) -> bool;
84
85    fn schedule_foreground(&self, session_id: SessionId, runnable: Runnable<RunnableMeta>);
86
87    /// Schedule a background task with the given priority.
88    fn schedule_background_with_priority(
89        &self,
90        runnable: Runnable<RunnableMeta>,
91        priority: Priority,
92    );
93
94    /// Spawn a closure on a dedicated realtime thread for audio processing.
95    fn spawn_realtime(&self, f: Box<dyn FnOnce() + Send>);
96
97    /// Schedule a background task with default (medium) priority.
98    fn schedule_background(&self, runnable: Runnable<RunnableMeta>) {
99        self.schedule_background_with_priority(runnable, Priority::default());
100    }
101
102    #[track_caller]
103    fn timer(&self, timeout: Duration) -> Timer;
104    fn clock(&self) -> Arc<dyn Clock>;
105
106    fn as_test(&self) -> Option<&TestScheduler> {
107        None
108    }
109}
110
111#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
112pub struct SessionId(u16);
113
114impl SessionId {
115    pub fn new(id: u16) -> Self {
116        SessionId(id)
117    }
118}
119
120pub struct Timer(oneshot::Receiver<()>);
121
122impl Timer {
123    pub fn new(rx: oneshot::Receiver<()>) -> Self {
124        Timer(rx)
125    }
126}
127
128impl Future for Timer {
129    type Output = ();
130
131    fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<()> {
132        match Pin::new(&mut self.0).poll(cx) {
133            Poll::Ready(_) => Poll::Ready(()),
134            Poll::Pending => Poll::Pending,
135        }
136    }
137}