From a9c69bf7744ef236ad8bb5e69fdf94010b561c51 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Wed, 11 Oct 2023 12:45:09 +0200 Subject: [PATCH] Checkpoint --- crates/gpui3/src/app.rs | 39 ++++------ crates/gpui3/src/app/model_context.rs | 31 ++++---- crates/gpui3/src/gpui3.rs | 2 + crates/gpui3/src/subscription.rs | 103 ++++++++++++++++++++++++++ crates/gpui3/src/window.rs | 30 ++++---- 5 files changed, 150 insertions(+), 55 deletions(-) create mode 100644 crates/gpui3/src/subscription.rs diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index 8b52ce5071..20dc3d3f34 100644 --- a/crates/gpui3/src/app.rs +++ b/crates/gpui3/src/app.rs @@ -9,15 +9,15 @@ use refineable::Refineable; use crate::{ current_platform, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor, LayoutId, - MainThread, MainThreadOnly, Platform, PlatformDisplayLinker, RootView, SvgRenderer, Task, - TextStyle, TextStyleRefinement, TextSystem, Window, WindowContext, WindowHandle, WindowId, + MainThread, MainThreadOnly, Platform, PlatformDisplayLinker, RootView, SubscriberSet, + SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, Window, WindowContext, + WindowHandle, WindowId, }; use anyhow::{anyhow, Result}; use collections::{HashMap, VecDeque}; use futures::Future; use parking_lot::Mutex; use slotmap::SlotMap; -use smallvec::SmallVec; use std::{ any::{type_name, Any, TypeId}, mem, @@ -67,8 +67,8 @@ impl App { entities, windows: SlotMap::with_key(), pending_effects: Default::default(), - observers: Default::default(), - event_handlers: Default::default(), + observers: SubscriberSet::new(), + event_handlers: SubscriberSet::new(), layout_id_buffer: Default::default(), }) })) @@ -88,9 +88,8 @@ impl App { } } -type Handlers = SmallVec<[Arc bool + Send + Sync + 'static>; 2]>; -type EventHandlers = - SmallVec<[Arc bool + Send + Sync + 'static>; 2]>; +type Handler = Arc bool + Send + Sync + 'static>; +type EventHandler = Arc bool + Send + Sync + 'static>; type FrameCallback = Box; pub struct AppContext { @@ -109,8 +108,8 @@ pub struct AppContext { pub(crate) entities: EntityMap, pub(crate) windows: SlotMap>, pub(crate) pending_effects: VecDeque, - pub(crate) observers: HashMap, - pub(crate) event_handlers: HashMap, + pub(crate) observers: SubscriberSet, + pub(crate) event_handlers: SubscriberSet, pub(crate) layout_id_buffer: Vec, // We recycle this memory across layout requests. } @@ -176,23 +175,15 @@ impl AppContext { } fn apply_notify_effect(&mut self, updated_entity: EntityId) { - if let Some(mut handlers) = self.observers.remove(&updated_entity) { - handlers.retain(|handler| handler(self)); - if let Some(new_handlers) = self.observers.remove(&updated_entity) { - handlers.extend(new_handlers); - } - self.observers.insert(updated_entity, handlers); - } + self.observers + .clone() + .retain(&updated_entity, |handler| handler(self)); } fn apply_emit_effect(&mut self, updated_entity: EntityId, event: Box) { - if let Some(mut handlers) = self.event_handlers.remove(&updated_entity) { - handlers.retain(|handler| handler(&event, self)); - if let Some(new_handlers) = self.event_handlers.remove(&updated_entity) { - handlers.extend(new_handlers); - } - self.event_handlers.insert(updated_entity, handlers); - } + self.event_handlers + .clone() + .retain(&updated_entity, |handler| handler(&event, self)); } pub fn to_async(&self) -> AsyncAppContext { diff --git a/crates/gpui3/src/app/model_context.rs b/crates/gpui3/src/app/model_context.rs index 895e8edc04..0b30442fef 100644 --- a/crates/gpui3/src/app/model_context.rs +++ b/crates/gpui3/src/app/model_context.rs @@ -1,4 +1,7 @@ -use crate::{AppContext, Context, Effect, EntityId, EventEmitter, Handle, Reference, WeakHandle}; +use crate::{ + AppContext, Context, Effect, EntityId, EventEmitter, Handle, Reference, Subscription, + WeakHandle, +}; use std::{marker::PhantomData, sync::Arc}; pub struct ModelContext<'a, T> { @@ -42,21 +45,20 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> { &mut self, handle: &Handle, on_notify: impl Fn(&mut T, Handle, &mut ModelContext<'_, T>) + Send + Sync + 'static, - ) { + ) -> Subscription { let this = self.handle(); let handle = handle.downgrade(); - self.app - .observers - .entry(handle.id) - .or_default() - .push(Arc::new(move |cx| { + self.app.observers.insert( + handle.id, + Arc::new(move |cx| { if let Some((this, handle)) = this.upgrade(cx).zip(handle.upgrade(cx)) { this.update(cx, |this, cx| on_notify(this, handle, cx)); true } else { false } - })); + }), + ) } pub fn subscribe( @@ -66,14 +68,12 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> { + Send + Sync + 'static, - ) { + ) -> Subscription { let this = self.handle(); let handle = handle.downgrade(); - self.app - .event_handlers - .entry(handle.id) - .or_default() - .push(Arc::new(move |event, cx| { + self.app.event_handlers.insert( + handle.id, + Arc::new(move |event, cx| { let event = event.downcast_ref().expect("invalid event type"); if let Some((this, handle)) = this.upgrade(cx).zip(handle.upgrade(cx)) { this.update(cx, |this, cx| on_event(this, handle, event, cx)); @@ -81,7 +81,8 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> { } else { false } - })); + }), + ) } pub fn notify(&mut self) { diff --git a/crates/gpui3/src/gpui3.rs b/crates/gpui3/src/gpui3.rs index f1868db3c9..ea0ad219a8 100644 --- a/crates/gpui3/src/gpui3.rs +++ b/crates/gpui3/src/gpui3.rs @@ -13,6 +13,7 @@ mod scene; mod style; mod style_helpers; mod styled; +mod subscription; mod svg_renderer; mod taffy; mod text_system; @@ -42,6 +43,7 @@ pub use smol::Timer; pub use style::*; pub use style_helpers::*; pub use styled::*; +pub use subscription::*; pub use svg_renderer::*; pub use taffy::{AvailableSpace, LayoutId}; pub use text_system::*; diff --git a/crates/gpui3/src/subscription.rs b/crates/gpui3/src/subscription.rs new file mode 100644 index 0000000000..4eaf9d9787 --- /dev/null +++ b/crates/gpui3/src/subscription.rs @@ -0,0 +1,103 @@ +use collections::{BTreeMap, BTreeSet}; +use parking_lot::Mutex; +use std::{fmt::Debug, mem, sync::Arc}; +use util::post_inc; + +#[derive(Clone)] +pub(crate) struct SubscriberSet( + Arc>>, +); + +struct SubscriberSetState { + subscribers: BTreeMap>, + dropped_subscribers: BTreeSet<(EmitterKey, usize)>, + next_subscriber_id: usize, +} + +impl SubscriberSet +where + EmitterKey: 'static + Ord + Clone + Debug, + Callback: 'static, +{ + pub fn new() -> Self { + Self(Arc::new(Mutex::new(SubscriberSetState { + subscribers: Default::default(), + dropped_subscribers: Default::default(), + next_subscriber_id: 0, + }))) + } + + pub fn insert(&self, emitter: EmitterKey, callback: Callback) -> Subscription { + let mut lock = self.0.lock(); + let subscriber_id = post_inc(&mut lock.next_subscriber_id); + lock.subscribers + .entry(emitter.clone()) + .or_default() + .insert(subscriber_id, callback); + let this = self.0.clone(); + Subscription { + unsubscribe: Some(Box::new(move || { + let mut lock = this.lock(); + if let Some(subscribers) = lock.subscribers.get_mut(&emitter) { + subscribers.remove(&subscriber_id); + if subscribers.is_empty() { + lock.subscribers.remove(&emitter); + return; + } + } + + // We didn't manage to remove the subscription, which means it was dropped + // while invoking the callback. Mark it as dropped so that we can remove it + // later. + lock.dropped_subscribers.insert((emitter, subscriber_id)); + })), + } + } + + pub fn retain(&self, emitter: &EmitterKey, mut f: F) + where + F: FnMut(&mut Callback) -> bool, + { + let entry = self.0.lock().subscribers.remove_entry(emitter); + if let Some((emitter, mut subscribers)) = entry { + subscribers.retain(|_, callback| f(callback)); + let mut lock = self.0.lock(); + + // Add any new subscribers that were added while invoking the callback. + if let Some(new_subscribers) = lock.subscribers.remove(&emitter) { + subscribers.extend(new_subscribers); + } + + // Remove any dropped subscriptions that were dropped while invoking the callback. + for (dropped_emitter, dropped_subscription_id) in + mem::take(&mut lock.dropped_subscribers) + { + debug_assert_eq!(emitter, dropped_emitter); + subscribers.remove(&dropped_subscription_id); + } + + if !subscribers.is_empty() { + lock.subscribers.insert(emitter, subscribers); + } + } + } +} + +#[must_use] +pub struct Subscription { + unsubscribe: Option>, +} + +impl Subscription { + pub fn detach(mut self) { + self.unsubscribe.take(); + } +} + +impl Drop for Subscription { + fn drop(&mut self) { + if let Some(unsubscribe) = self.unsubscribe.take() { + unsubscribe(); + } + } +} diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index ffb0d875fc..1c0ded433e 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -5,8 +5,8 @@ use crate::{ LayoutId, MainThread, MainThreadOnly, MonochromeSprite, MouseMoveEvent, Path, Pixels, PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, - Style, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, WindowOptions, - SUBPIXEL_VARIANTS, + Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, WeakHandle, + WindowOptions, SUBPIXEL_VARIANTS, }; use anyhow::Result; use collections::HashMap; @@ -903,15 +903,13 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { &mut self, handle: &Handle, on_notify: impl Fn(&mut S, Handle, &mut ViewContext<'_, '_, S>) + Send + Sync + 'static, - ) { + ) -> Subscription { let this = self.handle(); let handle = handle.downgrade(); let window_handle = self.window.handle; - self.app - .observers - .entry(handle.id) - .or_default() - .push(Arc::new(move |cx| { + self.app.observers.insert( + handle.id, + Arc::new(move |cx| { cx.update_window(window_handle.id, |cx| { if let Some(handle) = handle.upgrade(cx) { this.update(cx, |this, cx| on_notify(this, handle, cx)) @@ -921,7 +919,8 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { } }) .unwrap_or(false) - })); + }), + ) } pub fn subscribe( @@ -931,15 +930,13 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { + Send + Sync + 'static, - ) { + ) -> Subscription { let this = self.handle(); let handle = handle.downgrade(); let window_handle = self.window.handle; - self.app - .event_handlers - .entry(handle.id) - .or_default() - .push(Arc::new(move |event, cx| { + self.app.event_handlers.insert( + handle.id, + Arc::new(move |event, cx| { cx.update_window(window_handle.id, |cx| { if let Some(handle) = handle.upgrade(cx) { let event = event.downcast_ref().expect("invalid event type"); @@ -950,7 +947,8 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { } }) .unwrap_or(false) - })); + }), + ) } pub fn notify(&mut self) {