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 pub fn is_test(&self) -> bool {
261 self.inner.is_test()
262 }
263
264 #[doc(hidden)]
265 pub fn dispatcher(&self) -> &Arc<dyn PlatformDispatcher> {
266 &self.dispatcher
267 }
268}
269
270impl ForegroundExecutor {
271 pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
273 #[cfg(any(test, feature = "test-support"))]
274 let (scheduler, session_id): (Arc<dyn Scheduler>, _) =
275 if let Some(test_dispatcher) = dispatcher.as_test() {
276 (
277 test_dispatcher.scheduler().clone(),
278 test_dispatcher.session_id(),
279 )
280 } else {
281 let platform_scheduler = Arc::new(PlatformScheduler::new(dispatcher.clone()));
282 let inner = platform_scheduler.foreground_executor();
283 return Self {
284 inner,
285 dispatcher,
286 #[cfg(feature = "profiler")]
287 foreground_runnables: Some(platform_scheduler.foreground_runnable_counter()),
288 not_send: PhantomData,
289 };
290 };
291
292 #[cfg(not(any(test, feature = "test-support")))]
293 let platform_scheduler = Arc::new(PlatformScheduler::new(dispatcher.clone()));
294 #[cfg(not(any(test, feature = "test-support")))]
295 let inner = platform_scheduler.foreground_executor();
296 #[cfg(all(not(any(test, feature = "test-support")), feature = "profiler"))]
297 let foreground_runnables = Some(platform_scheduler.foreground_runnable_counter());
298
299 #[cfg(any(test, feature = "test-support"))]
300 let inner = {
301 let scheduler_for_dispatch = Arc::downgrade(&scheduler);
302 scheduler::LocalExecutor::new(session_id, scheduler, move |runnable| {
303 if let Some(scheduler) = scheduler_for_dispatch.upgrade() {
304 scheduler.schedule_local(session_id, runnable);
305 }
306 })
307 };
308
309 #[cfg(all(any(test, feature = "test-support"), feature = "profiler"))]
310 let foreground_runnables = None;
313
314 Self {
315 inner,
316 dispatcher,
317 #[cfg(feature = "profiler")]
318 foreground_runnables,
319 not_send: PhantomData,
320 }
321 }
322
323 #[track_caller]
325 pub fn spawn<R>(&self, future: impl Future<Output = R> + 'static) -> Task<R>
326 where
327 R: 'static,
328 {
329 self.inner.spawn(future.boxed_local())
330 }
331
332 #[track_caller]
334 pub fn spawn_with_priority<R>(
335 &self,
336 _priority: Priority,
337 future: impl Future<Output = R> + 'static,
338 ) -> Task<R>
339 where
340 R: 'static,
341 {
342 self.inner.spawn(future)
344 }
345
346 #[track_caller]
356 pub fn spawn_when_idle<R>(
357 &self,
358 timeout: Option<Duration>,
359 future: impl Future<Output = R> + 'static,
360 ) -> Task<R>
361 where
362 R: 'static,
363 {
364 let dispatcher = self.dispatcher.clone();
365 #[cfg(feature = "profiler")]
366 let foreground_runnables = self.foreground_runnables.clone();
367 self.inner
368 .spawn_with_dispatch(future.boxed_local(), move |runnable| {
369 #[cfg(feature = "profiler")]
370 if let Some(foreground_runnables) = &foreground_runnables {
371 foreground_runnables.queued();
372 }
373 dispatcher.dispatch_on_main_thread_when_idle(runnable, timeout);
374 })
375 }
376
377 pub fn idle_time_remaining(&self) -> Option<Duration> {
383 self.dispatcher.idle_time_remaining()
384 }
385
386 #[cfg(all(not(target_family = "wasm"), any(test, feature = "test-support")))]
388 #[track_caller]
389 pub fn block_test<R>(&self, future: impl Future<Output = R>) -> R {
390 use std::cell::Cell;
391
392 let scheduler = self.inner.scheduler();
393
394 let output = Cell::new(None);
395 let future = async {
396 output.set(Some(future.await));
397 };
398 let mut future = std::pin::pin!(future);
399
400 scheduler.block(None, future.as_mut(), None);
404
405 output.take().expect("block_test future did not complete")
406 }
407
408 #[cfg(not(target_family = "wasm"))]
411 pub fn block_on<R>(&self, future: impl Future<Output = R>) -> R {
412 self.inner.block_on(future)
413 }
414
415 #[cfg(not(target_family = "wasm"))]
417 pub fn block_with_timeout<R, Fut: Future<Output = R>>(
418 &self,
419 duration: Duration,
420 future: Fut,
421 ) -> Result<R, impl Future<Output = R> + use<R, Fut>> {
422 self.inner.block_with_timeout(duration, future)
423 }
424
425 #[doc(hidden)]
426 pub fn dispatcher(&self) -> &Arc<dyn PlatformDispatcher> {
427 &self.dispatcher
428 }
429
430 #[doc(hidden)]
431 pub fn scheduler_executor(&self) -> SchedulerLocalExecutor {
432 self.inner.clone()
433 }
434}
435
436#[cfg(not(target_family = "wasm"))]
438pub struct Scope<'a> {
439 executor: BackgroundExecutor,
440 priority: Priority,
441 futures: Vec<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
442 tx: Option<mpsc::Sender<()>>,
443 rx: mpsc::Receiver<()>,
444 lifetime: PhantomData<&'a ()>,
445}
446
447#[cfg(not(target_family = "wasm"))]
448impl<'a> Scope<'a> {
449 fn new(executor: BackgroundExecutor, priority: Priority) -> Self {
450 let (tx, rx) = mpsc::channel(1);
451 Self {
452 executor,
453 priority,
454 tx: Some(tx),
455 rx,
456 futures: Default::default(),
457 lifetime: PhantomData,
458 }
459 }
460
461 pub fn num_cpus(&self) -> usize {
463 self.executor.num_cpus()
464 }
465
466 #[track_caller]
468 pub fn spawn<F>(&mut self, f: F)
469 where
470 F: Future<Output = ()> + Send + 'a,
471 {
472 let tx = self.tx.clone().unwrap();
473
474 let f = unsafe {
477 mem::transmute::<
478 Pin<Box<dyn Future<Output = ()> + Send + 'a>>,
479 Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
480 >(Box::pin(async move {
481 f.await;
482 drop(tx);
483 }))
484 };
485 self.futures.push(f);
486 }
487}
488
489#[cfg(not(target_family = "wasm"))]
490impl Drop for Scope<'_> {
491 fn drop(&mut self) {
492 self.tx.take().unwrap();
493
494 let future = async {
497 self.rx.next().await;
498 };
499 let mut future = std::pin::pin!(future);
500 self.executor
501 .inner
502 .scheduler()
503 .block(None, future.as_mut(), None);
504 }
505}