Checkpoint

Antonio Scandurra created

Change summary

crates/gpui3/src/app.rs      | 32 +++++++++++-----
crates/gpui3/src/executor.rs | 10 ++++
crates/gpui3/src/gpui3.rs    | 37 +++++++++++++++++++
crates/gpui3/src/window.rs   | 71 ++++++++++++++++++++++++++-----------
4 files changed, 117 insertions(+), 33 deletions(-)

Detailed changes

crates/gpui3/src/app.rs 🔗

@@ -14,7 +14,7 @@ use crate::{
 };
 use anyhow::{anyhow, Result};
 use collections::{HashMap, VecDeque};
-use futures::Future;
+use futures::{channel::oneshot, Future};
 use parking_lot::Mutex;
 use slotmap::SlotMap;
 use smallvec::SmallVec;
@@ -93,9 +93,9 @@ type Handlers = SmallVec<[Arc<dyn Fn(&mut AppContext) -> bool + Send + Sync + 's
 pub struct AppContext {
     this: Weak<Mutex<MainThread<AppContext>>>,
     platform: MainThreadOnly<dyn Platform>,
-    dispatcher: Arc<dyn PlatformDispatcher>,
     text_system: Arc<TextSystem>,
     pending_updates: usize,
+    pub(crate) dispatcher: Arc<dyn PlatformDispatcher>,
     pub(crate) svg_renderer: SvgRenderer,
     pub(crate) image_cache: ImageCache,
     pub(crate) text_style_stack: Vec<TextStyleRefinement>,
@@ -185,19 +185,29 @@ impl AppContext {
     }
 
     pub fn run_on_main<R>(
-        &self,
+        &mut self,
         f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
     ) -> impl Future<Output = R>
     where
         R: Send + 'static,
     {
-        let this = self.this.upgrade().unwrap();
-        run_on_main(self.dispatcher.clone(), move || {
-            let cx = &mut *this.lock();
-            cx.update(|cx| {
-                f(unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) })
-            })
-        })
+        let (tx, rx) = oneshot::channel();
+        if self.dispatcher.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)
+                    }));
+                })
+            });
+        }
+        async move { rx.await.unwrap() }
     }
 
     pub fn spawn_on_main<F, R>(
@@ -307,7 +317,7 @@ impl MainThread<AppContext> {
     pub(crate) fn update_window<R>(
         &mut self,
         id: WindowId,
-        update: impl FnOnce(&mut WindowContext) -> R,
+        update: impl FnOnce(&mut MainThread<WindowContext>) -> R,
     ) -> Result<R> {
         self.0.update_window(id, |cx| {
             update(unsafe {

crates/gpui3/src/executor.rs 🔗

@@ -27,7 +27,15 @@ where
     F: FnOnce() -> R + Send + 'static,
     R: Send + 'static,
 {
-    spawn_on_main(dispatcher, move || async move { func() })
+    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

crates/gpui3/src/gpui3.rs 🔗

@@ -37,6 +37,7 @@ pub use serde_json;
 pub use smallvec;
 pub use smol::Timer;
 use std::{
+    mem,
     ops::{Deref, DerefMut},
     sync::Arc,
 };
@@ -89,6 +90,42 @@ impl<T> DerefMut for MainThread<T> {
     }
 }
 
+impl<C: Context> Context for MainThread<C> {
+    type EntityContext<'a, 'w, T: 'static + Send + Sync> = MainThread<C::EntityContext<'a, 'w, T>>;
+    type Result<T> = C::Result<T>;
+
+    fn entity<T: Send + Sync + 'static>(
+        &mut self,
+        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
+    ) -> Self::Result<Handle<T>> {
+        self.0.entity(|cx| {
+            let cx = unsafe {
+                mem::transmute::<
+                    &mut C::EntityContext<'_, '_, T>,
+                    &mut MainThread<C::EntityContext<'_, '_, T>>,
+                >(cx)
+            };
+            build_entity(cx)
+        })
+    }
+
+    fn update_entity<T: Send + Sync + 'static, R>(
+        &mut self,
+        handle: &Handle<T>,
+        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
+    ) -> Self::Result<R> {
+        self.0.update_entity(handle, |entity, cx| {
+            let cx = unsafe {
+                mem::transmute::<
+                    &mut C::EntityContext<'_, '_, T>,
+                    &mut MainThread<C::EntityContext<'_, '_, T>>,
+                >(cx)
+            };
+            update(entity, cx)
+        })
+    }
+}
+
 pub trait BorrowAppContext {
     fn app_mut(&mut self) -> &mut AppContext;
 

crates/gpui3/src/window.rs 🔗

@@ -7,7 +7,7 @@ use crate::{
     WindowOptions, SUBPIXEL_VARIANTS,
 };
 use anyhow::Result;
-use futures::Future;
+use futures::{channel::oneshot, Future};
 use smallvec::SmallVec;
 use std::{any::TypeId, borrow::Cow, marker::PhantomData, mem, sync::Arc};
 use util::ResultExt;
@@ -112,6 +112,29 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         self.window.dirty = true;
     }
 
+    fn run_on_main<R>(
+        &mut self,
+        f: impl FnOnce(&mut MainThread<WindowContext<'_, '_>>) -> R + Send + 'static,
+    ) -> impl Future<Output = R>
+    where
+        R: Send + 'static,
+    {
+        let (tx, rx) = oneshot::channel();
+        if self.dispatcher.is_main_thread() {
+            let _ = tx.send(f(unsafe {
+                mem::transmute::<&mut Self, &mut MainThread<Self>>(self)
+            }));
+        } else {
+            let id = self.window.handle.id;
+            let _ = self.app.run_on_main(move |cx| {
+                cx.update_window(id, |cx| {
+                    let _ = tx.send(f(cx));
+                })
+            });
+        }
+        async move { rx.await.unwrap() }
+    }
+
     pub fn request_layout(
         &mut self,
         style: Style,
@@ -191,23 +214,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         self.window.current_layer_id.clone()
     }
 
-    pub fn run_on_main<R>(
-        &self,
-        f: impl FnOnce(&mut MainThread<WindowContext>) -> R + Send + 'static,
-    ) -> impl Future<Output = Result<R>>
-    where
-        R: Send + 'static,
-    {
-        let id = self.window.handle.id;
-        self.app.run_on_main(move |cx| {
-            cx.update_window(id, |cx| {
-                f(unsafe {
-                    mem::transmute::<&mut WindowContext, &mut MainThread<WindowContext>>(cx)
-                })
-            })
-        })
-    }
-
     pub fn paint_glyph(
         &mut self,
         origin: Point<Pixels>,
@@ -389,7 +395,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
     pub(crate) fn draw(&mut self) -> Result<()> {
         let unit_entity = self.unit_entity.clone();
-        self.update_entity(&unit_entity, |_, cx| {
+        self.update_entity(&unit_entity, |view, cx| {
             let mut root_view = cx.window.root_view.take().unwrap();
             let (root_layout_id, mut frame_state) = root_view.layout(&mut (), cx)?;
             let available_space = cx.window.content_size.map(Into::into);
@@ -403,14 +409,14 @@ impl<'a, 'w> WindowContext<'a, 'w> {
             cx.window.root_view = Some(root_view);
             let scene = cx.window.scene.take();
 
-            let _ = cx.run_on_main(|cx| {
+            let _ = cx.run_on_main(view, |_, cx| {
                 cx.window
                     .platform_window
                     .borrow_on_main_thread()
                     .draw(scene);
+                cx.window.dirty = false;
             });
 
-            cx.window.dirty = false;
             Ok(())
         })
     }
@@ -596,6 +602,29 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> {
             .push_back(Effect::Notify(self.entity_id));
     }
 
+    pub fn run_on_main<R>(
+        &mut self,
+        view: &mut S,
+        f: impl FnOnce(&mut S, &mut MainThread<ViewContext<'_, '_, S>>) -> R + Send + 'static,
+    ) -> impl Future<Output = R>
+    where
+        R: Send + 'static,
+    {
+        let (tx, rx) = oneshot::channel();
+        if self.dispatcher.is_main_thread() {
+            let cx = unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(self) };
+            let _ = tx.send(f(view, cx));
+        } else {
+            let handle = self.handle().upgrade(self).unwrap();
+            let _ = self.window_cx.run_on_main(move |cx| {
+                handle.update(cx, |view, cx| {
+                    let _ = tx.send(f(view, cx));
+                })
+            });
+        }
+        async move { rx.await.unwrap() }
+    }
+
     pub(crate) fn erase_state<R>(&mut self, f: impl FnOnce(&mut ViewContext<()>) -> R) -> R {
         let entity_id = self.unit_entity.id;
         let mut cx = ViewContext::mutable(