1use std::{future::Future, marker::PhantomData, rc::Rc, sync::Arc, time::Duration};
2#[cfg(not(target_family = "wasm"))]
3use std::{mem, pin::Pin};
4
5#[cfg(not(target_family = "wasm"))]
6use futures::channel::mpsc;
7use futures::prelude::*;
8use scheduler::Instant;
9use scheduler::Scheduler;
10
11use crate::{ActivityGuard, PlatformDispatcher, PlatformScheduler};
12
13pub use scheduler::{
14 DedicatedExecutor, FallibleTask, LocalExecutor as SchedulerLocalExecutor, Priority,
15 RunnableMeta, Task,
16};
17
18#[derive(Clone)]
21pub struct BackgroundExecutor {
22 inner: scheduler::BackgroundExecutor,
23 dispatcher: Arc<dyn PlatformDispatcher>,
24}
25
26#[derive(Clone)]
29pub struct ForegroundExecutor {
30 inner: scheduler::LocalExecutor,
31 dispatcher: Arc<dyn PlatformDispatcher>,
32 #[cfg(feature = "profiler")]
33 foreground_runnables: Option<crate::profiler::ForegroundRunnableCounter>,
34 not_send: PhantomData<Rc<()>>,
35}
36
37impl BackgroundExecutor {
40 pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
42 #[cfg(any(test, feature = "test-support"))]
43 let scheduler: Arc<dyn Scheduler> = if let Some(test_dispatcher) = dispatcher.as_test() {
44 test_dispatcher.scheduler().clone()
45 } else {
46 Arc::new(PlatformScheduler::new(dispatcher.clone()))
47 };
48
49 #[cfg(not(any(test, feature = "test-support")))]
50 let scheduler: Arc<dyn Scheduler> = Arc::new(PlatformScheduler::new(dispatcher.clone()));
51
52 Self {
53 inner: scheduler::BackgroundExecutor::new(scheduler),
54 dispatcher,
55 }
56 }
57
58 pub fn scheduler_executor(&self) -> scheduler::BackgroundExecutor {
62 self.inner.clone()
63 }
64
65 pub fn prevent_app_nap(&self, reason: &str) -> ActivityGuard {
69 self.dispatcher.prevent_app_nap(reason)
70 }
71
72 #[track_caller]
74 pub fn spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
75 where
76 R: Send + 'static,
77 {
78 self.spawn_with_priority(Priority::default(), future.boxed())
79 }
80
81 #[track_caller]
86 pub fn spawn_with_priority<R>(
87 &self,
88 priority: Priority,
89 future: impl Future<Output = R> + Send + 'static,
90 ) -> Task<R>
91 where
92 R: Send + 'static,
93 {
94 if priority == Priority::RealtimeAudio {
95 self.inner.spawn_realtime(future)
96 } else {
97 self.inner.spawn_with_priority(priority, future)
98 }
99 }
100
101 #[cfg(not(target_family = "wasm"))]
106 pub async fn scoped<'scope, F>(&self, scheduler: F)
107 where
108 F: FnOnce(&mut Scope<'scope>),
109 {
110 let mut scope = Scope::new(self.clone(), Priority::default());
111 (scheduler)(&mut scope);
112 let spawned = mem::take(&mut scope.futures)
113 .into_iter()
114 .map(|f| self.spawn_with_priority(scope.priority, f))
115 .collect::<Vec<_>>();
116 for task in spawned {
117 task.await;
118 }
119 }
120
121 #[cfg(not(target_family = "wasm"))]
127 pub async fn scoped_priority<'scope, F>(&self, priority: Priority, scheduler: F)
128 where
129 F: FnOnce(&mut Scope<'scope>),
130 {
131 let mut scope = Scope::new(self.clone(), priority);
132 (scheduler)(&mut scope);
133 let spawned = mem::take(&mut scope.futures)
134 .into_iter()
135 .map(|f| self.spawn_with_priority(scope.priority, f))
136 .collect::<Vec<_>>();
137 for task in spawned {
138 task.await;
139 }
140 }
141
142 pub fn now(&self) -> Instant {
147 self.inner.scheduler().clock().now()
148 }
149
150 #[track_caller]
154 pub fn timer(&self, duration: Duration) -> Task<()> {
155 if duration.is_zero() {
156 return Task::ready(());
157 }
158 self.spawn(self.inner.scheduler().timer(duration))
159 }
160
161 #[cfg(any(test, feature = "test-support"))]
163 pub fn simulate_random_delay(&self) -> impl Future<Output = ()> + use<> {
164 self.dispatcher.as_test().unwrap().simulate_random_delay()
165 }
166
167 #[cfg(any(test, feature = "test-support"))]
169 pub fn advance_clock(&self, duration: Duration) {
170 self.dispatcher.as_test().unwrap().advance_clock(duration)
171 }
172
173 #[cfg(any(test, feature = "test-support"))]
175 pub fn tick(&self) -> bool {
176 self.dispatcher.as_test().unwrap().scheduler().tick()
177 }
178
179 #[cfg(any(test, feature = "test-support"))]
186 pub fn run_until_parked(&self) {
187 let scheduler = self.dispatcher.as_test().unwrap().scheduler();
188 scheduler.run();
189 }
190
191 #[cfg(any(test, feature = "test-support"))]
193 pub fn allow_parking(&self) {
194 self.dispatcher
195 .as_test()
196 .unwrap()
197 .scheduler()
198 .allow_parking();
199
200 if std::env::var("GPUI_RUN_UNTIL_PARKED_LOG").ok().as_deref() == Some("1") {
201 log::warn!("[gpui::executor] allow_parking: enabled");
202 }
203 }
204
205 #[cfg(any(test, feature = "test-support"))]
207 pub fn set_block_on_ticks(&self, range: std::ops::RangeInclusive<usize>) {
208 self.dispatcher
209 .as_test()
210 .unwrap()
211 .scheduler()
212 .set_timeout_ticks(range);
213 }
214
215 #[cfg(any(test, feature = "test-support"))]
217 pub fn forbid_parking(&self) {
218 self.dispatcher
219 .as_test()
220 .unwrap()
221 .scheduler()
222 .forbid_parking();
223 }
224
225 #[cfg(any(test, feature = "test-support"))]
227 pub fn rng(&self) -> scheduler::SharedRng {
228 self.dispatcher.as_test().unwrap().scheduler().rng()
229 }
230
231 pub fn num_cpus(&self) -> usize {
233 #[cfg(any(test, feature = "test-support"))]
234 if let Some(test) = self.dispatcher.as_test() {
235 return test.num_cpus_override().unwrap_or(4);
236 }
237 num_cpus::get()
238 }
239
240 #[cfg(any(test, feature = "test-support"))]
243 pub fn set_num_cpus(&self, count: usize) {
244 self.dispatcher
245 .as_test()
246 .expect("set_num_cpus can only be called on a test executor")
247 .set_num_cpus(count);
248 }
249
250 pub fn is_main_thread(&self) -> bool {
252 self.dispatcher.is_main_thread()
253 }
254
255 #[doc(hidden)]
256 pub fn dispatcher(&self) -> &Arc<dyn PlatformDispatcher> {
257 &self.dispatcher
258 }
259}
260
261impl ForegroundExecutor {
262 pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
264 #[cfg(any(test, feature = "test-support"))]
265 let (scheduler, session_id): (Arc<dyn Scheduler>, _) =
266 if let Some(test_dispatcher) = dispatcher.as_test() {
267 (
268 test_dispatcher.scheduler().clone(),
269 test_dispatcher.session_id(),
270 )
271 } else {
272 let platform_scheduler = Arc::new(PlatformScheduler::new(dispatcher.clone()));
273 let inner = platform_scheduler.foreground_executor();
274 return Self {
275 inner,
276 dispatcher,
277 #[cfg(feature = "profiler")]
278 foreground_runnables: Some(platform_scheduler.foreground_runnable_counter()),
279 not_send: PhantomData,
280 };
281 };
282
283 #[cfg(not(any(test, feature = "test-support")))]
284 let platform_scheduler = Arc::new(PlatformScheduler::new(dispatcher.clone()));
285 #[cfg(not(any(test, feature = "test-support")))]
286 let inner = platform_scheduler.foreground_executor();
287 #[cfg(all(not(any(test, feature = "test-support")), feature = "profiler"))]
288 let foreground_runnables = Some(platform_scheduler.foreground_runnable_counter());
289
290 #[cfg(any(test, feature = "test-support"))]
291 let inner = {
292 let scheduler_for_dispatch = Arc::downgrade(&scheduler);
293 scheduler::LocalExecutor::new(session_id, scheduler, move |runnable| {
294 if let Some(scheduler) = scheduler_for_dispatch.upgrade() {
295 scheduler.schedule_local(session_id, runnable);
296 }
297 })
298 };
299
300 #[cfg(all(any(test, feature = "test-support"), feature = "profiler"))]
301 let foreground_runnables = None;
304
305 Self {
306 inner,
307 dispatcher,
308 #[cfg(feature = "profiler")]
309 foreground_runnables,
310 not_send: PhantomData,
311 }
312 }
313
314 #[track_caller]
316 pub fn spawn<R>(&self, future: impl Future<Output = R> + 'static) -> Task<R>
317 where
318 R: 'static,
319 {
320 self.inner.spawn(future.boxed_local())
321 }
322
323 #[track_caller]
325 pub fn spawn_with_priority<R>(
326 &self,
327 _priority: Priority,
328 future: impl Future<Output = R> + 'static,
329 ) -> Task<R>
330 where
331 R: 'static,
332 {
333 self.inner.spawn(future)
335 }
336
337 #[track_caller]
347 pub fn spawn_when_idle<R>(
348 &self,
349 timeout: Option<Duration>,
350 future: impl Future<Output = R> + 'static,
351 ) -> Task<R>
352 where
353 R: 'static,
354 {
355 let dispatcher = self.dispatcher.clone();
356 #[cfg(feature = "profiler")]
357 let foreground_runnables = self.foreground_runnables.clone();
358 self.inner
359 .spawn_with_dispatch(future.boxed_local(), move |runnable| {
360 #[cfg(feature = "profiler")]
361 if let Some(foreground_runnables) = &foreground_runnables {
362 foreground_runnables.queued();
363 }
364 dispatcher.dispatch_on_main_thread_when_idle(runnable, timeout);
365 })
366 }
367
368 pub fn idle_time_remaining(&self) -> Option<Duration> {
374 self.dispatcher.idle_time_remaining()
375 }
376
377 #[cfg(all(not(target_family = "wasm"), any(test, feature = "test-support")))]
379 #[track_caller]
380 pub fn block_test<R>(&self, future: impl Future<Output = R>) -> R {
381 use std::cell::Cell;
382
383 let scheduler = self.inner.scheduler();
384
385 let output = Cell::new(None);
386 let future = async {
387 output.set(Some(future.await));
388 };
389 let mut future = std::pin::pin!(future);
390
391 scheduler.block(None, future.as_mut(), None);
395
396 output.take().expect("block_test future did not complete")
397 }
398
399 #[cfg(not(target_family = "wasm"))]
402 pub fn block_on<R>(&self, future: impl Future<Output = R>) -> R {
403 self.inner.block_on(future)
404 }
405
406 #[cfg(not(target_family = "wasm"))]
408 pub fn block_with_timeout<R, Fut: Future<Output = R>>(
409 &self,
410 duration: Duration,
411 future: Fut,
412 ) -> Result<R, impl Future<Output = R> + use<R, Fut>> {
413 self.inner.block_with_timeout(duration, future)
414 }
415
416 #[doc(hidden)]
417 pub fn dispatcher(&self) -> &Arc<dyn PlatformDispatcher> {
418 &self.dispatcher
419 }
420
421 #[doc(hidden)]
422 pub fn scheduler_executor(&self) -> SchedulerLocalExecutor {
423 self.inner.clone()
424 }
425}
426
427#[cfg(not(target_family = "wasm"))]
429pub struct Scope<'a> {
430 executor: BackgroundExecutor,
431 priority: Priority,
432 futures: Vec<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
433 tx: Option<mpsc::Sender<()>>,
434 rx: mpsc::Receiver<()>,
435 lifetime: PhantomData<&'a ()>,
436}
437
438#[cfg(not(target_family = "wasm"))]
439impl<'a> Scope<'a> {
440 fn new(executor: BackgroundExecutor, priority: Priority) -> Self {
441 let (tx, rx) = mpsc::channel(1);
442 Self {
443 executor,
444 priority,
445 tx: Some(tx),
446 rx,
447 futures: Default::default(),
448 lifetime: PhantomData,
449 }
450 }
451
452 pub fn num_cpus(&self) -> usize {
454 self.executor.num_cpus()
455 }
456
457 #[track_caller]
459 pub fn spawn<F>(&mut self, f: F)
460 where
461 F: Future<Output = ()> + Send + 'a,
462 {
463 let tx = self.tx.clone().unwrap();
464
465 let f = unsafe {
468 mem::transmute::<
469 Pin<Box<dyn Future<Output = ()> + Send + 'a>>,
470 Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
471 >(Box::pin(async move {
472 f.await;
473 drop(tx);
474 }))
475 };
476 self.futures.push(f);
477 }
478}
479
480#[cfg(not(target_family = "wasm"))]
481impl Drop for Scope<'_> {
482 fn drop(&mut self) {
483 self.tx.take().unwrap();
484
485 let future = async {
488 self.rx.next().await;
489 };
490 let mut future = std::pin::pin!(future);
491 self.executor
492 .inner
493 .scheduler()
494 .block(None, future.as_mut(), None);
495 }
496}