Merge pull request #57 from zed-industries/dropped-entities

Max Brunsfeld created

Fix panic when an entity's ref count is decremented to 0 and then is incremented again

Change summary

gpui/src/app.rs | 44 ++++++++++++++++++++++++++++++++++----------
1 file changed, 34 insertions(+), 10 deletions(-)

Detailed changes

gpui/src/app.rs 🔗

@@ -18,7 +18,7 @@ use smol::prelude::*;
 use std::{
     any::{type_name, Any, TypeId},
     cell::RefCell,
-    collections::{HashMap, HashSet, VecDeque},
+    collections::{hash_map::Entry, HashMap, HashSet, VecDeque},
     fmt::{self, Debug},
     hash::{Hash, Hasher},
     marker::PhantomData,
@@ -1064,6 +1064,7 @@ impl MutableAppContext {
                             self.focus(window_id, view_id);
                         }
                     }
+                    self.remove_dropped_entities();
                 } else {
                     self.remove_dropped_entities();
                     self.update_windows();
@@ -1988,7 +1989,7 @@ pub struct ModelHandle<T> {
 
 impl<T: Entity> ModelHandle<T> {
     fn new(model_id: usize, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
-        ref_counts.lock().inc_entity(model_id);
+        ref_counts.lock().inc_model(model_id);
         Self {
             model_id,
             model_type: PhantomData,
@@ -2089,7 +2090,7 @@ impl<T: Entity> ModelHandle<T> {
 
 impl<T> Clone for ModelHandle<T> {
     fn clone(&self) -> Self {
-        self.ref_counts.lock().inc_entity(self.model_id);
+        self.ref_counts.lock().inc_model(self.model_id);
         Self {
             model_id: self.model_id,
             model_type: PhantomData,
@@ -2186,7 +2187,7 @@ pub struct ViewHandle<T> {
 
 impl<T: View> ViewHandle<T> {
     fn new(window_id: usize, view_id: usize, ref_counts: &Arc<Mutex<RefCounts>>) -> Self {
-        ref_counts.lock().inc_entity(view_id);
+        ref_counts.lock().inc_view(window_id, view_id);
         Self {
             window_id,
             view_id,
@@ -2298,7 +2299,9 @@ impl<T: View> ViewHandle<T> {
 
 impl<T> Clone for ViewHandle<T> {
     fn clone(&self) -> Self {
-        self.ref_counts.lock().inc_entity(self.view_id);
+        self.ref_counts
+            .lock()
+            .inc_view(self.window_id, self.view_id);
         Self {
             window_id: self.window_id,
             view_id: self.view_id,
@@ -2380,7 +2383,9 @@ impl AnyViewHandle {
 
 impl Clone for AnyViewHandle {
     fn clone(&self) -> Self {
-        self.ref_counts.lock().inc_entity(self.view_id);
+        self.ref_counts
+            .lock()
+            .inc_view(self.window_id, self.view_id);
         Self {
             window_id: self.window_id,
             view_id: self.view_id,
@@ -2392,7 +2397,10 @@ impl Clone for AnyViewHandle {
 
 impl<T: View> From<&ViewHandle<T>> for AnyViewHandle {
     fn from(handle: &ViewHandle<T>) -> Self {
-        handle.ref_counts.lock().inc_entity(handle.view_id);
+        handle
+            .ref_counts
+            .lock()
+            .inc_view(handle.window_id, handle.view_id);
         AnyViewHandle {
             window_id: handle.window_id,
             view_id: handle.view_id,
@@ -2433,7 +2441,7 @@ pub struct AnyModelHandle {
 
 impl<T: Entity> From<ModelHandle<T>> for AnyModelHandle {
     fn from(handle: ModelHandle<T>) -> Self {
-        handle.ref_counts.lock().inc_entity(handle.model_id);
+        handle.ref_counts.lock().inc_model(handle.model_id);
         Self {
             model_id: handle.model_id,
             ref_counts: handle.ref_counts.clone(),
@@ -2542,8 +2550,24 @@ struct RefCounts {
 }
 
 impl RefCounts {
-    fn inc_entity(&mut self, entity_id: usize) {
-        *self.entity_counts.entry(entity_id).or_insert(0) += 1;
+    fn inc_model(&mut self, model_id: usize) {
+        match self.entity_counts.entry(model_id) {
+            Entry::Occupied(mut entry) => *entry.get_mut() += 1,
+            Entry::Vacant(entry) => {
+                entry.insert(1);
+                self.dropped_models.remove(&model_id);
+            }
+        }
+    }
+
+    fn inc_view(&mut self, window_id: usize, view_id: usize) {
+        match self.entity_counts.entry(view_id) {
+            Entry::Occupied(mut entry) => *entry.get_mut() += 1,
+            Entry::Vacant(entry) => {
+                entry.insert(1);
+                self.dropped_views.remove(&(window_id, view_id));
+            }
+        }
     }
 
     fn inc_value(&mut self, tag_type_id: TypeId, id: usize) {