1use std::{
2 collections::BinaryHeap,
3 sync::Arc,
4 thread,
5 time::{Duration, Instant},
6};
7
8use parking_lot::{Condvar, Mutex};
9
10use crate::{
11 PlatformDispatcher, Priority, RunnableVariant, profiler,
12 queue::{PriorityQueueReceiver, PriorityQueueSender},
13};
14
15const MIN_THREADS: usize = 2;
16
17pub struct ThreadedDispatcher {
60 background_sender: PriorityQueueSender<RunnableVariant>,
61 main_sender: PriorityQueueSender<RunnableVariant>,
62 main_receiver: Mutex<PriorityQueueReceiver<RunnableVariant>>,
63 timers: Arc<TimerQueue>,
64 idle: Arc<IdleTracker>,
65 main_thread_id: thread::ThreadId,
66}
67
68#[derive(Default)]
71struct IdleTracker {
72 inflight: Mutex<usize>,
73 condvar: Condvar,
74}
75
76impl IdleTracker {
77 fn increment(&self) {
78 *self.inflight.lock() += 1;
79 }
80
81 fn decrement(&self) {
82 let mut inflight = self.inflight.lock();
83 *inflight -= 1;
84 if *inflight == 0 {
85 self.condvar.notify_all();
86 }
87 }
88
89 fn decrement_on_drop(&self) -> impl Drop + '_ {
92 gpui_util::defer(|| self.decrement())
93 }
94
95 fn notify_under_lock(&self) {
99 let _inflight = self.inflight.lock();
100 self.condvar.notify_all();
101 }
102}
103
104struct TimerQueue {
105 state: Mutex<TimerQueueState>,
106 condvar: Condvar,
107}
108
109struct TimerQueueState {
110 heap: BinaryHeap<TimerEntry>,
111 next_seq: u64,
112}
113
114struct TimerEntry {
115 due: Instant,
116 seq: u64,
117 runnable: RunnableVariant,
118}
119
120impl PartialEq for TimerEntry {
121 fn eq(&self, other: &Self) -> bool {
122 self.due == other.due && self.seq == other.seq
123 }
124}
125
126impl Eq for TimerEntry {}
127
128impl PartialOrd for TimerEntry {
129 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
130 Some(self.cmp(other))
131 }
132}
133
134impl Ord for TimerEntry {
135 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
136 other
139 .due
140 .cmp(&self.due)
141 .then_with(|| other.seq.cmp(&self.seq))
142 }
143}
144
145impl Default for ThreadedDispatcher {
146 fn default() -> Self {
147 Self::new()
148 }
149}
150
151impl ThreadedDispatcher {
152 pub fn new() -> Self {
158 let (background_sender, background_receiver) = PriorityQueueReceiver::new();
159 let (main_sender, main_receiver) = PriorityQueueReceiver::new();
160 let idle = Arc::new(IdleTracker::default());
161
162 let thread_count =
163 thread::available_parallelism().map_or(MIN_THREADS, |i| i.get().max(MIN_THREADS));
164 for i in 0..thread_count {
165 let mut receiver: PriorityQueueReceiver<RunnableVariant> = background_receiver.clone();
166 let idle = idle.clone();
167 thread::Builder::new()
168 .name(format!("ThreadedDispatcherWorker-{i}"))
169 .spawn(move || {
170 while let Ok(runnable) = receiver.pop() {
171 let _decrement = idle.decrement_on_drop();
172 let location = runnable.metadata().location;
173 let spawned = runnable.metadata().spawned;
174 profiler::update_running_task(spawned, location);
175 runnable.run();
176 profiler::save_task_timing();
177 }
178 })
179 .expect("failed to spawn threaded dispatcher worker");
180 }
181 drop(background_receiver);
182
183 let timers = Arc::new(TimerQueue {
184 state: Mutex::new(TimerQueueState {
185 heap: BinaryHeap::new(),
186 next_seq: 0,
187 }),
188 condvar: Condvar::new(),
189 });
190 {
191 let timers = timers.clone();
192 let idle = idle.clone();
193 thread::Builder::new()
194 .name("ThreadedDispatcherTimer".to_owned())
195 .spawn(move || {
196 let mut state = timers.state.lock();
197 loop {
198 let Some(entry) = state.heap.peek() else {
199 timers.condvar.wait(&mut state);
200 continue;
201 };
202 let due = entry.due;
203 if due > Instant::now() {
204 timers.condvar.wait_until(&mut state, due);
205 continue;
206 }
207 let Some(entry) = state.heap.pop() else {
208 continue;
209 };
210 idle.increment();
216 drop(state);
217
218 {
219 let _decrement = idle.decrement_on_drop();
220 let location = entry.runnable.metadata().location;
221 let spawned = entry.runnable.metadata().spawned;
222 profiler::update_running_task(spawned, location);
223 entry.runnable.run();
224 profiler::save_task_timing();
225 }
226
227 state = timers.state.lock();
228 }
229 })
230 .expect("failed to spawn threaded dispatcher timer");
231 }
232
233 Self {
234 background_sender,
235 main_sender,
236 main_receiver: Mutex::new(main_receiver),
237 timers,
238 idle,
239 main_thread_id: thread::current().id(),
240 }
241 }
242
243 pub fn run_until_idle(&self) {
253 self.run_until_idle_with(|| self.drain_main_queue());
254 }
255
256 pub(crate) fn run_until_idle_with(&self, mut run_turn: impl FnMut() -> bool) {
257 assert!(
258 self.is_main_thread(),
259 "run_until_idle must be called on the threaded dispatcher's main thread"
260 );
261 loop {
262 if run_turn() {
263 continue;
264 }
265
266 if self.has_due_timer() {
269 let mut inflight = self.idle.inflight.lock();
272 self.idle
273 .condvar
274 .wait_for(&mut inflight, Duration::from_millis(1));
275 continue;
276 }
277
278 let mut inflight = self.idle.inflight.lock();
279 if self.main_queue_has_work() {
282 continue;
283 }
284 if *inflight == 0 {
285 return;
289 }
290 self.idle.condvar.wait(&mut inflight);
293 }
294 }
295
296 #[cfg(test)]
308 pub(crate) fn run_until<R>(&self, ready: impl FnMut() -> Option<R>) -> R {
309 self.run_until_with_frames(ready, || false)
310 }
311
312 fn run_one_main_task(&self) -> bool {
317 assert!(
318 self.is_main_thread(),
319 "main tasks must run on the main thread"
320 );
321 let runnable = self.main_receiver.lock().try_pop();
322 match runnable {
323 Ok(Some(runnable)) => {
324 let location = runnable.metadata().location;
325 let spawned = runnable.metadata().spawned;
326 profiler::update_running_task(spawned, location);
327 runnable.run();
328 profiler::save_task_timing();
329 true
330 }
331 Ok(None) | Err(_) => false,
332 }
333 }
334
335 pub fn run_ready_main_tasks(&self) -> bool {
340 self.run_ready_main_tasks_with(|| true, || {})
341 }
342
343 pub(crate) fn run_ready_main_tasks_with(
346 &self,
347 mut should_continue: impl FnMut() -> bool,
348 mut after_poll: impl FnMut(),
349 ) -> bool {
350 assert!(
351 self.is_main_thread(),
352 "run_ready_main_tasks must be called on the threaded dispatcher's main thread"
353 );
354 let pending = self.main_receiver.lock().len();
355 let mut ran_any = false;
356 for _ in 0..pending {
357 if !should_continue() || !self.run_one_main_task() {
358 break;
359 }
360 ran_any = true;
361 after_poll();
362 }
363 ran_any
364 }
365
366 #[cfg(any(test, feature = "bench-support"))]
367 pub(crate) fn run_until_with_frames<R>(
368 &self,
369 mut ready: impl FnMut() -> Option<R>,
370 mut dispatch_frames: impl FnMut() -> bool,
371 ) -> R {
372 assert!(
373 self.is_main_thread(),
374 "main tasks must run on the main thread"
375 );
376 loop {
377 if let Some(result) = ready() {
378 return result;
379 }
380 let ran_task = self.run_one_main_task();
381 if let Some(result) = ready() {
384 return result;
385 }
386 let dispatched_frames = dispatch_frames();
387 if ran_task || dispatched_frames {
388 continue;
389 }
390
391 let mut inflight = self.idle.inflight.lock();
392 if self.main_queue_has_work() {
393 continue;
394 }
395 self.idle.condvar.wait(&mut inflight);
396 }
397 }
398
399 pub fn cancel_pending_timers(&self) -> usize {
406 let timers = {
407 let mut state = self.timers.state.lock();
408 let timers: Vec<_> = state.heap.drain().collect();
409 self.timers.condvar.notify_all();
410 timers
411 };
412 let canceled = timers.len();
413 drop(timers);
414 canceled
415 }
416
417 pub fn debug_state(&self) -> String {
423 let inflight = *self.idle.inflight.lock();
424 let timers = self.timers.state.lock().heap.len();
425 let main_queue_has_work = self.main_queue_has_work();
426 format!(
427 "ThreadedDispatcher {{ inflight: {inflight}, pending_timers: {timers}, \
428 main_queue_has_work: {main_queue_has_work} }}"
429 )
430 }
431
432 #[cfg(any(test, feature = "bench-support"))]
436 pub(crate) fn is_idle(&self) -> bool {
437 !self.main_queue_has_work() && !self.has_due_timer() && *self.idle.inflight.lock() == 0
438 }
439
440 fn has_due_timer(&self) -> bool {
441 let state = self.timers.state.lock();
442 state
443 .heap
444 .peek()
445 .is_some_and(|entry| entry.due <= Instant::now())
446 }
447
448 fn main_queue_has_work(&self) -> bool {
449 !self.main_receiver.lock().is_empty()
450 }
451
452 fn drain_main_queue(&self) -> bool {
453 let mut ran_any = false;
454 loop {
455 let runnable = self.main_receiver.lock().try_pop();
458 match runnable {
459 Ok(Some(runnable)) => {
460 let location = runnable.metadata().location;
461 let spawned = runnable.metadata().spawned;
462 profiler::update_running_task(spawned, location);
463 runnable.run();
464 profiler::save_task_timing();
465 ran_any = true;
466 }
467 Ok(None) | Err(_) => return ran_any,
468 }
469 }
470 }
471}
472
473impl PlatformDispatcher for ThreadedDispatcher {
474 fn is_main_thread(&self) -> bool {
475 thread::current().id() == self.main_thread_id
476 }
477
478 fn dispatch(&self, runnable: RunnableVariant, priority: Priority) {
479 self.idle.increment();
480 self.background_sender
481 .send(priority, runnable)
482 .unwrap_or_else(|_| panic!("threaded dispatcher workers are no longer running"));
483 }
484
485 fn dispatch_on_main_thread(&self, runnable: RunnableVariant, priority: Priority) {
486 if let Err(error) = self.main_sender.send(priority, runnable) {
487 std::mem::forget(error);
492 return;
493 }
494 self.idle.notify_under_lock();
496 }
497
498 fn dispatch_after(&self, duration: Duration, runnable: RunnableVariant) {
499 let mut state = self.timers.state.lock();
500 let seq = state.next_seq;
501 state.next_seq += 1;
502 state.heap.push(TimerEntry {
503 due: Instant::now() + duration,
504 seq,
505 runnable,
506 });
507 self.timers.condvar.notify_one();
508 }
509
510 fn spawn_realtime(&self, f: Box<dyn FnOnce() + Send>) {
511 thread::Builder::new()
514 .name("ThreadedDispatcherRealtime".to_owned())
515 .spawn(f)
516 .expect("failed to spawn threaded dispatcher realtime thread");
517 }
518
519 fn as_threaded(&self) -> Option<&ThreadedDispatcher> {
520 Some(self)
521 }
522}
523
524#[cfg(test)]
525mod tests {
526 use std::future::Future;
527 use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
528
529 use super::*;
530 use crate::{BackgroundExecutor, ForegroundExecutor};
531
532 #[test]
533 fn is_idle_tracks_queued_work_but_ignores_undue_timers() {
534 let dispatcher = Arc::new(ThreadedDispatcher::new());
535 let foreground = ForegroundExecutor::new(dispatcher.clone());
536 assert!(dispatcher.is_idle());
537
538 foreground.spawn(async {}).detach();
539 assert!(!dispatcher.is_idle());
540 dispatcher.run_until_idle();
541 assert!(dispatcher.is_idle());
542
543 let background = BackgroundExecutor::new(dispatcher.clone());
544 let timer = background.timer(Duration::from_secs(60));
545 dispatcher.run_until_idle();
548 assert!(
549 dispatcher.is_idle(),
550 "a timer that is not due yet should not count as pending work"
551 );
552 drop(timer);
553 dispatcher.cancel_pending_timers();
554 dispatcher.run_until_idle();
555 assert!(dispatcher.is_idle());
556 }
557
558 #[test]
559 fn run_ready_main_tasks_does_not_wait_for_background_handoffs() {
560 let dispatcher = Arc::new(ThreadedDispatcher::new());
561 let background = BackgroundExecutor::new(dispatcher.clone());
562 let foreground = ForegroundExecutor::new(dispatcher.clone());
563
564 let (sender, receiver) = futures::channel::oneshot::channel();
565 background
566 .spawn(async move {
567 thread::sleep(Duration::from_millis(10));
568 sender.send(()).ok();
569 })
570 .detach();
571
572 let completed = Arc::new(AtomicBool::new(false));
573 foreground
574 .spawn({
575 let completed = completed.clone();
576 async move {
577 receiver.await.ok();
578 completed.store(true, Ordering::SeqCst);
579 }
580 })
581 .detach();
582
583 assert!(dispatcher.run_ready_main_tasks());
584 assert!(!completed.load(Ordering::SeqCst));
585
586 dispatcher.run_until_idle();
587 assert!(completed.load(Ordering::SeqCst));
588 }
589
590 #[test]
591 fn run_until_idle_completes_background_to_main_handoffs() {
592 let dispatcher = Arc::new(ThreadedDispatcher::new());
593 let background = BackgroundExecutor::new(dispatcher.clone());
594 let foreground = ForegroundExecutor::new(dispatcher.clone());
595
596 let (sender, receiver) = futures::channel::oneshot::channel();
597 background
598 .spawn(async move {
599 thread::sleep(Duration::from_millis(10));
600 sender.send(()).ok();
601 })
602 .detach();
603
604 let completed = Arc::new(AtomicBool::new(false));
605 foreground
606 .spawn({
607 let completed = completed.clone();
608 async move {
609 receiver.await.ok();
610 completed.store(true, Ordering::SeqCst);
611 }
612 })
613 .detach();
614
615 dispatcher.run_until_idle();
616 assert!(completed.load(Ordering::SeqCst));
617 }
618
619 #[test]
620 fn run_until_waits_for_untracked_external_wakes() {
621 let dispatcher = Arc::new(ThreadedDispatcher::new());
622 let foreground = ForegroundExecutor::new(dispatcher.clone());
623 let (sender, receiver) = futures::channel::oneshot::channel();
624 let sender_thread = thread::spawn(move || {
625 thread::sleep(Duration::from_millis(10));
626 sender
627 .send(())
628 .expect("foreground receiver should remain alive");
629 });
630
631 let completed = Arc::new(AtomicBool::new(false));
632 foreground
633 .spawn({
634 let completed = completed.clone();
635 async move {
636 receiver
637 .await
638 .expect("external sender should deliver its wake");
639 completed.store(true, Ordering::SeqCst);
640 }
641 })
642 .detach();
643
644 dispatcher.run_until(|| completed.load(Ordering::SeqCst).then_some(()));
645 sender_thread.join().expect("sender thread should finish");
646 assert!(completed.load(Ordering::SeqCst));
647 }
648
649 #[test]
650 fn run_until_returns_at_readiness_despite_requeuing_main_work() {
651 const REQUEUE_LIMIT: usize = 10_000;
652
653 let dispatcher = Arc::new(ThreadedDispatcher::new());
654 let foreground = ForegroundExecutor::new(dispatcher.clone());
655
656 let iterations = Arc::new(AtomicUsize::new(0));
661 foreground
662 .spawn({
663 let iterations = iterations.clone();
664 async move {
665 for _ in 0..REQUEUE_LIMIT {
666 iterations.fetch_add(1, Ordering::SeqCst);
667 yield_once().await;
668 }
669 }
670 })
671 .detach();
672
673 let completed = Arc::new(AtomicBool::new(false));
674 foreground
675 .spawn({
676 let completed = completed.clone();
677 async move {
678 completed.store(true, Ordering::SeqCst);
679 }
680 })
681 .detach();
682
683 dispatcher.run_until(|| completed.load(Ordering::SeqCst).then_some(()));
684 assert!(
685 iterations.load(Ordering::SeqCst) < REQUEUE_LIMIT,
686 "run_until should return at readiness instead of draining re-queued main work"
687 );
688 }
689
690 fn yield_once() -> impl Future<Output = ()> {
693 let mut yielded = false;
694 std::future::poll_fn(move |poll_context| {
695 if yielded {
696 std::task::Poll::Ready(())
697 } else {
698 yielded = true;
699 poll_context.waker().wake_by_ref();
700 std::task::Poll::Pending
701 }
702 })
703 }
704
705 #[test]
706 fn run_ready_main_tasks_advances_requeuing_work_one_batch_per_call() {
707 const REQUEUE_LIMIT: usize = 10_000;
708
709 let dispatcher = Arc::new(ThreadedDispatcher::new());
710 let foreground = ForegroundExecutor::new(dispatcher.clone());
711
712 let iterations = Arc::new(AtomicUsize::new(0));
713 foreground
714 .spawn({
715 let iterations = iterations.clone();
716 async move {
717 for _ in 0..REQUEUE_LIMIT {
718 iterations.fetch_add(1, Ordering::SeqCst);
719 yield_once().await;
720 }
721 }
722 })
723 .detach();
724
725 assert!(dispatcher.run_ready_main_tasks());
726 assert_eq!(iterations.load(Ordering::SeqCst), 1);
727 assert!(dispatcher.run_ready_main_tasks());
728 assert_eq!(iterations.load(Ordering::SeqCst), 2);
729 }
730
731 #[test]
732 fn timers_fire_in_real_time() {
733 let dispatcher = Arc::new(ThreadedDispatcher::new());
734 let background = BackgroundExecutor::new(dispatcher);
735
736 let fired = Arc::new(AtomicBool::new(false));
737 let timer = background.timer(Duration::from_millis(10));
738 background
739 .spawn({
740 let fired = fired.clone();
741 async move {
742 timer.await;
743 fired.store(true, Ordering::SeqCst);
744 }
745 })
746 .detach();
747
748 let deadline = Instant::now() + Duration::from_secs(10);
749 while !fired.load(Ordering::SeqCst) && Instant::now() < deadline {
750 thread::sleep(Duration::from_millis(1));
751 }
752 assert!(fired.load(Ordering::SeqCst));
753 }
754
755 #[test]
756 fn cancel_pending_timers_wakes_waiters_without_waiting_for_deadline() {
757 let dispatcher = Arc::new(ThreadedDispatcher::new());
758 let background = BackgroundExecutor::new(dispatcher.clone());
759
760 let fired = Arc::new(AtomicBool::new(false));
761 let timer = background.timer(Duration::from_secs(10));
762 background
763 .spawn({
764 let fired = fired.clone();
765 async move {
766 timer.await;
767 fired.store(true, Ordering::SeqCst);
768 }
769 })
770 .detach();
771
772 dispatcher.run_until_idle();
773 assert_eq!(dispatcher.cancel_pending_timers(), 1);
774 dispatcher.run_until_idle();
775
776 assert!(fired.load(Ordering::SeqCst));
777 assert_eq!(dispatcher.cancel_pending_timers(), 0);
778 }
779}