regc/
lib.rs

1use std::any::type_name;
2use std::cell::Cell;
3use std::fmt::{Debug, Formatter};
4use std::marker::PhantomData;
5use std::mem::{forget, transmute, ManuallyDrop};
6use std::ops::Deref;
7use std::ptr::NonNull;
8
9use log::{info, trace, warn};
10
11pub mod trace;
12
13#[derive(Copy, Clone, Eq, PartialEq, Debug)]
14enum GcState {
15    Active,
16    Dropped,
17    Tracked,
18    Untracked,
19}
20
21struct GcInfo<'c> {
22    next: Cell<Option<NonNullGcBox<'c>>>,
23    prev: Cell<Option<NonNullGcBox<'c>>>,
24    state: Cell<GcState>,
25    root: Cell<usize>,
26    count: Cell<usize>,
27}
28
29#[repr(C)]
30struct GcBox<'c, T: GcTarget<'c> + ?Sized + 'c> {
31    metadata: &'static (),
32    info: GcInfo<'c>,
33    value: ManuallyDrop<T>,
34}
35
36impl<'c, T: GcTarget<'c> + 'c> GcBox<'c, T> {
37    fn new(value: T) -> Self {
38        let mut r = Self {
39            metadata: GcBoxDynPtr::from_ptr(std::ptr::null::<GcBox<'c, T>>()).metadata,
40            info: GcInfo {
41                next: Cell::new(None),
42                prev: Cell::new(None),
43                state: Cell::new(GcState::Active),
44                root: Cell::new(0),
45                count: Cell::new(0),
46            },
47            value: ManuallyDrop::new(value),
48        };
49        r.metadata = GcBoxDynPtr::from_ptr(&r).metadata;
50        r
51    }
52
53    fn alloc(value: T) -> NonNull<Self> {
54        let r = Box::into_raw(Box::new(Self::new(value)));
55        trace!("alloc {} {:?}", type_name::<T>(), r as *mut ());
56        unsafe { NonNull::new_unchecked(r) }
57    }
58}
59
60impl<'c, T: GcTarget<'c> + ?Sized> GcBox<'c, T> {
61    fn value(&self) -> *const T {
62        self.value.deref()
63    }
64
65    unsafe fn free(this: NonNull<Self>) {
66        trace!("free {:?}", this.as_ptr() as *mut ());
67        drop(Box::from_raw(this.as_ptr()));
68    }
69
70    unsafe fn remove(this: NonNull<Self>) {
71        let r = this.as_ref();
72        let prev = r.info.prev.get();
73        let next = r.info.next.get();
74        prev.unwrap_unchecked().as_ref().info.next.set(next);
75        next.unwrap_unchecked().as_ref().info.prev.set(prev);
76        Self::free(this);
77    }
78
79    unsafe fn drop_value(&mut self) {
80        trace!("drop {:?}", self as *mut Self as *mut ());
81        ManuallyDrop::drop(&mut self.value);
82    }
83
84    unsafe fn check_ref(mut this: NonNull<Self>) {
85        let r = this.as_ref();
86        match r.info.state.get() {
87            GcState::Active => {
88                if r.info.root.get() == 0 && r.info.count.get() == 0 {
89                    r.info.state.set(GcState::Dropped);
90                    Self::drop_value(this.as_mut());
91                    Self::remove(this);
92                }
93            }
94            GcState::Dropped => Self::remove(this),
95            GcState::Tracked | GcState::Untracked => {}
96        }
97    }
98}
99
100#[derive(Copy, Clone)]
101#[repr(C)]
102struct GcBoxDynPtr<'c> {
103    ptr: *const (),
104    metadata: &'static (),
105    marker: PhantomData<*mut GcBox<'c, dyn GcTarget<'c> + 'c>>,
106}
107
108impl<'c> GcBoxDynPtr<'c> {
109    const fn from_part(ptr: *const (), metadata: &'static ()) -> Self {
110        Self {
111            ptr,
112            metadata,
113            marker: PhantomData,
114        }
115    }
116
117    const fn from_ptr(ptr: *const GcBox<'c, dyn GcTarget<'c> + 'c>) -> Self {
118        unsafe { transmute(ptr) }
119    }
120
121    const fn as_mut(self) -> *mut GcBox<'c, dyn GcTarget<'c> + 'c> {
122        unsafe { transmute(self) }
123    }
124}
125
126#[derive(Copy, Clone, Eq, PartialEq)]
127#[repr(transparent)]
128struct NonNullGcBox<'c> {
129    ptr: NonNull<()>,
130    marker: PhantomData<*const GcBox<'c, dyn GcTarget<'c> + 'c>>,
131}
132
133impl<'c> NonNullGcBox<'c> {
134    const fn from_non_null<T: GcTarget<'c> + ?Sized + 'c>(ptr: NonNull<GcBox<'c, T>>) -> Self {
135        Self {
136            ptr: ptr.cast(),
137            marker: PhantomData,
138        }
139    }
140
141    fn from_ptr<T: GcTarget<'c> + ?Sized + 'c>(ptr: *const GcBox<'c, T>) -> Option<Self> {
142        NonNull::new(ptr.cast_mut()).map(|ptr| Self {
143            ptr: ptr.cast(),
144            marker: PhantomData,
145        })
146    }
147
148    fn as_non_null(self) -> NonNull<GcBox<'c, dyn GcTarget<'c> + 'c>> {
149        unsafe {
150            let metadata = *self.ptr.as_ptr().cast::<&'static ()>();
151            NonNull::new_unchecked(GcBoxDynPtr::from_part(self.ptr.as_ptr(), metadata).as_mut())
152        }
153    }
154
155    fn as_ptr(self) -> *const GcBox<'c, dyn GcTarget<'c> + 'c> {
156        self.as_non_null().as_ptr()
157    }
158
159    unsafe fn as_ref(&self) -> &GcBox<'c, dyn GcTarget<'c> + 'c> {
160        &*self.as_ptr()
161    }
162}
163
164impl<'c> Debug for NonNullGcBox<'c> {
165    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
166        Debug::fmt(&self.ptr, f)
167    }
168}
169
170#[derive(Eq, PartialEq)]
171pub struct GcRootThin<'c> {
172    ptr: NonNullGcBox<'c>,
173    marker: PhantomData<GcRoot<'c, dyn GcTarget<'c> + 'c>>,
174}
175
176impl<'c> Drop for GcRootThin<'c> {
177    fn drop(&mut self) {
178        unsafe {
179            let node = self.ptr.as_ref();
180            node.info.root.set(node.info.root.get() - 1);
181            GcBox::check_ref(self.ptr.as_non_null());
182        }
183    }
184}
185
186impl<'c> GcRootThin<'c> {
187    unsafe fn from_box(ptr: NonNull<GcBox<'c, dyn GcTarget<'c> + 'c>>) -> Self {
188        let r = ptr.as_ref();
189        r.info.root.set(r.info.root.get() + 1);
190        Self {
191            ptr: NonNullGcBox::from_non_null(ptr),
192            marker: PhantomData,
193        }
194    }
195
196    pub fn base_ptr(&self) -> NonNull<()> {
197        self.ptr.ptr
198    }
199
200    pub fn as_ptr(&self) -> *const (dyn GcTarget<'c> + 'c) {
201        unsafe { self.ptr.as_ref().value() }
202    }
203
204    pub fn downgrade(&self) -> GcObjectThin<'c> {
205        unsafe { GcObjectThin::from_box(self.ptr.as_non_null()) }
206    }
207
208    pub fn cast_fat(self) -> GcRoot<'c, dyn GcTarget<'c> + 'c> {
209        let r = GcRoot {
210            ptr: self.ptr.as_non_null(),
211        };
212        forget(self);
213        r
214    }
215}
216
217impl<'c> Clone for GcRootThin<'c> {
218    fn clone(&self) -> Self {
219        unsafe { Self::from_box(self.ptr.as_non_null()) }
220    }
221}
222
223impl<'c> GcTarget<'c> for GcRootThin<'c> {
224    fn trace(&self, token: &mut GcTraceToken<'c>) {
225        let _ = token;
226    }
227}
228
229impl<'c> Debug for GcRootThin<'c> {
230    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
231        Debug::fmt(&self.ptr, f)
232    }
233}
234
235impl<'c> Deref for GcRootThin<'c> {
236    type Target = dyn GcTarget<'c> + 'c;
237
238    fn deref(&self) -> &Self::Target {
239        unsafe { &*self.as_ptr() }
240    }
241}
242
243#[derive(Eq)]
244pub struct GcRoot<'c, T: GcTarget<'c> + ?Sized + 'c> {
245    ptr: NonNull<GcBox<'c, T>>,
246}
247
248impl<'c, T: GcTarget<'c> + ?Sized + 'c> Drop for GcRoot<'c, T> {
249    fn drop(&mut self) {
250        unsafe {
251            let node = self.ptr.as_ref();
252            node.info.root.set(node.info.root.get() - 1);
253            GcBox::check_ref(self.ptr);
254        }
255    }
256}
257
258impl<'c, T: GcTarget<'c> + ?Sized + 'c> GcRoot<'c, T> {
259    unsafe fn from_box(ptr: NonNull<GcBox<'c, T>>) -> Self {
260        let r = ptr.as_ref();
261        r.info.root.set(r.info.root.get() + 1);
262        Self { ptr }
263    }
264
265    pub fn base_ptr(&self) -> NonNull<()> {
266        self.ptr.cast()
267    }
268
269    pub fn as_ptr(&self) -> *const T {
270        unsafe { self.ptr.as_ref().value() }
271    }
272
273    pub fn downgrade(&self) -> GcObject<'c, T> {
274        unsafe { GcObject::from_box(self.ptr) }
275    }
276
277    pub fn cast_dyn(self) -> GcRoot<'c, dyn GcTarget<'c> + 'c> {
278        unsafe {
279            let r = GcRoot {
280                ptr: NonNull::new_unchecked(
281                    NonNullGcBox::from_non_null(self.ptr).as_ptr().cast_mut(),
282                ),
283            };
284            forget(self);
285            r
286        }
287    }
288
289    pub fn cast_thin(self) -> GcRootThin<'c> {
290        let r = GcRootThin {
291            ptr: NonNullGcBox::from_non_null(self.ptr),
292            marker: PhantomData,
293        };
294        forget(self);
295        r
296    }
297}
298
299impl<'c, T: GcTarget<'c> + ?Sized + 'c> PartialEq for GcRoot<'c, T> {
300    fn eq(&self, other: &Self) -> bool {
301        PartialEq::eq(&self.ptr.cast::<()>(), &other.ptr.cast::<()>())
302    }
303    fn ne(&self, other: &Self) -> bool {
304        PartialEq::ne(&self.ptr.cast::<()>(), &other.ptr.cast::<()>())
305    }
306}
307
308impl<'c, T: GcTarget<'c> + ?Sized + 'c> Clone for GcRoot<'c, T> {
309    fn clone(&self) -> Self {
310        unsafe { Self::from_box(self.ptr) }
311    }
312}
313
314impl<'c, T: GcTarget<'c> + ?Sized + 'c> GcTarget<'c> for GcRoot<'c, T> {
315    fn trace(&self, token: &mut GcTraceToken<'c>) {
316        let _ = token;
317    }
318}
319
320impl<'c, T: GcTarget<'c> + ?Sized + 'c> Debug for GcRoot<'c, T> {
321    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
322        Debug::fmt(&self.ptr, f)
323    }
324}
325
326impl<'c, T: GcTarget<'c> + ?Sized + 'c> Deref for GcRoot<'c, T> {
327    type Target = T;
328
329    fn deref(&self) -> &Self::Target {
330        unsafe { &*self.as_ptr() }
331    }
332}
333
334#[derive(Eq, PartialEq)]
335pub struct GcObjectThin<'c> {
336    ptr: NonNullGcBox<'c>,
337    marker: PhantomData<GcObject<'c, dyn GcTarget<'c> + 'c>>,
338}
339
340impl<'c> Drop for GcObjectThin<'c> {
341    fn drop(&mut self) {
342        unsafe {
343            let node = self.ptr.as_ref();
344            node.info.count.set(node.info.count.get() - 1);
345            GcBox::check_ref(self.ptr.as_non_null());
346        }
347    }
348}
349
350impl<'c> GcObjectThin<'c> {
351    unsafe fn from_box(ptr: NonNull<GcBox<'c, dyn GcTarget<'c> + 'c>>) -> Self {
352        let r = ptr.as_ref();
353        r.info.count.set(r.info.count.get() + 1);
354        Self {
355            ptr: NonNullGcBox::from_non_null(ptr),
356            marker: PhantomData,
357        }
358    }
359
360    pub fn base_ptr(&self) -> NonNull<()> {
361        self.ptr.ptr
362    }
363
364    pub fn as_ptr(&self) -> *const (dyn GcTarget<'c> + 'c) {
365        unsafe { self.ptr.as_ref().value() }
366    }
367
368    pub fn upgrade(&self) -> Option<GcRootThin<'c>> {
369        let r = unsafe { self.ptr.as_ref() };
370        match r.info.state.get() {
371            GcState::Active | GcState::Tracked => unsafe {
372                Some(GcRootThin::from_box(self.ptr.as_non_null()))
373            },
374            GcState::Dropped | GcState::Untracked => None,
375        }
376    }
377
378    pub fn cast_fat(self) -> GcObject<'c, dyn GcTarget<'c> + 'c> {
379        let r = GcObject {
380            ptr: self.ptr.as_non_null(),
381        };
382        forget(self);
383        r
384    }
385}
386
387impl<'c> Clone for GcObjectThin<'c> {
388    fn clone(&self) -> Self {
389        unsafe { Self::from_box(self.ptr.as_non_null()) }
390    }
391}
392
393impl<'c> GcTarget<'c> for GcObjectThin<'c> {
394    fn trace(&self, token: &mut GcTraceToken<'c>) {
395        token.accept_thin(self);
396    }
397}
398
399impl<'c> Debug for GcObjectThin<'c> {
400    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
401        Debug::fmt(&self.ptr, f)
402    }
403}
404
405pub struct GcObject<'c, T: GcTarget<'c> + ?Sized + 'c> {
406    ptr: NonNull<GcBox<'c, T>>,
407}
408
409impl<'c, T: GcTarget<'c> + ?Sized + 'c> Drop for GcObject<'c, T> {
410    fn drop(&mut self) {
411        unsafe {
412            let node = self.ptr.as_ref();
413            node.info.count.set(node.info.count.get() - 1);
414            GcBox::check_ref(self.ptr);
415        }
416    }
417}
418
419impl<'c, T: GcTarget<'c> + ?Sized + 'c> GcObject<'c, T> {
420    unsafe fn from_box(ptr: NonNull<GcBox<'c, T>>) -> Self {
421        let r = ptr.as_ref();
422        r.info.count.set(r.info.count.get() + 1);
423        Self { ptr }
424    }
425
426    pub fn base_ptr(&self) -> NonNull<()> {
427        self.ptr.cast()
428    }
429
430    pub fn as_ptr(&self) -> *const T {
431        unsafe { self.ptr.as_ref().value() }
432    }
433
434    pub fn upgrade(&self) -> Option<GcRoot<'c, T>> {
435        let r = unsafe { &*self.ptr.as_ptr() };
436        match r.info.state.get() {
437            GcState::Active | GcState::Tracked => unsafe { Some(GcRoot::from_box(self.ptr)) },
438            GcState::Dropped | GcState::Untracked => None,
439        }
440    }
441
442    pub fn cast_dyn(self) -> GcObject<'c, dyn GcTarget<'c> + 'c> {
443        let r = GcObject {
444            ptr: NonNullGcBox::from_non_null(self.ptr).as_non_null(),
445        };
446        forget(self);
447        r
448    }
449
450    pub fn cast_thin(self) -> GcObjectThin<'c> {
451        let r = GcObjectThin {
452            ptr: NonNullGcBox::from_non_null(self.ptr),
453            marker: PhantomData,
454        };
455        forget(self);
456        r
457    }
458}
459
460impl<'c, T: GcTarget<'c> + ?Sized + 'c> PartialEq for GcObject<'c, T> {
461    fn eq(&self, other: &Self) -> bool {
462        PartialEq::eq(&self.ptr.cast::<()>(), &other.ptr.cast::<()>())
463    }
464    fn ne(&self, other: &Self) -> bool {
465        PartialEq::ne(&self.ptr.cast::<()>(), &other.ptr.cast::<()>())
466    }
467}
468
469impl<'c, T: GcTarget<'c> + ?Sized + 'c> Clone for GcObject<'c, T> {
470    fn clone(&self) -> Self {
471        unsafe { Self::from_box(self.ptr) }
472    }
473}
474
475impl<'c, T: GcTarget<'c> + ?Sized + 'c> GcTarget<'c> for GcObject<'c, T> {
476    fn trace(&self, token: &mut GcTraceToken<'c>) {
477        token.accept(self);
478    }
479}
480
481impl<'c, T: GcTarget<'c> + ?Sized + 'c> Debug for GcObject<'c, T> {
482    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
483        Debug::fmt(&self.ptr, f)
484    }
485}
486
487pub struct GcTraceToken<'c> {
488    head: Option<NonNullGcBox<'c>>,
489}
490
491impl<'c> GcTraceToken<'c> {
492    unsafe fn push(&mut self, node: NonNullGcBox<'c>) {
493        let node = node.as_ref();
494        node.info.next.set(self.head);
495        self.head = NonNullGcBox::from_ptr(node);
496    }
497
498    unsafe fn pop(&mut self) -> Option<NonNullGcBox<'c>> {
499        let r = self.head;
500        if r != None {
501            self.head = r.unwrap_unchecked().as_ref().info.next.get();
502            r
503        } else {
504            None
505        }
506    }
507
508    unsafe fn accept_box<T: GcTarget<'c> + ?Sized + 'c>(&mut self, value: NonNull<GcBox<'c, T>>) {
509        let value = value.as_ref();
510        match value.info.state.get() {
511            GcState::Untracked => {
512                value.info.state.set(GcState::Tracked);
513                value.info.next.set(self.head);
514                self.head = NonNullGcBox::from_ptr(value);
515            }
516            GcState::Tracked | GcState::Active | GcState::Dropped => {}
517        }
518    }
519
520    pub fn accept<T: GcTarget<'c> + ?Sized + 'c>(&mut self, value: &GcObject<'c, T>) {
521        unsafe { self.accept_box(value.ptr) };
522    }
523
524    pub fn accept_thin(&mut self, value: &GcObjectThin<'c>) {
525        unsafe { self.accept_box(value.ptr.as_non_null()) };
526    }
527}
528
529pub trait GcTarget<'c> {
530    fn trace(&self, token: &mut GcTraceToken<'c>);
531}
532
533struct GcNodeBackIter<'c> {
534    node: Option<NonNullGcBox<'c>>,
535}
536
537impl<'c> GcNodeBackIter<'c> {
538    unsafe fn clone(&self) -> Self {
539        Self { node: self.node }
540    }
541
542    fn steal(gc: &GcContextRaw<'c>) -> Self {
543        let head = gc.head.deref();
544        let tail = gc.tail.deref();
545        let left = gc.head.info.next.get();
546        let right = gc.tail.info.prev.get();
547        if left == NonNullGcBox::from_ptr(tail) {
548            debug_assert!(right == NonNullGcBox::from_ptr(head));
549            return Self { node: None };
550        }
551
552        head.info.next.set(NonNullGcBox::from_ptr(tail));
553        tail.info.prev.set(NonNullGcBox::from_ptr(head));
554        unsafe {
555            left.unwrap_unchecked().as_ref().info.prev.set(None);
556            right.unwrap_unchecked().as_ref().info.next.set(None);
557            Self { node: right }
558        }
559    }
560
561    fn is_empty(&self) -> bool {
562        self.node == None
563    }
564}
565
566impl<'c> Iterator for GcNodeBackIter<'c> {
567    type Item = NonNullGcBox<'c>;
568
569    fn next(&mut self) -> Option<Self::Item> {
570        let current = self.node;
571        if let Some(current) = current {
572            self.node = unsafe { current.as_ref().info.prev.get() };
573        }
574        current
575    }
576}
577
578#[derive(Copy, Clone, Eq, PartialEq, Debug)]
579enum GcContextState {
580    Normal,
581    Gc,
582}
583
584struct GcContextRaw<'c> {
585    auto_gc: usize,
586    state: Cell<GcContextState>,
587    alloc_count: Cell<usize>,
588    head: Box<GcBox<'c, ()>>,
589    tail: Box<GcBox<'c, ()>>,
590}
591
592impl<'c> GcContextRaw<'c> {
593    fn new() -> Self {
594        let head = Box::new(GcBox::new(()));
595        head.info.root.set(1);
596        let tail = Box::new(GcBox::new(()));
597        tail.info.root.set(1);
598
599        head.info.next.set(NonNullGcBox::from_ptr(tail.deref()));
600        tail.info.prev.set(NonNullGcBox::from_ptr(head.deref()));
601
602        Self {
603            state: Cell::new(GcContextState::Normal),
604            auto_gc: 0,
605            alloc_count: Cell::new(0),
606            head,
607            tail,
608        }
609    }
610
611    fn set_auto_gc(&mut self, auto_gc: usize) {
612        self.auto_gc = auto_gc;
613        self.alloc_count.set(0);
614    }
615
616    fn alloc<T: GcTarget<'c> + 'c>(&'c self, value: T) -> GcRoot<'c, T> {
617        if self.auto_gc != 0 {
618            let alloc_count = self.alloc_count.get() + 1;
619            if alloc_count == self.auto_gc {
620                self.alloc_count.set(0);
621                info!("auto gc");
622                self.gc();
623            } else {
624                self.alloc_count.set(alloc_count);
625            }
626        }
627
628        let value = GcBox::alloc(value);
629        let value_ref = unsafe { value.as_ref() };
630        let value_ptr = NonNullGcBox::from_non_null(value);
631
632        let tail = self.tail.deref();
633        let prev = tail.info.prev.get();
634
635        value_ref.info.prev.set(prev);
636        value_ref.info.next.set(NonNullGcBox::from_ptr(tail));
637        unsafe {
638            prev.unwrap_unchecked()
639                .as_ref()
640                .info
641                .next
642                .set(Some(value_ptr))
643        };
644        tail.info.prev.set(Some(value_ptr));
645
646        unsafe { GcRoot::from_box(value) }
647    }
648
649    fn gc(&self) {
650        info!("call gc");
651        match self.state.get() {
652            GcContextState::Normal => {
653                struct Guard<'s, 'c>(&'s GcContextRaw<'c>, std::time::Instant);
654
655                impl<'s, 'c> Drop for Guard<'s, 'c> {
656                    fn drop(&mut self) {
657                        self.0.state.set(GcContextState::Normal);
658                        info!("end gc {:?}", self.1.elapsed());
659                    }
660                }
661
662                info!("begin gc");
663                self.state.set(GcContextState::Gc);
664                let _guard = Guard(self, std::time::Instant::now());
665
666                let iter = GcNodeBackIter::steal(self);
667                if iter.is_empty() {
668                    return;
669                }
670                unsafe {
671                    let mut token = GcTraceToken { head: None };
672
673                    let mut count = 0;
674                    for node in iter.clone() {
675                        count += 1;
676                        let n = node.as_ref();
677                        debug_assert!(n.info.state.get() == GcState::Active);
678                        if n.info.root.get() != 0 {
679                            n.info.state.set(GcState::Tracked);
680                            token.push(node);
681                        } else {
682                            n.info.state.set(GcState::Untracked);
683                        }
684                    }
685
686                    info!("trace {} target", count);
687
688                    while let Some(node) = token.pop() {
689                        let node = node.as_ref();
690                        let value = &*node.value();
691                        value.trace(&mut token);
692                    }
693
694                    let mut hold_count = 0;
695                    let mut drop_count = 0;
696
697                    let mut that = self.head.info.next.get();
698                    for node in iter {
699                        let n = node.as_ref();
700                        let node = NonNullGcBox::from_ptr(n);
701                        match n.info.state.get() {
702                            GcState::Active | GcState::Dropped => unreachable!(),
703                            GcState::Tracked => {
704                                hold_count += 1;
705                                n.info.next.set(that);
706                                that.unwrap_unchecked().as_ref().info.prev.set(node);
707                                n.info.state.set(GcState::Active);
708                                that = node;
709                            }
710                            GcState::Untracked => {
711                                drop_count += 1;
712                                GcBox::drop_value(
713                                    &mut *node.unwrap_unchecked().as_ptr().cast_mut(),
714                                );
715                                GcBox::free(node.unwrap_unchecked().as_non_null());
716                            }
717                        }
718                    }
719
720                    info!("hold {} target", hold_count);
721                    info!("drop {} target", drop_count);
722
723                    let head = self.head.deref();
724                    that.unwrap_unchecked()
725                        .as_ref()
726                        .info
727                        .prev
728                        .set(NonNullGcBox::from_ptr(head));
729                    head.info.next.set(that);
730                }
731            }
732            GcContextState::Gc => {}
733        }
734    }
735}
736
737impl<'c> Drop for GcContextRaw<'c> {
738    fn drop(&mut self) {
739        info!("drop gc");
740        self.gc();
741        let iter = GcNodeBackIter::steal(self);
742        let mut leak_count = 0;
743        for node in iter {
744            trace!("leak {:?}", node.as_ptr());
745            leak_count += 1;
746            let n = unsafe { node.as_ref() };
747            debug_assert!(n.info.state.get() == GcState::Active);
748            n.info.prev.set(None);
749            n.info.next.set(None);
750            n.info.root.set(n.info.root.get() + 1);
751        }
752        if leak_count != 0 {
753            warn!("leak {} target", leak_count);
754        }
755    }
756}
757
758impl<'c> Debug for GcContextRaw<'c> {
759    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
760        f.debug_struct("GcContextRaw")
761            .field("auto_gc", &self.auto_gc)
762            .field("alloc_count", &self.alloc_count.get())
763            .finish()
764    }
765}
766
767#[derive(Debug)]
768pub struct GcContext<'c> {
769    inner: GcContextRaw<'static>,
770    marker: PhantomData<*mut &'c ()>,
771}
772
773impl<'c> GcContext<'c> {
774    fn inner(&self) -> &GcContextRaw<'c> {
775        unsafe { &*((&self.inner as *const GcContextRaw<'static>).cast()) }
776    }
777
778    fn inner_mut(&mut self) -> &mut GcContextRaw<'c> {
779        unsafe { &mut *((&mut self.inner as *mut GcContextRaw<'static>).cast()) }
780    }
781
782    pub fn new() -> Self {
783        Self {
784            inner: GcContextRaw::new(),
785            marker: PhantomData,
786        }
787    }
788
789    pub fn set_auto_gc(&mut self, auto_gc: usize) {
790        self.inner_mut().set_auto_gc(auto_gc);
791    }
792
793    pub fn alloc<T: GcTarget<'c> + 'c>(&'c self, value: T) -> GcRoot<'c, T> {
794        self.inner().alloc(value)
795    }
796
797    pub fn gc(&self) {
798        self.inner().gc()
799    }
800}
801
802#[macro_export]
803macro_rules! trace_none {
804    ($type:ty) => {
805        impl<'c> $crate::GcTarget<'c> for $type {
806            fn trace(&self, token: &mut $crate::GcTraceToken<'c>) {
807                let _ = token;
808            }
809        }
810    };
811}
812
813#[test]
814fn test() {
815    let _ = env_logger::try_init();
816
817    struct Foo<'c> {
818        r: std::cell::RefCell<Option<GcObject<'c, Self>>>,
819    }
820
821    impl<'c> Drop for Foo<'c> {
822        fn drop(&mut self) {
823            println!("Drop for Foo");
824        }
825    }
826
827    impl<'c> GcTarget<'c> for Foo<'c> {
828        fn trace(&self, token: &mut GcTraceToken<'c>) {
829            self.r.trace(token);
830        }
831    }
832
833    let context = GcContext::new();
834    let x = context.alloc(Foo {
835        r: std::cell::RefCell::new(None),
836    });
837    *x.r.borrow_mut() = Some(x.downgrade());
838    context.gc();
839    *x.r.borrow_mut() = None;
840}