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<T> {
18    Owned {
19        lifetime: Lifetime,
20        data: *mut T,
21    },
22    Referenced {
23        lifetime: LifetimeLazy,
24        data: *mut T,
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 read<const LOCKING: bool>(&'_ self) -> ValueReadAccess<'_, T> {
95        self.dynamic.read::<LOCKING, T>()
96    }
97
98    pub fn write<const LOCKING: bool>(&'_ mut self) -> ValueWriteAccess<'_, T> {
99        self.dynamic.write::<LOCKING, T>()
100    }
101
102    pub fn borrow<const LOCKING: bool>(&self) -> ManagedRef<T> {
103        self.dynamic
104            .borrow::<LOCKING>()
105            .into_typed()
106            .ok()
107            .expect("ManagedGc cannot be immutably borrowed")
108    }
109
110    pub fn borrow_mut<const LOCKING: bool>(&mut self) -> ManagedRefMut<T> {
111        self.dynamic
112            .borrow_mut::<LOCKING>()
113            .into_typed()
114            .ok()
115            .expect("ManagedGc cannot be mutably borrowed")
116    }
117
118    pub fn lazy(&self) -> ManagedLazy<T> {
119        self.dynamic
120            .lazy()
121            .into_typed()
122            .ok()
123            .expect("ManagedGc cannot be lazily borrowed")
124    }
125
126    /// # Safety
127    pub unsafe fn as_ptr(&self) -> *const T {
128        unsafe { self.dynamic.as_ptr_raw().cast::<T>() }
129    }
130
131    /// # Safety
132    pub unsafe fn as_mut_ptr(&mut self) -> *mut T {
133        unsafe { self.dynamic.as_mut_ptr_raw().cast::<T>() }
134    }
135}
136
137impl<T> Clone for ManagedGc<T> {
138    fn clone(&self) -> Self {
139        Self {
140            dynamic: self.dynamic.clone(),
141            _phantom: PhantomData,
142        }
143    }
144}
145
146pub struct DynamicManagedGc {
147    type_hash: TypeHash,
148    kind: Kind<u8>,
149    layout: Layout,
150    finalizer: unsafe fn(*mut ()),
151    drop: bool,
152}
153
154unsafe impl Send for DynamicManagedGc {}
155unsafe impl Sync for DynamicManagedGc {}
156
157impl Drop for DynamicManagedGc {
158    fn drop(&mut self) {
159        if let Kind::Owned { data, .. } = self.kind
160            && self.drop
161        {
162            unsafe {
163                if data.is_null() {
164                    return;
165                }
166                (self.finalizer)(data.cast::<()>());
167                non_zero_dealloc(data, self.layout);
168            }
169        }
170    }
171}
172
173impl DynamicManagedGc {
174    pub fn new<T: Finalize>(data: T) -> Self {
175        let layout = Layout::new::<T>().pad_to_align();
176        unsafe {
177            let memory = non_zero_alloc(layout) as *mut T;
178            if memory.is_null() {
179                handle_alloc_error(layout);
180            }
181            memory.cast::<T>().write(data);
182            Self {
183                type_hash: TypeHash::of::<T>(),
184                kind: Kind::Owned {
185                    lifetime: Default::default(),
186                    data: memory.cast::<u8>(),
187                },
188                layout,
189                finalizer: T::finalize_raw,
190                drop: true,
191            }
192        }
193    }
194
195    pub fn new_raw(
196        type_hash: TypeHash,
197        lifetime: Lifetime,
198        memory: *mut u8,
199        layout: Layout,
200        finalizer: unsafe fn(*mut ()),
201    ) -> Self {
202        if memory.is_null() {
203            handle_alloc_error(layout);
204        }
205        Self {
206            type_hash,
207            kind: Kind::Owned {
208                lifetime,
209                data: memory,
210            },
211            layout,
212            finalizer,
213            drop: true,
214        }
215    }
216
217    pub fn new_uninitialized(
218        type_hash: TypeHash,
219        layout: Layout,
220        finalizer: unsafe fn(*mut ()),
221    ) -> Self {
222        let memory = unsafe { non_zero_alloc(layout) };
223        if memory.is_null() {
224            handle_alloc_error(layout);
225        }
226        Self {
227            type_hash,
228            kind: Kind::Owned {
229                lifetime: Default::default(),
230                data: memory,
231            },
232            layout,
233            finalizer,
234            drop: true,
235        }
236    }
237
238    pub fn consume<T>(mut self) -> Result<T, Self> {
239        if let Kind::Owned { lifetime, data } = &mut self.kind {
240            if self.type_hash == TypeHash::of::<T>() && !lifetime.state().is_in_use() {
241                if data.is_null() {
242                    return Err(self);
243                }
244                self.drop = false;
245                let mut result = MaybeUninit::<T>::uninit();
246                unsafe {
247                    result.as_mut_ptr().copy_from(data.cast::<T>(), 1);
248                    non_zero_dealloc(*data, self.layout);
249                    Ok(result.assume_init())
250                }
251            } else {
252                Err(self)
253            }
254        } else {
255            Err(self)
256        }
257    }
258
259    pub fn into_typed<T>(self) -> ManagedGc<T> {
260        ManagedGc {
261            dynamic: self,
262            _phantom: PhantomData,
263        }
264    }
265
266    pub fn renew(&mut self) {
267        if let Kind::Owned { lifetime, .. } = &mut self.kind {
268            *lifetime = Default::default();
269        }
270    }
271
272    pub fn type_hash(&self) -> TypeHash {
273        self.type_hash
274    }
275
276    pub fn lifetime(&self) -> ManagedGcLifetime<'_> {
277        match &self.kind {
278            Kind::Owned { lifetime, .. } => ManagedGcLifetime::Owned(lifetime),
279            Kind::Referenced { lifetime, .. } => ManagedGcLifetime::Referenced(lifetime),
280        }
281    }
282
283    pub fn exists(&self) -> bool {
284        match &self.kind {
285            Kind::Owned { .. } => true,
286            Kind::Referenced { lifetime, .. } => lifetime.exists(),
287        }
288    }
289
290    pub fn is_owning(&self) -> bool {
291        matches!(self.kind, Kind::Owned { .. })
292    }
293
294    pub fn is_referencing(&self) -> bool {
295        matches!(self.kind, Kind::Referenced { .. })
296    }
297
298    pub fn is_owned_by(&self, other: &Self) -> bool {
299        if let (Kind::Referenced { lifetime: l1, .. }, Kind::Owned { lifetime: l2, .. }) =
300            (&self.kind, &other.kind)
301        {
302            l1.state().is_owned_by(l2.state())
303        } else {
304            false
305        }
306    }
307
308    pub fn is<T>(&self) -> bool {
309        self.type_hash == TypeHash::of::<T>()
310    }
311
312    pub fn read<const LOCKING: bool, T>(&'_ self) -> ValueReadAccess<'_, T> {
313        if !self.is::<T>() {
314            panic!("DynamicManagedGc is not of the requested type");
315        }
316        unsafe {
317            if LOCKING {
318                match &self.kind {
319                    Kind::Owned { lifetime, data } => loop {
320                        let data = data
321                            .cast::<T>()
322                            .as_ref()
323                            .expect("DynamicManagedGc data pointer is null");
324                        if let Some(access) = lifetime.read(data) {
325                            return access;
326                        }
327                        std::hint::spin_loop();
328                    },
329                    Kind::Referenced { lifetime, data } => loop {
330                        let data = data
331                            .cast::<T>()
332                            .as_ref()
333                            .expect("DynamicManagedGc data pointer is null");
334                        if let Some(access) = lifetime.read(data) {
335                            return access;
336                        }
337                        std::hint::spin_loop();
338                    },
339                }
340            } else {
341                match &self.kind {
342                    Kind::Owned { lifetime, data } => {
343                        let data = data
344                            .cast::<T>()
345                            .as_ref()
346                            .expect("DynamicManagedGc data pointer is null");
347                        lifetime
348                            .read(data)
349                            .expect("DynamicManagedGc is inaccessible for reading")
350                    }
351                    Kind::Referenced { lifetime, data } => {
352                        let data = data
353                            .cast::<T>()
354                            .as_ref()
355                            .expect("DynamicManagedGc data pointer is null");
356                        lifetime
357                            .read(data)
358                            .expect("DynamicManagedGc is inaccessible for reading")
359                    }
360                }
361            }
362        }
363    }
364
365    pub fn write<const LOCKING: bool, T>(&'_ mut self) -> ValueWriteAccess<'_, T> {
366        if !self.is::<T>() {
367            panic!("DynamicManagedGc is not of the requested type");
368        }
369        unsafe {
370            if LOCKING {
371                match &self.kind {
372                    Kind::Owned { lifetime, data } => loop {
373                        let data = data
374                            .cast::<T>()
375                            .as_mut()
376                            .expect("DynamicManagedGc data pointer is null");
377                        if let Some(access) = lifetime.write(data) {
378                            return access;
379                        }
380                        std::hint::spin_loop();
381                    },
382                    Kind::Referenced { lifetime, data } => loop {
383                        let data = data
384                            .cast::<T>()
385                            .as_mut()
386                            .expect("DynamicManagedGc data pointer is null");
387                        if let Some(access) = lifetime.write(data) {
388                            return access;
389                        }
390                        std::hint::spin_loop();
391                    },
392                }
393            } else {
394                match &self.kind {
395                    Kind::Owned { lifetime, data } => {
396                        let data = data
397                            .cast::<T>()
398                            .as_mut()
399                            .expect("DynamicManagedGc data pointer is null");
400                        lifetime
401                            .write(data)
402                            .expect("DynamicManagedGc is inaccessible for writing")
403                    }
404                    Kind::Referenced { lifetime, data } => {
405                        let data = data
406                            .cast::<T>()
407                            .as_mut()
408                            .expect("DynamicManagedGc data pointer is null");
409                        lifetime
410                            .write(data)
411                            .expect("DynamicManagedGc is inaccessible for writing")
412                    }
413                }
414            }
415        }
416    }
417
418    pub fn borrow<const LOCKING: bool>(&self) -> DynamicManagedRef {
419        unsafe {
420            if LOCKING {
421                match &self.kind {
422                    Kind::Owned { lifetime, data } => loop {
423                        if let Some(lifetime) = lifetime.borrow() {
424                            return DynamicManagedRef::new_raw(self.type_hash, lifetime, *data)
425                                .expect("DynamicManagedGc cannot be immutably borrowed");
426                        }
427                        std::hint::spin_loop();
428                    },
429                    Kind::Referenced { lifetime, data } => loop {
430                        if let Some(lifetime) = lifetime.borrow() {
431                            return DynamicManagedRef::new_raw(self.type_hash, lifetime, *data)
432                                .expect("DynamicManagedGc cannot be immutably borrowed");
433                        }
434                        std::hint::spin_loop();
435                    },
436                }
437            } else {
438                match &self.kind {
439                    Kind::Owned { lifetime, data } => DynamicManagedRef::new_raw(
440                        self.type_hash,
441                        lifetime
442                            .borrow()
443                            .expect("DynamicManagedGc is inaccessible for immutable borrowing"),
444                        *data,
445                    )
446                    .expect("DynamicManagedGc cannot be immutably borrowed"),
447                    Kind::Referenced { lifetime, data } => DynamicManagedRef::new_raw(
448                        self.type_hash,
449                        lifetime
450                            .borrow()
451                            .expect("DynamicManagedGc is inaccessible for immutable borrowing"),
452                        *data,
453                    )
454                    .expect("DynamicManagedGc cannot be immutably borrowed"),
455                }
456            }
457        }
458    }
459
460    pub fn borrow_mut<const LOCKING: bool>(&mut self) -> DynamicManagedRefMut {
461        unsafe {
462            if LOCKING {
463                match &self.kind {
464                    Kind::Owned { lifetime, data } => loop {
465                        if let Some(lifetime) = lifetime.borrow_mut() {
466                            return DynamicManagedRefMut::new_raw(self.type_hash, lifetime, *data)
467                                .expect("DynamicManagedGc cannot be mutably borrowed");
468                        }
469                        std::hint::spin_loop();
470                    },
471                    Kind::Referenced { lifetime, data } => loop {
472                        if let Some(lifetime) = lifetime.borrow_mut() {
473                            return DynamicManagedRefMut::new_raw(self.type_hash, lifetime, *data)
474                                .expect("DynamicManagedGc cannot be mutably borrowed");
475                        }
476                        std::hint::spin_loop();
477                    },
478                }
479            } else {
480                match &self.kind {
481                    Kind::Owned { lifetime, data } => DynamicManagedRefMut::new_raw(
482                        self.type_hash,
483                        lifetime
484                            .borrow_mut()
485                            .expect("DynamicManagedGc is inaccessible for mutable borrowing"),
486                        *data,
487                    )
488                    .expect("DynamicManagedGc cannot be mutably borrowed"),
489                    Kind::Referenced { lifetime, data } => DynamicManagedRefMut::new_raw(
490                        self.type_hash,
491                        lifetime
492                            .borrow_mut()
493                            .expect("DynamicManagedGc is inaccessible for mutable borrowing"),
494                        *data,
495                    )
496                    .expect("DynamicManagedGc cannot be mutably borrowed"),
497                }
498            }
499        }
500    }
501
502    pub fn lazy(&self) -> DynamicManagedLazy {
503        unsafe {
504            match &self.kind {
505                Kind::Owned { lifetime, data } => {
506                    DynamicManagedLazy::new_raw(self.type_hash, lifetime.lazy(), *data)
507                        .expect("DynamicManagedGc cannot be lazily borrowed")
508                }
509                Kind::Referenced { lifetime, data } => {
510                    DynamicManagedLazy::new_raw(self.type_hash, lifetime.clone(), *data)
511                        .expect("DynamicManagedGc cannot be lazily borrowed")
512                }
513            }
514        }
515    }
516
517    /// # Safety
518    pub unsafe fn as_ptr_raw(&self) -> *const u8 {
519        match &self.kind {
520            Kind::Owned { data, .. } => *data as *const u8,
521            Kind::Referenced { data, .. } => *data as *const u8,
522        }
523    }
524
525    /// # Safety
526    pub unsafe fn as_mut_ptr_raw(&mut self) -> *mut u8 {
527        match &self.kind {
528            Kind::Owned { data, .. } => *data,
529            Kind::Referenced { data, .. } => *data,
530        }
531    }
532}
533
534impl Clone for DynamicManagedGc {
535    fn clone(&self) -> Self {
536        match &self.kind {
537            Kind::Owned { lifetime, data } => Self {
538                type_hash: self.type_hash,
539                kind: Kind::Referenced {
540                    lifetime: lifetime.lazy(),
541                    data: *data,
542                },
543                layout: self.layout,
544                finalizer: self.finalizer,
545                drop: true,
546            },
547            Kind::Referenced { lifetime, data } => Self {
548                type_hash: self.type_hash,
549                kind: Kind::Referenced {
550                    lifetime: lifetime.clone(),
551                    data: *data,
552                },
553                layout: self.layout,
554                finalizer: self.finalizer,
555                drop: true,
556            },
557        }
558    }
559}
560
561#[cfg(test)]
562mod tests {
563    use super::*;
564
565    #[test]
566    fn test_is_async() {
567        fn is_async<T: Send + Sync>() {}
568
569        is_async::<ManagedGc<()>>();
570        is_async::<DynamicManagedGc>();
571    }
572
573    #[test]
574    fn test_managed_gc() {
575        let mut managed = ManagedGc::new(42);
576        {
577            let read_access = managed.read::<true>();
578            assert_eq!(*read_access, 42);
579        }
580        {
581            let mut write_access = managed.write::<true>();
582            *write_access = 100;
583        }
584        {
585            let read_access = managed.read::<true>();
586            assert_eq!(*read_access, 100);
587        }
588    }
589
590    #[test]
591    fn test_managed_gc_cycles() {
592        #[derive(Default)]
593        struct Foo {
594            other: Option<ManagedGc<Self>>,
595        }
596
597        {
598            let mut a = ManagedGc::<Foo>::default();
599            let mut b = ManagedGc::<Foo>::default();
600            a.write::<true>().other = Some(b.clone());
601            b.write::<true>().other = Some(a.clone());
602
603            assert!(a.exists());
604            assert!(a.is_owning());
605            assert!(a.read::<true>().other.as_ref().unwrap().is_referencing());
606            assert!(a.read::<true>().other.as_ref().unwrap().is_owned_by(&b));
607
608            assert!(b.exists());
609            assert!(b.is_owning());
610            assert!(b.read::<true>().other.as_ref().unwrap().is_referencing());
611            assert!(b.read::<true>().other.as_ref().unwrap().is_owned_by(&a));
612
613            drop(b);
614            assert!(!a.read::<true>().other.as_ref().unwrap().exists());
615        }
616
617        {
618            let mut a = ManagedGc::<Foo>::default();
619            a.write::<true>().other = Some(a.clone());
620
621            assert!(a.exists());
622            assert!(a.is_owning());
623            assert!(a.read::<true>().other.as_ref().unwrap().is_referencing());
624            assert!(a.read::<true>().other.as_ref().unwrap().is_owned_by(&a));
625        }
626    }
627
628    #[test]
629    fn test_dynamic_managed_gc() {
630        let mut managed = DynamicManagedGc::new(42);
631        {
632            let read_access = managed.read::<true, i32>();
633            assert_eq!(*read_access, 42);
634        }
635        {
636            let mut write_access = managed.write::<true, i32>();
637            *write_access = 100;
638        }
639        {
640            let read_access = managed.read::<true, i32>();
641            assert_eq!(*read_access, 100);
642        }
643    }
644
645    #[test]
646    fn test_dynamic_managed_gc_cycles() {
647        #[derive(Default)]
648        struct Foo {
649            other: Option<DynamicManagedGc>,
650        }
651
652        {
653            let mut a = DynamicManagedGc::new(Foo::default());
654            let mut b = DynamicManagedGc::new(Foo::default());
655            a.write::<true, Foo>().other = Some(b.clone());
656            b.write::<true, Foo>().other = Some(a.clone());
657
658            assert!(a.exists());
659            assert!(a.is_owning());
660            assert!(
661                a.read::<true, Foo>()
662                    .other
663                    .as_ref()
664                    .unwrap()
665                    .is_referencing()
666            );
667            assert!(
668                a.read::<true, Foo>()
669                    .other
670                    .as_ref()
671                    .unwrap()
672                    .is_owned_by(&b)
673            );
674
675            assert!(b.exists());
676            assert!(b.is_owning());
677            assert!(
678                b.read::<true, Foo>()
679                    .other
680                    .as_ref()
681                    .unwrap()
682                    .is_referencing()
683            );
684            assert!(
685                b.read::<true, Foo>()
686                    .other
687                    .as_ref()
688                    .unwrap()
689                    .is_owned_by(&a)
690            );
691
692            drop(b);
693            assert!(!a.read::<true, Foo>().other.as_ref().unwrap().exists());
694        }
695
696        {
697            let mut a = DynamicManagedGc::new(Foo::default());
698            a.write::<true, Foo>().other = Some(a.clone());
699
700            assert!(a.exists());
701            assert!(a.is_owning());
702            assert!(
703                a.read::<true, Foo>()
704                    .other
705                    .as_ref()
706                    .unwrap()
707                    .is_referencing()
708            );
709            assert!(
710                a.read::<true, Foo>()
711                    .other
712                    .as_ref()
713                    .unwrap()
714                    .is_owned_by(&a)
715            );
716        }
717    }
718
719    #[test]
720    fn test_gc_conversions() {
721        let managed = ManagedGc::new(42);
722        assert_eq!(*managed.read::<true>(), 42);
723
724        let mut dynamic = managed.into_dynamic();
725        *dynamic.write::<true, i32>() = 100;
726
727        let managed = dynamic.into_typed::<i32>();
728        assert_eq!(*managed.read::<true>(), 100);
729    }
730}