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