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 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 pub fn as_non_zero_u64(self) -> NonZeroU64 {
42 NonZeroU64::new(self.0.as_ffi()).unwrap()
43 }
44
45 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
63struct AccessedEntities {
69 frame: FxHashSet<EntityId>,
75 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 #[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 #[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 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 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 #[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 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 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 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 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 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 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
346pub 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 #[inline]
379 pub fn entity_id(&self) -> EntityId {
380 self.entity_id
381 }
382
383 #[inline]
385 pub fn entity_type(&self) -> TypeId {
386 self.entity_type
387 }
388
389 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 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 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#[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 #[inline]
544 pub fn entity_id(&self) -> EntityId {
545 self.any_entity.entity_id
546 }
547
548 #[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 #[inline]
559 pub fn into_any(self) -> AnyEntity {
560 self.any_entity
561 }
562
563 #[inline]
565 pub fn read<'a>(&self, cx: &'a App) -> &'a T {
566 cx.entities.read(self)
567 }
568
569 #[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 #[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 #[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 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 #[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#[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 #[inline]
679 pub fn entity_id(&self) -> EntityId {
680 self.entity_id
681 }
682
683 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 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 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 #[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 pub fn new_invalid() -> Self {
774 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 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#[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 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 #[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 #[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 #[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 #[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#[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#[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#[cfg(any(test, gpui_leak_detection))]
1041pub(crate) struct LeakDetector {
1042 next_handle_id: u64,
1043 entity_handles: HashMap<EntityId, EntityLeakData>,
1044}
1045
1046#[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 #[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 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 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 pub fn snapshot(&self) -> LeakDetectorSnapshot {
1143 LeakDetectorSnapshot {
1144 entity_ids: self.entity_handles.keys().copied().collect(),
1145 }
1146 }
1147
1148 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 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 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 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 entity_map.assert_no_new_leaks(&snapshot);
1417
1418 drop(pre_existing);
1419 drop(leaked);
1420 }
1421}