dispatcher.rs

  1use crate::{PlatformDispatcher, Priority, RunnableVariant};
  2use scheduler::{Clock, Scheduler, SessionId, TestScheduler, TestSchedulerConfig, Yield};
  3use std::{
  4    sync::Arc,
  5    time::{Duration, Instant},
  6};
  7
  8/// TestDispatcher provides deterministic async execution for tests.
  9///
 10/// This implementation delegates task scheduling to the scheduler crate's `TestScheduler`.
 11/// Access the scheduler directly via `scheduler()` for clock, rng, and parking control.
 12#[doc(hidden)]
 13pub struct TestDispatcher {
 14    session_id: SessionId,
 15    scheduler: Arc<TestScheduler>,
 16}
 17
 18impl TestDispatcher {
 19    pub fn new(seed: u64) -> Self {
 20        let scheduler = Arc::new(TestScheduler::new(TestSchedulerConfig {
 21            seed,
 22            randomize_order: true,
 23            allow_parking: false,
 24            capture_pending_traces: std::env::var("PENDING_TRACES")
 25                .map_or(false, |var| var == "1" || var == "true"),
 26            timeout_ticks: 0..=1000,
 27        }));
 28
 29        let session_id = scheduler.allocate_session_id();
 30
 31        TestDispatcher {
 32            session_id,
 33            scheduler,
 34        }
 35    }
 36
 37    pub fn scheduler(&self) -> &Arc<TestScheduler> {
 38        &self.scheduler
 39    }
 40
 41    pub fn session_id(&self) -> SessionId {
 42        self.session_id
 43    }
 44
 45    pub fn advance_clock(&self, by: Duration) {
 46        self.scheduler.advance_clock(by);
 47    }
 48
 49    pub fn advance_clock_to_next_timer(&self) -> bool {
 50        self.scheduler.advance_clock_to_next_timer()
 51    }
 52
 53    pub fn simulate_random_delay(&self) -> Yield {
 54        self.scheduler.yield_random()
 55    }
 56
 57    pub fn tick(&self, background_only: bool) -> bool {
 58        if background_only {
 59            self.scheduler.tick_background_only()
 60        } else {
 61            self.scheduler.tick()
 62        }
 63    }
 64
 65    pub fn run_until_parked(&self) {
 66        while self.tick(false) {}
 67    }
 68}
 69
 70impl Clone for TestDispatcher {
 71    fn clone(&self) -> Self {
 72        let session_id = self.scheduler.allocate_session_id();
 73        Self {
 74            session_id,
 75            scheduler: self.scheduler.clone(),
 76        }
 77    }
 78}
 79
 80impl PlatformDispatcher for TestDispatcher {
 81    fn get_all_timings(&self) -> Vec<crate::ThreadTaskTimings> {
 82        Vec::new()
 83    }
 84
 85    fn get_current_thread_timings(&self) -> Vec<crate::TaskTiming> {
 86        Vec::new()
 87    }
 88
 89    fn is_main_thread(&self) -> bool {
 90        self.scheduler.is_main_thread()
 91    }
 92
 93    fn now(&self) -> Instant {
 94        self.scheduler.clock().now()
 95    }
 96
 97    fn dispatch(&self, runnable: RunnableVariant, priority: Priority) {
 98        self.scheduler
 99            .schedule_background_with_priority(runnable, priority);
100    }
101
102    fn dispatch_on_main_thread(&self, runnable: RunnableVariant, _priority: Priority) {
103        self.scheduler
104            .schedule_foreground(self.session_id, runnable);
105    }
106
107    fn dispatch_after(&self, _duration: Duration, _runnable: RunnableVariant) {
108        panic!(
109            "dispatch_after should not be called in tests. \
110            Use BackgroundExecutor::timer() which uses the scheduler's native timer."
111        );
112    }
113
114    fn as_test(&self) -> Option<&TestDispatcher> {
115        Some(self)
116    }
117
118    fn spawn_realtime(&self, f: Box<dyn FnOnce() + Send>) {
119        std::thread::spawn(move || {
120            f();
121        });
122    }
123}