From 02d6b91b73fb673f7b5338ceb59eb1accfdd0827 Mon Sep 17 00:00:00 2001 From: Nathan Sobo Date: Wed, 4 Oct 2023 15:05:04 -0600 Subject: [PATCH] Checkpoint --- crates/gpui3/src/app.rs | 46 +++++++++++-------- crates/gpui3/src/app/async_context.rs | 56 +++++++++++++++++++----- crates/gpui3/src/app/entity_map.rs | 63 +++++++++++++++++++-------- crates/gpui3/src/elements/img.rs | 16 ++++--- crates/gpui3/src/executor.rs | 9 ++-- crates/gpui3/src/gpui3.rs | 6 --- crates/gpui3/src/window.rs | 58 +++++++++++------------- 7 files changed, 156 insertions(+), 98 deletions(-) diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index 3b1a66326c56b4f199e1d4aaa68f94725b63d760..498c45c2ab153e9a0199fc62d3c2bb0b900a482d 100644 --- a/crates/gpui3/src/app.rs +++ b/crates/gpui3/src/app.rs @@ -29,7 +29,7 @@ use util::{ }; #[derive(Clone)] -pub struct App(Arc>>); +pub struct App(Arc>); impl App { pub fn production(asset_source: Arc) -> Self { @@ -53,9 +53,9 @@ impl App { 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, executor.clone()), executor, @@ -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>(cx) }; on_finish_launching(cx); })); } @@ -91,7 +92,7 @@ impl App { type Handlers = SmallVec<[Arc bool + Send + Sync + 'static>; 2]>; pub struct AppContext { - this: Weak>>, + this: Weak>, platform: MainThreadOnly, text_system: Arc, pending_updates: usize, @@ -109,7 +110,7 @@ pub struct AppContext { } impl AppContext { - fn update(&mut self, update: impl FnOnce(&mut Self) -> R) -> R { + pub(crate) fn update(&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), @@ -163,6 +165,8 @@ impl AppContext { }) .collect::>(); + dbg!(&dirty_window_ids); + for dirty_window_id in dirty_window_ids { self.update_window(dirty_window_id, |cx| cx.draw()) .unwrap() @@ -180,8 +184,8 @@ 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 { @@ -213,7 +217,7 @@ impl AppContext { f: impl FnOnce(&mut MainThread) -> F + Send + 'static, ) -> Task where - F: Future + 'static, + F: Future + Send + 'static, R: Send + 'static, { let this = self.this.upgrade().unwrap(); @@ -225,14 +229,14 @@ impl AppContext { }) } - pub fn spawn(&self, f: impl FnOnce(&mut AppContext) -> Fut + Send + 'static) -> Task + pub fn spawn(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task where Fut: Future + Send + 'static, R: Send + 'static, { - let this = self.this.upgrade().unwrap(); + let cx = self.to_async(); self.executor.spawn(async move { - let future = f(&mut this.lock()); + let future = f(cx); future.await }) } @@ -298,9 +302,11 @@ impl Context for AppContext { &mut self, build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T, ) -> Handle { - 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( @@ -308,10 +314,12 @@ impl Context for AppContext { handle: &Handle, 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 + }) } } diff --git a/crates/gpui3/src/app/async_context.rs b/crates/gpui3/src/app/async_context.rs index 1f4cf537776bc4a3b4d9e1658a327b72cbd1b348..92a26456e442d093a12f2d990d5eae6ffc07b817 100644 --- a/crates/gpui3/src/app/async_context.rs +++ b/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>); +pub struct AsyncAppContext(pub(crate) Weak>); -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 = Result; fn entity( @@ -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( +impl AsyncAppContext { + pub fn update_window( &self, handle: AnyWindowHandle, - update: impl FnOnce(&mut WindowContext) -> T + Send + Sync, - ) -> Result { + update: impl FnOnce(&mut WindowContext) -> R, + ) -> Result { 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 = Result; + + fn entity( + &mut self, + build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, R>) -> R, + ) -> Result> { + self.app + .update_window(self.window, |cx| cx.entity(build_entity)) + } + + fn update_entity( + &mut self, + handle: &Handle, + update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R, + ) -> Result { + self.app + .update_window(self.window, |cx| cx.update_entity(handle, update)) + } +} diff --git a/crates/gpui3/src/app/entity_map.rs b/crates/gpui3/src/app/entity_map.rs index 29db57bb39cc518b8c74bdcac13dd4c551d215f1..ac931ec66124952cff477389118b13581e4d7438 100644 --- a/crates/gpui3/src/app/entity_map.rs +++ b/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 { - #[deref] - #[deref_mut] - entity: Box, - pub id: EntityId, -} - pub(crate) struct EntityMap { ref_counts: Arc>, entities: Arc>>>, @@ -35,31 +27,38 @@ impl EntityMap { } } + /// Reserve a slot for an entity, which you can subsequently use with `insert`. pub fn reserve(&self) -> Slot { let id = self.ref_counts.write().insert(1.into()); Slot(Handle::new(id, Arc::downgrade(&self.ref_counts))) } - pub fn redeem(&self, slot: Slot, entity: T) -> Handle { + /// Insert an entity into a slot obtained by calling `reserve`. + pub fn insert(&self, slot: Slot, entity: T) -> Handle { let handle = slot.0; self.entities.lock().insert(handle.id, Box::new(entity)); handle } + /// Move an entity to the stack. pub fn lease(&self, handle: &Handle) -> Lease { let id = handle.id; - let entity = self - .entities - .lock() - .remove(id) - .expect("Circular entity lease. Is the entity already being updated?") - .downcast::() - .unwrap(); + let entity = Some( + self.entities + .lock() + .remove(id) + .expect("Circular entity lease. Is the entity already being updated?") + .downcast::() + .unwrap(), + ); Lease { id, entity } } - pub fn end_lease(&mut self, lease: Lease) { - self.entities.lock().insert(lease.id, lease.entity); + /// Return an entity after moving it to the stack. + pub fn end_lease(&mut self, mut lease: Lease) { + self.entities + .lock() + .insert(lease.id, lease.entity.take().unwrap()); } pub fn weak_handle(&self, id: EntityId) -> WeakHandle { @@ -71,6 +70,34 @@ impl EntityMap { } } +pub struct Lease { + entity: Option>, + pub id: EntityId, +} + +impl core::ops::Deref for Lease { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.entity.as_ref().unwrap() + } +} + +impl core::ops::DerefMut for Lease { + fn deref_mut(&mut self) -> &mut Self::Target { + self.entity.as_mut().unwrap() + } +} + +impl Drop for Lease { + fn drop(&mut self) { + assert!( + self.entity.is_none(), + "Leases must be ended with EntityMap::end_lease" + ); + } +} + #[derive(Deref, DerefMut)] pub struct Slot(Handle); diff --git a/crates/gpui3/src/elements/img.rs b/crates/gpui3/src/elements/img.rs index e72bad5d9f59956a0d13cdbcee89aef66871d4d9..245b5d3918743404bc692385cff0861fb778a4af 100644 --- a/crates/gpui3/src/elements/img.rs +++ b/crates/gpui3/src/elements/img.rs @@ -35,7 +35,7 @@ impl Img { } } -impl Element for Img { +impl Element for Img { type State = S; type FrameState = (); @@ -75,16 +75,18 @@ impl Element for Img { 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(|cx| async move { + dbg!("not loaded"); + cx.spawn(|view, mut cx| async move { + dbg!("awaiting image future"); if image_future.await.log_err().is_some() { - // this.update(&mut cx, |_, cx| cx.notify()).ok(); + view.update(&mut cx, |_, cx| { + dbg!("image future loaded"); + cx.notify(); + }) + .ok(); } }) .detach() - // cx.spawn(|this, mut cx| async move { - // }) - // .detach(); } } Ok(()) diff --git a/crates/gpui3/src/executor.rs b/crates/gpui3/src/executor.rs index 7c4ab3b8e50c647dcafbc162f6d80acd2122602e..166c265128e4372631b59243d950ee1298740639 100644 --- a/crates/gpui3/src/executor.rs +++ b/crates/gpui3/src/executor.rs @@ -76,14 +76,13 @@ impl Executor { /// closure returns a future which will be run to completion on the main thread. pub fn spawn_on_main(&self, func: impl FnOnce() -> F + Send + 'static) -> Task where - F: Future + 'static, + F: Future + Send + 'static, R: Send + 'static, { let dispatcher = self.dispatcher.clone(); - let (runnable, task) = - async_task::spawn_local(async move { func().await }, move |runnable| { - dispatcher.dispatch_on_main_thread(runnable) - }); + let (runnable, task) = async_task::spawn(async move { func().await }, move |runnable| { + dispatcher.dispatch_on_main_thread(runnable) + }); runnable.schedule(); Task::Spawned(task) } diff --git a/crates/gpui3/src/gpui3.rs b/crates/gpui3/src/gpui3.rs index a1b0a7d7bd554fdea244c87d198e811fe02f254f..53d2fd89f88712ae0a2f3171d11ca0194008fe80 100644 --- a/crates/gpui3/src/gpui3.rs +++ b/crates/gpui3/src/gpui3.rs @@ -69,12 +69,6 @@ pub trait Context { #[repr(transparent)] pub struct MainThread(T); -impl MainThread { - fn new(value: T) -> Self { - Self(value) - } -} - impl Deref for MainThread { type Target = T; diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index fcfc9b9c8f178771cdd58f185d1ddedbae529b44..36761f60d4ef10dc52bc0159b8c86f7575c350a2 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -1,10 +1,10 @@ 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, Task, - 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 smallvec::SmallVec; @@ -109,6 +109,7 @@ impl<'a, 'w> WindowContext<'a, 'w> { } pub fn notify(&mut self) { + dbg!("ViewContext::notify"); self.window.dirty = true; } @@ -129,33 +130,23 @@ impl<'a, 'w> WindowContext<'a, 'w> { } } - pub fn spawn( - &mut self, - f: impl FnOnce(&mut WindowContext<'_, '_>) -> Fut + Send + 'static, - ) -> Task> - where - R: Send + 'static, - Fut: Future + Send + 'static, - { - let id = self.window.handle.id; - self.app.spawn(move |cx| { - let future = cx.update_window(id, f); - async move { Ok(future?.await) } - }) + pub fn to_async(&self) -> AsyncWindowContext { + AsyncWindowContext::new(self.app.to_async(), self.window.handle) } - pub fn try_spawn( + pub fn spawn( &mut self, - f: impl FnOnce(&mut WindowContext<'_, '_>) -> Fut + Send + 'static, - ) -> Task> + f: impl FnOnce(AnyWindowHandle, AsyncWindowContext) -> Fut + Send + 'static, + ) -> Task where R: Send + 'static, - Fut: Future> + Send + 'static, + Fut: Future + Send + 'static, { - let id = self.window.handle.id; - self.app.spawn(move |cx| { - let future = cx.update_window(id, f); - async move { future?.await } + 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 } }) } @@ -448,7 +439,7 @@ impl Context for WindowContext<'_, '_> { &mut self.window, slot.id, )); - self.entities.redeem(slot, entity) + self.entities.insert(slot, entity) } fn update_entity( @@ -607,6 +598,7 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { } pub fn notify(&mut self) { + dbg!("ViewContext::notify"); self.window_cx.notify(); self.window_cx .app @@ -633,16 +625,16 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { pub fn spawn( &mut self, - f: impl FnOnce(&mut S, &mut ViewContext<'_, '_, S>) -> Fut + Send + 'static, - ) -> Task> + f: impl FnOnce(WeakHandle, AsyncWindowContext) -> Fut + Send + 'static, + ) -> Task where R: Send + 'static, Fut: Future + Send + 'static, { let handle = self.handle(); - self.window_cx.try_spawn(move |cx| { - let result = handle.update(cx, f); - async move { Ok(result?.await) } + self.window_cx.spawn(move |_, cx| { + let result = f(handle, cx); + async move { result.await } }) }