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        if let Poll::Ready(()) = future.as_mut().poll(&mut cx) {
 57            return true;
 58        }
 59        loop {
 60            match deadline {
 61                Some(deadline) if !parker.park_deadline(deadline) && deadline <= Instant::now() => {
 62                    return false;
 63                }
 64                Some(_) => (),
 65                None => parker.park(),
 66            }
 67            if let Poll::Ready(()) = future.as_mut().poll(&mut cx) {
 68                break true;
 69            }
 70        }
 71    }
 72
 73    fn schedule_foreground(&self, _session_id: SessionId, runnable: Runnable<RunnableMeta>) {
 74        self.dispatcher
 75            .dispatch_on_main_thread(runnable, Priority::default());
 76    }
 77
 78    fn schedule_background_with_priority(
 79        &self,
 80        runnable: Runnable<RunnableMeta>,
 81        priority: Priority,
 82    ) {
 83        self.dispatcher.dispatch(runnable, priority);
 84    }
 85
 86    fn spawn_realtime(&self, f: Box<dyn FnOnce() + Send>) {
 87        self.dispatcher.spawn_realtime(f);
 88    }
 89
 90    fn timer(&self, duration: Duration) -> Timer {
 91        use std::sync::{Arc, atomic::AtomicBool};
 92
 93        let (tx, rx) = oneshot::channel();
 94        let dispatcher = self.dispatcher.clone();
 95
 96        // Create a runnable that will send the completion signal
 97        let location = std::panic::Location::caller();
 98        let closed = Arc::new(AtomicBool::new(false));
 99        let (runnable, _task) = async_task::Builder::new()
100            .metadata(RunnableMeta { location, closed })
101            .spawn(
102                move |_| async move {
103                    let _ = tx.send(());
104                },
105                move |runnable| {
106                    dispatcher.dispatch_after(duration, runnable);
107                },
108            );
109        runnable.schedule();
110
111        Timer::new(rx)
112    }
113
114    fn clock(&self) -> Arc<dyn Clock> {
115        self.clock.clone()
116    }
117
118    fn as_test(&self) -> Option<&TestScheduler> {
119        None
120    }
121}
122
123/// A production clock that uses the platform dispatcher's time.
124struct PlatformClock {
125    dispatcher: Arc<dyn PlatformDispatcher>,
126}
127
128impl Clock for PlatformClock {
129    fn utc_now(&self) -> DateTime<Utc> {
130        Utc::now()
131    }
132
133    fn now(&self) -> Instant {
134        self.dispatcher.now()
135    }
136}