From 1f84cdb88c15b080eaaf568112294295556c9946 Mon Sep 17 00:00:00 2001 From: Antonio Scandurra Date: Thu, 12 Oct 2023 14:49:06 +0200 Subject: [PATCH] Checkpoint --- crates/gpui3/src/app.rs | 33 ++++++++- crates/gpui3/src/app/entity_map.rs | 103 +++++++++++++++++--------- crates/gpui3/src/app/model_context.rs | 30 ++++++++ crates/gpui3/src/subscription.rs | 5 ++ crates/gpui3/src/window.rs | 34 +++++++++ 5 files changed, 167 insertions(+), 38 deletions(-) diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index 4f0670fa9a..cb187d0343 100644 --- a/crates/gpui3/src/app.rs +++ b/crates/gpui3/src/app.rs @@ -67,6 +67,7 @@ impl App { pending_effects: Default::default(), observers: SubscriberSet::new(), event_handlers: SubscriberSet::new(), + release_handlers: SubscriberSet::new(), layout_id_buffer: Default::default(), }) })) @@ -88,6 +89,7 @@ impl App { type Handler = Box bool + Send + Sync + 'static>; type EventHandler = Box bool + Send + Sync + 'static>; +type ReleaseHandler = Box; type FrameCallback = Box; pub struct AppContext { @@ -107,6 +109,7 @@ pub struct AppContext { pub(crate) pending_effects: VecDeque, pub(crate) observers: SubscriberSet, pub(crate) event_handlers: SubscriberSet, + pub(crate) release_handlers: SubscriberSet, pub(crate) layout_id_buffer: Vec, // We recycle this memory across layout requests. } @@ -146,10 +149,15 @@ impl AppContext { } fn flush_effects(&mut self) { - while let Some(effect) = self.pending_effects.pop_front() { - match effect { - Effect::Notify { emitter } => self.apply_notify_effect(emitter), - Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event), + loop { + self.release_dropped_entities(); + if let Some(effect) = self.pending_effects.pop_front() { + match effect { + Effect::Notify { emitter } => self.apply_notify_effect(emitter), + Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event), + } + } else { + break; } } @@ -171,6 +179,23 @@ impl AppContext { } } + fn release_dropped_entities(&mut self) { + loop { + let dropped = self.entities.take_dropped(); + if dropped.is_empty() { + break; + } + + for (entity_id, mut entity) in dropped { + self.observers.remove(&entity_id); + self.event_handlers.remove(&entity_id); + for release_callback in self.release_handlers.remove(&entity_id) { + release_callback(&mut entity, self); + } + } + } + } + fn apply_notify_effect(&mut self, emitter: EntityId) { self.observers .clone() diff --git a/crates/gpui3/src/app/entity_map.rs b/crates/gpui3/src/app/entity_map.rs index ac931ec661..da170458cb 100644 --- a/crates/gpui3/src/app/entity_map.rs +++ b/crates/gpui3/src/app/entity_map.rs @@ -1,11 +1,12 @@ use crate::Context; use anyhow::{anyhow, Result}; use derive_more::{Deref, DerefMut}; -use parking_lot::{Mutex, RwLock}; +use parking_lot::{RwLock, RwLockUpgradableReadGuard}; use slotmap::{SecondaryMap, SlotMap}; use std::{ any::Any, marker::PhantomData, + mem, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, Weak, @@ -14,29 +15,33 @@ use std::{ slotmap::new_key_type! { pub struct EntityId; } -pub(crate) struct EntityMap { - ref_counts: Arc>, - entities: Arc>>>, +pub(crate) struct EntityMap(Arc>); + +struct EntityMapState { + ref_counts: SlotMap, + entities: SecondaryMap>, + dropped_entities: Vec<(EntityId, Box)>, } impl EntityMap { pub fn new() -> Self { - Self { - ref_counts: Arc::new(RwLock::new(SlotMap::with_key())), - entities: Arc::new(Mutex::new(SecondaryMap::new())), - } + Self(Arc::new(RwLock::new(EntityMapState { + ref_counts: SlotMap::with_key(), + entities: SecondaryMap::new(), + dropped_entities: Vec::new(), + }))) } /// 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))) + let id = self.0.write().ref_counts.insert(1.into()); + Slot(Handle::new(id, Arc::downgrade(&self.0))) } /// 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)); + self.0.write().entities.insert(handle.id, Box::new(entity)); handle } @@ -44,8 +49,9 @@ impl EntityMap { pub fn lease(&self, handle: &Handle) -> Lease { let id = handle.id; let entity = Some( - self.entities - .lock() + self.0 + .write() + .entities .remove(id) .expect("Circular entity lease. Is the entity already being updated?") .downcast::() @@ -56,8 +62,9 @@ impl EntityMap { /// Return an entity after moving it to the stack. pub fn end_lease(&mut self, mut lease: Lease) { - self.entities - .lock() + self.0 + .write() + .entities .insert(lease.id, lease.entity.take().unwrap()); } @@ -65,9 +72,13 @@ impl EntityMap { WeakHandle { id, entity_type: PhantomData, - ref_counts: Arc::downgrade(&self.ref_counts), + entity_map: Arc::downgrade(&self.0), } } + + pub fn take_dropped(&self) -> Vec<(EntityId, Box)> { + mem::take(&mut self.0.write().dropped_entities) + } } pub struct Lease { @@ -104,17 +115,15 @@ pub struct Slot(Handle); pub struct Handle { pub(crate) id: EntityId, entity_type: PhantomData, - ref_counts: Weak>, + entity_map: Weak>, } -type RefCounts = SlotMap; - impl Handle { - pub fn new(id: EntityId, ref_counts: Weak>) -> Self { + fn new(id: EntityId, entity_map: Weak>) -> Self { Self { id, entity_type: PhantomData, - ref_counts, + entity_map, } } @@ -122,7 +131,7 @@ impl Handle { WeakHandle { id: self.id, entity_type: self.entity_type, - ref_counts: self.ref_counts.clone(), + entity_map: self.entity_map.clone(), } } @@ -142,40 +151,66 @@ impl Handle { impl Clone for Handle { fn clone(&self) -> Self { + if let Some(entity_map) = self.entity_map.upgrade() { + let entity_map = entity_map.read(); + let count = entity_map + .ref_counts + .get(self.id) + .expect("detected over-release of a handle"); + let prev_count = count.fetch_add(1, SeqCst); + assert_ne!(prev_count, 0, "Detected over-release of a handle."); + } + Self { id: self.id, entity_type: PhantomData, - ref_counts: self.ref_counts.clone(), + entity_map: self.entity_map.clone(), } } } impl Drop for Handle { fn drop(&mut self) { - if let Some(_ref_counts) = self.ref_counts.upgrade() { - // todo!() - // if let Some(count) = ref_counts.read().get(self.id) { - // let prev_count = count.fetch_sub(1, SeqCst); - // assert_ne!(prev_count, 0, "Detected over-release of a handle."); - // } + if let Some(entity_map) = self.entity_map.upgrade() { + let entity_map = entity_map.upgradable_read(); + let count = entity_map + .ref_counts + .get(self.id) + .expect("Detected over-release of a handle."); + let prev_count = count.fetch_sub(1, SeqCst); + assert_ne!(prev_count, 0, "Detected over-release of a handle."); + if prev_count == 1 { + // We were the last reference to this entity, so we can remove it. + let mut entity_map = RwLockUpgradableReadGuard::upgrade(entity_map); + let entity = entity_map + .entities + .remove(self.id) + .expect("entity was removed twice"); + entity_map.ref_counts.remove(self.id); + entity_map.dropped_entities.push((self.id, entity)); + } } } } pub struct WeakHandle { pub(crate) id: EntityId, - pub(crate) entity_type: PhantomData, - pub(crate) ref_counts: Weak>, + entity_type: PhantomData, + entity_map: Weak>, } impl WeakHandle { pub fn upgrade(&self, _: &impl Context) -> Option> { - let ref_counts = self.ref_counts.upgrade()?; - ref_counts.read().get(self.id).unwrap().fetch_add(1, SeqCst); + let entity_map = &self.entity_map.upgrade()?; + entity_map + .read() + .ref_counts + .get(self.id)? + .fetch_add(1, SeqCst); Some(Handle { id: self.id, entity_type: self.entity_type, - ref_counts: self.ref_counts.clone(), + entity_map: self.entity_map.clone(), }) } diff --git a/crates/gpui3/src/app/model_context.rs b/crates/gpui3/src/app/model_context.rs index d646bd4611..82175f9a27 100644 --- a/crates/gpui3/src/app/model_context.rs +++ b/crates/gpui3/src/app/model_context.rs @@ -85,6 +85,36 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> { ) } + pub fn on_release( + &mut self, + on_release: impl Fn(&mut T, &mut AppContext) + Send + Sync + 'static, + ) -> Subscription { + self.app.release_handlers.insert( + self.entity_id, + Box::new(move |this, cx| { + let this = this.downcast_mut().expect("invalid entity type"); + on_release(this, cx); + }), + ) + } + + pub fn observe_release( + &mut self, + handle: &Handle, + on_release: impl Fn(&mut T, &mut E, &mut ModelContext<'_, T>) + Send + Sync + 'static, + ) -> Subscription { + let this = self.handle(); + self.app.release_handlers.insert( + handle.id, + Box::new(move |entity, cx| { + let entity = entity.downcast_mut().expect("invalid entity type"); + if let Some(this) = this.upgrade(cx) { + this.update(cx, |this, cx| on_release(this, entity, cx)); + } + }), + ) + } + pub fn notify(&mut self) { self.app.pending_effects.push_back(Effect::Notify { emitter: self.entity_id, diff --git a/crates/gpui3/src/subscription.rs b/crates/gpui3/src/subscription.rs index 509b29e468..73ce4b253d 100644 --- a/crates/gpui3/src/subscription.rs +++ b/crates/gpui3/src/subscription.rs @@ -59,6 +59,11 @@ where } } + pub fn remove(&self, emitter: &EmitterKey) -> impl IntoIterator { + let subscribers = self.0.lock().subscribers.remove(&emitter); + subscribers.unwrap_or_default().into_values() + } + pub fn retain(&self, emitter: &EmitterKey, mut f: F) where F: FnMut(&mut Callback) -> bool, diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index fefa617a0b..40942e08b9 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -961,6 +961,40 @@ impl<'a, 'w, S: Send + Sync + 'static> ViewContext<'a, 'w, S> { ) } + pub fn on_release( + &mut self, + on_release: impl Fn(&mut S, &mut WindowContext) + Send + Sync + 'static, + ) -> Subscription { + let window_handle = self.window.handle; + self.app.release_handlers.insert( + self.entity_id, + Box::new(move |this, cx| { + let this = this.downcast_mut().expect("invalid entity type"); + // todo!("are we okay with silently swallowing the error?") + let _ = cx.update_window(window_handle.id, |cx| on_release(this, cx)); + }), + ) + } + + pub fn observe_release( + &mut self, + handle: &Handle, + on_release: impl Fn(&mut S, &mut E, &mut ViewContext<'_, '_, S>) + Send + Sync + 'static, + ) -> Subscription { + let this = self.handle(); + let window_handle = self.window.handle; + self.app.release_handlers.insert( + handle.id, + Box::new(move |entity, cx| { + let entity = entity.downcast_mut().expect("invalid entity type"); + // todo!("are we okay with silently swallowing the error?") + let _ = cx.update_window(window_handle.id, |cx| { + this.update(cx, |this, cx| on_release(this, entity, cx)) + }); + }), + ) + } + pub fn notify(&mut self) { self.window_cx.notify(); self.window_cx