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 timer(&self, duration: Duration) -> Timer {
 89        use std::sync::{Arc, atomic::AtomicBool};
 90
 91        let (tx, rx) = oneshot::channel();
 92        let dispatcher = self.dispatcher.clone();
 93
 94        // Create a runnable that will send the completion signal
 95        let location = std::panic::Location::caller();
 96        let closed = Arc::new(AtomicBool::new(false));
 97        let (runnable, _task) = async_task::Builder::new()
 98            .metadata(RunnableMeta { location, closed })
 99            .spawn(
100                move |_| async move {
101                    let _ = tx.send(());
102                },
103                move |runnable| {
104                    dispatcher.dispatch_after(duration, runnable);
105                },
106            );
107        runnable.schedule();
108
109        Timer::new(rx)
110    }
111
112    fn clock(&self) -> Arc<dyn Clock> {
113        self.clock.clone()
114    }
115
116    fn as_test(&self) -> Option<&TestScheduler> {
117        None
118    }
119}
120
121/// A production clock that uses the platform dispatcher's time.
122struct PlatformClock {
123    dispatcher: Arc<dyn PlatformDispatcher>,
124}
125
126impl Clock for PlatformClock {
127    fn utc_now(&self) -> DateTime<Utc> {
128        Utc::now()
129    }
130
131    fn now(&self) -> Instant {
132        self.dispatcher.now()
133    }
134}