Checkpoint

Nathan Sobo created

Change summary

crates/gpui3/src/app.rs               |  4 
crates/gpui3/src/app/entity_map.rs    | 68 +++++++++++++++++++++-------
crates/gpui3/src/app/model_context.rs |  5 -
crates/gpui3/src/view.rs              | 13 +++-
crates/gpui3/src/window.rs            |  5 -
5 files changed, 63 insertions(+), 32 deletions(-)

Detailed changes

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::{

crates/gpui3/src/app/entities.rs → 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<T> {
 }
 
 pub(crate) struct EntityMap {
-    ref_counts: Arc<Mutex<SlotMap<EntityId, usize>>>,
+    ref_counts: Arc<RwLock<RefCounts>>,
     entities: Arc<Mutex<SecondaryMap<EntityId, Box<dyn Any + Send + Sync>>>>,
 }
 
 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<T>(&self) -> Slot<T> {
-        let id = self.ref_counts.lock().insert(1);
-        Slot(Handle {
-            id,
-            entity_type: PhantomData,
-        })
+    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> {
@@ -42,7 +46,7 @@ impl EntityMap {
         handle
     }
 
-    pub fn lease<T: 'static>(&self, handle: &Handle<T>) -> Lease<T> {
+    pub fn lease<T: 'static + Send + Sync>(&self, handle: &Handle<T>) -> Lease<T> {
         let id = handle.id;
         let entity = self
             .entities
@@ -57,21 +61,33 @@ impl EntityMap {
     pub fn end_lease<T: 'static + Send + Sync>(&mut self, lease: Lease<T>) {
         self.entities.lock().insert(lease.id, lease.entity);
     }
+
+    pub fn weak_handle<T: 'static + Send + Sync>(&self, id: EntityId) -> WeakHandle<T> {
+        WeakHandle {
+            id,
+            entity_type: PhantomData,
+            ref_counts: Arc::downgrade(&self.ref_counts),
+        }
+    }
 }
 
 #[derive(Deref, DerefMut)]
-pub struct Slot<T>(Handle<T>);
+pub struct Slot<T: Send + Sync + 'static>(Handle<T>);
 
-pub struct Handle<T> {
+pub struct Handle<T: Send + Sync> {
     pub(crate) id: EntityId,
-    pub(crate) entity_type: PhantomData<T>,
+    entity_type: PhantomData<T>,
+    ref_counts: Weak<RwLock<RefCounts>>,
 }
 
-impl<T: Send + Sync + 'static> Handle<T> {
-    pub fn new(id: EntityId) -> Self {
+type RefCounts = SlotMap<EntityId, AtomicUsize>;
+
+impl<T: 'static + Send + Sync> Handle<T> {
+    pub fn new(id: EntityId, ref_counts: Weak<RwLock<RefCounts>>) -> Self {
         Self {
             id,
             entity_type: PhantomData,
+            ref_counts,
         }
     }
 
@@ -79,6 +95,7 @@ impl<T: Send + Sync + 'static> Handle<T> {
         WeakHandle {
             id: self.id,
             entity_type: self.entity_type,
+            ref_counts: self.ref_counts.clone(),
         }
     }
 
@@ -96,11 +113,23 @@ impl<T: Send + Sync + 'static> Handle<T> {
     }
 }
 
-impl<T> Clone for Handle<T> {
+impl<T: Send + Sync> Clone for Handle<T> {
     fn clone(&self) -> Self {
         Self {
             id: self.id,
             entity_type: PhantomData,
+            ref_counts: self.ref_counts.clone(),
+        }
+    }
+}
+
+impl<T: Send + Sync> Drop for Handle<T> {
+    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<T> Clone for Handle<T> {
 pub struct WeakHandle<T> {
     pub(crate) id: EntityId,
     pub(crate) entity_type: PhantomData<T>,
+    pub(crate) ref_counts: Weak<RwLock<RefCounts>>,
 }
 
 impl<T: Send + Sync + 'static> WeakHandle<T> {
     pub fn upgrade(&self, _: &impl Context) -> Option<Handle<T>> {
-        // 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(),
         })
     }
 

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<T> {
-        WeakHandle {
-            id: self.entity_id,
-            entity_type: PhantomData,
-        }
+        self.app.entities.weak_handle(self.entity_id)
     }
 
     pub fn observe<E: Send + Sync + 'static>(

crates/gpui3/src/view.rs 🔗

@@ -6,7 +6,7 @@ use crate::{
 };
 use std::{any::Any, marker::PhantomData, sync::Arc};
 
-pub struct View<S, P> {
+pub struct View<S: Send + Sync, P> {
     state: Handle<S>,
     render: Arc<dyn Fn(&mut S, &mut ViewContext<S>) -> AnyElement<S> + Send + Sync + 'static>,
     parent_state_type: PhantomData<P>,
@@ -21,7 +21,7 @@ impl<S: 'static + Send + Sync, P: 'static + Send> View<S, P> {
     }
 }
 
-impl<S, P> Clone for View<S, P> {
+impl<S: Send + Sync, P> Clone for View<S, P> {
     fn clone(&self) -> Self {
         Self {
             state: self.state.clone(),
@@ -33,10 +33,15 @@ impl<S, P> Clone for View<S, P> {
 
 pub type RootView<S> = View<S, ()>;
 
-pub fn view<S: 'static, P: 'static, E: Element<State = S>>(
+pub fn view<S, P, E>(
     state: Handle<S>,
     render: impl Fn(&mut S, &mut ViewContext<S>) -> E + Send + Sync + 'static,
-) -> View<S, P> {
+) -> View<S, P>
+where
+    S: 'static + Send + Sync,
+    P: 'static,
+    E: Element<State = S>,
+{
     View {
         state,
         render: Arc::new(move |state, cx| render(state, cx).into_any()),

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<T> {
-        WeakHandle {
-            id: self.entity_id,
-            entity_type: PhantomData,
-        }
+        self.entities.weak_handle(self.entity_id)
     }
 
     pub fn observe<E: Send + Sync + 'static>(