1use crate::header::{
2 GcId, GcListKind, GcObjectHeader, GcObjectType, GcPhase, RefCountHeader, RefCountId,
3};
4use crate::list::{GcList, GcListIter};
5use crate::malloc::{MallocState, DEFAULT_GC_THRESHOLD};
6use std::any::Any;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum MarkFunc {
12 Decref,
13 ScanIncref,
14 ScanIncref2,
15}
16
17pub trait GcObject: Any {
19 fn trace(&self, visit: &mut dyn FnMut(GcId));
20 fn on_free(&mut self, _rt: &mut GcRuntime) {}
21}
22
23struct GcObjectEntry {
24 header: GcObjectHeader,
25 object: Option<Box<dyn GcObject>>,
26}
27
28struct RefCountEntry {
29 header: RefCountHeader,
30 payload: Box<dyn FnOnce(&mut GcRuntime)>,
31}
32
33pub struct GcRuntime {
35 objects: Vec<Option<GcObjectEntry>>,
36 free_slots: Vec<GcId>,
37 ref_counts: Vec<Option<RefCountEntry>>,
38 ref_count_free_slots: Vec<RefCountId>,
39
40 gc_obj_list: GcList,
41 gc_zero_ref_count_list: GcList,
42 tmp_obj_list: GcList,
43 gc_phase: GcPhase,
44 malloc_state: MallocState,
45 malloc_gc_threshold: usize,
46}
47
48impl GcRuntime {
49 pub fn new() -> Self {
50 GcRuntime {
51 objects: Vec::new(),
52 free_slots: Vec::new(),
53 ref_counts: Vec::new(),
54 ref_count_free_slots: Vec::new(),
55 gc_obj_list: GcList::new(),
56 gc_zero_ref_count_list: GcList::new(),
57 tmp_obj_list: GcList::new(),
58 gc_phase: GcPhase::None,
59 malloc_state: MallocState::new(),
60 malloc_gc_threshold: DEFAULT_GC_THRESHOLD,
61 }
62 }
63
64 pub fn malloc_state(&self) -> &MallocState {
65 &self.malloc_state
66 }
67
68 pub fn malloc_state_mut(&mut self) -> &mut MallocState {
69 &mut self.malloc_state
70 }
71
72 pub fn gc_threshold(&self) -> usize {
73 self.malloc_gc_threshold
74 }
75
76 pub fn set_gc_threshold(&mut self, threshold: usize) {
78 self.malloc_gc_threshold = threshold;
79 }
80
81 pub fn gc_phase(&self) -> GcPhase {
82 self.gc_phase
83 }
84
85 pub fn gc_object_count(&self) -> usize {
86 GcListIter {
87 rt: self,
88 current: self.gc_obj_list.head,
89 }
90 .count()
91 }
92
93 pub(crate) fn header(&self, id: GcId) -> &GcObjectHeader {
94 &self.objects[id].as_ref().expect("invalid GcId").header
95 }
96
97 pub(crate) fn header_mut(&mut self, id: GcId) -> &mut GcObjectHeader {
98 &mut self.objects[id].as_mut().expect("invalid GcId").header
99 }
100
101 fn list_ptr(&mut self, kind: GcListKind) -> *mut GcList {
102 (match kind {
103 GcListKind::GcObj => &mut self.gc_obj_list,
104 GcListKind::Tmp => &mut self.tmp_obj_list,
105 GcListKind::ZeroRef => &mut self.gc_zero_ref_count_list,
106 }) as *mut GcList
107 }
108
109 fn list_push_back(&mut self, kind: GcListKind, id: GcId) {
110 let list_ptr = self.list_ptr(kind);
111 let tail = unsafe { (*list_ptr).tail };
112
113 {
114 let header = self.header_mut(id);
115 assert!(
116 header.list_kind.is_none(),
117 "object already belongs to a GC list: {:?}",
118 header.list_kind
119 );
120 header.list_kind = Some(kind);
121 header.list_prev = tail;
122 header.list_next = None;
123 }
124
125 if let Some(tail_id) = tail {
126 self.header_mut(tail_id).list_next = Some(id);
127 } else {
128 unsafe {
129 (*list_ptr).head = Some(id);
130 }
131 }
132 unsafe {
133 (*list_ptr).tail = Some(id);
134 }
135 }
136
137 fn list_remove(&mut self, kind: GcListKind, id: GcId) {
138 assert_eq!(self.header(id).list_kind, Some(kind), "object is not on the expected GC list");
139
140 let list_ptr = self.list_ptr(kind);
141 let (prev, next) = {
142 let header = self.header(id);
143 (header.list_prev, header.list_next)
144 };
145
146 match prev {
147 Some(p) => self.header_mut(p).list_next = next,
148 None => unsafe {
149 (*list_ptr).head = next;
150 },
151 }
152
153 match next {
154 Some(n) => self.header_mut(n).list_prev = prev,
155 None => unsafe {
156 (*list_ptr).tail = prev;
157 },
158 }
159
160 let header = self.header_mut(id);
161 header.list_kind = None;
162 header.list_prev = None;
163 header.list_next = None;
164 }
165
166 fn list_remove_current(&mut self, id: GcId) {
167 let kind = self
168 .header(id)
169 .list_kind
170 .expect("object is not on a GC list");
171 self.list_remove(kind, id);
172 }
173
174 fn list_move(&mut self, from: GcListKind, to: GcListKind, id: GcId) {
175 self.list_remove(from, id);
176 self.list_push_back(to, id);
177 }
178
179 fn list_move_current_to(&mut self, to: GcListKind, id: GcId) {
180 let from = self
181 .header(id)
182 .list_kind
183 .expect("object is not on a GC list");
184 self.list_move(from, to, id);
185 }
186
187 fn alloc_slot(&mut self, entry: GcObjectEntry) -> GcId {
188 let id = if let Some(id) = self.free_slots.pop() {
189 self.objects[id] = Some(entry);
190 id
191 } else {
192 let id = self.objects.len();
193 self.objects.push(Some(entry));
194 id
195 };
196 self.malloc_state
197 .record_alloc(std::mem::size_of::<GcObjectEntry>());
198 id
199 }
200
201 fn free_slot(&mut self, id: GcId) {
202 self.malloc_state
203 .record_free(std::mem::size_of::<GcObjectEntry>());
204 self.objects[id] = None;
205 self.free_slots.push(id);
206 }
207
208 pub fn add_gc_object(&mut self, object: Box<dyn GcObject>, gc_obj_type: GcObjectType) -> GcId {
211 let id = self.alloc_slot(GcObjectEntry {
212 header: GcObjectHeader::new(gc_obj_type, 1),
213 object: Some(object),
214 });
215 self.list_push_back(GcListKind::GcObj, id);
216 id
217 }
218
219 pub fn dup_gc(&mut self, id: GcId) -> GcId {
221 self.header_mut(id).ref_count += 1;
222 id
223 }
224
225 pub fn free_gc(&mut self, id: GcId) {
228 if !self.object_exists(id) {
229 return;
230 }
231 let ref_count = {
232 let header = self.header_mut(id);
233 header.ref_count -= 1;
234 header.ref_count
235 };
236
237 if ref_count > 0 {
238 return;
239 }
240
241 if self.gc_phase != GcPhase::RemoveCycles {
242 self.list_move(GcListKind::GcObj, GcListKind::ZeroRef, id);
243 if self.gc_phase == GcPhase::None {
244 self.free_zero_refcount();
245 }
246 }
247 }
248
249 pub fn add_ref_counted<F>(&mut self, on_free: F) -> RefCountId
251 where
252 F: FnOnce(&mut GcRuntime) + 'static,
253 {
254 let entry = RefCountEntry {
255 header: RefCountHeader::new(1),
256 payload: Box::new(on_free),
257 };
258 let id = if let Some(id) = self.ref_count_free_slots.pop() {
259 self.ref_counts[id] = Some(entry);
260 id
261 } else {
262 let id = self.ref_counts.len();
263 self.ref_counts.push(Some(entry));
264 id
265 };
266 self.malloc_state
267 .record_alloc(std::mem::size_of::<RefCountEntry>());
268 id
269 }
270
271 pub fn dup_ref_counted(&mut self, id: RefCountId) -> RefCountId {
272 self.ref_counts[id]
273 .as_mut()
274 .expect("invalid RefCountId")
275 .header
276 .ref_count += 1;
277 id
278 }
279
280 pub fn free_ref_counted(&mut self, id: RefCountId) {
281 let ref_count = {
282 let entry = self.ref_counts[id].as_mut().expect("invalid RefCountId");
283 entry.header.ref_count -= 1;
284 entry.header.ref_count
285 };
286
287 if ref_count <= 0 {
288 let entry = self.ref_counts[id].take().expect("double free");
289 self.malloc_state
290 .record_free(std::mem::size_of::<RefCountEntry>());
291 (entry.payload)(self);
292 self.ref_count_free_slots.push(id);
293 }
294 }
295
296 pub fn mark_gc_header(&mut self, id: GcId, mark_func: MarkFunc) {
298 match mark_func {
299 MarkFunc::Decref => self.gc_decref_child(id),
300 MarkFunc::ScanIncref => self.gc_scan_incref_child(id),
301 MarkFunc::ScanIncref2 => self.gc_scan_incref_child2(id),
302 }
303 }
304
305 pub fn mark_children(&mut self, id: GcId, mark_func: MarkFunc) {
307 let object = self.objects[id]
310 .as_mut()
311 .expect("invalid GcId")
312 .object
313 .take()
314 .expect("object already finalized");
315 object.trace(&mut |child| {
316 self.mark_gc_header(child, mark_func);
317 });
318 self.objects[id]
319 .as_mut()
320 .expect("object freed while tracing")
321 .object = Some(object);
322 }
323
324 pub fn trigger_gc(&mut self, alloc_size: usize) {
326 let force_gc =
327 self.malloc_state.malloc_size.saturating_add(alloc_size) > self.malloc_gc_threshold;
328 if force_gc {
329 self.run_gc();
330 self.malloc_gc_threshold =
331 self.malloc_state.malloc_size + (self.malloc_state.malloc_size >> 1);
332 }
333 }
334
335 pub fn run_gc(&mut self) {
337 self.gc_decref();
338 self.gc_scan();
339 self.gc_free_cycles();
340 }
341
342 pub fn is_live_object(&self, id: GcId) -> bool {
345 self.objects
346 .get(id)
347 .and_then(|o| o.as_ref())
348 .map_or(false, |e| !e.header.free_mark)
349 }
350
351 pub fn ref_count(&self, id: GcId) -> i32 {
352 self.header(id).ref_count
353 }
354
355 pub fn object_exists(&self, id: GcId) -> bool {
356 self.objects.get(id).and_then(|o| o.as_ref()).is_some()
357 }
358
359 pub fn object_downcast<T: GcObject>(&self, id: GcId) -> Option<&T> {
360 let entry = self.objects.get(id)?.as_ref()?;
361 (entry.object.as_ref()?.as_ref() as &dyn Any).downcast_ref()
362 }
363
364 pub fn object_downcast_mut<T: GcObject>(&mut self, id: GcId) -> Option<&mut T> {
365 let entry = self.objects.get_mut(id)?.as_mut()?;
366 (entry.object.as_mut()?.as_mut() as &mut dyn Any).downcast_mut()
367 }
368
369 fn gc_decref_child(&mut self, id: GcId) {
372 assert!(self.header(id).ref_count > 0);
373 self.header_mut(id).ref_count -= 1;
374 if self.header(id).ref_count == 0 && self.header(id).mark == 1 {
375 self.list_move(GcListKind::GcObj, GcListKind::Tmp, id);
376 }
377 }
378
379 fn gc_decref(&mut self) {
380 assert!(
381 self.tmp_obj_list.is_empty(),
382 "temporary GC list must be empty before trial deletion"
383 );
384 let mut current = self.gc_obj_list.head;
385 while let Some(id) = current {
386 let next = self.header(id).list_next;
387 assert_eq!(self.header(id).mark, 0);
388 self.mark_children(id, MarkFunc::Decref);
389 self.header_mut(id).mark = 1;
390 if self.header(id).ref_count == 0 {
391 self.list_move(GcListKind::GcObj, GcListKind::Tmp, id);
392 }
393 current = next;
394 }
395 }
396
397 fn gc_scan_incref_child(&mut self, id: GcId) {
400 self.header_mut(id).ref_count += 1;
401 if self.header(id).ref_count == 1 {
402 self.list_move(GcListKind::Tmp, GcListKind::GcObj, id);
403 self.header_mut(id).mark = 0;
404 }
405 }
406
407 fn gc_scan_incref_child2(&mut self, id: GcId) {
408 self.header_mut(id).ref_count += 1;
409 }
410
411 fn gc_scan(&mut self) {
412 let mut current = self.gc_obj_list.head;
413 while let Some(id) = current {
414 assert!(self.header(id).ref_count > 0);
415 self.header_mut(id).mark = 0;
416 self.mark_children(id, MarkFunc::ScanIncref);
417 current = self.header(id).list_next;
418 }
419
420 let mut current = self.tmp_obj_list.head;
421 while let Some(id) = current {
422 let next = self.header(id).list_next;
423 self.mark_children(id, MarkFunc::ScanIncref2);
424 current = next;
425 }
426 }
427
428 fn gc_free_cycles(&mut self) {
431 self.gc_phase = GcPhase::RemoveCycles;
432
433 loop {
434 let id = self.tmp_obj_list.head;
435 if id.is_none() {
436 break;
437 }
438 let id = id.unwrap();
439 let gc_obj_type = self.header(id).gc_obj_type;
440 match gc_obj_type {
441 GcObjectType::MonkeyObject | GcObjectType::FunctionBytecode => {
442 self.free_gc_object(id);
443 }
444 _ => {
445 self.list_move(GcListKind::Tmp, GcListKind::ZeroRef, id);
446 }
447 }
448 }
449
450 self.gc_phase = GcPhase::None;
451
452 while let Some(id) = self.gc_zero_ref_count_list.head {
453 let ty = self.header(id).gc_obj_type;
454 assert!(
455 ty == GcObjectType::MonkeyObject || ty == GcObjectType::FunctionBytecode,
456 "unexpected deferred type: {:?}",
457 ty
458 );
459 self.list_remove_current(id);
460 self.free_slot(id);
461 }
462 }
463
464 fn free_zero_refcount(&mut self) {
465 self.gc_phase = GcPhase::Decref;
466 loop {
467 let id = self.gc_zero_ref_count_list.head;
468 if id.is_none() {
469 break;
470 }
471 let id = id.unwrap();
472 assert_eq!(self.header(id).ref_count, 0);
473 self.free_gc_object(id);
474 }
475 self.gc_phase = GcPhase::None;
476 }
477
478 fn free_gc_object(&mut self, id: GcId) {
479 match self.header(id).gc_obj_type {
480 GcObjectType::MonkeyObject | GcObjectType::FunctionBytecode => {
481 self.free_heap_object(id)
482 }
483 other => panic!("free_gc_object: unsupported type {:?}", other),
484 }
485 }
486
487 fn free_heap_object(&mut self, id: GcId) {
488 self.header_mut(id).free_mark = true;
489
490 let mut object = self.objects[id]
493 .as_mut()
494 .expect("invalid GcId")
495 .object
496 .take()
497 .expect("object already finalized");
498 object.trace(&mut |child| {
499 self.free_gc(child);
500 });
501 object.on_free(self);
502 drop(object);
503
504 let defer_free = self.gc_phase == GcPhase::RemoveCycles && self.header(id).ref_count != 0;
505 if !defer_free {
506 self.list_remove_current(id);
507 self.free_slot(id);
508 } else {
509 self.list_move_current_to(GcListKind::ZeroRef, id);
510 }
511 }
512}
513
514impl Default for GcRuntime {
515 fn default() -> Self {
516 Self::new()
517 }
518}