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}