use crate::Context; use anyhow::{anyhow, Result}; use derive_more::{Deref, DerefMut}; use parking_lot::{RwLock, RwLockUpgradableReadGuard}; use slotmap::{SecondaryMap, SlotMap}; use std::{ any::Any, marker::PhantomData, mem, sync::{ atomic::{AtomicUsize, Ordering::SeqCst}, Arc, Weak, }, }; slotmap::new_key_type! { pub struct EntityId; } pub(crate) struct EntityMap(Arc>); struct EntityMapState { ref_counts: SlotMap, entities: SecondaryMap>, dropped_entities: Vec<(EntityId, Box)>, } impl EntityMap { pub fn new() -> Self { 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.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.0.write().entities.insert(handle.id, Box::new(entity)); handle } /// Move an entity to the stack. pub fn lease(&self, handle: &Handle) -> Lease { let id = handle.id; let entity = Some( self.0 .write() .entities .remove(id) .expect("Circular entity lease. Is the entity already being updated?") .downcast::() .unwrap(), ); Lease { id, entity } } /// Return an entity after moving it to the stack. pub fn end_lease(&mut self, mut lease: Lease) { self.0 .write() .entities .insert(lease.id, lease.entity.take().unwrap()); } pub fn weak_handle(&self, id: EntityId) -> WeakHandle { WeakHandle { id, entity_type: PhantomData, 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 { entity: Option>, pub id: EntityId, } impl core::ops::Deref for Lease { type Target = T; fn deref(&self) -> &Self::Target { self.entity.as_ref().unwrap() } } impl core::ops::DerefMut for Lease { fn deref_mut(&mut self) -> &mut Self::Target { self.entity.as_mut().unwrap() } } impl Drop for Lease { fn drop(&mut self) { if self.entity.is_some() { // We don't panic here, because other panics can cause us to drop the lease without ending it cleanly. log::error!("Leases must be ended with EntityMap::end_lease") } } } #[derive(Deref, DerefMut)] pub struct Slot(Handle); pub struct Handle { pub(crate) id: EntityId, entity_type: PhantomData, entity_map: Weak>, } impl Handle { fn new(id: EntityId, entity_map: Weak>) -> Self { Self { id, entity_type: PhantomData, entity_map, } } pub fn downgrade(&self) -> WeakHandle { WeakHandle { id: self.id, entity_type: self.entity_type, entity_map: self.entity_map.clone(), } } /// Update the entity referenced by this handle with the given function. /// /// The update function receives a context appropriate for its environment. /// When updating in an `AppContext`, it receives a `ModelContext`. /// When updating an a `WindowContext`, it receives a `ViewContext`. pub fn update( &self, cx: &mut C, update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R, ) -> C::Result { cx.update_entity(self, update) } } 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, entity_map: self.entity_map.clone(), } } } impl Drop for Handle { fn drop(&mut self) { 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, entity_type: PhantomData, entity_map: Weak>, } impl Clone for WeakHandle { fn clone(&self) -> Self { Self { id: self.id, entity_type: self.entity_type, entity_map: self.entity_map.clone(), } } } impl WeakHandle { pub fn upgrade(&self, _: &impl Context) -> Option> { 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, entity_map: self.entity_map.clone(), }) } /// Update the entity referenced by this handle with the given function if /// the referenced entity still exists. Returns an error if the entity has /// been released. /// /// The update function receives a context appropriate for its environment. /// When updating in an `AppContext`, it receives a `ModelContext`. /// When updating an a `WindowContext`, it receives a `ViewContext`. pub fn update( &self, cx: &mut C, update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R, ) -> Result where Result>: crate::Flatten, { crate::Flatten::flatten( self.upgrade(cx) .ok_or_else(|| anyhow!("entity release")) .map(|this| cx.update_entity(&this, update)), ) } }