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 num::NonZeroU64,
15 sync::{
16 Arc, Weak,
17 atomic::{AtomicU64, AtomicUsize, Ordering::SeqCst},
18 },
19 thread::panicking,
20};
21
22use super::Context;
23use crate::util::atomic_incr_if_not_zero;
24#[cfg(any(test, feature = "leak-detection"))]
25use collections::HashMap;
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 #[doc(hidden)]
87 pub fn ref_counts_drop_handle(&self) -> impl Sized + use<> {
88 self.ref_counts.clone()
89 }
90
91 /// Captures a snapshot of all entities that currently have alive handles.
92 ///
93 /// The returned [`LeakDetectorSnapshot`] can later be passed to
94 /// [`assert_no_new_leaks`](Self::assert_no_new_leaks) to verify that no
95 /// entities created after the snapshot are still alive.
96 #[cfg(any(test, feature = "leak-detection"))]
97 pub fn leak_detector_snapshot(&self) -> LeakDetectorSnapshot {
98 self.ref_counts.read().leak_detector.snapshot()
99 }
100
101 /// Asserts that no entities created after `snapshot` still have alive handles.
102 ///
103 /// See [`LeakDetector::assert_no_new_leaks`] for details.
104 #[cfg(any(test, feature = "leak-detection"))]
105 pub fn assert_no_new_leaks(&self, snapshot: &LeakDetectorSnapshot) {
106 self.ref_counts
107 .read()
108 .leak_detector
109 .assert_no_new_leaks(snapshot)
110 }
111
112 /// Reserve a slot for an entity, which you can subsequently use with `insert`.
113 pub fn reserve<T: 'static>(&self) -> Slot<T> {
114 let id = self.ref_counts.write().counts.insert(1.into());
115 Slot(Entity::new(id, Arc::downgrade(&self.ref_counts)))
116 }
117
118 /// Insert an entity into a slot obtained by calling `reserve`.
119 pub fn insert<T>(&mut self, slot: Slot<T>, entity: T) -> Entity<T>
120 where
121 T: 'static,
122 {
123 let mut accessed_entities = self.accessed_entities.get_mut();
124 accessed_entities.insert(slot.entity_id);
125
126 let handle = slot.0;
127 self.entities.insert(handle.entity_id, Box::new(entity));
128 handle
129 }
130
131 /// Move an entity to the stack.
132 #[track_caller]
133 pub fn lease<T>(&mut self, pointer: &Entity<T>) -> Lease<T> {
134 self.assert_valid_context(pointer);
135 let mut accessed_entities = self.accessed_entities.get_mut();
136 accessed_entities.insert(pointer.entity_id);
137
138 let entity = Some(
139 self.entities
140 .remove(pointer.entity_id)
141 .unwrap_or_else(|| double_lease_panic::<T>("update")),
142 );
143 Lease {
144 entity,
145 id: pointer.entity_id,
146 entity_type: PhantomData,
147 }
148 }
149
150 /// Returns an entity after moving it to the stack.
151 pub fn end_lease<T>(&mut self, mut lease: Lease<T>) {
152 self.entities.insert(lease.id, lease.entity.take().unwrap());
153 }
154
155 pub fn read<T: 'static>(&self, entity: &Entity<T>) -> &T {
156 self.assert_valid_context(entity);
157 let mut accessed_entities = self.accessed_entities.borrow_mut();
158 accessed_entities.insert(entity.entity_id);
159
160 self.entities
161 .get(entity.entity_id)
162 .and_then(|entity| entity.downcast_ref())
163 .unwrap_or_else(|| double_lease_panic::<T>("read"))
164 }
165
166 fn assert_valid_context(&self, entity: &AnyEntity) {
167 debug_assert!(
168 Weak::ptr_eq(&entity.entity_map, &Arc::downgrade(&self.ref_counts)),
169 "used a entity with the wrong context"
170 );
171 }
172
173 pub fn extend_accessed(&mut self, entities: &FxHashSet<EntityId>) {
174 self.accessed_entities
175 .get_mut()
176 .extend(entities.iter().copied());
177 }
178
179 pub fn clear_accessed(&mut self) {
180 self.accessed_entities.get_mut().clear();
181 }
182
183 pub fn take_dropped(&mut self) -> Vec<(EntityId, Box<dyn Any>)> {
184 let mut ref_counts = &mut *self.ref_counts.write();
185 let dropped_entity_ids = ref_counts.dropped_entity_ids.drain(..);
186 let mut accessed_entities = self.accessed_entities.get_mut();
187
188 dropped_entity_ids
189 .filter_map(|entity_id| {
190 let count = ref_counts.counts.remove(entity_id).unwrap();
191 debug_assert_eq!(
192 count.load(SeqCst),
193 0,
194 "dropped an entity that was referenced"
195 );
196 accessed_entities.remove(&entity_id);
197 // If the EntityId was allocated with `Context::reserve`,
198 // the entity may not have been inserted.
199 Some((entity_id, self.entities.remove(entity_id)?))
200 })
201 .collect()
202 }
203}
204
205#[track_caller]
206fn double_lease_panic<T>(operation: &str) -> ! {
207 panic!(
208 "cannot {operation} {} while it is already being updated",
209 std::any::type_name::<T>()
210 )
211}
212
213pub(crate) struct Lease<T> {
214 entity: Option<Box<dyn Any>>,
215 pub id: EntityId,
216 entity_type: PhantomData<T>,
217}
218
219impl<T: 'static> core::ops::Deref for Lease<T> {
220 type Target = T;
221
222 fn deref(&self) -> &Self::Target {
223 self.entity.as_ref().unwrap().downcast_ref().unwrap()
224 }
225}
226
227impl<T: 'static> core::ops::DerefMut for Lease<T> {
228 fn deref_mut(&mut self) -> &mut Self::Target {
229 self.entity.as_mut().unwrap().downcast_mut().unwrap()
230 }
231}
232
233impl<T> Drop for Lease<T> {
234 fn drop(&mut self) {
235 if self.entity.is_some() && !panicking() {
236 panic!("Leases must be ended with EntityMap::end_lease")
237 }
238 }
239}
240
241#[derive(Deref, DerefMut)]
242pub(crate) struct Slot<T>(Entity<T>);
243
244/// A dynamically typed reference to a entity, which can be downcast into a `Entity<T>`.
245pub struct AnyEntity {
246 pub(crate) entity_id: EntityId,
247 pub(crate) entity_type: TypeId,
248 entity_map: Weak<RwLock<EntityRefCounts>>,
249 #[cfg(any(test, feature = "leak-detection"))]
250 handle_id: HandleId,
251}
252
253impl AnyEntity {
254 fn new(
255 id: EntityId,
256 entity_type: TypeId,
257 entity_map: Weak<RwLock<EntityRefCounts>>,
258 #[cfg(any(test, feature = "leak-detection"))] type_name: &'static str,
259 ) -> Self {
260 Self {
261 entity_id: id,
262 entity_type,
263 #[cfg(any(test, feature = "leak-detection"))]
264 handle_id: entity_map
265 .clone()
266 .upgrade()
267 .unwrap()
268 .write()
269 .leak_detector
270 .handle_created(id, Some(type_name)),
271 entity_map,
272 }
273 }
274
275 /// Returns the id associated with this entity.
276 #[inline]
277 pub fn entity_id(&self) -> EntityId {
278 self.entity_id
279 }
280
281 /// Returns the [TypeId] associated with this entity.
282 #[inline]
283 pub fn entity_type(&self) -> TypeId {
284 self.entity_type
285 }
286
287 /// Converts this entity handle into a weak variant, which does not prevent it from being released.
288 pub fn downgrade(&self) -> AnyWeakEntity {
289 AnyWeakEntity {
290 entity_id: self.entity_id,
291 entity_type: self.entity_type,
292 entity_ref_counts: self.entity_map.clone(),
293 }
294 }
295
296 /// Converts this entity handle into a strongly-typed entity handle of the given type.
297 /// If this entity handle is not of the specified type, returns itself as an error variant.
298 pub fn downcast<T: 'static>(self) -> Result<Entity<T>, AnyEntity> {
299 if TypeId::of::<T>() == self.entity_type {
300 Ok(Entity {
301 any_entity: self,
302 entity_type: PhantomData,
303 })
304 } else {
305 Err(self)
306 }
307 }
308}
309
310impl Clone for AnyEntity {
311 fn clone(&self) -> Self {
312 if let Some(entity_map) = self.entity_map.upgrade() {
313 let entity_map = entity_map.read();
314 let count = entity_map
315 .counts
316 .get(self.entity_id)
317 .expect("detected over-release of a entity");
318 let prev_count = count.fetch_add(1, SeqCst);
319 assert_ne!(prev_count, 0, "Detected over-release of a entity.");
320 }
321
322 Self {
323 entity_id: self.entity_id,
324 entity_type: self.entity_type,
325 entity_map: self.entity_map.clone(),
326 #[cfg(any(test, feature = "leak-detection"))]
327 handle_id: self
328 .entity_map
329 .upgrade()
330 .unwrap()
331 .write()
332 .leak_detector
333 .handle_created(self.entity_id, None),
334 }
335 }
336}
337
338impl Drop for AnyEntity {
339 fn drop(&mut self) {
340 if let Some(entity_map) = self.entity_map.upgrade() {
341 let entity_map = entity_map.upgradable_read();
342 let count = entity_map
343 .counts
344 .get(self.entity_id)
345 .expect("detected over-release of a handle.");
346 let prev_count = count.fetch_sub(1, SeqCst);
347 assert_ne!(prev_count, 0, "Detected over-release of a entity.");
348 if prev_count == 1 {
349 // We were the last reference to this entity, so we can remove it.
350 let mut entity_map = RwLockUpgradableReadGuard::upgrade(entity_map);
351 entity_map.dropped_entity_ids.push(self.entity_id);
352 }
353 }
354
355 #[cfg(any(test, feature = "leak-detection"))]
356 if let Some(entity_map) = self.entity_map.upgrade() {
357 entity_map
358 .write()
359 .leak_detector
360 .handle_released(self.entity_id, self.handle_id)
361 }
362 }
363}
364
365impl<T> From<Entity<T>> for AnyEntity {
366 #[inline]
367 fn from(entity: Entity<T>) -> Self {
368 entity.any_entity
369 }
370}
371
372impl Hash for AnyEntity {
373 #[inline]
374 fn hash<H: Hasher>(&self, state: &mut H) {
375 self.entity_id.hash(state);
376 }
377}
378
379impl PartialEq for AnyEntity {
380 #[inline]
381 fn eq(&self, other: &Self) -> bool {
382 self.entity_id == other.entity_id
383 }
384}
385
386impl Eq for AnyEntity {}
387
388impl Ord for AnyEntity {
389 #[inline]
390 fn cmp(&self, other: &Self) -> Ordering {
391 self.entity_id.cmp(&other.entity_id)
392 }
393}
394
395impl PartialOrd for AnyEntity {
396 #[inline]
397 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
398 Some(self.cmp(other))
399 }
400}
401
402impl std::fmt::Debug for AnyEntity {
403 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
404 f.debug_struct("AnyEntity")
405 .field("entity_id", &self.entity_id.as_u64())
406 .finish()
407 }
408}
409
410/// A strong, well-typed reference to a struct which is managed
411/// by GPUI
412#[derive(Deref, DerefMut)]
413pub struct Entity<T> {
414 #[deref]
415 #[deref_mut]
416 pub(crate) any_entity: AnyEntity,
417 pub(crate) entity_type: PhantomData<fn(T) -> T>,
418}
419
420impl<T> Sealed for Entity<T> {}
421
422impl<T: 'static> Entity<T> {
423 #[inline]
424 fn new(id: EntityId, entity_map: Weak<RwLock<EntityRefCounts>>) -> Self
425 where
426 T: 'static,
427 {
428 Self {
429 any_entity: AnyEntity::new(
430 id,
431 TypeId::of::<T>(),
432 entity_map,
433 #[cfg(any(test, feature = "leak-detection"))]
434 std::any::type_name::<T>(),
435 ),
436 entity_type: PhantomData,
437 }
438 }
439
440 /// Get the entity ID associated with this entity
441 #[inline]
442 pub fn entity_id(&self) -> EntityId {
443 self.any_entity.entity_id
444 }
445
446 /// Downgrade this entity pointer to a non-retaining weak pointer
447 #[inline]
448 pub fn downgrade(&self) -> WeakEntity<T> {
449 WeakEntity {
450 any_entity: self.any_entity.downgrade(),
451 entity_type: self.entity_type,
452 }
453 }
454
455 /// Convert this into a dynamically typed entity.
456 #[inline]
457 pub fn into_any(self) -> AnyEntity {
458 self.any_entity
459 }
460
461 /// Grab a reference to this entity from the context.
462 #[inline]
463 pub fn read<'a>(&self, cx: &'a App) -> &'a T {
464 cx.entities.read(self)
465 }
466
467 /// Read the entity referenced by this handle with the given function.
468 #[inline]
469 pub fn read_with<R, C: AppContext>(&self, cx: &C, f: impl FnOnce(&T, &App) -> R) -> R {
470 cx.read_entity(self, f)
471 }
472
473 /// Updates the entity referenced by this handle with the given function.
474 #[inline]
475 pub fn update<R, C: AppContext>(
476 &self,
477 cx: &mut C,
478 update: impl FnOnce(&mut T, &mut Context<T>) -> R,
479 ) -> R {
480 cx.update_entity(self, update)
481 }
482
483 /// Updates the entity referenced by this handle with the given function.
484 #[inline]
485 pub fn as_mut<'a, C: AppContext>(&self, cx: &'a mut C) -> GpuiBorrow<'a, T> {
486 cx.as_mut(self)
487 }
488
489 /// Updates the entity referenced by this handle with the given function.
490 pub fn write<C: AppContext>(&self, cx: &mut C, value: T) {
491 self.update(cx, |entity, cx| {
492 *entity = value;
493 cx.notify();
494 })
495 }
496
497 /// Updates the entity referenced by this handle with the given function if
498 /// the referenced entity still exists, within a visual context that has a window.
499 /// Returns an error if the window has been closed.
500 #[inline]
501 pub fn update_in<R, C: VisualContext>(
502 &self,
503 cx: &mut C,
504 update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
505 ) -> C::Result<R> {
506 cx.update_window_entity(self, update)
507 }
508}
509
510impl<T> Clone for Entity<T> {
511 #[inline]
512 fn clone(&self) -> Self {
513 Self {
514 any_entity: self.any_entity.clone(),
515 entity_type: self.entity_type,
516 }
517 }
518}
519
520impl<T> std::fmt::Debug for Entity<T> {
521 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
522 f.debug_struct("Entity")
523 .field("entity_id", &self.any_entity.entity_id)
524 .field("entity_type", &type_name::<T>())
525 .finish()
526 }
527}
528
529impl<T> Hash for Entity<T> {
530 #[inline]
531 fn hash<H: Hasher>(&self, state: &mut H) {
532 self.any_entity.hash(state);
533 }
534}
535
536impl<T> PartialEq for Entity<T> {
537 #[inline]
538 fn eq(&self, other: &Self) -> bool {
539 self.any_entity == other.any_entity
540 }
541}
542
543impl<T> Eq for Entity<T> {}
544
545impl<T> PartialEq<WeakEntity<T>> for Entity<T> {
546 #[inline]
547 fn eq(&self, other: &WeakEntity<T>) -> bool {
548 self.any_entity.entity_id() == other.entity_id()
549 }
550}
551
552impl<T: 'static> Ord for Entity<T> {
553 #[inline]
554 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
555 self.entity_id().cmp(&other.entity_id())
556 }
557}
558
559impl<T: 'static> PartialOrd for Entity<T> {
560 #[inline]
561 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
562 Some(self.cmp(other))
563 }
564}
565
566/// A type erased, weak reference to a entity.
567#[derive(Clone)]
568pub struct AnyWeakEntity {
569 pub(crate) entity_id: EntityId,
570 entity_type: TypeId,
571 entity_ref_counts: Weak<RwLock<EntityRefCounts>>,
572}
573
574impl AnyWeakEntity {
575 /// Get the entity ID associated with this weak reference.
576 #[inline]
577 pub fn entity_id(&self) -> EntityId {
578 self.entity_id
579 }
580
581 /// Check if this weak handle can be upgraded, or if the entity has already been dropped
582 pub fn is_upgradable(&self) -> bool {
583 let ref_count = self
584 .entity_ref_counts
585 .upgrade()
586 .and_then(|ref_counts| Some(ref_counts.read().counts.get(self.entity_id)?.load(SeqCst)))
587 .unwrap_or(0);
588 ref_count > 0
589 }
590
591 /// Upgrade this weak entity reference to a strong reference.
592 pub fn upgrade(&self) -> Option<AnyEntity> {
593 let ref_counts = &self.entity_ref_counts.upgrade()?;
594 let ref_counts = ref_counts.read();
595 let ref_count = ref_counts.counts.get(self.entity_id)?;
596
597 if atomic_incr_if_not_zero(ref_count) == 0 {
598 // entity_id is in dropped_entity_ids
599 return None;
600 }
601 drop(ref_counts);
602
603 Some(AnyEntity {
604 entity_id: self.entity_id,
605 entity_type: self.entity_type,
606 entity_map: self.entity_ref_counts.clone(),
607 #[cfg(any(test, feature = "leak-detection"))]
608 handle_id: self
609 .entity_ref_counts
610 .upgrade()
611 .unwrap()
612 .write()
613 .leak_detector
614 .handle_created(self.entity_id, None),
615 })
616 }
617
618 /// Asserts that the entity referenced by this weak handle has been fully released.
619 ///
620 /// # Example
621 ///
622 /// ```ignore
623 /// let entity = cx.new(|_| MyEntity::new());
624 /// let weak = entity.downgrade();
625 /// drop(entity);
626 ///
627 /// // Verify the entity was released
628 /// weak.assert_released();
629 /// ```
630 ///
631 /// # Debugging Leaks
632 ///
633 /// If this method panics due to leaked handles, set the `LEAK_BACKTRACE` environment
634 /// variable to see where the leaked handles were allocated:
635 ///
636 /// ```bash
637 /// LEAK_BACKTRACE=1 cargo test my_test
638 /// ```
639 ///
640 /// # Panics
641 ///
642 /// - Panics if any strong handles to the entity are still alive.
643 /// - Panics if the entity was recently dropped but cleanup hasn't completed yet
644 /// (resources are retained until the end of the effect cycle).
645 #[cfg(any(test, feature = "leak-detection"))]
646 pub fn assert_released(&self) {
647 self.entity_ref_counts
648 .upgrade()
649 .unwrap()
650 .write()
651 .leak_detector
652 .assert_released(self.entity_id);
653
654 if self
655 .entity_ref_counts
656 .upgrade()
657 .and_then(|ref_counts| Some(ref_counts.read().counts.get(self.entity_id)?.load(SeqCst)))
658 .is_some()
659 {
660 panic!(
661 "entity was recently dropped but resources are retained until the end of the effect cycle."
662 )
663 }
664 }
665
666 /// Creates a weak entity that can never be upgraded.
667 pub fn new_invalid() -> Self {
668 /// To hold the invariant that all ids are unique, and considering that slotmap
669 /// increases their IDs from `0`, we can decrease ours from `u64::MAX` so these
670 /// two will never conflict (u64 is way too large).
671 static UNIQUE_NON_CONFLICTING_ID_GENERATOR: AtomicU64 = AtomicU64::new(u64::MAX);
672 let entity_id = UNIQUE_NON_CONFLICTING_ID_GENERATOR.fetch_sub(1, SeqCst);
673
674 Self {
675 // Safety:
676 // Docs say this is safe but can be unspecified if slotmap changes the representation
677 // after `1.0.7`, that said, providing a valid entity_id here is not necessary as long
678 // as we guarantee that `entity_id` is never used if `entity_ref_counts` equals
679 // to `Weak::new()` (that is, it's unable to upgrade), that is the invariant that
680 // actually needs to be hold true.
681 //
682 // And there is no sane reason to read an entity slot if `entity_ref_counts` can't be
683 // read in the first place, so we're good!
684 entity_id: entity_id.into(),
685 entity_type: TypeId::of::<()>(),
686 entity_ref_counts: Weak::new(),
687 }
688 }
689}
690
691impl std::fmt::Debug for AnyWeakEntity {
692 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
693 f.debug_struct(type_name::<Self>())
694 .field("entity_id", &self.entity_id)
695 .field("entity_type", &self.entity_type)
696 .finish()
697 }
698}
699
700impl<T> From<WeakEntity<T>> for AnyWeakEntity {
701 #[inline]
702 fn from(entity: WeakEntity<T>) -> Self {
703 entity.any_entity
704 }
705}
706
707impl Hash for AnyWeakEntity {
708 #[inline]
709 fn hash<H: Hasher>(&self, state: &mut H) {
710 self.entity_id.hash(state);
711 }
712}
713
714impl PartialEq for AnyWeakEntity {
715 #[inline]
716 fn eq(&self, other: &Self) -> bool {
717 self.entity_id == other.entity_id
718 }
719}
720
721impl Eq for AnyWeakEntity {}
722
723impl Ord for AnyWeakEntity {
724 #[inline]
725 fn cmp(&self, other: &Self) -> Ordering {
726 self.entity_id.cmp(&other.entity_id)
727 }
728}
729
730impl PartialOrd for AnyWeakEntity {
731 #[inline]
732 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
733 Some(self.cmp(other))
734 }
735}
736
737/// A weak reference to a entity of the given type.
738#[derive(Deref, DerefMut)]
739pub struct WeakEntity<T> {
740 #[deref]
741 #[deref_mut]
742 any_entity: AnyWeakEntity,
743 entity_type: PhantomData<fn(T) -> T>,
744}
745
746impl<T> std::fmt::Debug for WeakEntity<T> {
747 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
748 f.debug_struct(type_name::<Self>())
749 .field("entity_id", &self.any_entity.entity_id)
750 .field("entity_type", &type_name::<T>())
751 .finish()
752 }
753}
754
755impl<T> Clone for WeakEntity<T> {
756 fn clone(&self) -> Self {
757 Self {
758 any_entity: self.any_entity.clone(),
759 entity_type: self.entity_type,
760 }
761 }
762}
763
764impl<T: 'static> WeakEntity<T> {
765 /// Upgrade this weak entity reference into a strong entity reference
766 pub fn upgrade(&self) -> Option<Entity<T>> {
767 Some(Entity {
768 any_entity: self.any_entity.upgrade()?,
769 entity_type: self.entity_type,
770 })
771 }
772
773 /// Updates the entity referenced by this handle with the given function if
774 /// the referenced entity still exists. Returns an error if the entity has
775 /// been released.
776 pub fn update<C, R>(
777 &self,
778 cx: &mut C,
779 update: impl FnOnce(&mut T, &mut Context<T>) -> R,
780 ) -> Result<R>
781 where
782 C: AppContext,
783 {
784 let entity = self.upgrade().context("entity released")?;
785 Ok(cx.update_entity(&entity, update))
786 }
787
788 /// Updates the entity referenced by this handle with the given function if
789 /// the referenced entity still exists, within a visual context that has a window.
790 /// Returns an error if the entity has been released.
791 pub fn update_in<C, R>(
792 &self,
793 cx: &mut C,
794 update: impl FnOnce(&mut T, &mut Window, &mut Context<T>) -> R,
795 ) -> Result<R>
796 where
797 C: VisualContext,
798 {
799 let window = cx.window_handle();
800 let entity = self.upgrade().context("entity released")?;
801
802 window.update(cx, |_, window, cx| {
803 entity.update(cx, |entity, cx| update(entity, window, cx))
804 })
805 }
806
807 /// Reads the entity referenced by this handle with the given function if
808 /// the referenced entity still exists. Returns an error if the entity has
809 /// been released.
810 pub fn read_with<C, R>(&self, cx: &C, read: impl FnOnce(&T, &App) -> R) -> Result<R>
811 where
812 C: AppContext,
813 {
814 let entity = self.upgrade().context("entity released")?;
815 Ok(cx.read_entity(&entity, read))
816 }
817
818 /// Create a new weak entity that can never be upgraded.
819 #[inline]
820 pub fn new_invalid() -> Self {
821 Self {
822 any_entity: AnyWeakEntity::new_invalid(),
823 entity_type: PhantomData,
824 }
825 }
826}
827
828impl<T> Hash for WeakEntity<T> {
829 #[inline]
830 fn hash<H: Hasher>(&self, state: &mut H) {
831 self.any_entity.hash(state);
832 }
833}
834
835impl<T> PartialEq for WeakEntity<T> {
836 #[inline]
837 fn eq(&self, other: &Self) -> bool {
838 self.any_entity == other.any_entity
839 }
840}
841
842impl<T> Eq for WeakEntity<T> {}
843
844impl<T> PartialEq<Entity<T>> for WeakEntity<T> {
845 #[inline]
846 fn eq(&self, other: &Entity<T>) -> bool {
847 self.entity_id() == other.any_entity.entity_id()
848 }
849}
850
851impl<T: 'static> Ord for WeakEntity<T> {
852 #[inline]
853 fn cmp(&self, other: &Self) -> Ordering {
854 self.entity_id().cmp(&other.entity_id())
855 }
856}
857
858impl<T: 'static> PartialOrd for WeakEntity<T> {
859 #[inline]
860 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
861 Some(self.cmp(other))
862 }
863}
864
865/// Controls whether backtraces are captured when entity handles are created.
866///
867/// Set the `LEAK_BACKTRACE` environment variable to any non-empty value to enable
868/// backtrace capture. This helps identify where leaked handles were allocated.
869#[cfg(any(test, feature = "leak-detection"))]
870static LEAK_BACKTRACE: std::sync::LazyLock<bool> =
871 std::sync::LazyLock::new(|| std::env::var("LEAK_BACKTRACE").is_ok_and(|b| !b.is_empty()));
872
873/// Unique identifier for a specific entity handle instance.
874///
875/// This is distinct from `EntityId` - while multiple handles can point to the same
876/// entity (same `EntityId`), each handle has its own unique `HandleId`.
877#[cfg(any(test, feature = "leak-detection"))]
878#[derive(Clone, Copy, Debug, Default, Hash, PartialEq, Eq)]
879pub(crate) struct HandleId {
880 id: u64,
881}
882
883/// Tracks entity handle allocations to detect leaks.
884///
885/// The leak detector is enabled in tests and when the `leak-detection` feature is active.
886/// It tracks every `Entity<T>` and `AnyEntity` handle that is created and released,
887/// allowing you to verify that all handles to an entity have been properly dropped.
888///
889/// # How do leaks happen?
890///
891/// Entities are reference-counted structures that can own other entities
892/// allowing to form cycles. If such a strong-reference counted cycle is
893/// created, all participating strong entities in this cycle will effectively
894/// leak as they cannot be released anymore.
895///
896/// # Usage
897///
898/// You can use `WeakEntity::assert_released` or `AnyWeakEntity::assert_released`
899/// to verify that an entity has been fully released:
900///
901/// ```ignore
902/// let entity = cx.new(|_| MyEntity::new());
903/// let weak = entity.downgrade();
904/// drop(entity);
905///
906/// // This will panic if any handles to the entity are still alive
907/// weak.assert_released();
908/// ```
909///
910/// # Debugging Leaks
911///
912/// When a leak is detected, the detector will panic with information about the leaked
913/// handles. To see where the leaked handles were allocated, set the `LEAK_BACKTRACE`
914/// environment variable:
915///
916/// ```bash
917/// LEAK_BACKTRACE=1 cargo test my_test
918/// ```
919///
920/// This will capture and display backtraces for each leaked handle, helping you
921/// identify where handles were created but not released.
922///
923/// # How It Works
924///
925/// - When an entity handle is created (via `Entity::new`, `Entity::clone`, or
926/// `WeakEntity::upgrade`), `handle_created` is called to register the handle.
927/// - When a handle is dropped, `handle_released` removes it from tracking.
928/// - `assert_released` verifies that no handles remain for a given entity.
929#[cfg(any(test, feature = "leak-detection"))]
930pub(crate) struct LeakDetector {
931 next_handle_id: u64,
932 entity_handles: HashMap<EntityId, EntityLeakData>,
933}
934
935/// A snapshot of the set of alive entities at a point in time.
936///
937/// Created by [`LeakDetector::snapshot`]. Can later be passed to
938/// [`LeakDetector::assert_no_new_leaks`] to verify that no new entity
939/// handles remain between the snapshot and the current state.
940#[cfg(any(test, feature = "leak-detection"))]
941pub struct LeakDetectorSnapshot {
942 entity_ids: collections::HashSet<EntityId>,
943}
944
945#[cfg(any(test, feature = "leak-detection"))]
946struct EntityLeakData {
947 handles: HashMap<HandleId, Option<backtrace::Backtrace>>,
948 type_name: &'static str,
949}
950
951#[cfg(any(test, feature = "leak-detection"))]
952impl LeakDetector {
953 /// Records that a new handle has been created for the given entity.
954 ///
955 /// Returns a unique `HandleId` that must be passed to `handle_released` when
956 /// the handle is dropped. If `LEAK_BACKTRACE` is set, captures a backtrace
957 /// at the allocation site.
958 #[track_caller]
959 pub fn handle_created(
960 &mut self,
961 entity_id: EntityId,
962 type_name: Option<&'static str>,
963 ) -> HandleId {
964 let id = gpui_util::post_inc(&mut self.next_handle_id);
965 let handle_id = HandleId { id };
966 let handles = self
967 .entity_handles
968 .entry(entity_id)
969 .or_insert_with(|| EntityLeakData {
970 handles: HashMap::default(),
971 type_name: type_name.unwrap_or("<unknown>"),
972 });
973 handles.handles.insert(
974 handle_id,
975 LEAK_BACKTRACE.then(backtrace::Backtrace::new_unresolved),
976 );
977 handle_id
978 }
979
980 /// Records that a handle has been released (dropped).
981 ///
982 /// This removes the handle from tracking. The `handle_id` should be the same
983 /// one returned by `handle_created` when the handle was allocated.
984 pub fn handle_released(&mut self, entity_id: EntityId, handle_id: HandleId) {
985 if let std::collections::hash_map::Entry::Occupied(mut data) =
986 self.entity_handles.entry(entity_id)
987 {
988 data.get_mut().handles.remove(&handle_id);
989 if data.get().handles.is_empty() {
990 data.remove();
991 }
992 }
993 }
994
995 /// Asserts that all handles to the given entity have been released.
996 ///
997 /// # Panics
998 ///
999 /// Panics if any handles to the entity are still alive. The panic message
1000 /// includes backtraces for each leaked handle if `LEAK_BACKTRACE` is set,
1001 /// otherwise it suggests setting the environment variable to get more info.
1002 pub fn assert_released(&mut self, entity_id: EntityId) {
1003 use std::fmt::Write as _;
1004 if let Some(data) = self.entity_handles.remove(&entity_id) {
1005 let mut out = String::new();
1006 for (_, backtrace) in data.handles {
1007 if let Some(mut backtrace) = backtrace {
1008 backtrace.resolve();
1009 writeln!(out, "Leaked handle:\n{:?}", backtrace).unwrap();
1010 } else {
1011 writeln!(
1012 out,
1013 "Leaked handle: (export LEAK_BACKTRACE to find allocation site)"
1014 )
1015 .unwrap();
1016 }
1017 }
1018 panic!("{out}");
1019 }
1020 }
1021
1022 /// Captures a snapshot of all entity IDs that currently have alive handles.
1023 ///
1024 /// The returned [`LeakDetectorSnapshot`] can later be passed to
1025 /// [`assert_no_new_leaks`](Self::assert_no_new_leaks) to verify that no
1026 /// entities created after the snapshot are still alive.
1027 pub fn snapshot(&self) -> LeakDetectorSnapshot {
1028 LeakDetectorSnapshot {
1029 entity_ids: self.entity_handles.keys().copied().collect(),
1030 }
1031 }
1032
1033 /// Asserts that no entities created after `snapshot` still have alive handles.
1034 ///
1035 /// Entities that were already tracked at the time of the snapshot are ignored,
1036 /// even if they still have handles. Only *new* entities (those whose
1037 /// `EntityId` was not present in the snapshot) are considered leaks.
1038 ///
1039 /// # Panics
1040 ///
1041 /// Panics if any new entity handles exist. The panic message lists every
1042 /// leaked entity with its type name, and includes allocation-site backtraces
1043 /// when `LEAK_BACKTRACE` is set.
1044 pub fn assert_no_new_leaks(&self, snapshot: &LeakDetectorSnapshot) {
1045 use std::fmt::Write as _;
1046
1047 let mut out = String::new();
1048 for (entity_id, data) in &self.entity_handles {
1049 if snapshot.entity_ids.contains(entity_id) {
1050 continue;
1051 }
1052 for (_, backtrace) in &data.handles {
1053 if let Some(backtrace) = backtrace {
1054 let mut backtrace = backtrace.clone();
1055 backtrace.resolve();
1056 writeln!(
1057 out,
1058 "Leaked handle for entity {} ({entity_id:?}):\n{:?}",
1059 data.type_name, backtrace
1060 )
1061 .unwrap();
1062 } else {
1063 writeln!(
1064 out,
1065 "Leaked handle for entity {} ({entity_id:?}): (export LEAK_BACKTRACE to find allocation site)",
1066 data.type_name
1067 )
1068 .unwrap();
1069 }
1070 }
1071 }
1072
1073 if !out.is_empty() {
1074 panic!("New entity leaks detected since snapshot:\n{out}");
1075 }
1076 }
1077}
1078
1079#[cfg(any(test, feature = "leak-detection"))]
1080impl Drop for LeakDetector {
1081 fn drop(&mut self) {
1082 use std::fmt::Write;
1083
1084 if self.entity_handles.is_empty() || std::thread::panicking() {
1085 return;
1086 }
1087
1088 let mut out = String::new();
1089 for (entity_id, data) in self.entity_handles.drain() {
1090 for (_handle, backtrace) in data.handles {
1091 if let Some(mut backtrace) = backtrace {
1092 backtrace.resolve();
1093 writeln!(
1094 out,
1095 "Leaked handle for entity {} ({entity_id:?}):\n{:?}",
1096 data.type_name, backtrace
1097 )
1098 .unwrap();
1099 } else {
1100 writeln!(
1101 out,
1102 "Leaked handle for entity {} ({entity_id:?}): (export LEAK_BACKTRACE to find allocation site)",
1103 data.type_name
1104 )
1105 .unwrap();
1106 }
1107 }
1108 }
1109 panic!("Exited with leaked handles:\n{out}");
1110 }
1111}
1112
1113#[cfg(test)]
1114mod test {
1115 use crate::EntityMap;
1116
1117 struct TestEntity {
1118 pub i: i32,
1119 }
1120
1121 #[test]
1122 fn test_entity_map_slot_assignment_before_cleanup() {
1123 // Tests that slots are not re-used before take_dropped.
1124 let mut entity_map = EntityMap::new();
1125
1126 let slot = entity_map.reserve::<TestEntity>();
1127 entity_map.insert(slot, TestEntity { i: 1 });
1128
1129 let slot = entity_map.reserve::<TestEntity>();
1130 entity_map.insert(slot, TestEntity { i: 2 });
1131
1132 let dropped = entity_map.take_dropped();
1133 assert_eq!(dropped.len(), 2);
1134
1135 assert_eq!(
1136 dropped
1137 .into_iter()
1138 .map(|(_, entity)| entity.downcast::<TestEntity>().unwrap().i)
1139 .collect::<Vec<i32>>(),
1140 vec![1, 2],
1141 );
1142 }
1143
1144 #[test]
1145 fn test_entity_map_weak_upgrade_before_cleanup() {
1146 // Tests that weak handles are not upgraded before take_dropped
1147 let mut entity_map = EntityMap::new();
1148
1149 let slot = entity_map.reserve::<TestEntity>();
1150 let handle = entity_map.insert(slot, TestEntity { i: 1 });
1151 let weak = handle.downgrade();
1152 drop(handle);
1153
1154 let strong = weak.upgrade();
1155 assert_eq!(strong, None);
1156
1157 let dropped = entity_map.take_dropped();
1158 assert_eq!(dropped.len(), 1);
1159
1160 assert_eq!(
1161 dropped
1162 .into_iter()
1163 .map(|(_, entity)| entity.downcast::<TestEntity>().unwrap().i)
1164 .collect::<Vec<i32>>(),
1165 vec![1],
1166 );
1167 }
1168
1169 #[test]
1170 fn test_leak_detector_snapshot_no_leaks() {
1171 let mut entity_map = EntityMap::new();
1172
1173 let slot = entity_map.reserve::<TestEntity>();
1174 let pre_existing = entity_map.insert(slot, TestEntity { i: 1 });
1175
1176 let snapshot = entity_map.leak_detector_snapshot();
1177
1178 let slot = entity_map.reserve::<TestEntity>();
1179 let temporary = entity_map.insert(slot, TestEntity { i: 2 });
1180 drop(temporary);
1181
1182 entity_map.assert_no_new_leaks(&snapshot);
1183
1184 drop(pre_existing);
1185 }
1186
1187 #[test]
1188 #[should_panic(expected = "New entity leaks detected since snapshot")]
1189 fn test_leak_detector_snapshot_detects_new_leak() {
1190 let mut entity_map = EntityMap::new();
1191
1192 let slot = entity_map.reserve::<TestEntity>();
1193 let pre_existing = entity_map.insert(slot, TestEntity { i: 1 });
1194
1195 let snapshot = entity_map.leak_detector_snapshot();
1196
1197 let slot = entity_map.reserve::<TestEntity>();
1198 let leaked = entity_map.insert(slot, TestEntity { i: 2 });
1199
1200 // `leaked` is still alive, so this should panic.
1201 entity_map.assert_no_new_leaks(&snapshot);
1202
1203 drop(pre_existing);
1204 drop(leaked);
1205 }
1206}