diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index 6ddf01791fcc27b9fb9f37c5215d76c459641975..b9eb20bada210b3b7e3afc342d50f20838660678 100644 --- a/crates/gpui3/src/app.rs +++ b/crates/gpui3/src/app.rs @@ -1,9 +1,9 @@ mod async_context; -mod entities; +mod entity_map; mod model_context; pub use async_context::*; -pub use entities::*; +pub use entity_map::*; pub use model_context::*; use crate::{ diff --git a/crates/gpui3/src/app/entities.rs b/crates/gpui3/src/app/entity_map.rs similarity index 64% rename from crates/gpui3/src/app/entities.rs rename to crates/gpui3/src/app/entity_map.rs index 4e98883d0ed952a0828915c6e07b946c87afa378..4976b67aae889a116a772d78e1b7809863a73837 100644 --- a/crates/gpui3/src/app/entities.rs +++ b/crates/gpui3/src/app/entity_map.rs @@ -1,9 +1,16 @@ use crate::Context; use anyhow::{anyhow, Result}; use derive_more::{Deref, DerefMut}; -use parking_lot::Mutex; +use parking_lot::{Mutex, RwLock}; use slotmap::{SecondaryMap, SlotMap}; -use std::{any::Any, marker::PhantomData, sync::Arc}; +use std::{ + any::Any, + marker::PhantomData, + sync::{ + atomic::{AtomicUsize, Ordering::SeqCst}, + Arc, Weak, + }, +}; slotmap::new_key_type! { pub struct EntityId; } @@ -16,24 +23,21 @@ pub struct Lease { } pub(crate) struct EntityMap { - ref_counts: Arc>>, + ref_counts: Arc>, entities: Arc>>>, } impl EntityMap { pub fn new() -> Self { Self { - ref_counts: Arc::new(Mutex::new(SlotMap::with_key())), + ref_counts: Arc::new(RwLock::new(SlotMap::with_key())), entities: Arc::new(Mutex::new(SecondaryMap::new())), } } - pub fn reserve(&self) -> Slot { - let id = self.ref_counts.lock().insert(1); - Slot(Handle { - id, - entity_type: PhantomData, - }) + 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 { @@ -42,7 +46,7 @@ impl EntityMap { handle } - pub fn lease(&self, handle: &Handle) -> Lease { + pub fn lease(&self, handle: &Handle) -> Lease { let id = handle.id; let entity = self .entities @@ -57,21 +61,33 @@ impl EntityMap { pub fn end_lease(&mut self, lease: Lease) { self.entities.lock().insert(lease.id, lease.entity); } + + pub fn weak_handle(&self, id: EntityId) -> WeakHandle { + WeakHandle { + id, + entity_type: PhantomData, + ref_counts: Arc::downgrade(&self.ref_counts), + } + } } #[derive(Deref, DerefMut)] -pub struct Slot(Handle); +pub struct Slot(Handle); -pub struct Handle { +pub struct Handle { pub(crate) id: EntityId, - pub(crate) entity_type: PhantomData, + entity_type: PhantomData, + ref_counts: Weak>, } -impl Handle { - pub fn new(id: EntityId) -> Self { +type RefCounts = SlotMap; + +impl Handle { + pub fn new(id: EntityId, ref_counts: Weak>) -> Self { Self { id, entity_type: PhantomData, + ref_counts, } } @@ -79,6 +95,7 @@ impl Handle { WeakHandle { id: self.id, entity_type: self.entity_type, + ref_counts: self.ref_counts.clone(), } } @@ -96,11 +113,23 @@ impl Handle { } } -impl Clone for Handle { +impl Clone for Handle { fn clone(&self) -> Self { Self { id: self.id, entity_type: PhantomData, + ref_counts: self.ref_counts.clone(), + } + } +} + +impl Drop for Handle { + fn drop(&mut self) { + if let Some(ref_counts) = self.ref_counts.upgrade() { + 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."); + } } } } @@ -108,14 +137,17 @@ impl Clone for Handle { pub struct WeakHandle { pub(crate) id: EntityId, pub(crate) entity_type: PhantomData, + pub(crate) ref_counts: Weak>, } impl WeakHandle { pub fn upgrade(&self, _: &impl Context) -> Option> { - // todo!("Actually upgrade") + let ref_counts = self.ref_counts.upgrade()?; + ref_counts.read().get(self.id).unwrap().fetch_add(1, SeqCst); Some(Handle { id: self.id, entity_type: self.entity_type, + ref_counts: self.ref_counts.clone(), }) } diff --git a/crates/gpui3/src/app/model_context.rs b/crates/gpui3/src/app/model_context.rs index 6ad5a06a56a03a8aeda856c409621e166fd271cd..b16d29057a60dd7aad13da24a4fb7d1eea5d7cd9 100644 --- a/crates/gpui3/src/app/model_context.rs +++ b/crates/gpui3/src/app/model_context.rs @@ -35,10 +35,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> { // } pub fn handle(&self) -> WeakHandle { - WeakHandle { - id: self.entity_id, - entity_type: PhantomData, - } + self.app.entities.weak_handle(self.entity_id) } pub fn observe( diff --git a/crates/gpui3/src/view.rs b/crates/gpui3/src/view.rs index b4a4001692d1e5de2e9459972a64ef347043bb1c..86826606bcb6d86ede4a7eb211de5ba0f7d15bd0 100644 --- a/crates/gpui3/src/view.rs +++ b/crates/gpui3/src/view.rs @@ -6,7 +6,7 @@ use crate::{ }; use std::{any::Any, marker::PhantomData, sync::Arc}; -pub struct View { +pub struct View { state: Handle, render: Arc) -> AnyElement + Send + Sync + 'static>, parent_state_type: PhantomData

, @@ -21,7 +21,7 @@ impl View { } } -impl Clone for View { +impl Clone for View { fn clone(&self) -> Self { Self { state: self.state.clone(), @@ -33,10 +33,15 @@ impl Clone for View { pub type RootView = View; -pub fn view>( +pub fn view( state: Handle, render: impl Fn(&mut S, &mut ViewContext) -> E + Send + Sync + 'static, -) -> View { +) -> View +where + S: 'static + Send + Sync, + P: 'static, + E: Element, +{ View { state, render: Arc::new(move |state, cx| render(state, cx).into_any()), diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index 745ac217f0015a36c2885530ea97f3ce70776096..bdbb69d0b9eaff484281ff07d01ef4d7756d40bb 100644 --- a/crates/gpui3/src/window.rs +++ b/crates/gpui3/src/window.rs @@ -275,10 +275,7 @@ impl<'a, 'w, T: Send + Sync + 'static> ViewContext<'a, 'w, T> { } pub fn handle(&self) -> WeakHandle { - WeakHandle { - id: self.entity_id, - entity_type: PhantomData, - } + self.entities.weak_handle(self.entity_id) } pub fn observe(