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