Checkpoint

Antonio Scandurra created

Change summary

crates/gpui3/build.rs                       |    2 
crates/gpui3/src/app.rs                     |   40 
crates/gpui3/src/executor.rs                | 1120 +---------------------
crates/gpui3/src/gpui3.rs                   |   10 
crates/gpui3/src/platform.rs                |    9 
crates/gpui3/src/platform/mac/dispatcher.rs |   41 
crates/gpui3/src/platform/mac/platform.rs   |   41 
crates/gpui3/src/platform/mac/window.rs     |  115 +-
crates/gpui3/src/platform/test.rs           |    4 
crates/gpui3/src/util.rs                    |   24 
crates/gpui3/src/window.rs                  |    7 
11 files changed, 235 insertions(+), 1,178 deletions(-)

Detailed changes

crates/gpui3/build.rs 🔗

@@ -19,6 +19,8 @@ fn generate_dispatch_bindings() {
     let bindings = bindgen::Builder::default()
         .header("src/platform/mac/dispatch.h")
         .allowlist_var("_dispatch_main_q")
+        .allowlist_var("DISPATCH_QUEUE_PRIORITY_DEFAULT")
+        .allowlist_function("dispatch_get_global_queue")
         .allowlist_function("dispatch_async_f")
         .parse_callbacks(Box::new(bindgen::CargoCallbacks))
         .layout_tests(false)

crates/gpui3/src/app.rs 🔗

@@ -8,9 +8,9 @@ pub use model_context::*;
 use refineable::Refineable;
 
 use crate::{
-    current_platform, image_cache::ImageCache, run_on_main, spawn_on_main, AssetSource, Context,
-    LayoutId, MainThread, MainThreadOnly, Platform, PlatformDispatcher, RootView, SvgRenderer,
-    TextStyle, TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
+    current_platform, image_cache::ImageCache, AssetSource, Context, Executor, LayoutId,
+    MainThread, MainThreadOnly, Platform, RootView, SvgRenderer, Task, TextStyle,
+    TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId,
 };
 use anyhow::{anyhow, Result};
 use collections::{HashMap, VecDeque};
@@ -50,15 +50,15 @@ impl App {
         asset_source: Arc<dyn AssetSource>,
         http_client: Arc<dyn HttpClient>,
     ) -> Self {
-        let dispatcher = platform.dispatcher();
+        let executor = platform.executor();
         let text_system = Arc::new(TextSystem::new(platform.text_system()));
         let entities = EntityMap::new();
         let unit_entity = entities.redeem(entities.reserve(), ());
         Self(Arc::new_cyclic(|this| {
             Mutex::new(MainThread::new(AppContext {
                 this: this.clone(),
-                platform: MainThreadOnly::new(platform, dispatcher.clone()),
-                dispatcher,
+                platform: MainThreadOnly::new(platform, executor.clone()),
+                executor,
                 text_system,
                 svg_renderer: SvgRenderer::new(asset_source),
                 image_cache: ImageCache::new(http_client),
@@ -95,7 +95,7 @@ pub struct AppContext {
     platform: MainThreadOnly<dyn Platform>,
     text_system: Arc<TextSystem>,
     pending_updates: usize,
-    pub(crate) dispatcher: Arc<dyn PlatformDispatcher>,
+    pub(crate) executor: Executor,
     pub(crate) svg_renderer: SvgRenderer,
     pub(crate) image_cache: ImageCache,
     pub(crate) text_style_stack: Vec<TextStyleRefinement>,
@@ -184,6 +184,10 @@ impl AppContext {
         AsyncContext(unsafe { mem::transmute(self.this.clone()) })
     }
 
+    pub fn executor(&self) -> &Executor {
+        &self.executor
+    }
+
     pub fn run_on_main<R>(
         &mut self,
         f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
@@ -192,20 +196,22 @@ impl AppContext {
         R: Send + 'static,
     {
         let (tx, rx) = oneshot::channel();
-        if self.dispatcher.is_main_thread() {
+        if self.executor.is_main_thread() {
             let _ = tx.send(f(unsafe {
                 mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(self)
             }));
         } else {
             let this = self.this.upgrade().unwrap();
-            let _ = run_on_main(self.dispatcher.clone(), move || {
-                let cx = &mut *this.lock();
-                cx.update(|cx| {
-                    let _ = tx.send(f(unsafe {
-                        mem::transmute::<&mut Self, &mut MainThread<Self>>(cx)
-                    }));
+            self.executor
+                .run_on_main(move || {
+                    let cx = &mut *this.lock();
+                    cx.update(|cx| {
+                        let _ = tx.send(f(unsafe {
+                            mem::transmute::<&mut Self, &mut MainThread<Self>>(cx)
+                        }));
+                    })
                 })
-            });
+                .detach();
         }
         async move { rx.await.unwrap() }
     }
@@ -213,13 +219,13 @@ impl AppContext {
     pub fn spawn_on_main<F, R>(
         &self,
         f: impl FnOnce(&mut MainThread<AppContext>) -> F + Send + 'static,
-    ) -> impl Future<Output = R>
+    ) -> Task<R>
     where
         F: Future<Output = R> + 'static,
         R: Send + 'static,
     {
         let this = self.this.upgrade().unwrap();
-        spawn_on_main(self.dispatcher.clone(), move || {
+        self.executor.spawn_on_main(move || {
             let cx = &mut *this.lock();
             cx.update(|cx| {
                 f(unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) })

crates/gpui3/src/executor.rs 🔗

@@ -1,1103 +1,113 @@
-use crate::util;
 use crate::PlatformDispatcher;
-use anyhow::{anyhow, Result};
-use async_task::Runnable;
-use futures::channel::{mpsc, oneshot};
-use smol::{channel, prelude::*, Executor};
+use smol::prelude::*;
 use std::{
-    any::Any,
-    fmt::{self},
-    marker::PhantomData,
-    mem,
     pin::Pin,
-    rc::Rc,
     sync::Arc,
     task::{Context, Poll},
-    thread,
-    time::Duration,
 };
 
-/// Enqueues the given closure to run on the application's event loop.
-/// Returns the result asynchronously.
-pub(crate) fn run_on_main<F, R>(
+#[derive(Clone)]
+pub struct Executor {
     dispatcher: Arc<dyn PlatformDispatcher>,
-    func: F,
-) -> impl Future<Output = R>
-where
-    F: FnOnce() -> R + Send + 'static,
-    R: Send + 'static,
-{
-    let (tx, rx) = oneshot::channel();
-    if dispatcher.is_main_thread() {
-        let _ = tx.send(func());
-    } else {
-        let _ = spawn_on_main(dispatcher, move || async move {
-            let _ = tx.send(func());
-        });
-    }
-    async move { rx.await.unwrap() }
-}
-
-/// Enqueues the given closure to be run on the application's event loop. The
-/// closure returns a future which will be run to completion on the main thread.
-pub(crate) fn spawn_on_main<F, R>(
-    dispatcher: Arc<dyn PlatformDispatcher>,
-    func: impl FnOnce() -> F + Send + 'static,
-) -> impl Future<Output = R>
-where
-    F: Future<Output = R> + 'static,
-    R: Send + 'static,
-{
-    let (tx, rx) = oneshot::channel();
-    let (runnable, task) = async_task::spawn(
-        {
-            let dispatcher = dispatcher.clone();
-            async move {
-                let future = func();
-                let _ = spawn_on_main_local(dispatcher, async move {
-                    let result = future.await;
-                    let _ = tx.send(result);
-                });
-            }
-        },
-        move |runnable| dispatcher.run_on_main_thread(runnable),
-    );
-    runnable.schedule();
-    task.detach();
-    async move { rx.await.unwrap() }
-}
-
-/// Enqueues the given closure to be run on the application's event loop. Must
-/// be called on the main thread.
-pub(crate) fn spawn_on_main_local<R>(
-    dispatcher: Arc<dyn PlatformDispatcher>,
-    future: impl Future<Output = R> + 'static,
-) -> impl Future<Output = R>
-where
-    R: 'static,
-{
-    assert!(dispatcher.is_main_thread(), "must be called on main thread");
-
-    let (tx, rx) = oneshot::channel();
-    let (runnable, task) = async_task::spawn_local(
-        async move {
-            let result = future.await;
-            let _ = tx.send(result);
-        },
-        move |runnable| dispatcher.run_on_main_thread(runnable),
-    );
-    runnable.schedule();
-    task.detach();
-    async move { rx.await.unwrap() }
 }
 
-pub enum ForegroundExecutor {
-    Platform {
-        dispatcher: Arc<dyn PlatformDispatcher>,
-        _not_send_or_sync: PhantomData<Rc<()>>,
-    },
-    #[cfg(any(test, feature = "test"))]
-    Deterministic {
-        cx_id: usize,
-        executor: Arc<Deterministic>,
-    },
-}
-
-pub enum BackgroundExecutor {
-    #[cfg(any(test, feature = "test"))]
-    Deterministic { executor: Arc<Deterministic> },
-    Production {
-        executor: Arc<smol::Executor<'static>>,
-        _stop: channel::Sender<()>,
-    },
-}
-
-type AnyLocalFuture = Pin<Box<dyn 'static + Future<Output = Box<dyn Any + 'static>>>>;
-type AnyFuture = Pin<Box<dyn 'static + Send + Future<Output = Box<dyn Any + Send + 'static>>>>;
-type AnyTask = async_task::Task<Box<dyn Any + Send + 'static>>;
-type AnyLocalTask = async_task::Task<Box<dyn Any + 'static>>;
-
-#[must_use]
 pub enum Task<T> {
     Ready(Option<T>),
-    Local {
-        any_task: AnyLocalTask,
-        result_type: PhantomData<T>,
-    },
-    Send {
-        any_task: AnyTask,
-        result_type: PhantomData<T>,
-    },
-}
-
-unsafe impl<T: Send> Send for Task<T> {}
-
-#[cfg(any(test, feature = "test"))]
-struct DeterministicState {
-    rng: rand::prelude::StdRng,
-    seed: u64,
-    scheduled_from_foreground: collections::HashMap<usize, Vec<ForegroundRunnable>>,
-    scheduled_from_background: Vec<BackgroundRunnable>,
-    forbid_parking: bool,
-    block_on_ticks: std::ops::RangeInclusive<usize>,
-    now: std::time::Instant,
-    next_timer_id: usize,
-    pending_timers: Vec<(usize, std::time::Instant, postage::barrier::Sender)>,
-    waiting_backtrace: Option<backtrace::Backtrace>,
-    next_runnable_id: usize,
-    poll_history: Vec<ExecutorEvent>,
-    previous_poll_history: Option<Vec<ExecutorEvent>>,
-    enable_runnable_backtraces: bool,
-    runnable_backtraces: collections::HashMap<usize, backtrace::Backtrace>,
-}
-
-#[derive(Copy, Clone, Debug, PartialEq, Eq)]
-pub enum ExecutorEvent {
-    PollRunnable { id: usize },
-    EnqueuRunnable { id: usize },
-}
-
-#[cfg(any(test, feature = "test"))]
-struct ForegroundRunnable {
-    id: usize,
-    runnable: Runnable,
-    main: bool,
-}
-
-#[cfg(any(test, feature = "test"))]
-struct BackgroundRunnable {
-    id: usize,
-    runnable: Runnable,
-}
-
-#[cfg(any(test, feature = "test"))]
-pub struct Deterministic {
-    state: Arc<parking_lot::Mutex<DeterministicState>>,
-    parker: parking_lot::Mutex<parking::Parker>,
-}
-
-#[must_use]
-pub enum Timer {
-    Production(smol::Timer),
-    #[cfg(any(test, feature = "test"))]
-    Deterministic(DeterministicTimer),
-}
-
-#[cfg(any(test, feature = "test"))]
-pub struct DeterministicTimer {
-    rx: postage::barrier::Receiver,
-    id: usize,
-    state: Arc<parking_lot::Mutex<DeterministicState>>,
-}
-
-#[cfg(any(test, feature = "test"))]
-impl Deterministic {
-    pub fn new(seed: u64) -> Arc<Self> {
-        use rand::prelude::*;
-
-        Arc::new(Self {
-            state: Arc::new(parking_lot::Mutex::new(DeterministicState {
-                rng: StdRng::seed_from_u64(seed),
-                seed,
-                scheduled_from_foreground: Default::default(),
-                scheduled_from_background: Default::default(),
-                forbid_parking: false,
-                block_on_ticks: 0..=1000,
-                now: std::time::Instant::now(),
-                next_timer_id: Default::default(),
-                pending_timers: Default::default(),
-                waiting_backtrace: None,
-                next_runnable_id: 0,
-                poll_history: Default::default(),
-                previous_poll_history: Default::default(),
-                enable_runnable_backtraces: false,
-                runnable_backtraces: Default::default(),
-            })),
-            parker: Default::default(),
-        })
-    }
-
-    pub fn execution_history(&self) -> Vec<ExecutorEvent> {
-        self.state.lock().poll_history.clone()
-    }
-
-    pub fn set_previous_execution_history(&self, history: Option<Vec<ExecutorEvent>>) {
-        self.state.lock().previous_poll_history = history;
-    }
-
-    pub fn enable_runnable_backtrace(&self) {
-        self.state.lock().enable_runnable_backtraces = true;
-    }
-
-    pub fn runnable_backtrace(&self, runnable_id: usize) -> backtrace::Backtrace {
-        let mut backtrace = self.state.lock().runnable_backtraces[&runnable_id].clone();
-        backtrace.resolve();
-        backtrace
-    }
-
-    pub fn build_background(self: &Arc<Self>) -> Arc<BackgroundExecutor> {
-        Arc::new(BackgroundExecutor::Deterministic {
-            executor: self.clone(),
-        })
-    }
-
-    pub fn build_foreground(self: &Arc<Self>, id: usize) -> Rc<ForegroundExecutor> {
-        Rc::new(ForegroundExecutor::Deterministic {
-            cx_id: id,
-            executor: self.clone(),
-        })
-    }
-
-    fn spawn_from_foreground(
-        &self,
-        cx_id: usize,
-        future: AnyLocalFuture,
-        main: bool,
-    ) -> AnyLocalTask {
-        let state = self.state.clone();
-        let id;
-        {
-            let mut state = state.lock();
-            id = util::post_inc(&mut state.next_runnable_id);
-            if state.enable_runnable_backtraces {
-                state
-                    .runnable_backtraces
-                    .insert(id, backtrace::Backtrace::new_unresolved());
-            }
-        }
-
-        let unparker = self.parker.lock().unparker();
-        let (runnable, task) = async_task::spawn_local(future, move |runnable| {
-            let mut state = state.lock();
-            state.push_to_history(ExecutorEvent::EnqueuRunnable { id });
-            state
-                .scheduled_from_foreground
-                .entry(cx_id)
-                .or_default()
-                .push(ForegroundRunnable { id, runnable, main });
-            unparker.unpark();
-        });
-        runnable.schedule();
-        task
-    }
-
-    fn spawn(&self, future: AnyFuture) -> AnyTask {
-        let state = self.state.clone();
-        let id;
-        {
-            let mut state = state.lock();
-            id = util::post_inc(&mut state.next_runnable_id);
-            if state.enable_runnable_backtraces {
-                state
-                    .runnable_backtraces
-                    .insert(id, backtrace::Backtrace::new_unresolved());
-            }
-        }
-
-        let unparker = self.parker.lock().unparker();
-        let (runnable, task) = async_task::spawn(future, move |runnable| {
-            let mut state = state.lock();
-            state
-                .poll_history
-                .push(ExecutorEvent::EnqueuRunnable { id });
-            state
-                .scheduled_from_background
-                .push(BackgroundRunnable { id, runnable });
-            unparker.unpark();
-        });
-        runnable.schedule();
-        task
-    }
-
-    fn run<'a>(
-        &self,
-        cx_id: usize,
-        main_future: Pin<Box<dyn 'a + Future<Output = Box<dyn Any>>>>,
-    ) -> Box<dyn Any> {
-        use std::sync::atomic::{AtomicBool, Ordering::SeqCst};
-
-        let woken = Arc::new(AtomicBool::new(false));
-
-        let state = self.state.clone();
-        let id;
-        {
-            let mut state = state.lock();
-            id = util::post_inc(&mut state.next_runnable_id);
-            if state.enable_runnable_backtraces {
-                state
-                    .runnable_backtraces
-                    .insert(id, backtrace::Backtrace::new_unresolved());
-            }
-        }
-
-        let unparker = self.parker.lock().unparker();
-        let (runnable, mut main_task) = unsafe {
-            async_task::spawn_unchecked(main_future, move |runnable| {
-                let state = &mut *state.lock();
-                state
-                    .scheduled_from_foreground
-                    .entry(cx_id)
-                    .or_default()
-                    .push(ForegroundRunnable {
-                        id: util::post_inc(&mut state.next_runnable_id),
-                        runnable,
-                        main: true,
-                    });
-                unparker.unpark();
-            })
-        };
-        runnable.schedule();
-
-        loop {
-            if let Some(result) = self.run_internal(woken.clone(), Some(&mut main_task)) {
-                return result;
-            }
-
-            if !woken.load(SeqCst) {
-                self.state.lock().will_park();
-            }
-
-            woken.store(false, SeqCst);
-            self.parker.lock().park();
-        }
-    }
-
-    pub fn run_until_parked(&self) {
-        use std::sync::atomic::AtomicBool;
-        let woken = Arc::new(AtomicBool::new(false));
-        self.run_internal(woken, None);
-    }
-
-    fn run_internal(
-        &self,
-        woken: Arc<std::sync::atomic::AtomicBool>,
-        mut main_task: Option<&mut AnyLocalTask>,
-    ) -> Option<Box<dyn Any>> {
-        use rand::prelude::*;
-        use std::sync::atomic::Ordering::SeqCst;
-
-        let unparker = self.parker.lock().unparker();
-        let waker = waker_fn::waker_fn(move || {
-            woken.store(true, SeqCst);
-            unparker.unpark();
-        });
-
-        let mut cx = Context::from_waker(&waker);
-        loop {
-            let mut state = self.state.lock();
-
-            if state.scheduled_from_foreground.is_empty()
-                && state.scheduled_from_background.is_empty()
-            {
-                if let Some(main_task) = main_task {
-                    if let Poll::Ready(result) = main_task.poll(&mut cx) {
-                        return Some(result);
-                    }
-                }
-
-                return None;
-            }
-
-            if !state.scheduled_from_background.is_empty() && state.rng.gen() {
-                let background_len = state.scheduled_from_background.len();
-                let ix = state.rng.gen_range(0..background_len);
-                let background_runnable = state.scheduled_from_background.remove(ix);
-                state.push_to_history(ExecutorEvent::PollRunnable {
-                    id: background_runnable.id,
-                });
-                drop(state);
-                background_runnable.runnable.run();
-            } else if !state.scheduled_from_foreground.is_empty() {
-                let available_cx_ids = state
-                    .scheduled_from_foreground
-                    .keys()
-                    .copied()
-                    .collect::<Vec<_>>();
-                let cx_id_to_run = *available_cx_ids.iter().choose(&mut state.rng).unwrap();
-                let scheduled_from_cx = state
-                    .scheduled_from_foreground
-                    .get_mut(&cx_id_to_run)
-                    .unwrap();
-                let foreground_runnable = scheduled_from_cx.remove(0);
-                if scheduled_from_cx.is_empty() {
-                    state.scheduled_from_foreground.remove(&cx_id_to_run);
-                }
-                state.push_to_history(ExecutorEvent::PollRunnable {
-                    id: foreground_runnable.id,
-                });
-
-                drop(state);
-
-                foreground_runnable.runnable.run();
-                if let Some(main_task) = main_task.as_mut() {
-                    if foreground_runnable.main {
-                        if let Poll::Ready(result) = main_task.poll(&mut cx) {
-                            return Some(result);
-                        }
-                    }
-                }
-            }
-        }
-    }
-
-    fn block<F, T>(&self, future: &mut F, max_ticks: usize) -> Option<T>
-    where
-        F: Unpin + Future<Output = T>,
-    {
-        use rand::prelude::*;
-
-        let unparker = self.parker.lock().unparker();
-        let waker = waker_fn::waker_fn(move || {
-            unparker.unpark();
-        });
-
-        let mut cx = Context::from_waker(&waker);
-        for _ in 0..max_ticks {
-            let mut state = self.state.lock();
-            let runnable_count = state.scheduled_from_background.len();
-            let ix = state.rng.gen_range(0..=runnable_count);
-            if ix < state.scheduled_from_background.len() {
-                let background_runnable = state.scheduled_from_background.remove(ix);
-                state.push_to_history(ExecutorEvent::PollRunnable {
-                    id: background_runnable.id,
-                });
-                drop(state);
-                background_runnable.runnable.run();
-            } else {
-                drop(state);
-                if let Poll::Ready(result) = future.poll(&mut cx) {
-                    return Some(result);
-                }
-                let mut state = self.state.lock();
-                if state.scheduled_from_background.is_empty() {
-                    state.will_park();
-                    drop(state);
-                    self.parker.lock().park();
-                }
-
-                continue;
-            }
-        }
-
-        None
-    }
-
-    pub fn timer(&self, duration: Duration) -> Timer {
-        let (tx, rx) = postage::barrier::channel();
-        let mut state = self.state.lock();
-        let wakeup_at = state.now + duration;
-        let id = util::post_inc(&mut state.next_timer_id);
-        match state
-            .pending_timers
-            .binary_search_by_key(&wakeup_at, |e| e.1)
-        {
-            Ok(ix) | Err(ix) => state.pending_timers.insert(ix, (id, wakeup_at, tx)),
-        }
-        let state = self.state.clone();
-        Timer::Deterministic(DeterministicTimer { rx, id, state })
-    }
-
-    pub fn now(&self) -> std::time::Instant {
-        let state = self.state.lock();
-        state.now
-    }
-
-    pub fn advance_clock(&self, duration: Duration) {
-        let new_now = self.state.lock().now + duration;
-        loop {
-            self.run_until_parked();
-            let mut state = self.state.lock();
-
-            if let Some((_, wakeup_time, _)) = state.pending_timers.first() {
-                let wakeup_time = *wakeup_time;
-                if wakeup_time <= new_now {
-                    let timer_count = state
-                        .pending_timers
-                        .iter()
-                        .take_while(|(_, t, _)| *t == wakeup_time)
-                        .count();
-                    state.now = wakeup_time;
-                    let timers_to_wake = state
-                        .pending_timers
-                        .drain(0..timer_count)
-                        .collect::<Vec<_>>();
-                    drop(state);
-                    drop(timers_to_wake);
-                    continue;
-                }
-            }
-
-            break;
-        }
-
-        self.state.lock().now = new_now;
-    }
-
-    pub fn start_waiting(&self) {
-        self.state.lock().waiting_backtrace = Some(backtrace::Backtrace::new_unresolved());
-    }
-
-    pub fn finish_waiting(&self) {
-        self.state.lock().waiting_backtrace.take();
-    }
-
-    pub fn forbid_parking(&self) {
-        use rand::prelude::*;
-
-        let mut state = self.state.lock();
-        state.forbid_parking = true;
-        state.rng = StdRng::seed_from_u64(state.seed);
-    }
-
-    pub fn allow_parking(&self) {
-        use rand::prelude::*;
-
-        let mut state = self.state.lock();
-        state.forbid_parking = false;
-        state.rng = StdRng::seed_from_u64(state.seed);
-    }
-
-    pub async fn simulate_random_delay(&self) {
-        use rand::prelude::*;
-        use smol::future::yield_now;
-        if self.state.lock().rng.gen_bool(0.2) {
-            let yields = self.state.lock().rng.gen_range(1..=10);
-            for _ in 0..yields {
-                yield_now().await;
-            }
-        }
-    }
-
-    pub fn record_backtrace(&self) {
-        let mut state = self.state.lock();
-        if state.enable_runnable_backtraces {
-            let current_id = state
-                .poll_history
-                .iter()
-                .rev()
-                .find_map(|event| match event {
-                    ExecutorEvent::PollRunnable { id } => Some(*id),
-                    _ => None,
-                });
-            if let Some(id) = current_id {
-                state
-                    .runnable_backtraces
-                    .insert(id, backtrace::Backtrace::new_unresolved());
-            }
-        }
-    }
-}
-
-impl Drop for Timer {
-    fn drop(&mut self) {
-        #[cfg(any(test, feature = "test"))]
-        if let Timer::Deterministic(DeterministicTimer { state, id, .. }) = self {
-            state
-                .lock()
-                .pending_timers
-                .retain(|(timer_id, _, _)| timer_id != id)
-        }
-    }
-}
-
-impl Future for Timer {
-    type Output = ();
-
-    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        match &mut *self {
-            #[cfg(any(test, feature = "test"))]
-            Self::Deterministic(DeterministicTimer { rx, .. }) => {
-                use postage::stream::{PollRecv, Stream as _};
-                smol::pin!(rx);
-                match rx.poll_recv(&mut postage::Context::from_waker(cx.waker())) {
-                    PollRecv::Ready(()) | PollRecv::Closed => Poll::Ready(()),
-                    PollRecv::Pending => Poll::Pending,
-                }
-            }
-            Self::Production(timer) => {
-                smol::pin!(timer);
-                match timer.poll(cx) {
-                    Poll::Ready(_) => Poll::Ready(()),
-                    Poll::Pending => Poll::Pending,
-                }
-            }
-        }
-    }
+    Spawned(async_task::Task<T>),
 }
 
-#[cfg(any(test, feature = "test"))]
-impl DeterministicState {
-    fn push_to_history(&mut self, event: ExecutorEvent) {
-        use std::fmt::Write as _;
-
-        self.poll_history.push(event);
-        if let Some(prev_history) = &self.previous_poll_history {
-            let ix = self.poll_history.len() - 1;
-            let prev_event = prev_history[ix];
-            if event != prev_event {
-                let mut message = String::new();
-                writeln!(
-                    &mut message,
-                    "current runnable backtrace:\n{:?}",
-                    self.runnable_backtraces.get_mut(&event.id()).map(|trace| {
-                        trace.resolve();
-                        crate::util::CwdBacktrace(trace)
-                    })
-                )
-                .unwrap();
-                writeln!(
-                    &mut message,
-                    "previous runnable backtrace:\n{:?}",
-                    self.runnable_backtraces
-                        .get_mut(&prev_event.id())
-                        .map(|trace| {
-                            trace.resolve();
-                            util::CwdBacktrace(trace)
-                        })
-                )
-                .unwrap();
-                panic!("detected non-determinism after {ix}. {message}");
-            }
-        }
-    }
-
-    fn will_park(&mut self) {
-        if self.forbid_parking {
-            let mut backtrace_message = String::new();
-            #[cfg(any(test, feature = "test"))]
-            if let Some(backtrace) = self.waiting_backtrace.as_mut() {
-                backtrace.resolve();
-                backtrace_message = format!(
-                    "\nbacktrace of waiting future:\n{:?}",
-                    util::CwdBacktrace(backtrace)
-                );
-            }
-
-            panic!(
-                "deterministic executor parked after a call to forbid_parking{}",
-                backtrace_message
-            );
-        }
+impl<T> Task<T> {
+    pub fn ready(val: T) -> Self {
+        Task::Ready(Some(val))
     }
-}
 
-#[cfg(any(test, feature = "test"))]
-impl ExecutorEvent {
-    pub fn id(&self) -> usize {
+    pub fn detach(self) {
         match self {
-            ExecutorEvent::PollRunnable { id } => *id,
-            ExecutorEvent::EnqueuRunnable { id } => *id,
+            Task::Ready(_) => {}
+            Task::Spawned(task) => task.detach(),
         }
     }
 }
 
-impl ForegroundExecutor {
-    pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Result<Self> {
-        if dispatcher.is_main_thread() {
-            Ok(Self::Platform {
-                dispatcher,
-                _not_send_or_sync: PhantomData,
-            })
-        } else {
-            Err(anyhow!("must be constructed on main thread"))
-        }
-    }
-
-    pub fn spawn<T: 'static>(&self, future: impl Future<Output = T> + 'static) -> Task<T> {
-        let future = any_local_future(future);
-        let any_task = match self {
-            #[cfg(any(test, feature = "test"))]
-            Self::Deterministic { cx_id, executor } => {
-                executor.spawn_from_foreground(*cx_id, future, false)
-            }
-            Self::Platform { dispatcher, .. } => {
-                fn spawn_inner(
-                    future: AnyLocalFuture,
-                    dispatcher: &Arc<dyn PlatformDispatcher>,
-                ) -> AnyLocalTask {
-                    let dispatcher = dispatcher.clone();
-                    let schedule =
-                        move |runnable: Runnable| dispatcher.run_on_main_thread(runnable);
-                    let (runnable, task) = async_task::spawn_local(future, schedule);
-                    runnable.schedule();
-                    task
-                }
-                spawn_inner(future, dispatcher)
-            }
-        };
-        Task::local(any_task)
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub fn run<T: 'static>(&self, future: impl Future<Output = T>) -> T {
-        let future = async move { Box::new(future.await) as Box<dyn Any> }.boxed_local();
-        let result = match self {
-            Self::Deterministic { cx_id, executor } => executor.run(*cx_id, future),
-            Self::Platform { .. } => panic!("you can't call run on a platform foreground executor"),
-        };
-        *result.downcast().unwrap()
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub fn run_until_parked(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.run_until_parked(),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub fn parking_forbidden(&self) -> bool {
-        match self {
-            Self::Deterministic { executor, .. } => executor.state.lock().forbid_parking,
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub fn start_waiting(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.start_waiting(),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub fn finish_waiting(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.finish_waiting(),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub fn forbid_parking(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.forbid_parking(),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub fn allow_parking(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.allow_parking(),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub fn advance_clock(&self, duration: Duration) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.advance_clock(duration),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
-    }
+impl<T> Future for Task<T> {
+    type Output = T;
 
-    #[cfg(any(test, feature = "test"))]
-    pub fn set_block_on_ticks(&self, range: std::ops::RangeInclusive<usize>) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.state.lock().block_on_ticks = range,
-            _ => panic!("this method can only be called on a deterministic executor"),
+    fn poll(self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
+        match unsafe { self.get_unchecked_mut() } {
+            Task::Ready(val) => Poll::Ready(val.take().unwrap()),
+            Task::Spawned(task) => task.poll(cx),
         }
     }
 }
 
-impl BackgroundExecutor {
-    pub fn new() -> Self {
-        let executor = Arc::new(Executor::new());
-        let stop = channel::unbounded::<()>();
-
-        for i in 0..2 * num_cpus::get() {
-            let executor = executor.clone();
-            let stop = stop.1.clone();
-            thread::Builder::new()
-                .name(format!("background-executor-{}", i))
-                .spawn(move || smol::block_on(executor.run(stop.recv())))
-                .unwrap();
-        }
-
-        Self::Production {
-            executor,
-            _stop: stop.0,
-        }
-    }
-
-    pub fn num_cpus(&self) -> usize {
-        num_cpus::get()
+impl Executor {
+    pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
+        Self { dispatcher }
     }
 
-    pub fn spawn<T, F>(&self, future: F) -> Task<T>
+    /// Enqueues the given closure to be run on any thread. The closure returns
+    /// a future which will be run to completion on any available thread.
+    pub fn spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R>
     where
-        T: 'static + Send,
-        F: Send + Future<Output = T> + 'static,
+        R: Send + 'static,
     {
-        let future = any_future(future);
-        let any_task = match self {
-            Self::Production { executor, .. } => executor.spawn(future),
-            #[cfg(any(test, feature = "test"))]
-            Self::Deterministic { executor } => executor.spawn(future),
-        };
-        Task::send(any_task)
-    }
-
-    pub fn block<F, T>(&self, future: F) -> T
-    where
-        F: Future<Output = T>,
-    {
-        smol::pin!(future);
-        match self {
-            Self::Production { .. } => smol::block_on(&mut future),
-            #[cfg(any(test, feature = "test"))]
-            Self::Deterministic { executor, .. } => {
-                executor.block(&mut future, usize::MAX).unwrap()
-            }
-        }
+        let dispatcher = self.dispatcher.clone();
+        let (runnable, task) =
+            async_task::spawn(future, move |runnable| dispatcher.dispatch(runnable));
+        runnable.schedule();
+        Task::Spawned(task)
     }
 
-    pub fn block_with_timeout<F, T>(
-        &self,
-        timeout: Duration,
-        future: F,
-    ) -> Result<T, impl Future<Output = T>>
+    /// Enqueues the given closure to run on the application's event loop.
+    /// Returns the result asynchronously.
+    pub fn run_on_main<F, R>(&self, func: F) -> Task<R>
     where
-        T: 'static,
-        F: 'static + Unpin + Future<Output = T>,
+        F: FnOnce() -> R + Send + 'static,
+        R: Send + 'static,
     {
-        let mut future = any_local_future(future);
-        if !timeout.is_zero() {
-            let output = match self {
-                Self::Production { .. } => smol::block_on(util::timeout(timeout, &mut future)).ok(),
-                #[cfg(any(test, feature = "test"))]
-                Self::Deterministic { executor, .. } => {
-                    use rand::prelude::*;
-                    let max_ticks = {
-                        let mut state = executor.state.lock();
-                        let range = state.block_on_ticks.clone();
-                        state.rng.gen_range(range)
-                    };
-                    executor.block(&mut future, max_ticks)
-                }
-            };
-            if let Some(output) = output {
-                return Ok(*output.downcast().unwrap());
-            }
+        if self.dispatcher.is_main_thread() {
+            Task::ready(func())
+        } else {
+            self.spawn_on_main(move || async move { func() })
         }
-        Err(async { *future.await.downcast().unwrap() })
     }
 
-    pub async fn scoped<'scope, F>(self: &Arc<Self>, scheduler: F)
+    /// Enqueues the given closure to be run on the application's event loop. The
+    /// closure returns a future which will be run to completion on the main thread.
+    pub fn spawn_on_main<F, R>(&self, func: impl FnOnce() -> F + Send + 'static) -> Task<R>
     where
-        F: FnOnce(&mut Scope<'scope>),
+        F: Future<Output = R> + 'static,
+        R: Send + 'static,
     {
-        let mut scope = Scope::new(self.clone());
-        (scheduler)(&mut scope);
-        let spawned = mem::take(&mut scope.futures)
-            .into_iter()
-            .map(|f| self.spawn(f))
-            .collect::<Vec<_>>();
-        for task in spawned {
-            task.await;
-        }
-    }
-
-    pub fn timer(&self, duration: Duration) -> Timer {
-        match self {
-            BackgroundExecutor::Production { .. } => {
-                Timer::Production(smol::Timer::after(duration))
-            }
-            #[cfg(any(test, feature = "test"))]
-            BackgroundExecutor::Deterministic { executor } => executor.timer(duration),
-        }
-    }
-
-    pub fn now(&self) -> std::time::Instant {
-        match self {
-            BackgroundExecutor::Production { .. } => std::time::Instant::now(),
-            #[cfg(any(test, feature = "test"))]
-            BackgroundExecutor::Deterministic { executor } => executor.now(),
-        }
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub fn rng<'a>(&'a self) -> impl 'a + std::ops::DerefMut<Target = rand::prelude::StdRng> {
-        match self {
-            Self::Deterministic { executor, .. } => {
-                parking_lot::lock_api::MutexGuard::map(executor.state.lock(), |s| &mut s.rng)
-            }
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub async fn simulate_random_delay(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => {
-                executor.simulate_random_delay().await;
-            }
-            _ => {
-                panic!("this method can only be called on a deterministic executor")
-            }
-        }
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub fn record_backtrace(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.record_backtrace(),
-            _ => {
-                panic!("this method can only be called on a deterministic executor")
-            }
-        }
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub fn start_waiting(&self) {
-        match self {
-            Self::Deterministic { executor, .. } => executor.start_waiting(),
-            _ => panic!("this method can only be called on a deterministic executor"),
-        }
-    }
-}
-
-impl Default for BackgroundExecutor {
-    fn default() -> Self {
-        Self::new()
-    }
-}
-
-pub struct Scope<'a> {
-    executor: Arc<BackgroundExecutor>,
-    futures: Vec<Pin<Box<dyn Future<Output = ()> + Send + 'static>>>,
-    tx: Option<mpsc::Sender<()>>,
-    rx: mpsc::Receiver<()>,
-    _phantom: PhantomData<&'a ()>,
-}
-
-impl<'a> Scope<'a> {
-    fn new(executor: Arc<BackgroundExecutor>) -> Self {
-        let (tx, rx) = mpsc::channel(1);
-        Self {
-            executor,
-            tx: Some(tx),
-            rx,
-            futures: Default::default(),
-            _phantom: PhantomData,
-        }
+        let dispatcher = self.dispatcher.clone();
+        let (runnable, task) =
+            async_task::spawn_local(async move { func().await }, move |runnable| {
+                dispatcher.dispatch_on_main_thread(runnable)
+            });
+        runnable.schedule();
+        Task::Spawned(task)
     }
 
-    pub fn spawn<F>(&mut self, f: F)
+    /// Enqueues the given closure to be run on the application's event loop. Must
+    /// be called on the main thread.
+    pub fn spawn_on_main_local<R>(&self, future: impl Future<Output = R> + 'static) -> Task<R>
     where
-        F: Future<Output = ()> + Send + 'a,
+        R: 'static,
     {
-        let tx = self.tx.clone().unwrap();
-
-        // Safety: The 'a lifetime is guaranteed to outlive any of these futures because
-        // dropping this `Scope` blocks until all of the futures have resolved.
-        let f = unsafe {
-            mem::transmute::<
-                Pin<Box<dyn Future<Output = ()> + Send + 'a>>,
-                Pin<Box<dyn Future<Output = ()> + Send + 'static>>,
-            >(Box::pin(async move {
-                f.await;
-                drop(tx);
-            }))
-        };
-        self.futures.push(f);
-    }
-}
-
-impl<'a> Drop for Scope<'a> {
-    fn drop(&mut self) {
-        self.tx.take().unwrap();
-
-        // Wait until the channel is closed, which means that all of the spawned
-        // futures have resolved.
-        self.executor.block(self.rx.next());
-    }
-}
+        assert!(
+            self.dispatcher.is_main_thread(),
+            "must be called on main thread"
+        );
 
-impl<T> Task<T> {
-    pub fn ready(value: T) -> Self {
-        Self::Ready(Some(value))
-    }
-
-    fn local(any_task: AnyLocalTask) -> Self {
-        Self::Local {
-            any_task,
-            result_type: PhantomData,
-        }
-    }
-
-    pub fn detach(self) {
-        match self {
-            Task::Ready(_) => {}
-            Task::Local { any_task, .. } => any_task.detach(),
-            Task::Send { any_task, .. } => any_task.detach(),
-        }
-    }
-}
-
-// impl<T: 'static, E: 'static + Display> Task<Result<T, E>> {
-//     #[track_caller]
-//     pub fn detach_and_log_err(self, cx: &mut AppContext) {
-//         let caller = Location::caller();
-//         cx.spawn(|_| async move {
-//             if let Err(err) = self.await {
-//                 log::error!("{}:{}: {:#}", caller.file(), caller.line(), err);
-//             }
-//         })
-//         .detach();
-//     }
-// }
-
-impl<T: Send> Task<T> {
-    fn send(any_task: AnyTask) -> Self {
-        Self::Send {
-            any_task,
-            result_type: PhantomData,
-        }
-    }
-}
-
-impl<T: fmt::Debug> fmt::Debug for Task<T> {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        match self {
-            Task::Ready(value) => value.fmt(f),
-            Task::Local { any_task, .. } => any_task.fmt(f),
-            Task::Send { any_task, .. } => any_task.fmt(f),
-        }
+        let dispatcher = self.dispatcher.clone();
+        let (runnable, task) = async_task::spawn_local(future, move |runnable| {
+            dispatcher.dispatch_on_main_thread(runnable)
+        });
+        runnable.schedule();
+        Task::Spawned(task)
     }
-}
 
-impl<T: 'static> Future for Task<T> {
-    type Output = T;
-
-    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
-        match unsafe { self.get_unchecked_mut() } {
-            Task::Ready(value) => Poll::Ready(value.take().unwrap()),
-            Task::Local { any_task, .. } => {
-                any_task.poll(cx).map(|value| *value.downcast().unwrap())
-            }
-            Task::Send { any_task, .. } => {
-                any_task.poll(cx).map(|value| *value.downcast().unwrap())
-            }
-        }
+    pub fn is_main_thread(&self) -> bool {
+        self.dispatcher.is_main_thread()
     }
 }
-
-fn any_future<T, F>(future: F) -> AnyFuture
-where
-    T: 'static + Send,
-    F: Future<Output = T> + Send + 'static,
-{
-    async { Box::new(future.await) as Box<dyn Any + Send> }.boxed()
-}
-
-fn any_local_future<T, F>(future: F) -> AnyLocalFuture
-where
-    T: 'static,
-    F: Future<Output = T> + 'static,
-{
-    async { Box::new(future.await) as Box<dyn Any> }.boxed_local()
-}

crates/gpui3/src/gpui3.rs 🔗

@@ -227,14 +227,14 @@ impl<'a, T> DerefMut for Reference<'a, T> {
 }
 
 pub(crate) struct MainThreadOnly<T: ?Sized> {
-    dispatcher: Arc<dyn PlatformDispatcher>,
+    executor: Executor,
     value: Arc<T>,
 }
 
 impl<T: ?Sized> Clone for MainThreadOnly<T> {
     fn clone(&self) -> Self {
         Self {
-            dispatcher: self.dispatcher.clone(),
+            executor: self.executor.clone(),
             value: self.value.clone(),
         }
     }
@@ -243,12 +243,12 @@ impl<T: ?Sized> Clone for MainThreadOnly<T> {
 /// Allows a value to be accessed only on the main thread, allowing a non-`Send` type
 /// to become `Send`.
 impl<T: 'static + ?Sized> MainThreadOnly<T> {
-    pub(crate) fn new(value: Arc<T>, dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
-        Self { dispatcher, value }
+    pub(crate) fn new(value: Arc<T>, executor: Executor) -> Self {
+        Self { executor, value }
     }
 
     pub(crate) fn borrow_on_main_thread(&self) -> &T {
-        assert!(self.dispatcher.is_main_thread());
+        assert!(self.executor.is_main_thread());
         &self.value
     }
 }

crates/gpui3/src/platform.rs 🔗

@@ -7,8 +7,8 @@ mod test;
 
 use crate::image_cache::RenderImageParams;
 use crate::{
-    AnyWindowHandle, Bounds, DevicePixels, Font, FontId, FontMetrics, GlyphId, Pixels, Point,
-    RenderGlyphParams, RenderSvgParams, Result, Scene, ShapedLine, SharedString, Size,
+    AnyWindowHandle, Bounds, DevicePixels, Executor, Font, FontId, FontMetrics, GlyphId, Pixels,
+    Point, RenderGlyphParams, RenderSvgParams, Result, Scene, ShapedLine, SharedString, Size,
 };
 use anyhow::anyhow;
 use async_task::Runnable;
@@ -43,7 +43,7 @@ pub(crate) fn current_platform() -> Arc<dyn Platform> {
 }
 
 pub trait Platform: 'static {
-    fn dispatcher(&self) -> Arc<dyn PlatformDispatcher>;
+    fn executor(&self) -> Executor;
     fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
 
     fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>);
@@ -154,7 +154,8 @@ pub trait PlatformWindow {
 
 pub trait PlatformDispatcher: Send + Sync {
     fn is_main_thread(&self) -> bool;
-    fn run_on_main_thread(&self, task: Runnable);
+    fn dispatch(&self, task: Runnable);
+    fn dispatch_on_main_thread(&self, task: Runnable);
 }
 
 pub trait PlatformTextSystem: Send + Sync {

crates/gpui3/src/platform/mac/dispatcher.rs 🔗

@@ -25,18 +25,49 @@ impl PlatformDispatcher for MacDispatcher {
         is_main_thread == YES
     }
 
-    fn run_on_main_thread(&self, runnable: Runnable) {
+    fn dispatch(&self, runnable: Runnable) {
         unsafe {
             dispatch_async_f(
-                dispatch_get_main_queue(),
+                dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0),
                 runnable.into_raw() as *mut c_void,
                 Some(trampoline),
             );
         }
+    }
 
-        extern "C" fn trampoline(runnable: *mut c_void) {
-            let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
-            task.run();
+    fn dispatch_on_main_thread(&self, runnable: Runnable) {
+        unsafe {
+            dispatch_async_f(
+                dispatch_get_main_queue(),
+                runnable.into_raw() as *mut c_void,
+                Some(trampoline),
+            );
         }
     }
 }
+
+extern "C" fn trampoline(runnable: *mut c_void) {
+    let task = unsafe { Runnable::from_raw(runnable as *mut ()) };
+    task.run();
+}
+
+// #include <dispatch/dispatch.h>
+
+// int main(void) {
+
+//     dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+//         // Do some lengthy background work here...
+//         printf("Background Work\n");
+
+//         dispatch_async(dispatch_get_main_queue(), ^{
+//             // Once done, update your UI on the main queue here.
+//             printf("UI Updated\n");
+
+//         });
+//     });
+
+//     sleep(3);  // prevent the program from terminating immediately
+
+//     return 0;
+// }
+// ```

crates/gpui3/src/platform/mac/platform.rs 🔗

@@ -1,8 +1,8 @@
 use super::BoolExt;
 use crate::{
-    AnyWindowHandle, ClipboardItem, CursorStyle, Event, MacDispatcher, MacScreen, MacTextSystem,
-    MacWindow, PathPromptOptions, Platform, PlatformScreen, PlatformTextSystem, PlatformWindow,
-    Result, ScreenId, SemanticVersion, WindowOptions,
+    AnyWindowHandle, ClipboardItem, CursorStyle, Event, Executor, MacDispatcher, MacScreen,
+    MacTextSystem, MacWindow, PathPromptOptions, Platform, PlatformScreen, PlatformTextSystem,
+    PlatformWindow, Result, ScreenId, SemanticVersion, WindowOptions,
 };
 use anyhow::anyhow;
 use block::ConcreteBlock;
@@ -142,7 +142,7 @@ unsafe fn build_classes() {
 pub struct MacPlatform(Mutex<MacPlatformState>);
 
 pub struct MacPlatformState {
-    dispatcher: Arc<MacDispatcher>,
+    executor: Executor,
     text_system: Arc<MacTextSystem>,
     pasteboard: id,
     text_hash_pasteboard_type: id,
@@ -163,7 +163,7 @@ pub struct MacPlatformState {
 impl MacPlatform {
     pub fn new() -> Self {
         Self(Mutex::new(MacPlatformState {
-            dispatcher: Arc::new(MacDispatcher),
+            executor: Executor::new(Arc::new(MacDispatcher)),
             text_system: Arc::new(MacTextSystem::new()),
             pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
             text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
@@ -343,8 +343,8 @@ impl MacPlatform {
 }
 
 impl Platform for MacPlatform {
-    fn dispatcher(&self) -> Arc<dyn crate::PlatformDispatcher> {
-        Arc::new(MacDispatcher)
+    fn executor(&self) -> Executor {
+        self.0.lock().executor.clone()
     }
 
     fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
@@ -479,7 +479,7 @@ impl Platform for MacPlatform {
         handle: AnyWindowHandle,
         options: WindowOptions,
     ) -> Box<dyn PlatformWindow> {
-        Box::new(MacWindow::open(handle, options, self))
+        Box::new(MacWindow::open(handle, options, self.executor()))
     }
 
     fn open_url(&self, url: &str) {
@@ -566,17 +566,20 @@ impl Platform for MacPlatform {
     fn reveal_path(&self, path: &Path) {
         unsafe {
             let path = path.to_path_buf();
-            let dispatcher = self.0.lock().dispatcher.clone();
-            let _ = crate::spawn_on_main_local(dispatcher, async move {
-                let full_path = ns_string(path.to_str().unwrap_or(""));
-                let root_full_path = ns_string("");
-                let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
-                let _: BOOL = msg_send![
-                    workspace,
-                    selectFile: full_path
-                    inFileViewerRootedAtPath: root_full_path
-                ];
-            });
+            self.0
+                .lock()
+                .executor
+                .spawn_on_main_local(async move {
+                    let full_path = ns_string(path.to_str().unwrap_or(""));
+                    let root_full_path = ns_string("");
+                    let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
+                    let _: BOOL = msg_send![
+                        workspace,
+                        selectFile: full_path
+                        inFileViewerRootedAtPath: root_full_path
+                    ];
+                })
+                .detach();
         }
     }
 

crates/gpui3/src/platform/mac/window.rs 🔗

@@ -1,10 +1,10 @@
 use super::{ns_string, MetalRenderer, NSRange};
 use crate::{
-    point, px, size, AnyWindowHandle, Bounds, Event, KeyDownEvent, Keystroke, MacScreen, Modifiers,
-    ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt,
-    Pixels, Platform, PlatformAtlas, PlatformDispatcher, PlatformInputHandler, PlatformScreen,
-    PlatformWindow, Point, Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind,
-    WindowOptions, WindowPromptLevel,
+    point, px, size, AnyWindowHandle, Bounds, Event, Executor, KeyDownEvent, Keystroke, MacScreen,
+    Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent, MouseUpEvent,
+    NSRectExt, Pixels, PlatformAtlas, PlatformInputHandler, PlatformScreen, PlatformWindow, Point,
+    Scene, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
+    WindowPromptLevel,
 };
 use block::ConcreteBlock;
 use cocoa::{
@@ -279,7 +279,7 @@ struct InsertText {
 
 struct MacWindowState {
     handle: AnyWindowHandle,
-    dispatcher: Arc<dyn PlatformDispatcher>,
+    executor: Executor,
     native_window: id,
     renderer: MetalRenderer,
     scene_to_render: Option<Scene>,
@@ -415,7 +415,7 @@ unsafe impl Send for MacWindowState {}
 pub struct MacWindow(Arc<Mutex<MacWindowState>>);
 
 impl MacWindow {
-    pub fn open(handle: AnyWindowHandle, options: WindowOptions, platform: &dyn Platform) -> Self {
+    pub fn open(handle: AnyWindowHandle, options: WindowOptions, executor: Executor) -> Self {
         unsafe {
             let pool = NSAutoreleasePool::new(nil);
 
@@ -479,7 +479,7 @@ impl MacWindow {
 
             let window = Self(Arc::new(Mutex::new(MacWindowState {
                 handle,
-                dispatcher: platform.dispatcher(),
+                executor,
                 native_window,
                 renderer: MetalRenderer::new(true),
                 scene_to_render: None,
@@ -616,12 +616,12 @@ impl MacWindow {
 impl Drop for MacWindow {
     fn drop(&mut self) {
         let this = self.0.clone();
-        let dispatcher = self.0.lock().dispatcher.clone();
-        let _ = crate::spawn_on_main(dispatcher, || async move {
-            unsafe {
+        let executor = self.0.lock().executor.clone();
+        executor
+            .run_on_main(move || unsafe {
                 this.lock().native_window.close();
-            }
-        });
+            })
+            .detach();
     }
 }
 
@@ -739,14 +739,16 @@ impl PlatformWindow for MacWindow {
             });
             let block = block.copy();
             let native_window = self.0.lock().native_window;
-            let dispatcher = self.0.lock().dispatcher.clone();
-            let _ = crate::spawn_on_main_local(dispatcher, async move {
-                let _: () = msg_send![
-                    alert,
-                    beginSheetModalForWindow: native_window
-                    completionHandler: block
-                ];
-            });
+            let executor = self.0.lock().executor.clone();
+            executor
+                .spawn_on_main_local(async move {
+                    let _: () = msg_send![
+                        alert,
+                        beginSheetModalForWindow: native_window
+                        completionHandler: block
+                    ];
+                })
+                .detach();
 
             done_rx
         }
@@ -754,12 +756,14 @@ impl PlatformWindow for MacWindow {
 
     fn activate(&self) {
         let window = self.0.lock().native_window;
-        let dispatcher = self.0.lock().dispatcher.clone();
-        let _ = crate::spawn_on_main_local(dispatcher.clone(), async move {
-            unsafe {
-                let _: () = msg_send![window, makeKeyAndOrderFront: nil];
-            }
-        });
+        let executor = self.0.lock().executor.clone();
+        executor
+            .spawn_on_main_local(async move {
+                unsafe {
+                    let _: () = msg_send![window, makeKeyAndOrderFront: nil];
+                }
+            })
+            .detach();
     }
 
     fn set_title(&mut self, title: &str) {
@@ -802,23 +806,25 @@ impl PlatformWindow for MacWindow {
     fn zoom(&self) {
         let this = self.0.lock();
         let window = this.native_window;
-        let dispatcher = this.dispatcher.clone();
-        let _ = crate::spawn_on_main_local(dispatcher, async move {
-            unsafe {
-                window.zoom_(nil);
-            }
-        });
+        this.executor
+            .spawn_on_main_local(async move {
+                unsafe {
+                    window.zoom_(nil);
+                }
+            })
+            .detach();
     }
 
     fn toggle_full_screen(&self) {
         let this = self.0.lock();
         let window = this.native_window;
-        let dispatcher = this.dispatcher.clone();
-        let _ = crate::spawn_on_main_local(dispatcher, async move {
-            unsafe {
-                window.toggleFullScreen_(nil);
-            }
-        });
+        this.executor
+            .spawn_on_main_local(async move {
+                unsafe {
+                    window.toggleFullScreen_(nil);
+                }
+            })
+            .detach();
     }
 
     fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>) {
@@ -1114,15 +1120,14 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
                 },
             ) => {
                 lock.synthetic_drag_counter += 1;
-                let dispatcher = lock.dispatcher.clone();
-                let _ = crate::spawn_on_main_local(
-                    dispatcher,
-                    synthetic_drag(
+                let executor = lock.executor.clone();
+                executor
+                    .spawn_on_main_local(synthetic_drag(
                         weak_window_state,
                         lock.synthetic_drag_counter,
                         event.clone(),
-                    ),
-                );
+                    ))
+                    .detach();
             }
 
             Event::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
@@ -1241,16 +1246,18 @@ extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id)
         }
     }
 
-    let dispatcher = lock.dispatcher.clone();
+    let executor = lock.executor.clone();
     drop(lock);
-    let _ = crate::spawn_on_main_local(dispatcher, async move {
-        let mut lock = window_state.as_ref().lock();
-        if let Some(mut callback) = lock.activate_callback.take() {
-            drop(lock);
-            callback(is_active);
-            window_state.lock().activate_callback = Some(callback);
-        };
-    });
+    executor
+        .spawn_on_main_local(async move {
+            let mut lock = window_state.as_ref().lock();
+            if let Some(mut callback) = lock.activate_callback.take() {
+                drop(lock);
+                callback(is_active);
+                window_state.lock().activate_callback = Some(callback);
+            };
+        })
+        .detach();
 }
 
 extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {

crates/gpui3/src/platform/test.rs 🔗

@@ -1,5 +1,5 @@
 use super::Platform;
-use crate::ScreenId;
+use crate::{Executor, ScreenId};
 
 pub struct TestPlatform;
 
@@ -11,7 +11,7 @@ impl TestPlatform {
 
 // todo!("implement out what our tests needed in GPUI 1")
 impl Platform for TestPlatform {
-    fn dispatcher(&self) -> std::sync::Arc<dyn crate::PlatformDispatcher> {
+    fn executor(&self) -> Executor {
         unimplemented!()
     }
 

crates/gpui3/src/util.rs 🔗

@@ -1,18 +1,16 @@
-use smol::future::FutureExt;
-use std::{future::Future, time::Duration};
 pub use util::*;
 
-pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
-where
-    F: Future<Output = T>,
-{
-    let timer = async {
-        smol::Timer::after(timeout).await;
-        Err(())
-    };
-    let future = async move { Ok(f.await) };
-    timer.race(future).await
-}
+// pub async fn timeout<F, T>(timeout: Duration, f: F) -> Result<T, ()>
+// where
+//     F: Future<Output = T>,
+// {
+//     let timer = async {
+//         smol::Timer::after(timeout).await;
+//         Err(())
+//     };
+//     let future = async move { Ok(f.await) };
+//     timer.race(future).await
+// }
 
 #[cfg(any(test, feature = "test"))]
 pub struct CwdBacktrace<'a>(pub &'a backtrace::Backtrace);

crates/gpui3/src/window.rs 🔗

@@ -53,8 +53,7 @@ impl Window {
             }
         }));
 
-        let platform_window =
-            MainThreadOnly::new(Arc::new(platform_window), cx.platform().dispatcher());
+        let platform_window = MainThreadOnly::new(Arc::new(platform_window), cx.executor.clone());
 
         Window {
             handle,
@@ -122,7 +121,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         R: Send + 'static,
     {
         let (tx, rx) = oneshot::channel();
-        if self.dispatcher.is_main_thread() {
+        if self.executor.is_main_thread() {
             let _ = tx.send(f(unsafe {
                 mem::transmute::<&mut Self, &mut MainThread<Self>>(self)
             }));
@@ -600,7 +599,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
         R: Send + 'static,
     {
         let (tx, rx) = oneshot::channel();
-        if self.dispatcher.is_main_thread() {
+        if self.executor.is_main_thread() {
             let cx = unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(self) };
             let _ = tx.send(f(view, cx));
         } else {