1use crate::{seal::Sealed, App, AppContext, VisualContext, Window};
2use anyhow::{anyhow, Result};
3use collections::FxHashSet;
4use derive_more::{Deref, DerefMut};
5use parking_lot::{RwLock, RwLockUpgradableReadGuard};
6use slotmap::{KeyData, SecondaryMap, SlotMap};
7use std::{
8 any::{type_name, Any, TypeId},
9 cell::RefCell,
10 fmt::{self, Display},
11 hash::{Hash, Hasher},
12 marker::PhantomData,
13 mem,
14 num::NonZeroU64,
15 sync::{
16 atomic::{AtomicUsize, Ordering::SeqCst},
17 Arc, Weak,
18 },
19 thread::panicking,
20};
21
22#[cfg(any(test, feature = "leak-detection"))]
23use collections::HashMap;
24
25use super::Context;
26
27slotmap::new_key_type! {
28 /// A unique identifier for a entity across the application.
29 pub struct EntityId;
30}
31
32impl From<u64> for EntityId {
33 fn from(value: u64) -> Self {
34 Self(KeyData::from_ffi(value))
35 }
36}
37
38impl EntityId {
39 /// Converts this entity id to a [NonZeroU64]
40 pub fn as_non_zero_u64(self) -> NonZeroU64 {
41 NonZeroU64::new(self.0.as_ffi()).unwrap()
42 }
43
44 /// Converts this entity id to a [u64]
45 pub fn as_u64(self) -> u64 {
46 self.0.as_ffi()
47 }
48}
49
50impl Display for EntityId {
51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52 write!(f, "{}", self.as_u64())
53 }
54}
55
56pub(crate) struct EntityMap {
57 entities: SecondaryMap<EntityId, Box<dyn Any>>,
58 pub accessed_entities: RefCell<FxHashSet<EntityId>>,
59 ref_counts: Arc<RwLock<EntityRefCounts>>,
60}
61
62struct EntityRefCounts {
63 counts: SlotMap<EntityId, AtomicUsize>,
64 dropped_entity_ids: Vec<EntityId>,
65 #[cfg(any(test, feature = "leak-detection"))]
66 leak_detector: LeakDetector,
67}
68
69impl EntityMap {
70 pub fn new() -> Self {
71 Self {
72 entities: SecondaryMap::new(),
73 accessed_entities: RefCell::new(FxHashSet::default()),
74 ref_counts: Arc::new(RwLock::new(EntityRefCounts {
75 counts: SlotMap::with_key(),
76 dropped_entity_ids: Vec::new(),
77 #[cfg(any(test, feature = "leak-detection"))]
78 leak_detector: LeakDetector {
79 next_handle_id: 0,
80 entity_handles: HashMap::default(),
81 },
82 })),
83 }
84 }
85
86 /// Reserve a slot for an entity, which you can subsequently use with `insert`.
87 pub fn reserve<T: 'static>(&self) -> Slot<T> {
88 let id = self.ref_counts.write().counts.insert(1.into());
89 Slot(Entity::new(id, Arc::downgrade(&self.ref_counts)))
90 }
91
92 /// Insert an entity into a slot obtained by calling `reserve`.
93 pub fn insert<T>(&mut self, slot: Slot<T>, entity: T) -> Entity<T>
94 where
95 T: 'static,
96 {
97 let mut accessed_entities = self.accessed_entities.borrow_mut();
98 accessed_entities.insert(slot.entity_id);
99
100 let handle = slot.0;
101 self.entities.insert(handle.entity_id, Box::new(entity));
102 handle
103 }
104
105 /// Move an entity to the stack.
106 #[track_caller]
107 pub fn lease<'a, T>(&mut self, pointer: &'a Entity<T>) -> Lease<'a, T> {
108 self.assert_valid_context(pointer);
109 let mut accessed_entities = self.accessed_entities.borrow_mut();
110 accessed_entities.insert(pointer.entity_id);
111
112 let entity = Some(
113 self.entities
114 .remove(pointer.entity_id)
115 .unwrap_or_else(|| double_lease_panic::<T>("update")),
116 );
117 Lease {
118 entity,
119 pointer,
120 entity_type: PhantomData,
121 }
122 }
123
124 /// Returns an entity after moving it to the stack.
125 pub fn end_lease<T>(&mut self, mut lease: Lease<T>) {
126 self.entities
127 .insert(lease.pointer.entity_id, lease.entity.take().unwrap());
128 }
129
130 pub fn read<T: 'static>(&self, entity: &Entity<T>) -> &T {
131 self.assert_valid_context(entity);
132 let mut accessed_entities = self.accessed_entities.borrow_mut();
133 accessed_entities.insert(entity.entity_id);
134
135 self.entities
136 .get(entity.entity_id)
137 .and_then(|entity| entity.downcast_ref())
138 .unwrap_or_else(|| double_lease_panic::<T>("read"))
139 }
140
141 fn assert_valid_context(&self, entity: &AnyEntity) {
142 debug_assert!(
143 Weak::ptr_eq(&entity.entity_map, &Arc::downgrade(&self.ref_counts)),
144 "used a entity with the wrong context"
145 );
146 }
147
148 pub fn extend_accessed(&mut self, entities: &FxHashSet<EntityId>) {
149 self.accessed_entities
150 .borrow_mut()
151 .extend(entities.iter().copied());
152 }
153
154 pub fn clear_accessed(&mut self) {
155 self.accessed_entities.borrow_mut().clear();
156 }
157
158 pub fn take_dropped(&mut self) -> Vec<(EntityId, Box<dyn Any>)> {
159 let mut ref_counts = self.ref_counts.write();
160 let dropped_entity_ids = mem::take(&mut ref_counts.dropped_entity_ids);
161 let mut accessed_entities = self.accessed_entities.borrow_mut();
162
163 dropped_entity_ids
164 .into_iter()
165 .filter_map(|entity_id| {
166 let count = ref_counts.counts.remove(entity_id).unwrap();
167 debug_assert_eq!(
168 count.load(SeqCst),
169 0,
170 "dropped an entity that was referenced"
171 );
172 accessed_entities.remove(&entity_id);
173 // If the EntityId was allocated with `Context::reserve`,
174 // the entity may not have been inserted.
175 Some((entity_id, self.entities.remove(entity_id)?))
176 })
177 .collect()
178 }
179}
180
181fn double_lease_panic<T>(operation: &str) -> ! {
182 panic!(
183 "cannot {operation} {} while it is already being updated",
184 std::any::type_name::<T>()
185 )
186}
187
188pub(crate) struct Lease<'a, T> {
189 entity: Option<Box<dyn Any>>,
190 pub pointer: &'a Entity<T>,
191 entity_type: PhantomData<T>,
192}
193
194impl<T: 'static> core::ops::Deref for Lease<'_, T> {
195 type Target = T;
196
197 fn deref(&self) -> &Self::Target {
198 self.entity.as_ref().unwrap().downcast_ref().unwrap()
199 }
200}
201
202impl<T: 'static> core::ops::DerefMut for Lease<'_, T> {
203 fn deref_mut(&mut self) -> &mut Self::Target {
204 self.entity.as_mut().unwrap().downcast_mut().unwrap()
205 }
206}
207
208impl<T> Drop for Lease<'_, T> {
209 fn drop(&mut self) {
210 if self.entity.is_some() && !panicking() {
211 panic!("Leases must be ended with EntityMap::end_lease")
212 }
213 }
214}
215
216#[derive(Deref, DerefMut)]
217pub(crate) struct Slot<T>(Entity<T>);
218
219/// A dynamically typed reference to a entity, which can be downcast into a `Entity<T>`.
220pub struct AnyEntity {
221 pub(crate) entity_id: EntityId,
222 pub(crate) entity_type: TypeId,
223 entity_map: Weak<RwLock<EntityRefCounts>>,
224 #[cfg(any(test, feature = "leak-detection"))]
225 handle_id: HandleId,
226}
227
228impl AnyEntity {
229 fn new(id: EntityId, entity_type: TypeId, entity_map: Weak<RwLock<EntityRefCounts>>) -> Self {
230 Self {
231 entity_id: id,
232 entity_type,
233 entity_map: entity_map.clone(),
234 #[cfg(any(test, feature = "leak-detection"))]
235 handle_id: entity_map
236 .upgrade()
237 .unwrap()
238 .write()
239 .leak_detector
240 .handle_created(id),
241 }
242 }
243
244 /// Returns the id associated with this entity.
245 pub fn entity_id(&self) -> EntityId {
246 self.entity_id
247 }
248
249 /// Returns the [TypeId] associated with this entity.
250 pub fn entity_type(&self) -> TypeId {
251 self.entity_type
252 }
253
254 /// Converts this entity handle into a weak variant, which does not prevent it from being released.
255 pub fn downgrade(&self) -> AnyWeakEntity {
256 AnyWeakEntity {
257 entity_id: self.entity_id,
258 entity_type: self.entity_type,
259 entity_ref_counts: self.entity_map.clone(),
260 }
261 }
262
263 /// Converts this entity handle into a strongly-typed entity handle of the given type.
264 /// If this entity handle is not of the specified type, returns itself as an error variant.
265 pub fn downcast<T: 'static>(self) -> Result<Entity<T>, AnyEntity> {
266 if TypeId::of::<T>() == self.entity_type {
267 Ok(Entity {
268 any_entity: self,
269 entity_type: PhantomData,
270 })
271 } else {
272 Err(self)
273 }
274 }
275}
276
277impl Clone for AnyEntity {
278 fn clone(&self) -> Self {
279 if let Some(entity_map) = self.entity_map.upgrade() {
280 let entity_map = entity_map.read();
281 let count = entity_map
282 .counts
283 .get(self.entity_id)
284 .expect("detected over-release of a entity");
285 let prev_count = count.fetch_add(1, SeqCst);
286 assert_ne!(prev_count, 0, "Detected over-release of a entity.");
287 }
288
289 Self {
290 entity_id: self.entity_id,
291 entity_type: self.entity_type,
292 entity_map: self.entity_map.clone(),
293 #[cfg(any(test, feature = "leak-detection"))]
294 handle_id: self
295 .entity_map
296 .upgrade()
297 .unwrap()
298 .write()
299 .leak_detector
300 .handle_created(self.entity_id),
301 }
302 }
303}
304
305impl Drop for AnyEntity {
306 fn drop(&mut self) {
307 if let Some(entity_map) = self.entity_map.upgrade() {
308 let entity_map = entity_map.upgradable_read();
309 let count = entity_map
310 .counts
311 .get(self.entity_id)
312 .expect("detected over-release of a handle.");
313 let prev_count = count.fetch_sub(1, SeqCst);
314 assert_ne!(prev_count, 0, "Detected over-release of a entity.");
315 if prev_count == 1 {
316 // We were the last reference to this entity, so we can remove it.
317 let mut entity_map = RwLockUpgradableReadGuard::upgrade(entity_map);
318 entity_map.dropped_entity_ids.push(self.entity_id);
319 }
320 }
321
322 #[cfg(any(test, feature = "leak-detection"))]
323 if let Some(entity_map) = self.entity_map.upgrade() {
324 entity_map
325 .write()
326 .leak_detector
327 .handle_released(self.entity_id, self.handle_id)
328 }
329 }
330}
331
332impl<T> From<Entity<T>> for AnyEntity {
333 fn from(entity: Entity<T>) -> Self {
334 entity.any_entity
335 }
336}
337
338impl Hash for AnyEntity {
339 fn hash<H: Hasher>(&self, state: &mut H) {
340 self.entity_id.hash(state);
341 }
342}
343
344impl PartialEq for AnyEntity {
345 fn eq(&self, other: &Self) -> bool {
346 self.entity_id == other.entity_id
347 }
348}
349
350impl Eq for AnyEntity {}
351
352impl std::fmt::Debug for AnyEntity {
353 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
354 f.debug_struct("AnyEntity")
355 .field("entity_id", &self.entity_id.as_u64())
356 .finish()
357 }
358}
359
360/// A strong, well typed reference to a struct which is managed
361/// by GPUI
362#[derive(Deref, DerefMut)]
363pub struct Entity<T> {
364 #[deref]
365 #[deref_mut]
366 pub(crate) any_entity: AnyEntity,
367 pub(crate) entity_type: PhantomData<T>,
368}
369
370unsafe impl<T> Send for Entity<T> {}
371unsafe impl<T> Sync for Entity<T> {}
372impl<T> Sealed for Entity<T> {}
373
374impl<T: 'static> Entity<T> {
375 fn new(id: EntityId, entity_map: Weak<RwLock<EntityRefCounts>>) -> Self
376 where
377 T: 'static,
378 {
379 Self {
380 any_entity: AnyEntity::new(id, TypeId::of::<T>(), entity_map),
381 entity_type: PhantomData,
382 }
383 }
384
385 /// Get the entity ID associated with this entity
386 pub fn entity_id(&self) -> EntityId {
387 self.any_entity.entity_id
388 }
389
390 /// Downgrade this entity pointer to a non-retaining weak pointer
391 pub fn downgrade(&self) -> WeakEntity<T> {
392 WeakEntity {
393 any_entity: self.any_entity.downgrade(),
394 entity_type: self.entity_type,
395 }
396 }
397
398 /// Upgrade the given weak pointer to a retaining pointer, if it still exists
399 pub fn upgrade_from(weak: &WeakEntity<T>) -> Option<Self>
400 where
401 Self: Sized,
402 {
403 Some(Entity {
404 any_entity: weak.any_entity.upgrade()?,
405 entity_type: weak.entity_type,
406 })
407 }
408
409 /// Convert this into a dynamically typed entity.
410 pub fn into_any(self) -> AnyEntity {
411 self.any_entity
412 }
413
414 /// Grab a reference to this entity from the context.
415 pub fn read<'a>(&self, cx: &'a App) -> &'a T {
416 cx.entities.read(self)
417 }
418
419 /// Read the entity referenced by this handle with the given function.
420 pub fn read_with<R, C: AppContext>(
421 &self,
422 cx: &C,
423 f: impl FnOnce(&T, &App) -> R,
424 ) -> C::Result<R> {
425 cx.read_entity(self, f)
426 }
427
428 /// Updates the entity referenced by this handle with the given function.
429 ///
430 /// The update function receives a context appropriate for its environment.
431 /// When updating in an `App`, it receives a `Context`.
432 /// When updating in a `Window`, it receives a `Window` and a `Context`.
433 pub fn update<C, R>(
434 &self,
435 cx: &mut C,
436 update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
437 ) -> C::Result<R>
438 where
439 C: AppContext,
440 {
441 cx.update_entity(self, update)
442 }
443
444 /// Updates the entity referenced by this handle with the given function if
445 /// the referenced entity still exists, within a visual context that has a window.
446 /// Returns an error if the entity has been released.
447 pub fn update_in<C, R>(
448 &self,
449 cx: &mut C,
450 update: impl FnOnce(&mut T, &mut Window, &mut Context<'_, T>) -> R,
451 ) -> C::Result<R>
452 where
453 C: VisualContext,
454 {
455 cx.update_window_entity(self, update)
456 }
457}
458
459impl<T> Clone for Entity<T> {
460 fn clone(&self) -> Self {
461 Self {
462 any_entity: self.any_entity.clone(),
463 entity_type: self.entity_type,
464 }
465 }
466}
467
468impl<T> std::fmt::Debug for Entity<T> {
469 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
470 f.debug_struct("Entity")
471 .field("entity_id", &self.any_entity.entity_id)
472 .field("entity_type", &type_name::<T>())
473 .finish()
474 }
475}
476
477impl<T> Hash for Entity<T> {
478 fn hash<H: Hasher>(&self, state: &mut H) {
479 self.any_entity.hash(state);
480 }
481}
482
483impl<T> PartialEq for Entity<T> {
484 fn eq(&self, other: &Self) -> bool {
485 self.any_entity == other.any_entity
486 }
487}
488
489impl<T> Eq for Entity<T> {}
490
491impl<T> PartialEq<WeakEntity<T>> for Entity<T> {
492 fn eq(&self, other: &WeakEntity<T>) -> bool {
493 self.any_entity.entity_id() == other.entity_id()
494 }
495}
496
497/// A type erased, weak reference to a entity.
498#[derive(Clone)]
499pub struct AnyWeakEntity {
500 pub(crate) entity_id: EntityId,
501 entity_type: TypeId,
502 entity_ref_counts: Weak<RwLock<EntityRefCounts>>,
503}
504
505impl AnyWeakEntity {
506 /// Get the entity ID associated with this weak reference.
507 pub fn entity_id(&self) -> EntityId {
508 self.entity_id
509 }
510
511 /// Check if this weak handle can be upgraded, or if the entity has already been dropped
512 pub fn is_upgradable(&self) -> bool {
513 let ref_count = self
514 .entity_ref_counts
515 .upgrade()
516 .and_then(|ref_counts| Some(ref_counts.read().counts.get(self.entity_id)?.load(SeqCst)))
517 .unwrap_or(0);
518 ref_count > 0
519 }
520
521 /// Upgrade this weak entity reference to a strong reference.
522 pub fn upgrade(&self) -> Option<AnyEntity> {
523 let ref_counts = &self.entity_ref_counts.upgrade()?;
524 let ref_counts = ref_counts.read();
525 let ref_count = ref_counts.counts.get(self.entity_id)?;
526
527 // entity_id is in dropped_entity_ids
528 if ref_count.load(SeqCst) == 0 {
529 return None;
530 }
531 ref_count.fetch_add(1, SeqCst);
532 drop(ref_counts);
533
534 Some(AnyEntity {
535 entity_id: self.entity_id,
536 entity_type: self.entity_type,
537 entity_map: self.entity_ref_counts.clone(),
538 #[cfg(any(test, feature = "leak-detection"))]
539 handle_id: self
540 .entity_ref_counts
541 .upgrade()
542 .unwrap()
543 .write()
544 .leak_detector
545 .handle_created(self.entity_id),
546 })
547 }
548
549 /// Assert that entity referenced by this weak handle has been released.
550 #[cfg(any(test, feature = "leak-detection"))]
551 pub fn assert_released(&self) {
552 self.entity_ref_counts
553 .upgrade()
554 .unwrap()
555 .write()
556 .leak_detector
557 .assert_released(self.entity_id);
558
559 if self
560 .entity_ref_counts
561 .upgrade()
562 .and_then(|ref_counts| Some(ref_counts.read().counts.get(self.entity_id)?.load(SeqCst)))
563 .is_some()
564 {
565 panic!(
566 "entity was recently dropped but resources are retained until the end of the effect cycle."
567 )
568 }
569 }
570}
571
572impl std::fmt::Debug for AnyWeakEntity {
573 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
574 f.debug_struct(type_name::<Self>())
575 .field("entity_id", &self.entity_id)
576 .field("entity_type", &self.entity_type)
577 .finish()
578 }
579}
580
581impl<T> From<WeakEntity<T>> for AnyWeakEntity {
582 fn from(entity: WeakEntity<T>) -> Self {
583 entity.any_entity
584 }
585}
586
587impl Hash for AnyWeakEntity {
588 fn hash<H: Hasher>(&self, state: &mut H) {
589 self.entity_id.hash(state);
590 }
591}
592
593impl PartialEq for AnyWeakEntity {
594 fn eq(&self, other: &Self) -> bool {
595 self.entity_id == other.entity_id
596 }
597}
598
599impl Eq for AnyWeakEntity {}
600
601/// A weak reference to a entity of the given type.
602#[derive(Deref, DerefMut)]
603pub struct WeakEntity<T> {
604 #[deref]
605 #[deref_mut]
606 any_entity: AnyWeakEntity,
607 entity_type: PhantomData<T>,
608}
609
610impl<T> std::fmt::Debug for WeakEntity<T> {
611 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
612 f.debug_struct(&type_name::<Self>())
613 .field("entity_id", &self.any_entity.entity_id)
614 .field("entity_type", &type_name::<T>())
615 .finish()
616 }
617}
618
619unsafe impl<T> Send for WeakEntity<T> {}
620unsafe impl<T> Sync for WeakEntity<T> {}
621
622impl<T> Clone for WeakEntity<T> {
623 fn clone(&self) -> Self {
624 Self {
625 any_entity: self.any_entity.clone(),
626 entity_type: self.entity_type,
627 }
628 }
629}
630
631impl<T: 'static> WeakEntity<T> {
632 /// Upgrade this weak entity reference into a strong entity reference
633 pub fn upgrade(&self) -> Option<Entity<T>> {
634 // Delegate to the trait implementation to keep behavior in one place.
635 Entity::upgrade_from(self)
636 }
637
638 /// Updates the entity referenced by this handle with the given function if
639 /// the referenced entity still exists. Returns an error if the entity has
640 /// been released.
641 pub fn update<C, R>(
642 &self,
643 cx: &mut C,
644 update: impl FnOnce(&mut T, &mut Context<'_, T>) -> R,
645 ) -> Result<R>
646 where
647 C: AppContext,
648 Result<C::Result<R>>: crate::Flatten<R>,
649 {
650 crate::Flatten::flatten(
651 self.upgrade()
652 .ok_or_else(|| anyhow!("entity released"))
653 .map(|this| cx.update_entity(&this, update)),
654 )
655 }
656
657 /// Updates the entity referenced by this handle with the given function if
658 /// the referenced entity still exists, within a visual context that has a window.
659 /// Returns an error if the entity has been released.
660 pub fn update_in<C, R>(
661 &self,
662 cx: &mut C,
663 update: impl FnOnce(&mut T, &mut Window, &mut Context<'_, T>) -> R,
664 ) -> Result<R>
665 where
666 C: VisualContext,
667 Result<C::Result<R>>: crate::Flatten<R>,
668 {
669 let window = cx.window_handle();
670 let this = self.upgrade().ok_or_else(|| anyhow!("entity released"))?;
671
672 crate::Flatten::flatten(window.update(cx, |_, window, cx| {
673 this.update(cx, |entity, cx| update(entity, window, cx))
674 }))
675 }
676
677 /// Reads the entity referenced by this handle with the given function if
678 /// the referenced entity still exists. Returns an error if the entity has
679 /// been released.
680 pub fn read_with<C, R>(&self, cx: &C, read: impl FnOnce(&T, &App) -> R) -> Result<R>
681 where
682 C: AppContext,
683 Result<C::Result<R>>: crate::Flatten<R>,
684 {
685 crate::Flatten::flatten(
686 self.upgrade()
687 .ok_or_else(|| anyhow!("entity release"))
688 .map(|this| cx.read_entity(&this, read)),
689 )
690 }
691}
692
693impl<T> Hash for WeakEntity<T> {
694 fn hash<H: Hasher>(&self, state: &mut H) {
695 self.any_entity.hash(state);
696 }
697}
698
699impl<T> PartialEq for WeakEntity<T> {
700 fn eq(&self, other: &Self) -> bool {
701 self.any_entity == other.any_entity
702 }
703}
704
705impl<T> Eq for WeakEntity<T> {}
706
707impl<T> PartialEq<Entity<T>> for WeakEntity<T> {
708 fn eq(&self, other: &Entity<T>) -> bool {
709 self.entity_id() == other.any_entity.entity_id()
710 }
711}
712
713#[cfg(any(test, feature = "leak-detection"))]
714static LEAK_BACKTRACE: std::sync::LazyLock<bool> =
715 std::sync::LazyLock::new(|| std::env::var("LEAK_BACKTRACE").map_or(false, |b| !b.is_empty()));
716
717#[cfg(any(test, feature = "leak-detection"))]
718#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
719pub(crate) struct HandleId {
720 id: u64, // id of the handle itself, not the pointed at object
721}
722
723#[cfg(any(test, feature = "leak-detection"))]
724pub(crate) struct LeakDetector {
725 next_handle_id: u64,
726 entity_handles: HashMap<EntityId, HashMap<HandleId, Option<backtrace::Backtrace>>>,
727}
728
729#[cfg(any(test, feature = "leak-detection"))]
730impl LeakDetector {
731 #[track_caller]
732 pub fn handle_created(&mut self, entity_id: EntityId) -> HandleId {
733 let id = util::post_inc(&mut self.next_handle_id);
734 let handle_id = HandleId { id };
735 let handles = self.entity_handles.entry(entity_id).or_default();
736 handles.insert(
737 handle_id,
738 LEAK_BACKTRACE.then(backtrace::Backtrace::new_unresolved),
739 );
740 handle_id
741 }
742
743 pub fn handle_released(&mut self, entity_id: EntityId, handle_id: HandleId) {
744 let handles = self.entity_handles.entry(entity_id).or_default();
745 handles.remove(&handle_id);
746 }
747
748 pub fn assert_released(&mut self, entity_id: EntityId) {
749 let handles = self.entity_handles.entry(entity_id).or_default();
750 if !handles.is_empty() {
751 for backtrace in handles.values_mut() {
752 if let Some(mut backtrace) = backtrace.take() {
753 backtrace.resolve();
754 eprintln!("Leaked handle: {:#?}", backtrace);
755 } else {
756 eprintln!("Leaked handle: export LEAK_BACKTRACE to find allocation site");
757 }
758 }
759 panic!();
760 }
761 }
762}
763
764#[cfg(test)]
765mod test {
766 use crate::EntityMap;
767
768 struct TestEntity {
769 pub i: i32,
770 }
771
772 #[test]
773 fn test_entity_map_slot_assignment_before_cleanup() {
774 // Tests that slots are not re-used before take_dropped.
775 let mut entity_map = EntityMap::new();
776
777 let slot = entity_map.reserve::<TestEntity>();
778 entity_map.insert(slot, TestEntity { i: 1 });
779
780 let slot = entity_map.reserve::<TestEntity>();
781 entity_map.insert(slot, TestEntity { i: 2 });
782
783 let dropped = entity_map.take_dropped();
784 assert_eq!(dropped.len(), 2);
785
786 assert_eq!(
787 dropped
788 .into_iter()
789 .map(|(_, entity)| entity.downcast::<TestEntity>().unwrap().i)
790 .collect::<Vec<i32>>(),
791 vec![1, 2],
792 );
793 }
794
795 #[test]
796 fn test_entity_map_weak_upgrade_before_cleanup() {
797 // Tests that weak handles are not upgraded before take_dropped
798 let mut entity_map = EntityMap::new();
799
800 let slot = entity_map.reserve::<TestEntity>();
801 let handle = entity_map.insert(slot, TestEntity { i: 1 });
802 let weak = handle.downgrade();
803 drop(handle);
804
805 let strong = weak.upgrade();
806 assert_eq!(strong, None);
807
808 let dropped = entity_map.take_dropped();
809 assert_eq!(dropped.len(), 1);
810
811 assert_eq!(
812 dropped
813 .into_iter()
814 .map(|(_, entity)| entity.downcast::<TestEntity>().unwrap().i)
815 .collect::<Vec<i32>>(),
816 vec![1],
817 );
818 }
819}