1use std::{
5 marker::PhantomData,
6 ops::{Deref, DerefMut},
7 ptr::NonNull,
8};
9
10use crate::gctype::{GcTypeRegistry, payload_offset_of};
11use crate::{GcHeap, GcPartitionId, GcTrace, GcWeak, weak::GcWeakRawId};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14#[repr(u8)]
15pub enum GcTriColor {
16 White = 0b00,
17 Gray = 0b01,
18 Black = 0b10,
19}
20
21impl From<GcTriColor> for u32 {
22 fn from(color: GcTriColor) -> Self {
23 color as u32
24 }
25}
26
27impl TryFrom<u32> for GcTriColor {
28 type Error = &'static str;
29
30 fn try_from(value: u32) -> Result<Self, Self::Error> {
31 match value {
32 0b00 => Ok(GcTriColor::White),
33 0b01 => Ok(GcTriColor::Gray),
34 0b10 => Ok(GcTriColor::Black),
35 _ => Err("Invalid value for TriColor"),
36 }
37 }
38}
39
40const COLOR_MASK: u32 = 0b11;
41
42bitflags::bitflags! {
43 #[repr(transparent)]
44 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
45 pub struct GcNodeFlag :u8 {
46 const ARENA_ALLOC = 1 << 2;
48 const GRAY_LISTED = 1 << 3;
50 const ROOT = 1 << 5;
52 const LOCAL = 1 << 6;
54 const TRAVERSE_VISITED = 1 << 7;
56 }
57}
58
59#[repr(C)]
61pub struct GcHead {
62 pub(super) attrs: u32,
70
71 pub(super) partition: u32,
73
74 pub(super) weak_id: GcWeakRawId,
75
76 pub(super) next: Option<NonNull<GcHead>>,
78
79 #[cfg(debug_assertions)]
80 pub(crate) dbg_string: std::borrow::Cow<'static, str>,
81}
82
83impl std::fmt::Debug for GcHead {
84 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85 let mut s = f.debug_struct("GcNode");
86 s.field("ptr", &(self as *const Self))
87 .field("partition", &self.partition_id())
88 .field("color", &self.color())
89 .field("local", &self.is_local());
90
91 if self.is_root() {
92 s.field("root", &true);
93 }
94 if !self.weak_id.is_null() {
95 let w = self.weak_id;
96 s.field("weakref", &format!("{}#{}", w.index(), w.version()));
97 }
98
99 #[cfg(debug_assertions)]
100 {
101 s.field("dbg_string", &self.dbg_string);
102 }
103
104 s.finish()
105 }
106}
107
108impl GcHead {
109 #[inline(always)]
111 pub(crate) fn dtype(&self) -> u8 {
112 ((self.attrs & 0xFF00) >> 8) as u8
113 }
114
115 #[inline(always)]
117 pub(crate) fn color(&self) -> GcTriColor {
118 GcTriColor::try_from(self.attrs & COLOR_MASK).unwrap()
120 }
121
122 #[inline(always)]
124 pub(crate) fn set_color(&mut self, color: GcTriColor) {
125 self.attrs = (self.attrs & !COLOR_MASK) | (color as u32);
126 }
127
128 #[inline(always)]
129 pub(crate) fn flags(&self) -> GcNodeFlag {
130 GcNodeFlag::from_bits_truncate(self.attrs as u8)
131 }
132
133 #[inline(always)]
135 pub(crate) fn insert_flag(&mut self, flag: GcNodeFlag) {
136 self.attrs |= flag.bits() as u32;
137 }
138
139 #[inline(always)]
141 pub(crate) fn remove_flag(&mut self, flag: GcNodeFlag) {
142 self.attrs &= !(flag.bits() as u32);
143 }
144
145 #[inline(always)]
147 pub(crate) fn contains_flag(&self, flag: GcNodeFlag) -> bool {
148 (self.attrs & flag.bits() as u32) == flag.bits() as u32
149 }
150
151 #[inline(always)]
153 pub fn is_root(&self) -> bool {
154 self.contains_flag(GcNodeFlag::ROOT)
155 }
156
157 #[inline(always)]
158 pub fn is_local(&self) -> bool {
159 self.contains_flag(GcNodeFlag::LOCAL)
160 }
161
162 #[inline]
163 pub fn is_root_or_local(&self) -> bool {
164 let f = self.flags();
165 f.intersects(GcNodeFlag::ROOT | GcNodeFlag::LOCAL)
166 }
167
168 #[inline(always)]
169 pub(super) fn traverse_visited(&self) -> bool {
170 self.contains_flag(GcNodeFlag::TRAVERSE_VISITED)
171 }
172
173 #[inline(always)]
174 pub(super) fn set_traverse_visited(&mut self, visited: bool) {
175 if visited {
176 self.insert_flag(GcNodeFlag::TRAVERSE_VISITED);
177 } else {
178 self.remove_flag(GcNodeFlag::TRAVERSE_VISITED);
179 }
180 }
181
182 #[inline(always)]
184 pub(super) fn is_gray_listed(&self) -> bool {
185 (self.attrs & (GcNodeFlag::GRAY_LISTED.bits() as u32)) != 0
186 }
187
188 #[inline(always)]
190 pub(super) fn set_gray_listed(&mut self, b: bool) {
191 if b {
192 self.attrs |= GcNodeFlag::GRAY_LISTED.bits() as u32;
193 } else {
194 self.attrs &= !(GcNodeFlag::GRAY_LISTED.bits() as u32);
195 }
196 }
197
198 #[inline(always)]
200 pub fn partition_id(&self) -> GcPartitionId {
201 GcPartitionId((self.partition & 0x0000_FFFF) as u16)
202 }
203
204 #[inline(always)]
206 pub fn payload(&self, registry: &GcTypeRegistry) -> NonNull<u8> {
207 #[cfg(debug_assertions)]
208 self.debug_assert_node_valid_simple();
209
210 let info = ®istry.type_info_list[self.dtype() as usize];
211 info.payload_ptr(NonNull::from_ref(self))
212 }
213
214 #[inline(always)]
215 pub(crate) fn payload_for<T>(&self) -> NonNull<u8> {
216 #[cfg(debug_assertions)]
217 self.debug_assert_node_valid_simple();
218
219 unsafe {
220 NonNull::from_ref(self)
221 .cast::<u8>()
222 .add(payload_offset_of::<T>())
223 }
224 }
225}
226
227pub trait GcNode: GcTrace {
228 const GC_TYPE_ID: u8;
230
231 fn gc_ref(&self) -> GcRef<Self>
233 where
234 Self: std::marker::Sized;
235
236 #[inline(always)]
238 fn gc_head_ptr(&self) -> std::ptr::NonNull<GcHead>
239 where
240 Self: std::marker::Sized,
241 {
242 self.gc_ref().node_ptr()
243 }
244
245 #[inline(always)]
247 fn gc_head(&self) -> &GcHead
248 where
249 Self: std::marker::Sized,
250 {
251 unsafe { self.gc_head_ptr().as_ref() }
252 }
253
254 #[inline(always)]
256 fn gc_head_mut(&mut self) -> &mut GcHead
257 where
258 Self: std::marker::Sized,
259 {
260 unsafe { self.gc_head_ptr().as_mut() }
261 }
262}
263
264#[repr(transparent)]
266pub struct GcRef<T: GcNode> {
267 pub(super) head_ptr: NonNull<GcHead>,
268 pub(super) _marker: PhantomData<T>,
269}
270
271impl<T: GcNode> Clone for GcRef<T> {
272 #[inline(always)]
273 fn clone(&self) -> Self {
274 *self
275 }
276}
277
278impl<T: GcNode> Copy for GcRef<T> {}
279
280impl<T: GcNode> PartialEq for GcRef<T> {
281 #[inline(always)]
282 fn eq(&self, other: &Self) -> bool {
283 self.head_ptr == other.head_ptr
284 }
285}
286
287impl<T: GcNode> Eq for GcRef<T> {}
288
289impl<T: GcNode> From<GcRef<T>> for NonNull<GcHead> {
290 #[inline(always)]
291 fn from(r: GcRef<T>) -> Self {
292 r.head_ptr
293 }
294}
295
296impl<T: GcNode> From<&GcRef<T>> for NonNull<GcHead> {
297 #[inline(always)]
298 fn from(r: &GcRef<T>) -> Self {
299 r.head_ptr
300 }
301}
302
303impl<T: GcNode> std::fmt::Debug for GcRef<T> {
304 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
305 write!(f, "GcRef<{:p}>", self.head_ptr)
306 }
307}
308
309impl<T: GcNode> GcRef<T> {
310 #[inline(always)]
318 pub unsafe fn as_ref(&self) -> &T {
319 unsafe {
320 self.head_ptr
321 .as_ref()
322 .payload_for::<T>()
323 .cast::<T>()
324 .as_ref()
325 }
326 }
327
328 #[inline(always)]
334 pub unsafe fn as_mut(&mut self) -> &mut T {
335 unsafe {
336 self.head_ptr
337 .as_mut()
338 .payload_for::<T>()
339 .cast::<T>()
340 .as_mut()
341 }
342 }
343
344 #[inline]
352 pub unsafe fn try_from_ref(heap: &GcHeap, data_ref: &T) -> Option<Self> {
353 let node = unsafe {
354 NonNull::from_ref(data_ref)
355 .cast::<u8>()
356 .sub(payload_offset_of::<T>())
357 .cast::<GcHead>()
358 };
359
360 if T::GC_TYPE_ID == unsafe { node.as_ref().dtype() } {
361 #[cfg(debug_assertions)]
362 unsafe {
363 node.as_ref().debug_assert_node_valid(heap);
364 }
365
366 Some(Self {
367 head_ptr: node,
368 _marker: PhantomData,
369 })
370 } else {
371 None
372 }
373 }
374
375 #[inline]
381 pub unsafe fn from_ref_unchecked(data_ref: &T) -> Self {
382 let node = unsafe {
383 NonNull::from_ref(data_ref)
384 .cast::<u8>()
385 .sub(payload_offset_of::<T>())
386 .cast::<GcHead>()
387 };
388
389 #[cfg(debug_assertions)]
390 unsafe {
391 node.as_ref().debug_assert_node_valid_simple();
392 }
393
394 Self {
395 head_ptr: node,
396 _marker: PhantomData,
397 }
398 }
399
400 #[inline]
408 pub unsafe fn with_write_barrier<F, R>(&mut self, heap: &mut GcHeap, mutator: F) -> R
409 where
410 F: FnOnce(&mut T) -> R,
411 {
412 if unsafe { heap.is_node_partition_marking(*self) } {
415 let node = unsafe { self.head_ptr.as_mut() };
417 if node.color() == GcTriColor::Black {
418 node.set_color(GcTriColor::Gray);
419 heap.add_gray_node(self.head_ptr);
420 }
421 }
422
423 let value = unsafe {
425 self.head_ptr
426 .as_mut()
427 .payload_for::<T>()
428 .cast::<T>()
429 .as_mut()
430 };
431 mutator(value)
432 }
433
434 #[inline]
440 pub unsafe fn as_ptr(&self) -> NonNull<T> {
441 unsafe { self.head_ptr.as_ref().payload_for::<T>().cast::<T>() }
442 }
443
444 #[inline(always)]
450 pub unsafe fn downgrade(&self, heap: &mut GcHeap) -> GcWeak<T> {
451 heap.downgrade(self)
452 }
453
454 #[inline(always)]
460 pub unsafe fn is_root(&self) -> bool {
461 unsafe { self.head_ptr.as_ref().is_root() }
462 }
463
464 #[inline(always)]
466 pub fn node_ptr(&self) -> NonNull<GcHead> {
467 self.head_ptr
468 }
469
470 #[inline(always)]
476 pub unsafe fn node_info(&self) -> &GcHead {
477 unsafe { self.head_ptr.as_ref() }
478 }
479}
480
481#[repr(transparent)]
495pub struct Gc<'a, T: GcNode> {
496 inner: GcRef<T>,
497 _marker: PhantomData<&'a T>,
498}
499
500impl<'a, T: GcNode> Deref for Gc<'a, T> {
501 type Target = T;
502
503 #[inline(always)]
504 fn deref(&self) -> &Self::Target {
505 unsafe { self.inner.as_ref() }
507 }
508}
509
510impl<'a, T: GcNode> DerefMut for Gc<'a, T> {
511 #[inline(always)]
512 fn deref_mut(&mut self) -> &mut Self::Target {
513 unsafe { self.inner.as_mut() }
515 }
516}
517
518impl<'a, T: GcNode> Clone for Gc<'a, T> {
519 #[inline(always)]
520 fn clone(&self) -> Self {
521 *self
522 }
523}
524
525impl<'a, T: GcNode> Copy for Gc<'a, T> {}
526
527impl<'a, T: GcNode> PartialEq for Gc<'a, T> {
528 #[inline(always)]
529 fn eq(&self, other: &Self) -> bool {
530 self.inner == other.inner
531 }
532}
533
534impl<'a, T: GcNode> Eq for Gc<'a, T> {}
535
536impl<'a, T: GcNode> std::fmt::Debug for Gc<'a, T> {
537 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
538 write!(f, "Gc<{:p}>", self.inner.head_ptr)
539 }
540}
541
542impl<'a, T: GcNode> Gc<'a, T> {
543 #[inline(always)]
551 pub unsafe fn from_raw(inner: GcRef<T>) -> Self {
552 Self {
553 inner,
554 _marker: PhantomData,
555 }
556 }
557
558 #[inline(always)]
560 pub fn into_raw(self) -> GcRef<T> {
561 self.inner
562 }
563
564 #[inline(always)]
566 pub fn as_raw(&self) -> &GcRef<T> {
567 &self.inner
568 }
569}
570
571impl GcHeap {
572 pub fn bind(&mut self, master: NonNull<GcHead>, mut slave: NonNull<GcHead>) {
600 #[cfg(debug_assertions)]
601 unsafe {
602 master.as_ref().debug_assert_node_valid(self);
603 slave.as_ref().debug_assert_node_valid(self);
604 }
605
606 unsafe {
608 if matches!(
609 (master.as_ref().color(), slave.as_ref().color()),
610 (GcTriColor::Black, GcTriColor::White | GcTriColor::Gray)
611 ) {
612 slave.as_mut().set_color(GcTriColor::Gray);
613
614 let slave_pid = slave.as_ref().partition_id();
615 if self.partition(slave_pid).is_some_and(|p| p.is_marking()) {
616 self.add_gray_node(slave);
617 }
618 }
619 }
620
621 unsafe {
625 let master_pid = master.as_ref().partition_id();
626 let slave_pid = slave.as_ref().partition_id();
627
628 if master_pid != slave_pid {
629 let slave_color = slave.as_ref().color();
630
631 if matches!(slave_color, GcTriColor::White | GcTriColor::Gray)
632 && let Some(slave_par) = self.partition_mut(slave_pid)
633 && slave_par.is_marking()
634 {
635 slave.as_mut().set_color(GcTriColor::Gray);
636 if !slave.as_ref().is_gray_listed() {
637 slave.as_mut().set_gray_listed(true);
638 slave_par.gray_list.push(slave);
639 }
640 }
641 }
642 }
643 }
644}
645
646#[cfg(debug_assertions)]
647impl GcHead {
648 pub fn debug_set_dbg_string(&mut self, str: std::borrow::Cow<'static, str>) {
649 self.dbg_string = str;
650 }
651
652 pub fn debug_dbg_string(&self) -> &std::borrow::Cow<'static, str> {
653 &self.dbg_string
654 }
655
656 pub fn debug_assert_node_valid_simple(&self) {
657 if !std::thread::panicking() {
658 debug_assert!(
659 ((self.attrs >> 24) & 0xFF) == 0xFF && self.next.is_none_or(|n| n.is_aligned()),
660 "bad node: {self:p}"
661 );
662 }
663 }
664
665 pub fn debug_assert_node_valid(&self, heap: &GcHeap) {
666 if !std::thread::panicking() {
667 debug_assert!(
668 heap.dbg_living_nodes.contains(&NonNull::from_ref(self)),
669 "[O.o] bad node: {self:p}"
670 );
671 self.debug_assert_node_valid_simple();
672 }
673 }
674}