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