Skip to main content

gpui/app/
entity_map.rs

1use crate::{App, AppContext, GpuiBorrow, VisualContext, Window, seal::Sealed};
2use anyhow::{Context as _, Result};
3use collections::FxHashSet;
4use derive_more::{Deref, DerefMut};
5use parking_lot::{RwLock, RwLockUpgradableReadGuard};
6use slotmap::{KeyData, SecondaryMap, SlotMap};
7use std::{
8    any::{Any, TypeId, type_name},
9    cell::RefCell,
10    cmp::Ordering,
11    fmt::{self, Display},
12    hash::{Hash, Hasher},
13    marker::PhantomData,
14    mem,
15    num::NonZeroU64,
16    sync::{
17        Arc, Weak,
18        atomic::{AtomicU64, AtomicUsize, Ordering::SeqCst},
19    },
20    thread::panicking,
21};
22
23use super::Context;
24use crate::util::atomic_incr_if_not_zero;
25#[cfg(any(test, gpui_leak_detection))]
26use collections::HashMap;
27
28slotmap::new_key_type! {
29    /// A unique identifier for a entity across the application.
30    pub struct EntityId;
31}
32
33impl From<u64> for EntityId {
34    fn from(value: u64) -> Self {
35        Self(KeyData::from_ffi(value))
36    }
37}
38
39impl EntityId {
40    /// Converts this entity id to a [NonZeroU64]
41    pub fn as_non_zero_u64(self) -> NonZeroU64 {
42        NonZeroU64::new(self.0.as_ffi()).unwrap()
43    }
44
45    /// Converts this entity id to a [u64]
46    pub fn as_u64(self) -> u64 {
47        self.0.as_ffi()
48    }
49}
50
51impl Display for EntityId {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        write!(f, "{}", self.as_u64())
54    }
55}
56
57pub(crate) struct EntityMap {
58    entities: SecondaryMap<EntityId, Box<dyn Any>>,
59    accessed_entities: RefCell<AccessedEntities>,
60    ref_counts: Arc<RwLock<EntityRefCounts>>,
61}
62
63/// The entities accessed during the current draw. An access is recorded only
64/// in the innermost open scope, or in `frame` outside any scope, and a scope's
65/// accesses are added to the one around it when it ends. Each scope therefore
66/// collects exactly what was accessed inside it, without copying what the
67/// draw accessed before it began, and `frame` ends up with every access.
68struct AccessedEntities {
69    /// Every entity accessed during the draw: the window subscribes to
70    /// exactly this set when the draw ends. Accesses outside any scope land
71    /// here directly, and a scope's accesses reach it when the outermost open
72    /// scope ends. It is the innermost set only while no scope is open, and
73    /// unlike a scope it is never ended.
74    frame: FxHashSet<EntityId>,
75    /// The scopes currently recording, innermost last.
76    scopes: Vec<FxHashSet<EntityId>>,
77}
78
79impl AccessedEntities {
80    fn innermost(&mut self) -> &mut FxHashSet<EntityId> {
81        self.scopes.last_mut().unwrap_or(&mut self.frame)
82    }
83}
84
85#[doc(hidden)]
86pub(crate) struct EntityRefCounts {
87    counts: SlotMap<EntityId, AtomicUsize>,
88    dropped_entity_ids: Vec<EntityId>,
89    #[cfg(any(test, gpui_leak_detection))]
90    leak_detector: LeakDetector,
91}
92
93pub(super) struct LeaseInner {
94    pub(super) entity: Option<Box<dyn Any>>,
95}
96
97impl EntityMap {
98    pub fn new() -> Self {
99        Self {
100            entities: SecondaryMap::new(),
101            accessed_entities: RefCell::new(AccessedEntities {
102                frame: FxHashSet::default(),
103                scopes: Vec::new(),
104            }),
105            ref_counts: Arc::new(RwLock::new(EntityRefCounts {
106                counts: SlotMap::with_key(),
107                dropped_entity_ids: Vec::new(),
108                #[cfg(any(test, gpui_leak_detection))]
109                leak_detector: LeakDetector {
110                    next_handle_id: 0,
111                    entity_handles: HashMap::default(),
112                },
113            })),
114        }
115    }
116
117    #[doc(hidden)]
118    pub fn ref_counts_drop_handle(&self) -> Arc<RwLock<EntityRefCounts>> {
119        self.ref_counts.clone()
120    }
121
122    /// Captures a snapshot of all entities that currently have alive handles.
123    ///
124    /// The returned [`LeakDetectorSnapshot`] can later be passed to
125    /// [`assert_no_new_leaks`](Self::assert_no_new_leaks) to verify that no
126    /// entities created after the snapshot are still alive.
127    #[cfg(any(test, gpui_leak_detection))]
128    pub fn leak_detector_snapshot(&self) -> LeakDetectorSnapshot {
129        self.ref_counts.read().leak_detector.snapshot()
130    }
131
132    /// Asserts that no entities created after `snapshot` still have alive handles.
133    ///
134    /// See [`LeakDetector::assert_no_new_leaks`] for details.
135    #[cfg(any(test, gpui_leak_detection))]
136    pub fn assert_no_new_leaks(&self, snapshot: &LeakDetectorSnapshot) {
137        self.ref_counts
138            .read()
139            .leak_detector
140            .assert_no_new_leaks(snapshot)
141    }
142
143    /// Reserve a slot for an entity, which you can subsequently use with `insert`.
144    pub fn reserve<T: 'static>(&self) -> Slot<T> {
145        let id = self.ref_counts.write().counts.insert(1.into());
146        Slot(Entity::new(id, Arc::downgrade(&self.ref_counts)))
147    }
148
149    /// Insert an entity into a slot obtained by calling `reserve`.
150    pub fn insert<T>(&mut self, slot: Slot<T>, entity: T) -> Entity<T>
151    where
152        T: 'static,
153    {
154        self.accessed_entities
155            .get_mut()
156            .innermost()
157            .insert(slot.entity_id);
158
159        let handle = slot.0;
160        self.entities.insert(handle.entity_id, Box::new(entity));
161        handle
162    }
163
164    /// Move an entity to the stack.
165    #[track_caller]
166    pub fn lease<T>(&mut self, pointer: &Entity<T>) -> Lease<T> {
167        Lease {
168            inner: self.lease_erased(pointer, type_name::<T>()),
169            id: pointer.entity_id,
170            entity_type: PhantomData,
171        }
172    }
173
174    /// Returns an entity after moving it to the stack.
175    pub fn end_lease<T>(&mut self, lease: Lease<T>) {
176        self.end_lease_erased(lease.id, lease.inner);
177    }
178
179    #[inline(always)]
180    pub fn read<T: 'static>(&self, entity: &Entity<T>) -> &T {
181        self.assert_valid_context(entity);
182        self.read_inner(entity.entity_id)
183            .and_then(|entity| entity.downcast_ref())
184            .unwrap_or_else(|| double_lease_panic("read", type_name::<T>()))
185    }
186
187    #[track_caller]
188    pub(super) fn lease_erased(&mut self, pointer: &AnyEntity, entity_type: &str) -> LeaseInner {
189        self.assert_valid_context(pointer);
190        let entity = Some(
191            self.lease_inner(pointer.entity_id)
192                .unwrap_or_else(|| double_lease_panic("update", entity_type)),
193        );
194        LeaseInner { entity }
195    }
196
197    pub(super) fn end_lease_erased(&mut self, entity_id: EntityId, mut lease: LeaseInner) {
198        self.end_lease_inner(entity_id, lease.entity.take().unwrap());
199    }
200
201    fn assert_valid_context(&self, entity: &AnyEntity) {
202        debug_assert!(
203            Weak::ptr_eq(&entity.entity_map, &Arc::downgrade(&self.ref_counts)),
204            "used a entity with the wrong context"
205        );
206    }
207
208    /// Records entities accessed by a reused draw. Entities released since it
209    /// recorded them are skipped, because the window would otherwise subscribe
210    /// to them again after `App` has cleaned up their subscriptions, and
211    /// nothing would clean those up.
212    pub fn extend_accessed(&mut self, entities: impl IntoIterator<Item = EntityId>) {
213        let ref_counts = self.ref_counts.read();
214        self.accessed_entities.get_mut().innermost().extend(
215            entities
216                .into_iter()
217                .filter(|entity_id| ref_counts.counts.contains_key(*entity_id)),
218        );
219    }
220
221    /// Clears the accessed entities and ends any scope left open.
222    pub fn clear_accessed(&mut self) {
223        let accessed_entities = self.accessed_entities.get_mut();
224        accessed_entities.scopes.clear();
225        accessed_entities.frame.clear();
226    }
227
228    pub fn push_access_scope(&mut self) {
229        self.accessed_entities
230            .get_mut()
231            .scopes
232            .push(FxHashSet::default());
233    }
234
235    /// Ends the innermost scope, adds its accessed entities to the scope
236    /// around it, and returns them.
237    pub fn pop_access_scope(&mut self) -> FxHashSet<EntityId> {
238        let accessed_entities = self.accessed_entities.get_mut();
239        let Some(scope) = accessed_entities.scopes.pop() else {
240            debug_assert!(false, "popped an access scope that wasn't pushed");
241            return FxHashSet::default();
242        };
243        accessed_entities.innermost().extend(scope.iter().copied());
244        scope
245    }
246
247    /// Takes every entity accessed during the draw, to be handed back with
248    /// [`Self::restore_accessed`].
249    pub fn take_accessed(&mut self) -> FxHashSet<EntityId> {
250        let accessed_entities = self.accessed_entities.get_mut();
251        debug_assert!(
252            accessed_entities.scopes.is_empty(),
253            "access scope left open"
254        );
255        mem::take(&mut accessed_entities.frame)
256    }
257
258    pub fn restore_accessed(&mut self, entities: FxHashSet<EntityId>) {
259        self.accessed_entities.get_mut().frame = entities;
260    }
261
262    pub fn take_dropped(&mut self) -> Vec<(EntityId, Box<dyn Any>)> {
263        let ref_counts = &mut *self.ref_counts.write();
264        let dropped_entity_ids = ref_counts.dropped_entity_ids.drain(..);
265        let accessed_entities = self.accessed_entities.get_mut();
266
267        dropped_entity_ids
268            .filter_map(|entity_id| {
269                let count = ref_counts.counts.remove(entity_id).unwrap();
270                debug_assert_eq!(
271                    count.load(SeqCst),
272                    0,
273                    "dropped an entity that was referenced"
274                );
275                accessed_entities.frame.remove(&entity_id);
276                for scope in &mut accessed_entities.scopes {
277                    scope.remove(&entity_id);
278                }
279                // If the EntityId was allocated with `Context::reserve`,
280                // the entity may not have been inserted.
281                Some((entity_id, self.entities.remove(entity_id)?))
282            })
283            .collect()
284    }
285
286    #[inline(never)]
287    fn read_inner(&self, entity_id: EntityId) -> Option<&dyn Any> {
288        self.accessed_entities
289            .borrow_mut()
290            .innermost()
291            .insert(entity_id);
292        self.entities.get(entity_id).map(Box::as_ref)
293    }
294
295    #[inline(never)]
296    fn lease_inner(&mut self, entity_id: EntityId) -> Option<Box<dyn Any>> {
297        self.accessed_entities
298            .get_mut()
299            .innermost()
300            .insert(entity_id);
301        self.entities.remove(entity_id)
302    }
303
304    #[inline(never)]
305    fn end_lease_inner(&mut self, entity_id: EntityId, entity: Box<dyn Any>) {
306        self.entities.insert(entity_id, entity);
307    }
308}
309
310#[track_caller]
311fn double_lease_panic(operation: &str, entity_type: &str) -> ! {
312    panic!("cannot {operation} {entity_type} while it is already being updated")
313}
314
315pub(crate) struct Lease<T> {
316    pub id: EntityId,
317    inner: LeaseInner,
318    entity_type: PhantomData<T>,
319}
320
321impl<T: 'static> core::ops::Deref for Lease<T> {
322    type Target = T;
323
324    fn deref(&self) -> &Self::Target {
325        self.inner.entity.as_ref().unwrap().downcast_ref().unwrap()
326    }
327}
328
329impl<T: 'static> core::ops::DerefMut for Lease<T> {
330    fn deref_mut(&mut self) -> &mut Self::Target {
331        self.inner.entity.as_mut().unwrap().downcast_mut().unwrap()
332    }
333}
334
335impl Drop for LeaseInner {
336    fn drop(&mut self) {
337        if self.entity.is_some() && !panicking() {
338            panic!("Leases must be ended with EntityMap::end_lease")
339        }
340    }
341}
342
343#[derive(Deref, DerefMut)]
344pub(crate) struct Slot<T>(Entity<T>);
345
346/// A dynamically typed reference to a entity, which can be downcast into a `Entity<T>`.
347pub struct AnyEntity {
348    pub(crate) entity_id: EntityId,
349    pub(crate) entity_type: TypeId,
350    entity_map: Weak<RwLock<EntityRefCounts>>,
351    #[cfg(any(test, gpui_leak_detection))]
352    handle_id: HandleId,
353}
354
355impl AnyEntity {
356    fn new(
357        id: EntityId,
358        entity_type: TypeId,
359        entity_map: Weak<RwLock<EntityRefCounts>>,
360        #[cfg(any(test, gpui_leak_detection))] type_name: &'static str,
361    ) -> Self {
362        Self {
363            entity_id: id,
364            entity_type,
365            #[cfg(any(test, gpui_leak_detection))]
366            handle_id: entity_map
367                .clone()
368                .upgrade()
369                .unwrap()
370                .write()
371                .leak_detector
372                .handle_created(id, Some(type_name)),
373            entity_map,
374        }
375    }
376
377    /// Returns the id associated with this entity.
378    #[inline]
379    pub fn entity_id(&self) -> EntityId {
380        self.entity_id
381    }
382
383    /// Returns the [TypeId] associated with this entity.
384    #[inline]
385    pub fn entity_type(&self) -> TypeId {
386        self.entity_type
387    }
388
389    /// Converts this entity handle into a weak variant, which does not prevent it from being released.
390    pub fn downgrade(&self) -> AnyWeakEntity {
391        AnyWeakEntity {
392            entity_id: self.entity_id,
393            entity_type: self.entity_type,
394            entity_ref_counts: self.entity_map.clone(),
395        }
396    }
397
398    /// Converts this entity handle into a strongly-typed entity handle of the given type.
399    /// If this entity handle is not of the specified type, returns itself as an error variant.
400    pub fn downcast<T: 'static>(self) -> Result<Entity<T>, AnyEntity> {
401        if TypeId::of::<T>() == self.entity_type {
402            Ok(Entity {
403                any_entity: self,
404                entity_type: PhantomData,
405            })
406        } else {
407            Err(self)
408        }
409    }
410}
411
412impl Clone for AnyEntity {
413    fn clone(&self) -> Self {
414        if let Some(entity_map) = self.entity_map.upgrade() {
415            let entity_map = entity_map.read();
416            let count = entity_map
417                .counts
418                .get(self.entity_id)
419                .expect("detected over-release of a entity");
420            let prev_count = count.fetch_add(1, SeqCst);
421            assert_ne!(prev_count, 0, "Detected over-release of a entity.");
422        }
423
424        Self {
425            entity_id: self.entity_id,
426            entity_type: self.entity_type,
427            entity_map: self.entity_map.clone(),
428            #[cfg(any(test, gpui_leak_detection))]
429            handle_id: self
430                .entity_map
431                .upgrade()
432                .unwrap()
433                .write()
434                .leak_detector
435                .handle_created(self.entity_id, None),
436        }
437    }
438}
439
440impl Drop for AnyEntity {
441    fn drop(&mut self) {
442        if let Some(entity_map) = self.entity_map.upgrade() {
443            let entity_map = entity_map.upgradable_read();
444            let count = entity_map
445                .counts
446                .get(self.entity_id)
447                .expect("detected over-release of a handle.");
448            let prev_count = count.fetch_sub(1, SeqCst);
449            assert_ne!(prev_count, 0, "Detected over-release of a entity.");
450            if prev_count == 1 {
451                // We were the last reference to this entity, so we can remove it.
452                let mut entity_map = RwLockUpgradableReadGuard::upgrade(entity_map);
453                entity_map.dropped_entity_ids.push(self.entity_id);
454            }
455        }
456
457        #[cfg(any(test, gpui_leak_detection))]
458        if let Some(entity_map) = self.entity_map.upgrade() {
459            entity_map
460                .write()
461                .leak_detector
462                .handle_released(self.entity_id, self.handle_id)
463        }
464    }
465}
466
467impl<T> From<Entity<T>> for AnyEntity {
468    #[inline]
469    fn from(entity: Entity<T>) -> Self {
470        entity.any_entity
471    }
472}
473
474impl Hash for AnyEntity {
475    #[inline]
476    fn hash<H: Hasher>(&self, state: &mut H) {
477        self.entity_id.hash(state);
478    }
479}
480
481impl PartialEq for AnyEntity {
482    #[inline]
483    fn eq(&self, other: &Self) -> bool {
484        self.entity_id == other.entity_id
485    }
486}
487
488impl Eq for AnyEntity {}
489
490impl Ord for AnyEntity {
491    #[inline]
492    fn cmp(&self, other: &Self) -> Ordering {
493        self.entity_id.cmp(&other.entity_id)
494    }
495}
496
497impl PartialOrd for AnyEntity {
498    #[inline]
499    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
500        Some(self.cmp(other))
501    }
502}
503
504impl std::fmt::Debug for AnyEntity {
505    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
506        f.debug_struct("AnyEntity")
507            .field("entity_id", &self.entity_id.as_u64())
508            .finish()
509    }
510}
511
512/// A strong, well-typed reference to a struct which is managed
513/// by GPUI
514#[derive(Deref, DerefMut)]
515pub struct Entity<T> {
516    #[deref]
517    #[deref_mut]
518    pub(crate) any_entity: AnyEntity,
519    pub(crate) entity_type: PhantomData<fn(T) -> T>,
520}
521
522impl<T> Sealed for Entity<T> {}
523
524impl<T: 'static> Entity<T> {
525    #[inline]
526    fn new(id: EntityId, entity_map: Weak<RwLock<EntityRefCounts>>) -> Self
527    where
528        T: 'static,
529    {
530        Self {
531            any_entity: AnyEntity::new(
532                id,
533                TypeId::of::<T>(),
534                entity_map,
535                #[cfg(any(test, gpui_leak_detection))]
536                std::any::type_name::<T>(),
537            ),
538            entity_type: PhantomData,
539        }
540    }
541
542    /// Get the entity ID associated with this entity
543    #[inline]
544    pub fn entity_id(&self) -> EntityId {
545        self.any_entity.entity_id
546    }
547
548    /// Downgrade this entity pointer to a non-retaining weak pointer
549    #[inline]
550    pub fn downgrade(&self) -> WeakEntity<T> {
551        WeakEntity {
552            any_entity: self.any_entity.downgrade(),
553            entity_type: self.entity_type,
554        }
555    }
556
557    /// Convert this into a dynamically typed entity.
558    #[inline]
559    pub fn into_any(self) -> AnyEntity {
560        self.any_entity
561    }
562
563    /// Grab a reference to this entity from the context.
564    #[inline]
565    pub fn read<'a>(&self, cx: &'a App) -> &'a T {
566        cx.entities.read(self)
567    }
568
569    /// Read the entity referenced by this handle with the given function.
570    #[inline]
571    pub fn read_with<R, C: AppContext>(&self, cx: &C, f: impl FnOnce(&T, &App) -> R) -> R {
572        cx.read_entity(self, f)
573    }
574
575    /// Updates the entity referenced by this handle with the given function.
576    #[inline]
577    pub fn update<R, C: AppContext>(
578        &self,
579        cx: &mut C,
580        update: impl FnOnce(&mut T, &mut Context<T>) -> R,
581    ) -> R {
582        cx.update_entity(self, update)
583    }
584
585    /// Updates the entity referenced by this handle with the given function.
586    #[inline]
587    pub fn as_mut<'a, C: AppContext>(&self, cx: &'a mut C) -> GpuiBorrow<'a, T> {
588        cx.as_mut(self)
589    }
590
591    /// Updates the entity referenced by this handle with the given function.
592    pub fn write<C: AppContext>(&self, cx: &mut C, value: T) {
593        self.update(cx, |entity, cx| {
594            *entity = value;
595            cx.notify();
596        })
597    }
598
599    /// Updates the entity referenced by this handle with the given function if
600    /// the referenced entity still exists, within a visual context that has a window.
601    /// Returns an error if the window has been closed.
602    #[inline]
603    pub fn update_in<R, C: VisualContext>(
604        &self,
605        cx: &mut C,
606        update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
607    ) -> C::Result<R> {
608        cx.update_window_entity(self, update)
609    }
610}
611
612impl<T> Clone for Entity<T> {
613    #[inline]
614    fn clone(&self) -> Self {
615        Self {
616            any_entity: self.any_entity.clone(),
617            entity_type: self.entity_type,
618        }
619    }
620}
621
622impl<T> std::fmt::Debug for Entity<T> {
623    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
624        f.debug_struct("Entity")
625            .field("entity_id", &self.any_entity.entity_id)
626            .field("entity_type", &type_name::<T>())
627            .finish()
628    }
629}
630
631impl<T> Hash for Entity<T> {
632    #[inline]
633    fn hash<H: Hasher>(&self, state: &mut H) {
634        self.any_entity.hash(state);
635    }
636}
637
638impl<T> PartialEq for Entity<T> {
639    #[inline]
640    fn eq(&self, other: &Self) -> bool {
641        self.any_entity == other.any_entity
642    }
643}
644
645impl<T> Eq for Entity<T> {}
646
647impl<T> PartialEq<WeakEntity<T>> for Entity<T> {
648    #[inline]
649    fn eq(&self, other: &WeakEntity<T>) -> bool {
650        self.any_entity.entity_id() == other.entity_id()
651    }
652}
653
654impl<T: 'static> Ord for Entity<T> {
655    #[inline]
656    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
657        self.entity_id().cmp(&other.entity_id())
658    }
659}
660
661impl<T: 'static> PartialOrd for Entity<T> {
662    #[inline]
663    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
664        Some(self.cmp(other))
665    }
666}
667
668/// A type erased, weak reference to a entity.
669#[derive(Clone)]
670pub struct AnyWeakEntity {
671    pub(crate) entity_id: EntityId,
672    entity_type: TypeId,
673    entity_ref_counts: Weak<RwLock<EntityRefCounts>>,
674}
675
676impl AnyWeakEntity {
677    /// Get the entity ID associated with this weak reference.
678    #[inline]
679    pub fn entity_id(&self) -> EntityId {
680        self.entity_id
681    }
682
683    /// Check if this weak handle can be upgraded, or if the entity has already been dropped
684    pub fn is_upgradable(&self) -> bool {
685        let ref_count = self
686            .entity_ref_counts
687            .upgrade()
688            .and_then(|ref_counts| Some(ref_counts.read().counts.get(self.entity_id)?.load(SeqCst)))
689            .unwrap_or(0);
690        ref_count > 0
691    }
692
693    /// Upgrade this weak entity reference to a strong reference.
694    pub fn upgrade(&self) -> Option<AnyEntity> {
695        let ref_counts = &self.entity_ref_counts.upgrade()?;
696        let ref_counts = ref_counts.read();
697        let ref_count = ref_counts.counts.get(self.entity_id)?;
698
699        if atomic_incr_if_not_zero(ref_count) == 0 {
700            // entity_id is in dropped_entity_ids
701            return None;
702        }
703        drop(ref_counts);
704
705        Some(AnyEntity {
706            entity_id: self.entity_id,
707            entity_type: self.entity_type,
708            entity_map: self.entity_ref_counts.clone(),
709            #[cfg(any(test, gpui_leak_detection))]
710            handle_id: self
711                .entity_ref_counts
712                .upgrade()
713                .unwrap()
714                .write()
715                .leak_detector
716                .handle_created(self.entity_id, None),
717        })
718    }
719
720    /// Asserts that the entity referenced by this weak handle has been fully released.
721    ///
722    /// # Example
723    ///
724    /// ```ignore
725    /// let entity = cx.new(|_| MyEntity::new());
726    /// let weak = entity.downgrade();
727    /// drop(entity);
728    ///
729    /// // Verify the entity was released
730    /// weak.assert_released();
731    /// ```
732    ///
733    /// # Debugging Leaks
734    ///
735    /// With leak detection enabled (build with `GPUI_LEAK_DETECTION=1`), a failure
736    /// lists each leaked handle. Also set the `LEAK_BACKTRACE` environment variable
737    /// to see where they were allocated:
738    ///
739    /// ```bash
740    /// GPUI_LEAK_DETECTION=1 LEAK_BACKTRACE=1 cargo test my_test
741    /// ```
742    ///
743    /// # Panics
744    ///
745    /// - Panics if any strong handles to the entity are still alive.
746    /// - Panics if the entity was recently dropped but cleanup hasn't completed yet
747    ///   (resources are retained until the end of the effect cycle).
748    #[cfg(any(test, feature = "test-support", gpui_leak_detection))]
749    pub fn assert_released(&self) {
750        #[cfg(any(test, gpui_leak_detection))]
751        self.entity_ref_counts
752            .upgrade()
753            .unwrap()
754            .write()
755            .leak_detector
756            .assert_released(self.entity_id);
757
758        if self
759            .entity_ref_counts
760            .upgrade()
761            .and_then(|ref_counts| Some(ref_counts.read().counts.get(self.entity_id)?.load(SeqCst)))
762            .is_some()
763        {
764            panic!(
765                "entity is still alive, or was recently dropped and its resources are retained \
766                 until the end of the effect cycle. Build with GPUI_LEAK_DETECTION=1 to list \
767                 its live handles."
768            )
769        }
770    }
771
772    /// Creates a weak entity that can never be upgraded.
773    pub fn new_invalid() -> Self {
774        /// To hold the invariant that all ids are unique, and considering that slotmap
775        /// increases their IDs from `0`, we can decrease ours from `u64::MAX` so these
776        /// two will never conflict (u64 is way too large).
777        static UNIQUE_NON_CONFLICTING_ID_GENERATOR: AtomicU64 = AtomicU64::new(u64::MAX);
778        let entity_id = UNIQUE_NON_CONFLICTING_ID_GENERATOR.fetch_sub(1, SeqCst);
779
780        Self {
781            // Safety:
782            //   Docs say this is safe but can be unspecified if slotmap changes the representation
783            //   after `1.0.7`, that said, providing a valid entity_id here is not necessary as long
784            //   as we guarantee that `entity_id` is never used if `entity_ref_counts` equals
785            //   to `Weak::new()` (that is, it's unable to upgrade), that is the invariant that
786            //   actually needs to be hold true.
787            //
788            //   And there is no sane reason to read an entity slot if `entity_ref_counts` can't be
789            //   read in the first place, so we're good!
790            entity_id: entity_id.into(),
791            entity_type: TypeId::of::<()>(),
792            entity_ref_counts: Weak::new(),
793        }
794    }
795}
796
797impl std::fmt::Debug for AnyWeakEntity {
798    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
799        f.debug_struct(type_name::<Self>())
800            .field("entity_id", &self.entity_id)
801            .field("entity_type", &self.entity_type)
802            .finish()
803    }
804}
805
806impl<T> From<WeakEntity<T>> for AnyWeakEntity {
807    #[inline]
808    fn from(entity: WeakEntity<T>) -> Self {
809        entity.any_entity
810    }
811}
812
813impl Hash for AnyWeakEntity {
814    #[inline]
815    fn hash<H: Hasher>(&self, state: &mut H) {
816        self.entity_id.hash(state);
817    }
818}
819
820impl PartialEq for AnyWeakEntity {
821    #[inline]
822    fn eq(&self, other: &Self) -> bool {
823        self.entity_id == other.entity_id
824    }
825}
826
827impl Eq for AnyWeakEntity {}
828
829impl Ord for AnyWeakEntity {
830    #[inline]
831    fn cmp(&self, other: &Self) -> Ordering {
832        self.entity_id.cmp(&other.entity_id)
833    }
834}
835
836impl PartialOrd for AnyWeakEntity {
837    #[inline]
838    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
839        Some(self.cmp(other))
840    }
841}
842
843/// A weak reference to a entity of the given type.
844#[derive(Deref, DerefMut)]
845pub struct WeakEntity<T> {
846    #[deref]
847    #[deref_mut]
848    any_entity: AnyWeakEntity,
849    entity_type: PhantomData<fn(T) -> T>,
850}
851
852impl<T> std::fmt::Debug for WeakEntity<T> {
853    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
854        f.debug_struct(type_name::<Self>())
855            .field("entity_id", &self.any_entity.entity_id)
856            .field("entity_type", &type_name::<T>())
857            .finish()
858    }
859}
860
861impl<T> Clone for WeakEntity<T> {
862    fn clone(&self) -> Self {
863        Self {
864            any_entity: self.any_entity.clone(),
865            entity_type: self.entity_type,
866        }
867    }
868}
869
870impl<T: 'static> WeakEntity<T> {
871    /// Upgrade this weak entity reference into a strong entity reference
872    pub fn upgrade(&self) -> Option<Entity<T>> {
873        Some(Entity {
874            any_entity: self.any_entity.upgrade()?,
875            entity_type: self.entity_type,
876        })
877    }
878
879    /// Updates the entity referenced by this handle with the given function if
880    /// the referenced entity still exists. Returns an error if the entity has
881    /// been released.
882    #[inline(always)]
883    pub fn update<C, R>(
884        &self,
885        cx: &mut C,
886        update: impl FnOnce(&mut T, &mut Context<T>) -> R,
887    ) -> Result<R>
888    where
889        C: AppContext,
890    {
891        let entity = self.upgrade().context("entity released")?;
892        Ok(cx.update_entity(&entity, update))
893    }
894
895    /// Updates the entity referenced by this handle with the given function if
896    /// the referenced entity still exists, within a visual context that has a window.
897    /// Returns an error if the entity has been released.
898    #[inline(always)]
899    pub fn update_in<C, R>(
900        &self,
901        cx: &mut C,
902        update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
903    ) -> Result<R>
904    where
905        C: AppContext,
906    {
907        let entity = self.upgrade().context("entity released")?;
908        cx.with_window(entity.entity_id(), |window, app| {
909            entity.update(app, |entity, cx| update(entity, window, cx))
910        })
911        .context("entity has no current window")
912    }
913
914    /// Reads the entity referenced by this handle with the given function if
915    /// the referenced entity still exists. Returns an error if the entity has
916    /// been released.
917    #[inline(always)]
918    pub fn read_with<C, R>(&self, cx: &C, read: impl FnOnce(&T, &App) -> R) -> Result<R>
919    where
920        C: AppContext,
921    {
922        let entity = self.upgrade().context("entity released")?;
923        Ok(cx.read_entity(&entity, read))
924    }
925
926    /// Create a new weak entity that can never be upgraded.
927    #[inline]
928    pub fn new_invalid() -> Self {
929        Self {
930            any_entity: AnyWeakEntity::new_invalid(),
931            entity_type: PhantomData,
932        }
933    }
934}
935
936impl<T> Hash for WeakEntity<T> {
937    #[inline]
938    fn hash<H: Hasher>(&self, state: &mut H) {
939        self.any_entity.hash(state);
940    }
941}
942
943impl<T> PartialEq for WeakEntity<T> {
944    #[inline]
945    fn eq(&self, other: &Self) -> bool {
946        self.any_entity == other.any_entity
947    }
948}
949
950impl<T> Eq for WeakEntity<T> {}
951
952impl<T> PartialEq<Entity<T>> for WeakEntity<T> {
953    #[inline]
954    fn eq(&self, other: &Entity<T>) -> bool {
955        self.entity_id() == other.any_entity.entity_id()
956    }
957}
958
959impl<T: 'static> Ord for WeakEntity<T> {
960    #[inline]
961    fn cmp(&self, other: &Self) -> Ordering {
962        self.entity_id().cmp(&other.entity_id())
963    }
964}
965
966impl<T: 'static> PartialOrd for WeakEntity<T> {
967    #[inline]
968    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
969        Some(self.cmp(other))
970    }
971}
972
973/// Controls whether backtraces are captured when entity handles are created.
974///
975/// Set the `LEAK_BACKTRACE` environment variable to any non-empty value to enable
976/// backtrace capture. This helps identify where leaked handles were allocated.
977#[cfg(any(test, gpui_leak_detection))]
978static LEAK_BACKTRACE: std::sync::LazyLock<bool> =
979    std::sync::LazyLock::new(|| std::env::var("LEAK_BACKTRACE").is_ok_and(|b| !b.is_empty()));
980
981/// Unique identifier for a specific entity handle instance.
982///
983/// This is distinct from `EntityId` - while multiple handles can point to the same
984/// entity (same `EntityId`), each handle has its own unique `HandleId`.
985#[cfg(any(test, gpui_leak_detection))]
986#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
987pub(crate) struct HandleId {
988    id: u64,
989}
990
991/// Tracks entity handle allocations to detect leaks.
992///
993/// The leak detector is enabled in tests and when the `leak-detection` feature is active.
994/// It tracks every `Entity<T>` and `AnyEntity` handle that is created and released,
995/// allowing you to verify that all handles to an entity have been properly dropped.
996///
997/// # How do leaks happen?
998///
999/// Entities are reference-counted structures that can own other entities
1000/// allowing to form cycles. If such a strong-reference counted cycle is
1001/// created, all participating strong entities in this cycle will effectively
1002/// leak as they cannot be released anymore.
1003///
1004/// Cycles can also happen if an entity owns a task or subscription that it
1005/// itself owns a strong reference to the entity again.
1006///
1007/// # Usage
1008///
1009/// You can use `WeakEntity::assert_released` or `AnyWeakEntity::assert_released`
1010/// to verify that an entity has been fully released:
1011///
1012/// ```ignore
1013/// let entity = cx.new(|_| MyEntity::new());
1014/// let weak = entity.downgrade();
1015/// drop(entity);
1016///
1017/// // This will panic if any handles to the entity are still alive
1018/// weak.assert_released();
1019/// ```
1020///
1021/// # Debugging Leaks
1022///
1023/// When a leak is detected, the detector will panic with information about the leaked
1024/// handles. To see where the leaked handles were allocated, set the `LEAK_BACKTRACE`
1025/// environment variable:
1026///
1027/// ```bash
1028/// LEAK_BACKTRACE=1 cargo test my_test
1029/// ```
1030///
1031/// This will capture and display backtraces for each leaked handle, helping you
1032/// identify where leaked handles were created.
1033///
1034/// # How It Works
1035///
1036/// - When an entity handle is created (via `Entity::new`, `Entity::clone`, or
1037///   `WeakEntity::upgrade`), `handle_created` is called to register the handle.
1038/// - When a handle is dropped, `handle_released` removes it from tracking.
1039/// - `assert_released` verifies that no handles remain for a given entity.
1040#[cfg(any(test, gpui_leak_detection))]
1041pub(crate) struct LeakDetector {
1042    next_handle_id: u64,
1043    entity_handles: HashMap<EntityId, EntityLeakData>,
1044}
1045
1046/// A snapshot of the set of alive entities at a point in time.
1047///
1048/// Created by [`LeakDetector::snapshot`]. Can later be passed to
1049/// [`LeakDetector::assert_no_new_leaks`] to verify that no new entity
1050/// handles remain between the snapshot and the current state.
1051#[cfg(any(test, feature = "test-support", gpui_leak_detection))]
1052#[derive(Default)]
1053pub struct LeakDetectorSnapshot {
1054    #[cfg(any(test, gpui_leak_detection))]
1055    entity_ids: collections::HashSet<EntityId>,
1056}
1057
1058#[cfg(any(test, gpui_leak_detection))]
1059struct EntityLeakData {
1060    handles: HashMap<HandleId, Option<backtrace::Backtrace>>,
1061    type_name: &'static str,
1062}
1063
1064#[cfg(any(test, gpui_leak_detection))]
1065impl LeakDetector {
1066    /// Records that a new handle has been created for the given entity.
1067    ///
1068    /// Returns a unique `HandleId` that must be passed to `handle_released` when
1069    /// the handle is dropped. If `LEAK_BACKTRACE` is set, captures a backtrace
1070    /// at the allocation site.
1071    #[track_caller]
1072    pub fn handle_created(
1073        &mut self,
1074        entity_id: EntityId,
1075        type_name: Option<&'static str>,
1076    ) -> HandleId {
1077        let id = gpui_util::post_inc(&mut self.next_handle_id);
1078        let handle_id = HandleId { id };
1079        let handles = self
1080            .entity_handles
1081            .entry(entity_id)
1082            .or_insert_with(|| EntityLeakData {
1083                handles: HashMap::default(),
1084                type_name: type_name.unwrap_or("<unknown>"),
1085            });
1086        handles.handles.insert(
1087            handle_id,
1088            LEAK_BACKTRACE.then(backtrace::Backtrace::new_unresolved),
1089        );
1090        handle_id
1091    }
1092
1093    /// Records that a handle has been released (dropped).
1094    ///
1095    /// This removes the handle from tracking. The `handle_id` should be the same
1096    /// one returned by `handle_created` when the handle was allocated.
1097    pub fn handle_released(&mut self, entity_id: EntityId, handle_id: HandleId) {
1098        if let std::collections::hash_map::Entry::Occupied(mut data) =
1099            self.entity_handles.entry(entity_id)
1100        {
1101            data.get_mut().handles.remove(&handle_id);
1102            if data.get().handles.is_empty() {
1103                data.remove();
1104            }
1105        }
1106    }
1107
1108    /// Asserts that all handles to the given entity have been released.
1109    ///
1110    /// # Panics
1111    ///
1112    /// Panics if any handles to the entity are still alive. The panic message
1113    /// includes backtraces for each leaked handle if `LEAK_BACKTRACE` is set,
1114    /// otherwise it suggests setting the environment variable to get more info.
1115    pub fn assert_released(&mut self, entity_id: EntityId) {
1116        use std::fmt::Write as _;
1117
1118        if let Some(data) = self.entity_handles.remove(&entity_id) {
1119            let mut out = String::new();
1120            for (_, backtrace) in data.handles {
1121                if let Some(mut backtrace) = backtrace {
1122                    backtrace.resolve();
1123                    let backtrace = BacktraceFormatter(backtrace);
1124                    writeln!(out, "Leaked handle:\n{:?}", backtrace).unwrap();
1125                } else {
1126                    writeln!(
1127                        out,
1128                        "Leaked handle: (export LEAK_BACKTRACE to find allocation site)"
1129                    )
1130                    .unwrap();
1131                }
1132            }
1133            panic!("Handles for {} leaked:\n{out}", data.type_name);
1134        }
1135    }
1136
1137    /// Captures a snapshot of all entity IDs that currently have alive handles.
1138    ///
1139    /// The returned [`LeakDetectorSnapshot`] can later be passed to
1140    /// [`assert_no_new_leaks`](Self::assert_no_new_leaks) to verify that no
1141    /// entities created after the snapshot are still alive.
1142    pub fn snapshot(&self) -> LeakDetectorSnapshot {
1143        LeakDetectorSnapshot {
1144            entity_ids: self.entity_handles.keys().copied().collect(),
1145        }
1146    }
1147
1148    /// Asserts that no entities created after `snapshot` still have alive handles.
1149    ///
1150    /// Entities that were already tracked at the time of the snapshot are ignored,
1151    /// even if they still have handles. Only *new* entities (those whose
1152    /// `EntityId` was not present in the snapshot) are considered leaks.
1153    ///
1154    /// # Panics
1155    ///
1156    /// Panics if any new entity handles exist. The panic message lists every
1157    /// leaked entity with its type name, and includes allocation-site backtraces
1158    /// when `LEAK_BACKTRACE` is set.
1159    pub fn assert_no_new_leaks(&self, snapshot: &LeakDetectorSnapshot) {
1160        use std::fmt::Write as _;
1161
1162        let mut out = String::new();
1163        for (entity_id, data) in &self.entity_handles {
1164            if snapshot.entity_ids.contains(entity_id) {
1165                continue;
1166            }
1167            for (_, backtrace) in &data.handles {
1168                if let Some(backtrace) = backtrace {
1169                    let mut backtrace = backtrace.clone();
1170                    backtrace.resolve();
1171                    let backtrace = BacktraceFormatter(backtrace);
1172                    writeln!(
1173                        out,
1174                        "Leaked handle for entity {} ({entity_id:?}):\n{:?}",
1175                        data.type_name, backtrace
1176                    )
1177                    .unwrap();
1178                } else {
1179                    writeln!(
1180                        out,
1181                        "Leaked handle for entity {} ({entity_id:?}): (export LEAK_BACKTRACE to find allocation site)",
1182                        data.type_name
1183                    )
1184                    .unwrap();
1185                }
1186            }
1187        }
1188
1189        if !out.is_empty() {
1190            panic!("New entity leaks detected since snapshot:\n{out}");
1191        }
1192    }
1193}
1194
1195#[cfg(any(test, gpui_leak_detection))]
1196impl Drop for LeakDetector {
1197    fn drop(&mut self) {
1198        use std::fmt::Write;
1199
1200        if self.entity_handles.is_empty() || std::thread::panicking() {
1201            return;
1202        }
1203
1204        let mut out = String::new();
1205        for (entity_id, data) in self.entity_handles.drain() {
1206            for (_handle, backtrace) in data.handles {
1207                if let Some(mut backtrace) = backtrace {
1208                    backtrace.resolve();
1209                    let backtrace = BacktraceFormatter(backtrace);
1210                    writeln!(
1211                        out,
1212                        "Leaked handle for entity {} ({entity_id:?}):\n{:?}",
1213                        data.type_name, backtrace
1214                    )
1215                    .unwrap();
1216                } else {
1217                    writeln!(
1218                        out,
1219                        "Leaked handle for entity {} ({entity_id:?}): (export LEAK_BACKTRACE to find allocation site)",
1220                        data.type_name
1221                    )
1222                    .unwrap();
1223                }
1224            }
1225        }
1226        panic!("Exited with leaked handles:\n{out}");
1227    }
1228}
1229
1230#[cfg(any(test, gpui_leak_detection))]
1231struct BacktraceFormatter(backtrace::Backtrace);
1232
1233#[cfg(any(test, gpui_leak_detection))]
1234impl fmt::Debug for BacktraceFormatter {
1235    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
1236        use backtrace::{BacktraceFmt, BytesOrWideString, PrintFmt};
1237
1238        let style = if fmt.alternate() {
1239            PrintFmt::Full
1240        } else {
1241            PrintFmt::Short
1242        };
1243
1244        // When printing paths we try to strip the cwd if it exists, otherwise
1245        // we just print the path as-is. Note that we also only do this for the
1246        // short format, because if it's full we presumably want to print
1247        // everything.
1248        let cwd = std::env::current_dir();
1249        let mut print_path = move |fmt: &mut fmt::Formatter<'_>, path: BytesOrWideString<'_>| {
1250            let path = path.into_path_buf();
1251            if style != PrintFmt::Full {
1252                if let Ok(cwd) = &cwd {
1253                    if let Ok(suffix) = path.strip_prefix(cwd) {
1254                        return fmt::Display::fmt(&suffix.display(), fmt);
1255                    }
1256                }
1257            }
1258            fmt::Display::fmt(&path.display(), fmt)
1259        };
1260
1261        let mut f = BacktraceFmt::new(fmt, style, &mut print_path);
1262        f.add_context()?;
1263        let mut strip = true;
1264        for frame in self.0.frames() {
1265            if let [symbol, ..] = frame.symbols()
1266                && let Some(name) = symbol.name()
1267                && let Some(filename) = name.as_str()
1268            {
1269                match filename {
1270                    "test::run_test_in_process"
1271                    | "scheduler::executor::spawn_local_with_source_location::impl$1::poll<core::pin::Pin<alloc::boxed::Box<dyn$<core::future::future::Future<assoc$<Output,enum2$<core::result::Result<workspace::OpenResult,anyhow::Error> > > > >,alloc::alloc::Global> > >" => {
1272                        strip = true
1273                    }
1274                    "gpui::app::entity_map::LeakDetector::handle_created" => {
1275                        strip = false;
1276                        continue;
1277                    }
1278                    "zed::main" => {
1279                        strip = true;
1280                        f.frame().backtrace_frame(frame)?;
1281                    }
1282                    _ => {}
1283                }
1284            }
1285            if strip {
1286                continue;
1287            }
1288            f.frame().backtrace_frame(frame)?;
1289        }
1290        f.finish()?;
1291        Ok(())
1292    }
1293}
1294
1295#[cfg(test)]
1296mod test {
1297    use crate::EntityMap;
1298    use collections::FxHashSet;
1299
1300    struct TestEntity {
1301        pub i: i32,
1302    }
1303
1304    #[test]
1305    fn test_accessed_entity_scopes() {
1306        let mut entity_map = EntityMap::new();
1307        let slot = entity_map.reserve::<TestEntity>();
1308        let first = entity_map.insert(slot, TestEntity { i: 1 });
1309        let slot = entity_map.reserve::<TestEntity>();
1310        let second = entity_map.insert(slot, TestEntity { i: 2 });
1311        entity_map.clear_accessed();
1312
1313        entity_map.read(&first);
1314        entity_map.push_access_scope();
1315        entity_map.read(&first);
1316        entity_map.push_access_scope();
1317        entity_map.read(&second);
1318        let inner = entity_map.pop_access_scope();
1319        let outer = entity_map.pop_access_scope();
1320
1321        assert_eq!(inner, FxHashSet::from_iter([second.entity_id()]));
1322        let both = FxHashSet::from_iter([first.entity_id(), second.entity_id()]);
1323        assert_eq!(outer, both);
1324        assert_eq!(entity_map.take_accessed(), both);
1325
1326        drop(second);
1327        entity_map.take_dropped();
1328        entity_map.extend_accessed(both);
1329        assert_eq!(
1330            entity_map.take_accessed(),
1331            FxHashSet::from_iter([first.entity_id()]),
1332            "a released entity shouldn't be recorded again"
1333        );
1334    }
1335
1336    #[test]
1337    fn test_entity_map_slot_assignment_before_cleanup() {
1338        // Tests that slots are not re-used before take_dropped.
1339        let mut entity_map = EntityMap::new();
1340
1341        let slot = entity_map.reserve::<TestEntity>();
1342        entity_map.insert(slot, TestEntity { i: 1 });
1343
1344        let slot = entity_map.reserve::<TestEntity>();
1345        entity_map.insert(slot, TestEntity { i: 2 });
1346
1347        let dropped = entity_map.take_dropped();
1348        assert_eq!(dropped.len(), 2);
1349
1350        assert_eq!(
1351            dropped
1352                .into_iter()
1353                .map(|(_, entity)| entity.downcast::<TestEntity>().unwrap().i)
1354                .collect::<Vec<i32>>(),
1355            vec![1, 2],
1356        );
1357    }
1358
1359    #[test]
1360    fn test_entity_map_weak_upgrade_before_cleanup() {
1361        // Tests that weak handles are not upgraded before take_dropped
1362        let mut entity_map = EntityMap::new();
1363
1364        let slot = entity_map.reserve::<TestEntity>();
1365        let handle = entity_map.insert(slot, TestEntity { i: 1 });
1366        let weak = handle.downgrade();
1367        drop(handle);
1368
1369        let strong = weak.upgrade();
1370        assert_eq!(strong, None);
1371
1372        let dropped = entity_map.take_dropped();
1373        assert_eq!(dropped.len(), 1);
1374
1375        assert_eq!(
1376            dropped
1377                .into_iter()
1378                .map(|(_, entity)| entity.downcast::<TestEntity>().unwrap().i)
1379                .collect::<Vec<i32>>(),
1380            vec![1],
1381        );
1382    }
1383
1384    #[test]
1385    fn test_leak_detector_snapshot_no_leaks() {
1386        let mut entity_map = EntityMap::new();
1387
1388        let slot = entity_map.reserve::<TestEntity>();
1389        let pre_existing = entity_map.insert(slot, TestEntity { i: 1 });
1390
1391        let snapshot = entity_map.leak_detector_snapshot();
1392
1393        let slot = entity_map.reserve::<TestEntity>();
1394        let temporary = entity_map.insert(slot, TestEntity { i: 2 });
1395        drop(temporary);
1396
1397        entity_map.assert_no_new_leaks(&snapshot);
1398
1399        drop(pre_existing);
1400    }
1401
1402    #[test]
1403    #[should_panic(expected = "New entity leaks detected since snapshot")]
1404    fn test_leak_detector_snapshot_detects_new_leak() {
1405        let mut entity_map = EntityMap::new();
1406
1407        let slot = entity_map.reserve::<TestEntity>();
1408        let pre_existing = entity_map.insert(slot, TestEntity { i: 1 });
1409
1410        let snapshot = entity_map.leak_detector_snapshot();
1411
1412        let slot = entity_map.reserve::<TestEntity>();
1413        let leaked = entity_map.insert(slot, TestEntity { i: 2 });
1414
1415        // `leaked` is still alive, so this should panic.
1416        entity_map.assert_no_new_leaks(&snapshot);
1417
1418        drop(pre_existing);
1419        drop(leaked);
1420    }
1421}