intuicio_data/
managed_gc.rs

1use crate::{
2    Finalize,
3    lifetime::{Lifetime, LifetimeLazy, ValueReadAccess, ValueWriteAccess},
4    managed::{
5        DynamicManagedLazy, DynamicManagedRef, DynamicManagedRefMut, ManagedLazy, ManagedRef,
6        ManagedRefMut,
7    },
8    non_zero_alloc, non_zero_dealloc,
9    type_hash::TypeHash,
10};
11use std::{
12    alloc::{Layout, handle_alloc_error},
13    marker::PhantomData,
14    mem::MaybeUninit,
15};
16
17enum Kind {
18    Owned {
19        lifetime: Lifetime,
20        data: *mut u8,
21    },
22    Referenced {
23        lifetime: LifetimeLazy,
24        data: *mut u8,
25    },
26}
27
28pub enum ManagedGcLifetime<'a> {
29    Owned(&'a Lifetime),
30    Referenced(&'a LifetimeLazy),
31}
32
33pub struct ManagedGc<T> {
34    dynamic: DynamicManagedGc,
35    _phantom: PhantomData<fn() -> T>,
36}
37
38unsafe impl<T> Send for ManagedGc<T> {}
39unsafe impl<T> Sync for ManagedGc<T> {}
40
41impl<T: Default> Default for ManagedGc<T> {
42    fn default() -> Self {
43        Self::new(T::default())
44    }
45}
46
47impl<T> ManagedGc<T> {
48    pub fn new(data: T) -> Self {
49        Self {
50            dynamic: DynamicManagedGc::new(data),
51            _phantom: PhantomData,
52        }
53    }
54
55    pub fn consume(self) -> Result<T, Self> {
56        self.dynamic.consume().map_err(|value| Self {
57            dynamic: value,
58            _phantom: PhantomData,
59        })
60    }
61
62    pub fn into_dynamic(self) -> DynamicManagedGc {
63        self.dynamic
64    }
65
66    pub fn renew(&mut self) {
67        self.dynamic.renew();
68    }
69
70    pub fn type_hash(&self) -> TypeHash {
71        self.dynamic.type_hash()
72    }
73
74    pub fn lifetime(&self) -> ManagedGcLifetime<'_> {
75        self.dynamic.lifetime()
76    }
77
78    pub fn exists(&self) -> bool {
79        self.dynamic.exists()
80    }
81
82    pub fn is_owning(&self) -> bool {
83        self.dynamic.is_owning()
84    }
85
86    pub fn is_referencing(&self) -> bool {
87        self.dynamic.is_referencing()
88    }
89
90    pub fn is_owned_by(&self, other: &Self) -> bool {
91        self.dynamic.is_owned_by(&other.dynamic)
92    }
93
94    pub fn try_read(&'_ self) -> Option<ValueReadAccess<'_, T>> {
95        self.dynamic.try_read::<T>()
96    }
97
98    pub fn try_write(&'_ mut self) -> Option<ValueWriteAccess<'_, T>> {
99        self.dynamic.try_write::<T>()
100    }
101
102    pub fn read<const LOCKING: bool>(&'_ self) -> ValueReadAccess<'_, T> {
103        self.dynamic.read::<LOCKING, T>()
104    }
105
106    pub fn write<const LOCKING: bool>(&'_ mut self) -> ValueWriteAccess<'_, T> {
107        self.dynamic.write::<LOCKING, T>()
108    }
109
110    pub fn borrow<const LOCKING: bool>(&self) -> ManagedRef<T> {
111        self.dynamic
112            .borrow::<LOCKING>()
113            .into_typed()
114            .ok()
115            .expect("ManagedGc cannot be immutably borrowed")
116    }
117
118    pub fn borrow_mut<const LOCKING: bool>(&mut self) -> ManagedRefMut<T> {
119        self.dynamic
120            .borrow_mut::<LOCKING>()
121            .into_typed()
122            .ok()
123            .expect("ManagedGc cannot be mutably borrowed")
124    }
125
126    pub fn lazy(&self) -> ManagedLazy<T> {
127        self.dynamic
128            .lazy()
129            .into_typed()
130            .ok()
131            .expect("ManagedGc cannot be lazily borrowed")
132    }
133
134    /// # Safety
135    pub unsafe fn as_ptr(&self) -> *const T {
136        unsafe { self.dynamic.as_ptr_raw().cast::<T>() }
137    }
138
139    /// # Safety
140    pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
141        unsafe { self.dynamic.as_mut_ptr_raw().cast::<T>() }
142    }
143}
144
145impl<T> Clone for ManagedGc<T> {
146    fn clone(&self) -> Self {
147        Self {
148            dynamic: self.dynamic.clone(),
149            _phantom: PhantomData,
150        }
151    }
152}
153
154pub struct DynamicManagedGc {
155    type_hash: TypeHash,
156    kind: Kind,
157    layout: Layout,
158    finalizer: unsafe fn(*mut ()),
159    drop: bool,
160}
161
162unsafe impl Send for DynamicManagedGc {}
163unsafe impl Sync for DynamicManagedGc {}
164
165impl Drop for DynamicManagedGc {
166    fn drop(&mut self) {
167        if let Kind::Owned { lifetime, data } = &mut self.kind
168            && self.drop
169        {
170            while lifetime.state().is_in_use() {
171                std::hint::spin_loop();
172            }
173            lifetime.invalidate();
174            unsafe {
175                if data.is_null() {
176                    return;
177                }
178                (self.finalizer)(data.cast::<()>());
179                non_zero_dealloc(*data, self.layout);
180            }
181        }
182    }
183}
184
185impl DynamicManagedGc {
186    pub fn new<T: Finalize>(data: T) -> Self {
187        let layout = Layout::new::<T>().pad_to_align();
188        unsafe {
189            let memory = non_zero_alloc(layout) as *mut T;
190            if memory.is_null() {
191                handle_alloc_error(layout);
192            }
193            memory.cast::<T>().write(data);
194            Self {
195                type_hash: TypeHash::of::<T>(),
196                kind: Kind::Owned {
197                    lifetime: Default::default(),
198                    data: memory.cast::<u8>(),
199                },
200                layout,
201                finalizer: T::finalize_raw,
202                drop: true,
203            }
204        }
205    }
206
207    pub fn new_raw(
208        type_hash: TypeHash,
209        lifetime: Lifetime,
210        memory: *mut u8,
211        layout: Layout,
212        finalizer: unsafe fn(*mut ()),
213    ) -> Self {
214        if memory.is_null() {
215            handle_alloc_error(layout);
216        }
217        Self {
218            type_hash,
219            kind: Kind::Owned {
220                lifetime,
221                data: memory,
222            },
223            layout,
224            finalizer,
225            drop: true,
226        }
227    }
228
229    pub fn new_uninitialized(
230        type_hash: TypeHash,
231        layout: Layout,
232        finalizer: unsafe fn(*mut ()),
233    ) -> Self {
234        let memory = unsafe { non_zero_alloc(layout) };
235        if memory.is_null() {
236            handle_alloc_error(layout);
237        }
238        Self {
239            type_hash,
240            kind: Kind::Owned {
241                lifetime: Default::default(),
242                data: memory,
243            },
244            layout,
245            finalizer,
246            drop: true,
247        }
248    }
249
250    pub fn consume<T>(mut self) -> Result<T, Self> {
251        if let Kind::Owned { lifetime, data } = &mut self.kind {
252            if self.type_hash == TypeHash::of::<T>() && !lifetime.state().is_in_use() {
253                if data.is_null() {
254                    return Err(self);
255                }
256                self.drop = false;
257                let mut result = MaybeUninit::<T>::uninit();
258                unsafe {
259                    result.as_mut_ptr().copy_from(data.cast::<T>(), 1);
260                    non_zero_dealloc(*data, self.layout);
261                    Ok(result.assume_init())
262                }
263            } else {
264                Err(self)
265            }
266        } else {
267            Err(self)
268        }
269    }
270
271    pub fn into_typed<T>(self) -> ManagedGc<T> {
272        ManagedGc {
273            dynamic: self,
274            _phantom: PhantomData,
275        }
276    }
277
278    pub fn renew(&mut self) {
279        if let Kind::Owned { lifetime, .. } = &mut self.kind {
280            *lifetime = Default::default();
281        }
282    }
283
284    pub fn type_hash(&self) -> TypeHash {
285        self.type_hash
286    }
287
288    pub fn lifetime(&self) -> ManagedGcLifetime<'_> {
289        match &self.kind {
290            Kind::Owned { lifetime, .. } => ManagedGcLifetime::Owned(lifetime),
291            Kind::Referenced { lifetime, .. } => ManagedGcLifetime::Referenced(lifetime),
292        }
293    }
294
295    pub fn exists(&self) -> bool {
296        match &self.kind {
297            Kind::Owned { .. } => true,
298            Kind::Referenced { lifetime, .. } => lifetime.exists(),
299        }
300    }
301
302    pub fn is_owning(&self) -> bool {
303        matches!(self.kind, Kind::Owned { .. })
304    }
305
306    pub fn is_referencing(&self) -> bool {
307        matches!(self.kind, Kind::Referenced { .. })
308    }
309
310    pub fn is_owned_by(&self, other: &Self) -> bool {
311        if let (Kind::Referenced { lifetime: l1, .. }, Kind::Owned { lifetime: l2, .. }) =
312            (&self.kind, &other.kind)
313        {
314            l1.state().is_owned_by(l2.state())
315        } else {
316            false
317        }
318    }
319
320    pub fn is<T>(&self) -> bool {
321        self.type_hash == TypeHash::of::<T>()
322    }
323
324    pub fn try_read<T>(&'_ self) -> Option<ValueReadAccess<'_, T>> {
325        if !self.is::<T>() {
326            panic!(
327                "DynamicManagedGc is not of the requested type: {}",
328                std::any::type_name::<T>()
329            );
330        }
331        unsafe {
332            match &self.kind {
333                Kind::Owned { lifetime, data } => {
334                    let data = data.cast::<T>().as_ref()?;
335                    lifetime.read(data)
336                }
337                Kind::Referenced { lifetime, data } => {
338                    if lifetime.exists() {
339                        let data = data.cast::<T>().as_ref()?;
340                        lifetime.read(data)
341                    } else {
342                        None
343                    }
344                }
345            }
346        }
347    }
348
349    pub fn try_write<T>(&'_ mut self) -> Option<ValueWriteAccess<'_, T>> {
350        if !self.is::<T>() {
351            panic!(
352                "DynamicManagedGc is not of the requested type: {}",
353                std::any::type_name::<T>()
354            );
355        }
356        unsafe {
357            match &self.kind {
358                Kind::Owned { lifetime, data } => {
359                    let data = data.cast::<T>().as_mut()?;
360                    lifetime.write(data)
361                }
362                Kind::Referenced { lifetime, data } => {
363                    if lifetime.exists() {
364                        let data = data.cast::<T>().as_mut()?;
365                        lifetime.write(data)
366                    } else {
367                        None
368                    }
369                }
370            }
371        }
372    }
373
374    pub fn read<const LOCKING: bool, T>(&'_ self) -> ValueReadAccess<'_, T> {
375        if !self.is::<T>() {
376            panic!(
377                "DynamicManagedGc is not of the requested type: {}",
378                std::any::type_name::<T>()
379            );
380        }
381        unsafe {
382            if LOCKING {
383                match &self.kind {
384                    Kind::Owned { lifetime, data } => loop {
385                        let data = data
386                            .cast::<T>()
387                            .as_ref()
388                            .expect("DynamicManagedGc data pointer is null");
389                        if let Some(access) = lifetime.read(data) {
390                            return access;
391                        }
392                        std::hint::spin_loop();
393                    },
394                    Kind::Referenced { lifetime, data } => loop {
395                        if !lifetime.exists() {
396                            panic!("DynamicManagedGc owner is dead");
397                        }
398                        let data = data
399                            .cast::<T>()
400                            .as_ref()
401                            .expect("DynamicManagedGc data pointer is null");
402                        if let Some(access) = lifetime.read(data) {
403                            return access;
404                        }
405                        std::hint::spin_loop();
406                    },
407                }
408            } else {
409                match &self.kind {
410                    Kind::Owned { lifetime, data } => {
411                        let data = data
412                            .cast::<T>()
413                            .as_ref()
414                            .expect("DynamicManagedGc data pointer is null");
415                        lifetime
416                            .read(data)
417                            .expect("DynamicManagedGc is inaccessible for reading")
418                    }
419                    Kind::Referenced { lifetime, data } => {
420                        let data = data
421                            .cast::<T>()
422                            .as_ref()
423                            .expect("DynamicManagedGc data pointer is null");
424                        lifetime
425                            .read(data)
426                            .expect("DynamicManagedGc is inaccessible for reading")
427                    }
428                }
429            }
430        }
431    }
432
433    pub fn write<const LOCKING: bool, T>(&'_ mut self) -> ValueWriteAccess<'_, T> {
434        if !self.is::<T>() {
435            panic!(
436                "DynamicManagedGc is not of the requested type: {}",
437                std::any::type_name::<T>()
438            );
439        }
440        unsafe {
441            if LOCKING {
442                match &self.kind {
443                    Kind::Owned { lifetime, data } => loop {
444                        let data = data
445                            .cast::<T>()
446                            .as_mut()
447                            .expect("DynamicManagedGc data pointer is null");
448                        if let Some(access) = lifetime.write(data) {
449                            return access;
450                        }
451                        std::hint::spin_loop();
452                    },
453                    Kind::Referenced { lifetime, data } => loop {
454                        if !lifetime.exists() {
455                            panic!("DynamicManagedGc owner is dead");
456                        }
457                        let data = data
458                            .cast::<T>()
459                            .as_mut()
460                            .expect("DynamicManagedGc data pointer is null");
461                        if let Some(access) = lifetime.write(data) {
462                            return access;
463                        }
464                        std::hint::spin_loop();
465                    },
466                }
467            } else {
468                match &self.kind {
469                    Kind::Owned { lifetime, data } => {
470                        let data = data
471                            .cast::<T>()
472                            .as_mut()
473                            .expect("DynamicManagedGc data pointer is null");
474                        lifetime
475                            .write(data)
476                            .expect("DynamicManagedGc is inaccessible for writing")
477                    }
478                    Kind::Referenced { lifetime, data } => {
479                        let data = data
480                            .cast::<T>()
481                            .as_mut()
482                            .expect("DynamicManagedGc data pointer is null");
483                        lifetime
484                            .write(data)
485                            .expect("DynamicManagedGc is inaccessible for writing")
486                    }
487                }
488            }
489        }
490    }
491
492    pub fn borrow<const LOCKING: bool>(&self) -> DynamicManagedRef {
493        unsafe {
494            if LOCKING {
495                match &self.kind {
496                    Kind::Owned { lifetime, data } => loop {
497                        if let Some(lifetime) = lifetime.borrow() {
498                            return DynamicManagedRef::new_raw(self.type_hash, lifetime, *data)
499                                .expect("DynamicManagedGc cannot be immutably borrowed");
500                        }
501                        std::hint::spin_loop();
502                    },
503                    Kind::Referenced { lifetime, data } => loop {
504                        if !lifetime.exists() {
505                            panic!("DynamicManagedGc owner is dead");
506                        }
507                        if let Some(lifetime) = lifetime.borrow() {
508                            return DynamicManagedRef::new_raw(self.type_hash, lifetime, *data)
509                                .expect("DynamicManagedGc cannot be immutably borrowed");
510                        }
511                        std::hint::spin_loop();
512                    },
513                }
514            } else {
515                match &self.kind {
516                    Kind::Owned { lifetime, data } => DynamicManagedRef::new_raw(
517                        self.type_hash,
518                        lifetime
519                            .borrow()
520                            .expect("DynamicManagedGc is inaccessible for immutable borrowing"),
521                        *data,
522                    )
523                    .expect("DynamicManagedGc cannot be immutably borrowed"),
524                    Kind::Referenced { lifetime, data } => DynamicManagedRef::new_raw(
525                        self.type_hash,
526                        lifetime
527                            .borrow()
528                            .expect("DynamicManagedGc is inaccessible for immutable borrowing"),
529                        *data,
530                    )
531                    .expect("DynamicManagedGc cannot be immutably borrowed"),
532                }
533            }
534        }
535    }
536
537    pub fn borrow_mut<const LOCKING: bool>(&mut self) -> DynamicManagedRefMut {
538        unsafe {
539            if LOCKING {
540                match &self.kind {
541                    Kind::Owned { lifetime, data } => loop {
542                        if let Some(lifetime) = lifetime.borrow_mut() {
543                            return DynamicManagedRefMut::new_raw(self.type_hash, lifetime, *data)
544                                .expect("DynamicManagedGc cannot be mutably borrowed");
545                        }
546                        std::hint::spin_loop();
547                    },
548                    Kind::Referenced { lifetime, data } => loop {
549                        if !lifetime.exists() {
550                            panic!("DynamicManagedGc owner is dead");
551                        }
552                        if let Some(lifetime) = lifetime.borrow_mut() {
553                            return DynamicManagedRefMut::new_raw(self.type_hash, lifetime, *data)
554                                .expect("DynamicManagedGc cannot be mutably borrowed");
555                        }
556                        std::hint::spin_loop();
557                    },
558                }
559            } else {
560                match &self.kind {
561                    Kind::Owned { lifetime, data } => DynamicManagedRefMut::new_raw(
562                        self.type_hash,
563                        lifetime
564                            .borrow_mut()
565                            .expect("DynamicManagedGc is inaccessible for mutable borrowing"),
566                        *data,
567                    )
568                    .expect("DynamicManagedGc cannot be mutably borrowed"),
569                    Kind::Referenced { lifetime, data } => DynamicManagedRefMut::new_raw(
570                        self.type_hash,
571                        lifetime
572                            .borrow_mut()
573                            .expect("DynamicManagedGc is inaccessible for mutable borrowing"),
574                        *data,
575                    )
576                    .expect("DynamicManagedGc cannot be mutably borrowed"),
577                }
578            }
579        }
580    }
581
582    pub fn lazy(&self) -> DynamicManagedLazy {
583        unsafe {
584            match &self.kind {
585                Kind::Owned { lifetime, data } => {
586                    DynamicManagedLazy::new_raw(self.type_hash, lifetime.lazy(), *data)
587                        .expect("DynamicManagedGc cannot be lazily borrowed")
588                }
589                Kind::Referenced { lifetime, data } => {
590                    DynamicManagedLazy::new_raw(self.type_hash, lifetime.clone(), *data)
591                        .expect("DynamicManagedGc cannot be lazily borrowed")
592                }
593            }
594        }
595    }
596
597    /// # Safety
598    pub unsafe fn as_ptr_raw(&self) -> *const u8 {
599        match &self.kind {
600            Kind::Owned { data, .. } => *data as *const u8,
601            Kind::Referenced { data, .. } => *data as *const u8,
602        }
603    }
604
605    /// # Safety
606    pub unsafe fn as_mut_ptr_raw(&mut self) -> *mut u8 {
607        match &self.kind {
608            Kind::Owned { data, .. } => *data,
609            Kind::Referenced { data, .. } => *data,
610        }
611    }
612}
613
614impl Clone for DynamicManagedGc {
615    fn clone(&self) -> Self {
616        match &self.kind {
617            Kind::Owned { lifetime, data } => Self {
618                type_hash: self.type_hash,
619                kind: Kind::Referenced {
620                    lifetime: lifetime.lazy(),
621                    data: *data,
622                },
623                layout: self.layout,
624                finalizer: self.finalizer,
625                drop: true,
626            },
627            Kind::Referenced { lifetime, data } => Self {
628                type_hash: self.type_hash,
629                kind: Kind::Referenced {
630                    lifetime: lifetime.clone(),
631                    data: *data,
632                },
633                layout: self.layout,
634                finalizer: self.finalizer,
635                drop: true,
636            },
637        }
638    }
639}
640
641#[cfg(test)]
642mod tests {
643    use super::*;
644
645    #[test]
646    fn test_is_async() {
647        fn is_async<T: Send + Sync>() {}
648
649        is_async::<ManagedGc<()>>();
650        is_async::<DynamicManagedGc>();
651    }
652
653    #[test]
654    fn test_managed_gc() {
655        let mut managed = ManagedGc::new(42);
656        {
657            let read_access = managed.read::<true>();
658            assert_eq!(*read_access, 42);
659        }
660        {
661            let mut write_access = managed.write::<true>();
662            *write_access = 100;
663        }
664        {
665            let read_access = managed.read::<true>();
666            assert_eq!(*read_access, 100);
667        }
668    }
669
670    #[test]
671    fn test_managed_gc_cycles() {
672        #[derive(Default)]
673        struct Foo {
674            other: Option<ManagedGc<Self>>,
675        }
676
677        {
678            let mut a = ManagedGc::<Foo>::default();
679            let mut b = ManagedGc::<Foo>::default();
680            a.write::<true>().other = Some(b.clone());
681            b.write::<true>().other = Some(a.clone());
682
683            assert!(a.exists());
684            assert!(a.is_owning());
685            assert!(a.read::<true>().other.as_ref().unwrap().is_referencing());
686            assert!(a.read::<true>().other.as_ref().unwrap().is_owned_by(&b));
687
688            assert!(b.exists());
689            assert!(b.is_owning());
690            assert!(b.read::<true>().other.as_ref().unwrap().is_referencing());
691            assert!(b.read::<true>().other.as_ref().unwrap().is_owned_by(&a));
692
693            drop(b);
694            assert!(!a.read::<true>().other.as_ref().unwrap().exists());
695        }
696
697        {
698            let mut a = ManagedGc::<Foo>::default();
699            a.write::<true>().other = Some(a.clone());
700
701            assert!(a.exists());
702            assert!(a.is_owning());
703            assert!(a.read::<true>().other.as_ref().unwrap().is_referencing());
704            assert!(a.read::<true>().other.as_ref().unwrap().is_owned_by(&a));
705        }
706    }
707
708    #[test]
709    fn test_dynamic_managed_gc() {
710        let mut managed = DynamicManagedGc::new(42);
711        {
712            let read_access = managed.read::<true, i32>();
713            assert_eq!(*read_access, 42);
714        }
715        {
716            let mut write_access = managed.write::<true, i32>();
717            *write_access = 100;
718        }
719        {
720            let read_access = managed.read::<true, i32>();
721            assert_eq!(*read_access, 100);
722        }
723    }
724
725    #[test]
726    fn test_dynamic_managed_gc_cycles() {
727        #[derive(Default)]
728        struct Foo {
729            other: Option<DynamicManagedGc>,
730        }
731
732        {
733            let mut a = DynamicManagedGc::new(Foo::default());
734            let mut b = DynamicManagedGc::new(Foo::default());
735            a.write::<true, Foo>().other = Some(b.clone());
736            b.write::<true, Foo>().other = Some(a.clone());
737
738            assert!(a.exists());
739            assert!(a.is_owning());
740            assert!(
741                a.read::<true, Foo>()
742                    .other
743                    .as_ref()
744                    .unwrap()
745                    .is_referencing()
746            );
747            assert!(
748                a.read::<true, Foo>()
749                    .other
750                    .as_ref()
751                    .unwrap()
752                    .is_owned_by(&b)
753            );
754
755            assert!(b.exists());
756            assert!(b.is_owning());
757            assert!(
758                b.read::<true, Foo>()
759                    .other
760                    .as_ref()
761                    .unwrap()
762                    .is_referencing()
763            );
764            assert!(
765                b.read::<true, Foo>()
766                    .other
767                    .as_ref()
768                    .unwrap()
769                    .is_owned_by(&a)
770            );
771
772            drop(b);
773            assert!(!a.read::<true, Foo>().other.as_ref().unwrap().exists());
774        }
775
776        {
777            let mut a = DynamicManagedGc::new(Foo::default());
778            a.write::<true, Foo>().other = Some(a.clone());
779
780            assert!(a.exists());
781            assert!(a.is_owning());
782            assert!(
783                a.read::<true, Foo>()
784                    .other
785                    .as_ref()
786                    .unwrap()
787                    .is_referencing()
788            );
789            assert!(
790                a.read::<true, Foo>()
791                    .other
792                    .as_ref()
793                    .unwrap()
794                    .is_owned_by(&a)
795            );
796        }
797    }
798
799    #[test]
800    fn test_gc_conversions() {
801        let managed = ManagedGc::new(42);
802        assert_eq!(*managed.read::<true>(), 42);
803
804        let mut dynamic = managed.into_dynamic();
805        *dynamic.write::<true, i32>() = 100;
806
807        let managed = dynamic.into_typed::<i32>();
808        assert_eq!(*managed.read::<true>(), 100);
809    }
810
811    #[test]
812    fn test_gc_dead_owner() {
813        let a = ManagedGc::new(42);
814        let mut b = a.clone();
815
816        assert!(a.exists());
817        assert!(b.exists());
818        assert_eq!(*b.read::<true>(), 42);
819
820        drop(a);
821        assert!(!b.exists());
822        assert!(b.try_write().is_none());
823    }
824
825    #[test]
826    #[should_panic]
827    fn test_gc_dead_owner_panic() {
828        let a = ManagedGc::new(42);
829        let mut b = a.clone();
830
831        assert!(a.exists());
832        assert!(b.exists());
833        assert_eq!(*b.read::<true>(), 42);
834
835        drop(a);
836        assert!(!b.exists());
837        assert_eq!(*b.write::<true>(), 42);
838    }
839}