Merge branch 'gpui2' into marshall/gpui2-playground

Marshall Bowers created

Change summary

crates/gpui3/build.rs                           |    2 
crates/gpui3/src/app.rs                         |   94 +
crates/gpui3/src/app/async_context.rs           |   56 
crates/gpui3/src/app/entity_map.rs              |   63 
crates/gpui3/src/elements/img.rs                |   28 
crates/gpui3/src/executor.rs                    | 1111 +-----------------
crates/gpui3/src/geometry.rs                    |   54 
crates/gpui3/src/gpui3.rs                       |   56 
crates/gpui3/src/platform.rs                    |    9 
crates/gpui3/src/platform/mac/dispatcher.rs     |   41 
crates/gpui3/src/platform/mac/metal_renderer.rs |    2 
crates/gpui3/src/platform/mac/platform.rs       |   41 
crates/gpui3/src/platform/mac/shaders.metal     |   27 
crates/gpui3/src/platform/mac/window.rs         |  115 +
crates/gpui3/src/platform/test.rs               |    4 
crates/gpui3/src/scene.rs                       |    1 
crates/gpui3/src/style.rs                       |    2 
crates/gpui3/src/util.rs                        |   24 
crates/gpui3/src/window.rs                      |  146 +
19 files changed, 569 insertions(+), 1,307 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};
@@ -29,7 +29,7 @@ use util::{
 };
 
 #[derive(Clone)]
-pub struct App(Arc<Mutex<MainThread<AppContext>>>);
+pub struct App(Arc<Mutex<AppContext>>);
 
 impl App {
     pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
@@ -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(), ());
+        let unit_entity = entities.insert(entities.reserve(), ());
         Self(Arc::new_cyclic(|this| {
-            Mutex::new(MainThread::new(AppContext {
+            Mutex::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),
@@ -71,7 +71,7 @@ impl App {
                 pending_effects: Default::default(),
                 observers: Default::default(),
                 layout_id_buffer: Default::default(),
-            }))
+            })
         }))
     }
 
@@ -83,6 +83,7 @@ impl App {
         let platform = self.0.lock().platform.clone();
         platform.borrow_on_main_thread().run(Box::new(move || {
             let cx = &mut *this.0.lock();
+            let cx = unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) };
             on_finish_launching(cx);
         }));
     }
@@ -91,11 +92,11 @@ impl App {
 type Handlers = SmallVec<[Arc<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>; 2]>;
 
 pub struct AppContext {
-    this: Weak<Mutex<MainThread<AppContext>>>,
+    this: Weak<Mutex<AppContext>>,
     platform: MainThreadOnly<dyn Platform>,
-    dispatcher: Arc<dyn PlatformDispatcher>,
     text_system: Arc<TextSystem>,
     pending_updates: usize,
+    pub(crate) executor: Executor,
     pub(crate) svg_renderer: SvgRenderer,
     pub(crate) image_cache: ImageCache,
     pub(crate) text_style_stack: Vec<TextStyleRefinement>,
@@ -109,7 +110,7 @@ pub struct AppContext {
 }
 
 impl AppContext {
-    fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
+    pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
         self.pending_updates += 1;
         let result = update(self);
         if self.pending_updates == 1 {
@@ -144,6 +145,7 @@ impl AppContext {
     }
 
     fn flush_effects(&mut self) {
+        dbg!("flush effects");
         while let Some(effect) = self.pending_effects.pop_front() {
             match effect {
                 Effect::Notify(entity_id) => self.apply_notify_effect(entity_id),
@@ -180,36 +182,44 @@ impl AppContext {
         }
     }
 
-    pub fn to_async(&self) -> AsyncContext {
-        AsyncContext(unsafe { mem::transmute(self.this.clone()) })
+    pub fn to_async(&self) -> AsyncAppContext {
+        AsyncAppContext(unsafe { mem::transmute(self.this.clone()) })
+    }
+
+    pub fn executor(&self) -> &Executor {
+        &self.executor
     }
 
     pub fn run_on_main<R>(
-        &self,
+        &mut self,
         f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
-    ) -> impl Future<Output = R>
+    ) -> Task<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) })
+        if self.executor.is_main_thread() {
+            Task::ready(f(unsafe {
+                mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(self)
+            }))
+        } else {
+            let this = self.this.upgrade().unwrap();
+            self.executor.run_on_main(move || {
+                let cx = &mut *this.lock();
+                cx.update(|cx| f(unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(cx) }))
             })
-        })
+        }
     }
 
     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,
+        F: Future<Output = R> + Send + '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) })
@@ -217,6 +227,18 @@ impl AppContext {
         })
     }
 
+    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
+    where
+        Fut: Future<Output = R> + Send + 'static,
+        R: Send + 'static,
+    {
+        let cx = self.to_async();
+        self.executor.spawn(async move {
+            let future = f(cx);
+            future.await
+        })
+    }
+
     pub fn text_system(&self) -> &Arc<TextSystem> {
         &self.text_system
     }
@@ -278,9 +300,11 @@ impl Context for AppContext {
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
     ) -> Handle<T> {
-        let slot = self.entities.reserve();
-        let entity = build_entity(&mut ModelContext::mutable(self, slot.id));
-        self.entities.redeem(slot, entity)
+        self.update(|cx| {
+            let slot = cx.entities.reserve();
+            let entity = build_entity(&mut ModelContext::mutable(cx, slot.id));
+            cx.entities.insert(slot, entity)
+        })
     }
 
     fn update_entity<T: Send + Sync + 'static, R>(
@@ -288,10 +312,12 @@ impl Context for AppContext {
         handle: &Handle<T>,
         update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
     ) -> R {
-        let mut entity = self.entities.lease(handle);
-        let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
-        self.entities.end_lease(entity);
-        result
+        self.update(|cx| {
+            let mut entity = cx.entities.lease(handle);
+            let result = update(&mut entity, &mut ModelContext::mutable(cx, handle.id));
+            cx.entities.end_lease(entity);
+            result
+        })
     }
 }
 
@@ -307,7 +333,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/app/async_context.rs 🔗

@@ -1,13 +1,15 @@
-use crate::{AnyWindowHandle, AppContext, Context, Handle, ModelContext, Result, WindowContext};
+use crate::{
+    AnyWindowHandle, AppContext, Context, Handle, ModelContext, Result, ViewContext, WindowContext,
+};
 use anyhow::anyhow;
 use parking_lot::Mutex;
 use std::sync::Weak;
 
 #[derive(Clone)]
-pub struct AsyncContext(pub(crate) Weak<Mutex<AppContext>>);
+pub struct AsyncAppContext(pub(crate) Weak<Mutex<AppContext>>);
 
-impl Context for AsyncContext {
-    type EntityContext<'a, 'b, T: 'static + Send + Sync> = ModelContext<'a, T>;
+impl Context for AsyncAppContext {
+    type EntityContext<'a, 'w, T: 'static + Send + Sync> = ModelContext<'a, T>;
     type Result<T> = Result<T>;
 
     fn entity<T: Send + Sync + 'static>(
@@ -18,7 +20,7 @@ impl Context for AsyncContext {
             .0
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let mut lock = app.lock();
+        let mut lock = app.lock(); // Does not compile without this variable.
         Ok(lock.entity(build_entity))
     }
 
@@ -31,17 +33,17 @@ impl Context for AsyncContext {
             .0
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let mut lock = app.lock();
+        let mut lock = app.lock(); // Does not compile without this variable.
         Ok(lock.update_entity(handle, update))
     }
 }
 
-impl AsyncContext {
-    pub fn update_window<T>(
+impl AsyncAppContext {
+    pub fn update_window<R>(
         &self,
         handle: AnyWindowHandle,
-        update: impl FnOnce(&mut WindowContext) -> T + Send + Sync,
-    ) -> Result<T> {
+        update: impl FnOnce(&mut WindowContext) -> R,
+    ) -> Result<R> {
         let app = self
             .0
             .upgrade()
@@ -50,3 +52,37 @@ impl AsyncContext {
         app_context.update_window(handle.id, update)
     }
 }
+
+#[derive(Clone)]
+pub struct AsyncWindowContext {
+    app: AsyncAppContext,
+    window: AnyWindowHandle,
+}
+
+impl AsyncWindowContext {
+    pub fn new(app: AsyncAppContext, window: AnyWindowHandle) -> Self {
+        Self { app, window }
+    }
+}
+
+impl Context for AsyncWindowContext {
+    type EntityContext<'a, 'w, T: 'static + Send + Sync> = ViewContext<'a, 'w, T>;
+    type Result<T> = Result<T>;
+
+    fn entity<R: Send + Sync + 'static>(
+        &mut self,
+        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, R>) -> R,
+    ) -> Result<Handle<R>> {
+        self.app
+            .update_window(self.window, |cx| cx.entity(build_entity))
+    }
+
+    fn update_entity<T: Send + Sync + 'static, R>(
+        &mut self,
+        handle: &Handle<T>,
+        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
+    ) -> Result<R> {
+        self.app
+            .update_window(self.window, |cx| cx.update_entity(handle, update))
+    }
+}

crates/gpui3/src/app/entity_map.rs 🔗

@@ -14,14 +14,6 @@ use std::{
 
 slotmap::new_key_type! { pub struct EntityId; }
 
-#[derive(Deref, DerefMut)]
-pub struct Lease<T> {
-    #[deref]
-    #[deref_mut]
-    entity: Box<T>,
-    pub id: EntityId,
-}
-
 pub(crate) struct EntityMap {
     ref_counts: Arc<RwLock<RefCounts>>,
     entities: Arc<Mutex<SecondaryMap<EntityId, Box<dyn Any + Send + Sync>>>>,
@@ -35,31 +27,38 @@ impl EntityMap {
         }
     }
 
+    /// Reserve a slot for an entity, which you can subsequently use with `insert`.
     pub fn reserve<T: 'static + Send + Sync>(&self) -> Slot<T> {
         let id = self.ref_counts.write().insert(1.into());
         Slot(Handle::new(id, Arc::downgrade(&self.ref_counts)))
     }
 
-    pub fn redeem<T: 'static + Any + Send + Sync>(&self, slot: Slot<T>, entity: T) -> Handle<T> {
+    /// Insert an entity into a slot obtained by calling `reserve`.
+    pub fn insert<T: 'static + Any + Send + Sync>(&self, slot: Slot<T>, entity: T) -> Handle<T> {
         let handle = slot.0;
         self.entities.lock().insert(handle.id, Box::new(entity));
         handle
     }
 
+    /// Move an entity to the stack.
     pub fn lease<T: 'static + Send + Sync>(&self, handle: &Handle<T>) -> Lease<T> {
         let id = handle.id;
-        let entity = self
-            .entities
-            .lock()
-            .remove(id)
-            .expect("Circular entity lease. Is the entity already being updated?")
-            .downcast::<T>()
-            .unwrap();
+        let entity = Some(
+            self.entities
+                .lock()
+                .remove(id)
+                .expect("Circular entity lease. Is the entity already being updated?")
+                .downcast::<T>()
+                .unwrap(),
+        );
         Lease { id, entity }
     }
 
-    pub fn end_lease<T: 'static + Send + Sync>(&mut self, lease: Lease<T>) {
-        self.entities.lock().insert(lease.id, lease.entity);
+    /// Return an entity after moving it to the stack.
+    pub fn end_lease<T: 'static + Send + Sync>(&mut self, mut lease: Lease<T>) {
+        self.entities
+            .lock()
+            .insert(lease.id, lease.entity.take().unwrap());
     }
 
     pub fn weak_handle<T: 'static + Send + Sync>(&self, id: EntityId) -> WeakHandle<T> {
@@ -71,6 +70,34 @@ impl EntityMap {
     }
 }
 
+pub struct Lease<T> {
+    entity: Option<Box<T>>,
+    pub id: EntityId,
+}
+
+impl<T> core::ops::Deref for Lease<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        self.entity.as_ref().unwrap()
+    }
+}
+
+impl<T> core::ops::DerefMut for Lease<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        self.entity.as_mut().unwrap()
+    }
+}
+
+impl<T> Drop for Lease<T> {
+    fn drop(&mut self) {
+        assert!(
+            self.entity.is_none(),
+            "Leases must be ended with EntityMap::end_lease"
+        );
+    }
+}
+
 #[derive(Deref, DerefMut)]
 pub struct Slot<T: Send + Sync + 'static>(Handle<T>);
 

crates/gpui3/src/elements/img.rs 🔗

@@ -1,5 +1,6 @@
 use crate::{
-    Element, Layout, LayoutId, Result, SharedString, Style, StyleHelpers, Styled, ViewContext,
+    BorrowWindow, Element, Layout, LayoutId, Result, SharedString, Style, StyleHelpers, Styled,
+    ViewContext,
 };
 use futures::FutureExt;
 use refineable::RefinementCascade;
@@ -34,7 +35,7 @@ impl<S> Img<S> {
     }
 }
 
-impl<S: 'static> Element for Img<S> {
+impl<S: Send + Sync + 'static> Element for Img<S> {
     type State = S;
     type FrameState = ();
 
@@ -64,22 +65,25 @@ impl<S: 'static> Element for Img<S> {
 
         style.paint(order, bounds, cx);
 
-        if let Some(uri) = &self.uri {
-            let image_future = cx.image_cache.get(uri.clone());
+        if let Some(uri) = self.uri.clone() {
+            let image_future = cx.image_cache.get(uri);
             if let Some(data) = image_future
                 .clone()
                 .now_or_never()
                 .and_then(ResultExt::log_err)
             {
-                cx.paint_image(bounds, order, data, self.grayscale)?;
+                let corner_radii = style.corner_radii.to_pixels(bounds, cx.rem_size());
+                cx.paint_image(bounds, corner_radii, order, data, self.grayscale)?;
             } else {
-                log::warn!("image not loaded yet");
-                // cx.spawn(|this, mut cx| async move {
-                //     if image_future.await.log_err().is_some() {
-                //         this.update(&mut cx, |_, cx| cx.notify()).ok();
-                //     }
-                // })
-                // .detach();
+                cx.spawn(|view, mut cx| async move {
+                    if image_future.await.log_err().is_some() {
+                        view.update(&mut cx, |_, cx| {
+                            cx.notify();
+                        })
+                        .ok();
+                    }
+                })
+                .detach()
             }
         }
         Ok(())

crates/gpui3/src/executor.rs 🔗

@@ -1,1095 +1,112 @@
-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,
-{
-    spawn_on_main(dispatcher, move || async move { func() })
 }
 
-/// 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,
-        }
+impl Executor {
+    pub fn new(dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
+        Self { dispatcher }
     }
 
-    pub fn num_cpus(&self) -> usize {
-        num_cpus::get()
-    }
-
-    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> + Send + '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(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/geometry.rs 🔗

@@ -1,7 +1,10 @@
 use core::fmt::Debug;
 use derive_more::{Add, AddAssign, Div, Mul, Sub, SubAssign};
 use refineable::Refineable;
-use std::ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign};
+use std::{
+    cmp,
+    ops::{Add, AddAssign, Div, Mul, MulAssign, Sub, SubAssign},
+};
 
 #[derive(Refineable, Default, Add, AddAssign, Sub, SubAssign, Copy, Debug, PartialEq, Eq, Hash)]
 #[refineable(debug)]
@@ -84,7 +87,7 @@ impl<T: Clone + Debug + Div<S, Output = T>, S: Clone> Div<S> for Point<T> {
     }
 }
 
-impl<T: Clone + Debug + std::cmp::PartialOrd> Point<T> {
+impl<T: Clone + Debug + cmp::PartialOrd> Point<T> {
     pub fn max(&self, other: &Self) -> Self {
         Point {
             x: if self.x >= other.x {
@@ -99,6 +102,21 @@ impl<T: Clone + Debug + std::cmp::PartialOrd> Point<T> {
             },
         }
     }
+
+    pub fn min(&self, other: &Self) -> Self {
+        Point {
+            x: if self.x <= other.x {
+                self.x.clone()
+            } else {
+                other.x.clone()
+            },
+            y: if self.y <= other.y {
+                self.y.clone()
+            } else {
+                other.y.clone()
+            },
+        }
+    }
 }
 
 impl<T: Clone + Debug> Clone for Point<T> {
@@ -239,6 +257,14 @@ impl<T: Clone + Debug + Sub<Output = T>> Bounds<T> {
     }
 }
 
+impl<T: Clone + Debug + PartialOrd + Add<T, Output = T> + Sub<Output = T>> Bounds<T> {
+    pub fn intersect(&self, other: &Self) -> Self {
+        let upper_left = self.origin.max(&other.origin);
+        let lower_right = self.lower_right().min(&other.lower_right());
+        Self::from_corners(upper_left, lower_right)
+    }
+}
+
 impl<T, Rhs> Mul<Rhs> for Bounds<T>
 where
     T: Mul<Rhs, Output = Rhs> + Clone + Debug,
@@ -433,12 +459,13 @@ pub struct Corners<T: Clone + Debug> {
 }
 
 impl Corners<AbsoluteLength> {
-    pub fn to_pixels(&self, rem_size: Pixels) -> Corners<Pixels> {
+    pub fn to_pixels(&self, bounds: Bounds<Pixels>, rem_size: Pixels) -> Corners<Pixels> {
+        let max = bounds.size.width.max(bounds.size.height) / 2.;
         Corners {
-            top_left: self.top_left.to_pixels(rem_size),
-            top_right: self.top_right.to_pixels(rem_size),
-            bottom_right: self.bottom_right.to_pixels(rem_size),
-            bottom_left: self.bottom_left.to_pixels(rem_size),
+            top_left: self.top_left.to_pixels(rem_size).min(max),
+            top_right: self.top_right.to_pixels(rem_size).min(max),
+            bottom_right: self.bottom_right.to_pixels(rem_size).min(max),
+            bottom_left: self.bottom_left.to_pixels(rem_size).min(max),
         }
     }
 }
@@ -536,7 +563,7 @@ impl Mul<Pixels> for Pixels {
 impl Eq for Pixels {}
 
 impl Ord for Pixels {
-    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
+    fn cmp(&self, other: &Self) -> cmp::Ordering {
         self.0.partial_cmp(&other.0).unwrap()
     }
 }
@@ -925,6 +952,15 @@ impl<T: IsZero + Debug + Clone> IsZero for Size<T> {
 
 impl<T: IsZero + Debug + Clone> IsZero for Bounds<T> {
     fn is_zero(&self) -> bool {
-        self.origin.is_zero() && self.size.is_zero()
+        self.size.is_zero()
+    }
+}
+
+impl<T: IsZero + Debug + Clone> IsZero for Corners<T> {
+    fn is_zero(&self) -> bool {
+        self.top_left.is_zero()
+            && self.top_right.is_zero()
+            && self.bottom_right.is_zero()
+            && self.bottom_left.is_zero()
     }
 }

crates/gpui3/src/gpui3.rs 🔗

@@ -27,8 +27,6 @@ pub use elements::*;
 pub use executor::*;
 pub use geometry::*;
 pub use gpui3_macros::*;
-pub use svg_renderer::*;
-
 pub use platform::*;
 pub use refineable::*;
 pub use scene::*;
@@ -37,12 +35,14 @@ pub use serde_json;
 pub use smallvec;
 pub use smol::Timer;
 use std::{
+    mem,
     ops::{Deref, DerefMut},
     sync::Arc,
 };
 pub use style::*;
 pub use style_helpers::*;
 pub use styled::*;
+pub use svg_renderer::*;
 use taffy::TaffyLayoutEngine;
 pub use taffy::{AvailableSpace, LayoutId};
 pub use text_system::*;
@@ -69,12 +69,6 @@ pub trait Context {
 #[repr(transparent)]
 pub struct MainThread<T>(T);
 
-impl<T> MainThread<T> {
-    fn new(value: T) -> Self {
-        Self(value)
-    }
-}
-
 impl<T> Deref for MainThread<T> {
     type Target = T;
 
@@ -89,6 +83,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;
 
@@ -190,14 +220,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(),
         }
     }
@@ -206,12 +236,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/metal_renderer.rs 🔗

@@ -131,6 +131,8 @@ impl MetalRenderer {
     }
 
     pub fn draw(&mut self, scene: &mut Scene) {
+        dbg!("draw scene");
+
         let layer = self.layer.clone();
         let viewport_size = layer.drawable_size();
         let viewport_size: Size<DevicePixels> = size(

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/shaders.metal 🔗

@@ -106,11 +106,7 @@ fragment float4 quad_fragment(QuadVertexOutput input [[stage_in]],
     color = float4(premultiplied_output_rgb, output_alpha);
   }
 
-  float clip_distance =
-      quad_sdf(input.position.xy, quad.clip_bounds, quad.clip_corner_radii);
-  return color *
-         float4(1., 1., 1.,
-                saturate(0.5 - distance) * saturate(0.5 - clip_distance));
+  return color * float4(1., 1., 1., saturate(0.5 - distance));
 }
 
 struct MonochromeSpriteVertexOutput {
@@ -131,8 +127,9 @@ vertex MonochromeSpriteVertexOutput monochrome_sprite_vertex(
 
   float2 unit_vertex = unit_vertices[unit_vertex_id];
   MonochromeSprite sprite = sprites[sprite_id];
+  // Don't apply content mask at the vertex level because we don't have time to make sampling from the texture match the mask.
   float4 device_position = to_device_position(
-      unit_vertex, sprite.bounds, sprite.content_mask.bounds, viewport_size);
+      unit_vertex, sprite.bounds, sprite.bounds, viewport_size);
   float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
   float4 color = hsla_to_rgba(sprite.color);
   return MonochromeSpriteVertexOutput{device_position, tile_position, color,
@@ -148,8 +145,11 @@ fragment float4 monochrome_sprite_fragment(
                                           min_filter::linear);
   float4 sample =
       atlas_texture.sample(atlas_texture_sampler, input.tile_position);
-  float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds,
-                                 sprite.content_mask.corner_radii);
+  float clip_distance = quad_sdf(
+      input.position.xy,
+      sprite.content_mask.bounds,
+      Corners_ScaledPixels { 0., 0., 0., 0. }
+  );
   float4 color = input.color;
   color.a *= sample.a * saturate(0.5 - clip_distance);
   return color;
@@ -172,8 +172,9 @@ vertex PolychromeSpriteVertexOutput polychrome_sprite_vertex(
 
   float2 unit_vertex = unit_vertices[unit_vertex_id];
   PolychromeSprite sprite = sprites[sprite_id];
+  // Don't apply content mask at the vertex level because we don't have time to make sampling from the texture match the mask.
   float4 device_position = to_device_position(
-      unit_vertex, sprite.bounds, sprite.content_mask.bounds, viewport_size);
+      unit_vertex, sprite.bounds, sprite.bounds, viewport_size);
   float2 tile_position = to_tile_position(unit_vertex, sprite.tile, atlas_size);
   return PolychromeSpriteVertexOutput{device_position, tile_position,
                                       sprite_id};
@@ -188,8 +189,10 @@ fragment float4 polychrome_sprite_fragment(
                                           min_filter::linear);
   float4 sample =
       atlas_texture.sample(atlas_texture_sampler, input.tile_position);
-  float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds,
-                                 sprite.content_mask.corner_radii);
+  float quad_distance = quad_sdf(input.position.xy, sprite.bounds, sprite.corner_radii);
+  float clip_distance = quad_sdf(input.position.xy, sprite.content_mask.bounds, Corners_ScaledPixels { 0., 0., 0., 0. });
+  float distance = max(quad_distance, clip_distance);
+
   float4 color = sample;
   if (sprite.grayscale) {
     float grayscale = 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
@@ -197,7 +200,7 @@ fragment float4 polychrome_sprite_fragment(
     color.g = grayscale;
     color.b = grayscale;
   }
-  color.a *= saturate(0.5 - clip_distance);
+  color.a *= saturate(0.5 - distance);
   return color;
 }
 

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/scene.rs 🔗

@@ -275,6 +275,7 @@ pub struct PolychromeSprite {
     pub order: u32,
     pub bounds: Bounds<ScaledPixels>,
     pub content_mask: ScaledContentMask,
+    pub corner_radii: Corners<ScaledPixels>,
     pub tile: AtlasTile,
     pub grayscale: bool,
 }

crates/gpui3/src/style.rs 🔗

@@ -202,7 +202,6 @@ impl Style {
         let min = current_mask.bounds.origin;
         let max = current_mask.bounds.lower_right();
 
-        let mask_corner_radii = Corners::default();
         let mask_bounds = match (
             self.overflow.x == Overflow::Visible,
             self.overflow.y == Overflow::Visible,
@@ -224,7 +223,6 @@ impl Style {
         };
         let mask = ContentMask {
             bounds: mask_bounds,
-            corner_radii: mask_corner_radii,
         };
 
         cx.with_content_mask(mask, f)

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 🔗

@@ -1,15 +1,14 @@
 use crate::{
-    image_cache::RenderImageParams, px, AnyView, AppContext, AvailableSpace, BorrowAppContext,
-    Bounds, Context, Corners, DevicePixels, Effect, Element, EntityId, FontId, GlyphId, Handle,
-    Hsla, ImageData, IsZero, LayerId, LayoutId, MainThread, MainThreadOnly, MonochromeSprite,
-    Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Reference, RenderGlyphParams,
-    RenderSvgParams, ScaledPixels, Scene, SharedString, Size, Style, TaffyLayoutEngine, WeakHandle,
-    WindowOptions, SUBPIXEL_VARIANTS,
+    image_cache::RenderImageParams, px, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
+    BorrowAppContext, Bounds, Context, Corners, DevicePixels, Effect, Element, EntityId, FontId,
+    GlyphId, Handle, Hsla, ImageData, IsZero, LayerId, LayoutId, MainThread, MainThreadOnly,
+    MonochromeSprite, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Reference,
+    RenderGlyphParams, RenderSvgParams, ScaledPixels, Scene, SharedString, Size, Style,
+    TaffyLayoutEngine, Task, WeakHandle, WindowOptions, SUBPIXEL_VARIANTS,
 };
 use anyhow::Result;
-use futures::Future;
 use smallvec::SmallVec;
-use std::{any::TypeId, borrow::Cow, marker::PhantomData, mem, sync::Arc};
+use std::{any::TypeId, borrow::Cow, future::Future, marker::PhantomData, mem, sync::Arc};
 use util::ResultExt;
 
 pub struct AnyWindow {}
@@ -53,8 +52,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,
@@ -76,23 +74,25 @@ impl Window {
 #[derive(Clone, Debug)]
 pub struct ContentMask {
     pub bounds: Bounds<Pixels>,
-    pub corner_radii: Corners<Pixels>,
 }
 
 impl ContentMask {
     pub fn scale(&self, factor: f32) -> ScaledContentMask {
         ScaledContentMask {
             bounds: self.bounds.scale(factor),
-            corner_radii: self.corner_radii.scale(factor),
         }
     }
+
+    pub fn intersect(&self, other: &Self) -> Self {
+        let bounds = self.bounds.intersect(&other.bounds);
+        ContentMask { bounds }
+    }
 }
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 #[repr(C)]
 pub struct ScaledContentMask {
     bounds: Bounds<ScaledPixels>,
-    corner_radii: Corners<ScaledPixels>,
 }
 
 pub struct WindowContext<'a, 'w> {
@@ -112,6 +112,43 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         self.window.dirty = true;
     }
 
+    pub fn run_on_main<R>(
+        &mut self,
+        f: impl FnOnce(&mut MainThread<WindowContext<'_, '_>>) -> R + Send + 'static,
+    ) -> Task<Result<R>>
+    where
+        R: Send + 'static,
+    {
+        if self.executor.is_main_thread() {
+            Task::ready(Ok(f(unsafe {
+                mem::transmute::<&mut Self, &mut MainThread<Self>>(self)
+            })))
+        } else {
+            let id = self.window.handle.id;
+            self.app.run_on_main(move |cx| cx.update_window(id, f))
+        }
+    }
+
+    pub fn to_async(&self) -> AsyncWindowContext {
+        AsyncWindowContext::new(self.app.to_async(), self.window.handle)
+    }
+
+    pub fn spawn<Fut, R>(
+        &mut self,
+        f: impl FnOnce(AnyWindowHandle, AsyncWindowContext) -> Fut + Send + 'static,
+    ) -> Task<R>
+    where
+        R: Send + 'static,
+        Fut: Future<Output = R> + Send + 'static,
+    {
+        let window = self.window.handle;
+        self.app.spawn(move |app| {
+            let cx = AsyncWindowContext::new(app, window);
+            let future = f(window, cx);
+            async move { future.await }
+        })
+    }
+
     pub fn request_layout(
         &mut self,
         style: Style,
@@ -170,44 +207,10 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         result
     }
 
-    pub fn clip<F, R>(
-        &mut self,
-        bounds: Bounds<Pixels>,
-        corner_radii: Corners<Pixels>,
-        f: impl FnOnce(&mut Self) -> R,
-    ) -> R {
-        let clip_mask = ContentMask {
-            bounds,
-            corner_radii,
-        };
-
-        self.window.content_mask_stack.push(clip_mask);
-        let result = f(self);
-        self.window.content_mask_stack.pop();
-        result
-    }
-
     pub fn current_layer_id(&self) -> LayerId {
         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>,
@@ -303,6 +306,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 PolychromeSprite {
                     order,
                     bounds,
+                    corner_radii: Default::default(),
                     content_mask,
                     tile,
                     grayscale: false,
@@ -356,6 +360,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     pub fn paint_image(
         &mut self,
         bounds: Bounds<Pixels>,
+        corner_radii: Corners<Pixels>,
         order: u32,
         data: Arc<ImageData>,
         grayscale: bool,
@@ -372,6 +377,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 Ok((data.size(), Cow::Borrowed(data.as_bytes())))
             })?;
         let content_mask = self.content_mask().scale(scale_factor);
+        let corner_radii = corner_radii.scale(scale_factor);
 
         self.window.scene.insert(
             layer_id,
@@ -379,6 +385,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
                 order,
                 bounds,
                 content_mask,
+                corner_radii,
                 tile,
                 grayscale,
             },
@@ -389,7 +396,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 +410,15 @@ 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| {
+            cx.run_on_main(view, |_, cx| {
                 cx.window
                     .platform_window
                     .borrow_on_main_thread()
                     .draw(scene);
-            });
+                cx.window.dirty = false;
+            })
+            .detach();
 
-            cx.window.dirty = false;
             Ok(())
         })
     }
@@ -430,7 +438,7 @@ impl Context for WindowContext<'_, '_> {
             &mut self.window,
             slot.id,
         ));
-        self.entities.redeem(slot, entity)
+        self.entities.insert(slot, entity)
     }
 
     fn update_entity<T: Send + Sync + 'static, R>(
@@ -473,6 +481,7 @@ pub trait BorrowWindow: BorrowAppContext {
     fn window_mut(&mut self) -> &mut Window;
 
     fn with_content_mask<R>(&mut self, mask: ContentMask, f: impl FnOnce(&mut Self) -> R) -> R {
+        let mask = mask.intersect(&self.content_mask());
         self.window_mut().content_mask_stack.push(mask);
         let result = f(self);
         self.window_mut().content_mask_stack.pop();
@@ -489,7 +498,6 @@ pub trait BorrowWindow: BorrowAppContext {
                     origin: Point::default(),
                     size: self.window().content_size,
                 },
-                corner_radii: Default::default(),
             })
     }
 
@@ -596,6 +604,38 @@ 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,
+    ) -> Task<Result<R>>
+    where
+        R: Send + 'static,
+    {
+        if self.executor.is_main_thread() {
+            let cx = unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(self) };
+            Task::ready(Ok(f(view, cx)))
+        } else {
+            let handle = self.handle().upgrade(self).unwrap();
+            self.window_cx.run_on_main(move |cx| handle.update(cx, f))
+        }
+    }
+
+    pub fn spawn<Fut, R>(
+        &mut self,
+        f: impl FnOnce(WeakHandle<S>, AsyncWindowContext) -> Fut + Send + 'static,
+    ) -> Task<R>
+    where
+        R: Send + 'static,
+        Fut: Future<Output = R> + Send + 'static,
+    {
+        let handle = self.handle();
+        self.window_cx.spawn(move |_, cx| {
+            let result = f(handle, cx);
+            async move { result.await }
+        })
+    }
+
     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(