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 #[track_caller]
78 pub fn spawn_dedicated<F, Fut>(&self, f: F) -> Task<Fut::Output>
79 where
80 F: FnOnce(SchedulerLocalExecutor) -> Fut + Send + 'static,
81 Fut: Future + 'static,
82 Fut::Output: Send + Sync + 'static,
83 {
84 self.inner.spawn_dedicated(f)
85 }
86
87 pub fn prevent_app_nap(&self, reason: &str) -> ActivityGuard {
91 self.dispatcher.prevent_app_nap(reason)
92 }
93
94 #[track_caller]
96 pub fn spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
97 where
98 R: Send + 'static,
99 {
100 self.spawn_with_priority(Priority::default(), future.boxed())
101 }
102
103 #[track_caller]
108 pub fn spawn_with_priority<R>(
109 &self,
110 priority: Priority,
111 future: impl Future<Output = R> + Send + 'static,
112 ) -> Task<R>
113 where
114 R: Send + 'static,
115 {
116 if priority == Priority::RealtimeAudio {
117 self.inner.spawn_realtime(future)
118 } else {
119 self.inner.spawn_with_priority(priority, future)
120 }
121 }
122
123 #[cfg(not(target_family = "wasm"))]
128 pub async fn scoped<'scope, F>(&self, scheduler: F)
129 where
130 F: FnOnce(&mut Scope<'scope>),
131 {
132 let mut scope = Scope::new(self.clone(), Priority::default());
133 (scheduler)(&mut scope);
134 let spawned = mem::take(&mut scope.futures)
135 .into_iter()
136 .map(|f| self.spawn_with_priority(scope.priority, f))
137 .collect::<Vec<_>>();
138 for task in spawned {
139 task.await;
140 }
141 }
142
143 #[cfg(not(target_family = "wasm"))]
149 pub async fn scoped_priority<'scope, F>(&self, priority: Priority, scheduler: F)
150 where
151 F: FnOnce(&mut Scope<'scope>),
152 {
153 let mut scope = Scope::new(self.clone(), priority);
154 (scheduler)(&mut scope);
155 let spawned = mem::take(&mut scope.futures)
156 .into_iter()
157 .map(|f| self.spawn_with_priority(scope.priority, f))
158 .collect::<Vec<_>>();
159 for task in spawned {
160 task.await;
161 }
162 }
163
164 pub fn now(&self) -> Instant {
169 self.inner.scheduler().clock().now()
170 }
171
172 #[track_caller]
176 pub fn timer(&self, duration: Duration) -> Task<()> {
177 if duration.is_zero() {
178 return Task::ready(());
179 }
180 self.spawn(self.inner.scheduler().timer(duration))
181 }
182
183 #[cfg(any(test, feature = "test-support"))]
185 pub fn simulate_random_delay(&self) -> impl Future<Output = ()> + use<> {
186 self.dispatcher.as_test().unwrap().simulate_random_delay()
187 }
188
189 #[cfg(any(test, feature = "test-support"))]
191 pub fn advance_clock(&self, duration: Duration) {
192 self.dispatcher.as_test().unwrap().advance_clock(duration)
193 }
194
195 #[cfg(any(test, feature = "test-support"))]
197 pub fn tick(&self) -> bool {
198 self.dispatcher.as_test().unwrap().scheduler().tick()
199 }
200
201 #[cfg(any(test, feature = "test-support"))]
208 pub fn run_until_parked(&self) {
209 let scheduler = self.dispatcher.as_test().unwrap().scheduler();
210 scheduler.run();
211 }
212
213 #[cfg(any(test, feature = "test-support"))]
215 pub fn allow_parking(&self) {
216 self.dispatcher
217 .as_test()
218 .unwrap()
219 .scheduler()
220 .allow_parking();
221
222 if std::env::var("GPUI_RUN_UNTIL_PARKED_LOG").ok().as_deref() == Some("1") {
223 log::warn!("[gpui::executor] allow_parking: enabled");
224 }
225 }
226
227 #[cfg(any(test, feature = "test-support"))]
229 pub fn set_block_on_ticks(&self, range: std::ops::RangeInclusive<usize>) {
230 self.dispatcher
231 .as_test()
232 .unwrap()
233 .scheduler()
234 .set_timeout_ticks(range);
235 }
236
237 #[cfg(any(test, feature = "test-support"))]
239 pub fn forbid_parking(&self) {
240 self.dispatcher
241 .as_test()
242 .unwrap()
243 .scheduler()
244 .forbid_parking();
245 }
246
247 #[cfg(any(test, feature = "test-support"))]
249 pub fn rng(&self) -> scheduler::SharedRng {
250 self.dispatcher.as_test().unwrap().scheduler().rng()
251 }
252
253 pub fn num_cpus(&self) -> usize {
255 #[cfg(any(test, feature = "test-support"))]
256 if let Some(test) = self.dispatcher.as_test() {
257 return test.num_cpus_override().unwrap_or(4);
258 }
259 num_cpus::get()
260 }
261
262 #[cfg(any(test, feature = "test-support"))]
265 pub fn set_num_cpus(&self, count: usize) {
266 self.dispatcher
267 .as_test()
268 .expect("set_num_cpus can only be called on a test executor")
269 .set_num_cpus(count);
270 }
271
272 pub fn is_main_thread(&self) -> bool {
274 self.dispatcher.is_main_thread()
275 }
276
277 #[doc(hidden)]
278 pub fn dispatcher(&self) -> &Arc<dyn PlatformDispatcher> {
279 &self.dispatcher
280 }
281}
282
283impl ForegroundExecutor {
284 pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
286 #[cfg(any(test, feature = "test-support"))]
287 let (scheduler, session_id): (Arc<dyn Scheduler>, _) =
288 if let Some(test_dispatcher) = dispatcher.as_test() {
289 (
290 test_dispatcher.scheduler().clone(),
291 test_dispatcher.session_id(),
292 )
293 } else {
294 let platform_scheduler = Arc::new(PlatformScheduler::new(dispatcher.clone()));
295 let inner = platform_scheduler.foreground_executor();
296 return Self {
297 inner,
298 dispatcher,
299 #[cfg(feature = "profiler")]
300 foreground_runnables: Some(platform_scheduler.foreground_runnable_counter()),
301 not_send: PhantomData,
302 };
303 };
304
305 #[cfg(not(any(test, feature = "test-support")))]
306 let platform_scheduler = Arc::new(PlatformScheduler::new(dispatcher.clone()));
307 #[cfg(not(any(test, feature = "test-support")))]
308 let inner = platform_scheduler.foreground_executor();
309 #[cfg(all(not(any(test, feature = "test-support")), feature = "profiler"))]
310 let foreground_runnables = Some(platform_scheduler.foreground_runnable_counter());
311
312 #[cfg(any(test, feature = "test-support"))]
313 let inner = {
314 let scheduler_for_dispatch = Arc::downgrade(&scheduler);
315 scheduler::LocalExecutor::new(session_id, scheduler, move |runnable| {
316 if let Some(scheduler) = scheduler_for_dispatch.upgrade() {
317 scheduler.schedule_local(session_id, runnable);
318 }
319 })
320 };
321
322 #[cfg(all(any(test, feature = "test-support"), feature = "profiler"))]
323 let foreground_runnables = None;
326
327 Self {
328 inner,
329 dispatcher,
330 #[cfg(feature = "profiler")]
331 foreground_runnables,
332 not_send: PhantomData,
333 }
334 }
335
336 #[track_caller]
338 pub fn spawn<R>(&self, future: impl Future<Output = R> + 'static) -> Task<R>
339 where
340 R: 'static,
341 {
342 self.inner.spawn(future.boxed_local())
343 }
344
345 #[track_caller]
347 pub fn spawn_with_priority<R>(
348 &self,
349 _priority: Priority,
350 future: impl Future<Output = R> + 'static,
351 ) -> Task<R>
352 where
353 R: 'static,
354 {
355 self.inner.spawn(future)
357 }
358
359 #[track_caller]
369 pub fn spawn_when_idle<R>(
370 &self,
371 timeout: Option<Duration>,
372 future: impl Future<Output = R> + 'static,
373 ) -> Task<R>
374 where
375 R: 'static,
376 {
377 let dispatcher = self.dispatcher.clone();
378 #[cfg(feature = "profiler")]
379 let foreground_runnables = self.foreground_runnables.clone();
380 self.inner
381 .spawn_with_dispatch(future.boxed_local(), move |runnable| {
382 #[cfg(feature = "profiler")]
383 if let Some(foreground_runnables) = &foreground_runnables {
384 foreground_runnables.queued();
385 }
386 dispatcher.dispatch_on_main_thread_when_idle(runnable, timeout);
387 })
388 }
389
390 pub fn idle_time_remaining(&self) -> Option<Duration> {
396 self.dispatcher.idle_time_remaining()
397 }
398
399 #[cfg(all(not(target_family = "wasm"), any(test, feature = "test-support")))]
401 #[track_caller]
402 pub fn block_test<R>(&self, future: impl Future<Output = R>) -> R {
403 use std::cell::Cell;
404
405 let scheduler = self.inner.scheduler();
406
407 let output = Cell::new(None);
408 let future = async {
409 output.set(Some(future.await));
410 };
411 let mut future = std::pin::pin!(future);
412
413 scheduler.block(None, future.as_mut(), None);
417
418 output.take().expect("block_test future did not complete")
419 }
420
421 #[cfg(not(target_family = "wasm"))]
424 pub fn block_on<R>(&self, future: impl Future<Output = R>) -> R {
425 self.inner.block_on(future)
426 }
427
428 #[cfg(not(target_family = "wasm"))]
430 pub fn block_with_timeout<R, Fut: Future<Output = R>>(
431 &self,
432 duration: Duration,
433 future: Fut,
434 ) -> Result<R, impl Future<Output = R> + use<R, Fut>> {
435 self.inner.block_with_timeout(duration, future)
436 }
437
438 #[doc(hidden)]
439 pub fn dispatcher(&self) -> &Arc<dyn PlatformDispatcher> {
440 &self.dispatcher
441 }
442
443 #[doc(hidden)]
444 pub fn scheduler_executor(&self) -> SchedulerLocalExecutor {
445 self.inner.clone()
446 }
447}
448
449#[cfg(not(target_family = "wasm"))]
451pub struct Scope<'a> {
452 executor: BackgroundExecutor,
453 priority: Priority,
454 futures: Vec<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
455 tx: Option<mpsc::Sender<()>>,
456 rx: mpsc::Receiver<()>,
457 lifetime: PhantomData<&'a ()>,
458}
459
460#[cfg(not(target_family = "wasm"))]
461impl<'a> Scope<'a> {
462 fn new(executor: BackgroundExecutor, priority: Priority) -> Self {
463 let (tx, rx) = mpsc::channel(1);
464 Self {
465 executor,
466 priority,
467 tx: Some(tx),
468 rx,
469 futures: Default::default(),
470 lifetime: PhantomData,
471 }
472 }
473
474 pub fn num_cpus(&self) -> usize {
476 self.executor.num_cpus()
477 }
478
479 #[track_caller]
481 pub fn spawn<F>(&mut self, f: F)
482 where
483 F: Future<Output = ()> + Send + 'a,
484 {
485 let tx = self.tx.clone().unwrap();
486
487 let f = unsafe {
490 mem::transmute::<
491 Pin<Box<dyn Future<Output = ()> + Send + 'a>>,
492 Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
493 >(Box::pin(async move {
494 f.await;
495 drop(tx);
496 }))
497 };
498 self.futures.push(f);
499 }
500}
501
502#[cfg(not(target_family = "wasm"))]
503impl Drop for Scope<'_> {
504 fn drop(&mut self) {
505 self.tx.take().unwrap();
506
507 let future = async {
510 self.rx.next().await;
511 };
512 let mut future = std::pin::pin!(future);
513 self.executor
514 .inner
515 .scheduler()
516 .block(None, future.as_mut(), None);
517 }
518}