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}