platform_scheduler.rs

  1use crate::{PlatformDispatcher, RunnableMeta};
  2use async_task::Runnable;
  3use chrono::{DateTime, Utc};
  4use futures::channel::oneshot;
  5use scheduler::{Clock, Priority, Scheduler, SessionId, TestScheduler, Timer};
  6use std::{
  7    future::Future,
  8    pin::Pin,
  9    sync::{
 10        Arc,
 11        atomic::{AtomicU16, Ordering},
 12    },
 13    task::{Context, Poll},
 14    time::{Duration, Instant},
 15};
 16use waker_fn::waker_fn;
 17
 18/// A production implementation of [`Scheduler`] that wraps a [`PlatformDispatcher`].
 19///
 20/// This allows GPUI to use the scheduler crate's executor types with the platform's
 21/// native dispatch mechanisms (e.g., Grand Central Dispatch on macOS).
 22pub struct PlatformScheduler {
 23    dispatcher: Arc<dyn PlatformDispatcher>,
 24    clock: Arc<PlatformClock>,
 25    next_session_id: AtomicU16,
 26}
 27
 28impl PlatformScheduler {
 29    pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
 30        Self {
 31            dispatcher: dispatcher.clone(),
 32            clock: Arc::new(PlatformClock { dispatcher }),
 33            next_session_id: AtomicU16::new(0),
 34        }
 35    }
 36
 37    pub fn allocate_session_id(&self) -> SessionId {
 38        SessionId::new(self.next_session_id.fetch_add(1, Ordering::SeqCst))
 39    }
 40}
 41
 42impl Scheduler for PlatformScheduler {
 43    fn block(
 44        &self,
 45        _session_id: Option<SessionId>,
 46        mut future: Pin<&mut dyn Future<Output = ()>>,
 47        timeout: Option<Duration>,
 48    ) -> bool {
 49        let deadline = timeout.map(|t| Instant::now() + t);
 50        let parker = parking::Parker::new();
 51        let unparker = parker.unparker();
 52        let waker = waker_fn(move || {
 53            unparker.unpark();
 54        });
 55        let mut cx = Context::from_waker(&waker);
 56
 57        loop {
 58            match future.as_mut().poll(&mut cx) {
 59                Poll::Ready(()) => return true,
 60                Poll::Pending => {
 61                    if let Some(deadline) = deadline {
 62                        let now = Instant::now();
 63                        if now >= deadline {
 64                            return false;
 65                        }
 66                        parker.park_timeout(deadline - now);
 67                    } else {
 68                        parker.park();
 69                    }
 70                }
 71            }
 72        }
 73    }
 74
 75    fn schedule_foreground(&self, _session_id: SessionId, runnable: Runnable<RunnableMeta>) {
 76        self.dispatcher
 77            .dispatch_on_main_thread(runnable, Priority::default());
 78    }
 79
 80    fn schedule_background_with_priority(
 81        &self,
 82        runnable: Runnable<RunnableMeta>,
 83        priority: Priority,
 84    ) {
 85        self.dispatcher.dispatch(runnable, priority);
 86    }
 87
 88    fn spawn_realtime(&self, f: Box<dyn FnOnce() + Send>) {
 89        self.dispatcher.spawn_realtime(f);
 90    }
 91
 92    fn timer(&self, duration: Duration) -> Timer {
 93        use std::sync::{Arc, atomic::AtomicBool};
 94
 95        let (tx, rx) = oneshot::channel();
 96        let dispatcher = self.dispatcher.clone();
 97
 98        // Create a runnable that will send the completion signal
 99        let location = std::panic::Location::caller();
100        let closed = Arc::new(AtomicBool::new(false));
101        let (runnable, _task) = async_task::Builder::new()
102            .metadata(RunnableMeta { location, closed })
103            .spawn(
104                move |_| async move {
105                    let _ = tx.send(());
106                },
107                move |runnable| {
108                    dispatcher.dispatch_after(duration, runnable);
109                },
110            );
111        runnable.schedule();
112
113        Timer::new(rx)
114    }
115
116    fn clock(&self) -> Arc<dyn Clock> {
117        self.clock.clone()
118    }
119
120    fn as_test(&self) -> Option<&TestScheduler> {
121        None
122    }
123}
124
125/// A production clock that uses the platform dispatcher's time.
126struct PlatformClock {
127    dispatcher: Arc<dyn PlatformDispatcher>,
128}
129
130impl Clock for PlatformClock {
131    fn utc_now(&self) -> DateTime<Utc> {
132        Utc::now()
133    }
134
135    fn now(&self) -> Instant {
136        self.dispatcher.now()
137    }
138}