Ruby 4.1.0dev (2026-05-27 revision ce7f3ed832a50e493e3b6cb592791aa1fe784d85)
mmtk.c
1#include <pthread.h>
2#include <stdbool.h>
3
4#include "ruby/assert.h"
5#include "ruby/atomic.h"
6#include "ruby/debug.h"
7
8#include "gc/gc.h"
9#include "gc/gc_impl.h"
10#include "gc/mmtk/mmtk.h"
11
12#include "ccan/list/list.h"
13#include "darray.h"
14
15#ifdef __APPLE__
16#include <sys/sysctl.h>
17#endif
18
19struct objspace {
20 bool measure_gc_time;
21 bool gc_stress;
22
23 size_t gc_count;
24 size_t moving_gc_count;
25 size_t total_gc_time;
26 size_t total_allocated_objects;
27
28 st_table *finalizer_table;
29 struct MMTk_final_job *finalizer_jobs;
30 rb_postponed_job_handle_t finalizer_postponed_job;
31
32 struct ccan_list_head ractor_caches;
33 unsigned long live_ractor_cache_count;
34
35 pthread_mutex_t mutex;
36 rb_atomic_t mutator_blocking_count;
37 bool world_stopped;
38 pthread_cond_t cond_world_stopped;
39 pthread_cond_t cond_world_started;
40 size_t start_the_world_count;
41
42 pthread_mutex_t event_hook_mutex;
43
44 struct {
45 bool gc_thread_crashed;
46 char crash_msg[256];
47 } crash_context;
48
49 struct rb_gc_vm_context vm_context;
50
51 unsigned int fork_hook_vm_lock_lev;
52};
53
54#define OBJ_FREE_BUF_CAPACITY 128
55
57 struct ccan_list_node list_node;
58
59 MMTk_Mutator *mutator;
60 bool gc_mutator_p;
61
62 MMTk_BumpPointer *bump_pointer;
63
64 MMTk_ObjectReference obj_free_parallel_buf[OBJ_FREE_BUF_CAPACITY];
65 size_t obj_free_parallel_count;
66 MMTk_ObjectReference obj_free_non_parallel_buf[OBJ_FREE_BUF_CAPACITY];
67 size_t obj_free_non_parallel_count;
68};
69
71 struct MMTk_final_job *next;
72 enum {
73 MMTK_FINAL_JOB_DFREE,
74 MMTK_FINAL_JOB_FINALIZE,
75 } kind;
76 union {
77 struct {
78 void (*func)(void *);
79 void *data;
80 } dfree;
81 struct {
82 /* HACK: we store the object ID on the 0th element of this array. */
83 VALUE finalizer_array;
84 } finalize;
85 } as;
86};
87
88#ifdef RB_THREAD_LOCAL_SPECIFIER
89RB_THREAD_LOCAL_SPECIFIER struct MMTk_GCThreadTLS *rb_mmtk_gc_thread_tls;
90
91RB_THREAD_LOCAL_SPECIFIER VALUE marking_parent_object;
92#else
93# error We currently need language-supported TLS
94#endif
95
96#ifdef MMTK_DEBUG
97# define MMTK_ASSERT(expr, ...) RUBY_ASSERT_ALWAYS(expr, #expr RBIMPL_VA_OPT_ARGS(__VA_ARGS__))
98#else
99# define MMTK_ASSERT(expr, ...) ((void)0)
100#endif
101
102#include <pthread.h>
103
104static inline VALUE rb_mmtk_call_object_closure(VALUE obj, bool pin);
105
106static void
107rb_mmtk_init_gc_worker_thread(MMTk_VMWorkerThread gc_thread_tls)
108{
109 rb_mmtk_gc_thread_tls = gc_thread_tls;
110}
111
112static bool
113rb_mmtk_is_mutator(void)
114{
115 return ruby_native_thread_p();
116}
117
118static void
119rb_mmtk_stop_the_world(void)
120{
121 struct objspace *objspace = rb_gc_get_objspace();
122
123 int err;
124 if ((err = pthread_mutex_lock(&objspace->mutex)) != 0) {
125 rb_bug("ERROR: cannot lock objspace->mutex: %s", strerror(err));
126 }
127
128 while (!objspace->world_stopped) {
129 pthread_cond_wait(&objspace->cond_world_stopped, &objspace->mutex);
130 }
131
132 if ((err = pthread_mutex_unlock(&objspace->mutex)) != 0) {
133 rb_bug("ERROR: cannot release objspace->mutex: %s", strerror(err));
134 }
135}
136
137static void
138rb_mmtk_resume_mutators(bool current_gc_may_move)
139{
140 struct objspace *objspace = rb_gc_get_objspace();
141
142 int err;
143 if ((err = pthread_mutex_lock(&objspace->mutex)) != 0) {
144 rb_bug("ERROR: cannot lock objspace->mutex: %s", strerror(err));
145 }
146
147 objspace->world_stopped = false;
148 objspace->gc_count++;
149 if (current_gc_may_move) objspace->moving_gc_count++;
150 pthread_cond_broadcast(&objspace->cond_world_started);
151
152 if ((err = pthread_mutex_unlock(&objspace->mutex)) != 0) {
153 rb_bug("ERROR: cannot release objspace->mutex: %s", strerror(err));
154 }
155}
156
157static void mmtk_flush_obj_free_buffer(struct MMTk_ractor_cache *cache);
158
159static void
160rb_mmtk_block_for_gc(MMTk_VMMutatorThread mutator)
161{
162 struct objspace *objspace = rb_gc_get_objspace();
163
164 size_t starting_gc_count = objspace->gc_count;
165 RUBY_ATOMIC_INC(objspace->mutator_blocking_count);
166 int lock_lev = RB_GC_VM_LOCK();
167 RUBY_ATOMIC_DEC(objspace->mutator_blocking_count);
168 int err;
169 if ((err = pthread_mutex_lock(&objspace->mutex)) != 0) {
170 rb_bug("ERROR: cannot lock objspace->mutex: %s", strerror(err));
171 }
172
173 if (objspace->gc_count == starting_gc_count) {
174 rb_gc_event_hook(0, RUBY_INTERNAL_EVENT_GC_START);
175
176 rb_gc_initialize_vm_context(&objspace->vm_context);
177
178 mutator->gc_mutator_p = true;
179
180 struct timespec gc_start_time;
181 if (objspace->measure_gc_time) {
182 clock_gettime(CLOCK_MONOTONIC, &gc_start_time);
183 }
184
185 rb_gc_save_machine_context();
186
187 rb_gc_vm_barrier();
188
189 struct MMTk_ractor_cache *rc;
190 ccan_list_for_each(&objspace->ractor_caches, rc, list_node) {
191 mmtk_flush_obj_free_buffer(rc);
192 }
193
194 objspace->world_stopped = true;
195
196 pthread_cond_broadcast(&objspace->cond_world_stopped);
197
198 // Wait for GC end
199 while (objspace->world_stopped) {
200 pthread_cond_wait(&objspace->cond_world_started, &objspace->mutex);
201 }
202
203 if (RB_UNLIKELY(objspace->crash_context.gc_thread_crashed)) {
204 rb_bug("%s", objspace->crash_context.crash_msg);
205 }
206
207 if (objspace->measure_gc_time) {
208 struct timespec gc_end_time;
209 clock_gettime(CLOCK_MONOTONIC, &gc_end_time);
210
211 objspace->total_gc_time +=
212 (gc_end_time.tv_sec - gc_start_time.tv_sec) * (1000 * 1000 * 1000) +
213 (gc_end_time.tv_nsec - gc_start_time.tv_nsec);
214 }
215 }
216
217 if ((err = pthread_mutex_unlock(&objspace->mutex)) != 0) {
218 rb_bug("ERROR: cannot release objspace->mutex: %s", strerror(err));
219 }
220 RB_GC_VM_UNLOCK(lock_lev);
221}
222
223static void
224rb_mmtk_before_updating_jit_code(void)
225{
226 rb_gc_before_updating_jit_code();
227}
228
229static void
230rb_mmtk_after_updating_jit_code(void)
231{
232 rb_gc_after_updating_jit_code();
233}
234
235static size_t
236rb_mmtk_number_of_mutators(void)
237{
238 struct objspace *objspace = rb_gc_get_objspace();
239 return objspace->live_ractor_cache_count;
240}
241
242static void
243rb_mmtk_get_mutators(void (*visit_mutator)(MMTk_Mutator *mutator, void *data), void *data)
244{
245 struct objspace *objspace = rb_gc_get_objspace();
246 struct MMTk_ractor_cache *ractor_cache;
247
248 ccan_list_for_each(&objspace->ractor_caches, ractor_cache, list_node) {
249 visit_mutator(ractor_cache->mutator, data);
250 }
251}
252
253static void
254rb_mmtk_scan_gc_roots(void)
255{
256 struct objspace *objspace = rb_gc_get_objspace();
257
258 rb_gc_mark_roots(objspace, NULL);
259}
260
261static int
262pin_value(st_data_t key, st_data_t value, st_data_t data)
263{
264 rb_gc_impl_mark_and_pin((void *)data, (VALUE)value);
265
266 return ST_CONTINUE;
267}
268
269static void
270rb_mmtk_scan_objspace(void)
271{
272 struct objspace *objspace = rb_gc_get_objspace();
273
274 if (objspace->finalizer_table != NULL) {
275 st_foreach(objspace->finalizer_table, pin_value, (st_data_t)objspace);
276 }
277
278 struct MMTk_final_job *job = objspace->finalizer_jobs;
279 while (job != NULL) {
280 switch (job->kind) {
281 case MMTK_FINAL_JOB_DFREE:
282 break;
283 case MMTK_FINAL_JOB_FINALIZE:
284 rb_gc_impl_mark(objspace, job->as.finalize.finalizer_array);
285 break;
286 default:
287 rb_bug("rb_mmtk_scan_objspace: unknown final job type %d", job->kind);
288 }
289
290 job = job->next;
291 }
292}
293
294static void
295rb_mmtk_move_obj_during_marking(MMTk_ObjectReference from, MMTk_ObjectReference to)
296{
297 rb_gc_move_obj_during_marking((VALUE)from, (VALUE)to);
298}
299
300static void
301rb_mmtk_update_object_references(MMTk_ObjectReference mmtk_object)
302{
303 VALUE object = (VALUE)mmtk_object;
304
305 if (!RB_FL_TEST(object, RUBY_FL_WEAK_REFERENCE)) {
306 marking_parent_object = object;
307 rb_gc_update_object_references(rb_gc_get_objspace(), object);
308 marking_parent_object = 0;
309 }
310}
311
312static void
313rb_mmtk_call_gc_mark_children(MMTk_ObjectReference object)
314{
315 marking_parent_object = (VALUE)object;
316 rb_gc_mark_children(rb_gc_get_objspace(), (VALUE)object);
317 marking_parent_object = 0;
318}
319
320static void
321rb_mmtk_handle_weak_references(MMTk_ObjectReference mmtk_object, bool moving)
322{
323 VALUE object = (VALUE)mmtk_object;
324
325 marking_parent_object = object;
326
327 rb_gc_handle_weak_references(object);
328
329 if (moving) {
330 rb_gc_update_object_references(rb_gc_get_objspace(), object);
331 }
332
333 marking_parent_object = 0;
334}
335
336static void
337rb_mmtk_call_obj_free(MMTk_ObjectReference object)
338{
339 VALUE obj = (VALUE)object;
340 struct objspace *objspace = rb_gc_get_objspace();
341
342 if (RB_UNLIKELY(rb_gc_event_hook_required_p(RUBY_INTERNAL_EVENT_FREEOBJ))) {
343 pthread_mutex_lock(&objspace->event_hook_mutex);
344 rb_gc_event_hook(obj, RUBY_INTERNAL_EVENT_FREEOBJ);
345 pthread_mutex_unlock(&objspace->event_hook_mutex);
346 }
347
348 rb_gc_obj_free(objspace, obj);
349
350#ifdef MMTK_DEBUG
351 memset((void *)obj, 0, rb_gc_impl_obj_slot_size(obj));
352#endif
353}
354
355static size_t
356rb_mmtk_vm_live_bytes(void)
357{
358 return 0;
359}
360
361static void
362make_final_job(struct objspace *objspace, VALUE obj, VALUE table)
363{
364 MMTK_ASSERT(RB_BUILTIN_TYPE(table) == T_ARRAY);
365
366 struct MMTk_final_job *job = xmalloc(sizeof(struct MMTk_final_job));
367 job->next = objspace->finalizer_jobs;
368 job->kind = MMTK_FINAL_JOB_FINALIZE;
369 job->as.finalize.finalizer_array = table;
370
371 objspace->finalizer_jobs = job;
372}
373
374static int
375rb_mmtk_update_finalizer_table_i(st_data_t key, st_data_t value, st_data_t data, int error)
376{
377 MMTK_ASSERT(mmtk_is_reachable((MMTk_ObjectReference)value));
378 MMTK_ASSERT(RB_BUILTIN_TYPE(value) == T_ARRAY);
379
380 struct objspace *objspace = (struct objspace *)data;
381
382 if (mmtk_is_reachable((MMTk_ObjectReference)key)) {
383 VALUE new_key_location = rb_mmtk_call_object_closure((VALUE)key, false);
384
385 MMTK_ASSERT(RB_FL_TEST(new_key_location, RUBY_FL_FINALIZE));
386
387 if (new_key_location != key) {
388 return ST_REPLACE;
389 }
390 }
391 else {
392 make_final_job(objspace, (VALUE)key, (VALUE)value);
393
394 rb_postponed_job_trigger(objspace->finalizer_postponed_job);
395
396 return ST_DELETE;
397 }
398
399 return ST_CONTINUE;
400}
401
402static int
403rb_mmtk_update_finalizer_table_replace_i(st_data_t *key, st_data_t *value, st_data_t data, int existing)
404{
405 *key = rb_mmtk_call_object_closure((VALUE)*key, false);
406
407 return ST_CONTINUE;
408}
409
410static void
411rb_mmtk_update_finalizer_table(void)
412{
413 struct objspace *objspace = rb_gc_get_objspace();
414
415 st_foreach_with_replace(
416 objspace->finalizer_table,
417 rb_mmtk_update_finalizer_table_i,
418 rb_mmtk_update_finalizer_table_replace_i,
419 (st_data_t)objspace
420 );
421}
422
423static int
424rb_mmtk_global_tables_count(void)
425{
426 return RB_GC_VM_WEAK_TABLE_COUNT;
427}
428
429static inline VALUE rb_mmtk_call_object_closure(VALUE obj, bool pin);
430
431static int
432rb_mmtk_update_global_tables_i(VALUE val, void *data)
433{
434 if (!mmtk_is_reachable((MMTk_ObjectReference)val)) {
435 return ST_DELETE;
436 }
437
438 // TODO: check only if in moving GC
439 if (rb_mmtk_call_object_closure(val, false) != val) {
440 return ST_REPLACE;
441 }
442
443 return ST_CONTINUE;
444}
445
446static int
447rb_mmtk_update_global_tables_replace_i(VALUE *ptr, void *data)
448{
449 // TODO: cache the new location so we don't call rb_mmtk_call_object_closure twice
450 *ptr = rb_mmtk_call_object_closure(*ptr, false);
451
452 return ST_CONTINUE;
453}
454
455static void
456rb_mmtk_update_global_tables(int table, bool moving)
457{
458 MMTK_ASSERT(table < RB_GC_VM_WEAK_TABLE_COUNT);
459
460 rb_gc_vm_weak_table_foreach(
461 rb_mmtk_update_global_tables_i,
462 rb_mmtk_update_global_tables_replace_i,
463 NULL,
464 !moving,
465 (enum rb_gc_vm_weak_tables)table
466 );
467}
468
469static bool
470rb_mmtk_special_const_p(MMTk_ObjectReference object)
471{
472 VALUE obj = (VALUE)object;
473
474 return RB_SPECIAL_CONST_P(obj);
475}
476
477RBIMPL_ATTR_FORMAT(RBIMPL_PRINTF_FORMAT, 1, 2)
479static void
480rb_mmtk_gc_thread_bug(const char *msg, ...)
481{
482 struct objspace *objspace = rb_gc_get_objspace();
483
484 objspace->crash_context.gc_thread_crashed = true;
485
486 va_list args;
487 va_start(args, msg);
488 vsnprintf(objspace->crash_context.crash_msg, sizeof(objspace->crash_context.crash_msg), msg, args);
489 va_end(args);
490
491 fprintf(stderr, "-- GC thread backtrace "
492 "-------------------------------------------\n");
493 rb_gc_print_backtrace();
494 fprintf(stderr, "\n");
495
496 rb_mmtk_resume_mutators(false);
497
498 sleep(5);
499
500 rb_bug("rb_mmtk_gc_thread_bug");
501}
502
503static void
504rb_mmtk_gc_thread_panic_handler(void)
505{
506 rb_mmtk_gc_thread_bug("MMTk GC thread panicked");
507}
508
510static void
511rb_mmtk_mutator_thread_panic_handler(void)
512{
513 rb_bug("Ruby mutator thread panicked");
514}
515
516// Bootup
517MMTk_RubyUpcalls ruby_upcalls = {
518 rb_mmtk_init_gc_worker_thread,
519 rb_mmtk_is_mutator,
520 rb_mmtk_stop_the_world,
521 rb_mmtk_resume_mutators,
522 rb_mmtk_block_for_gc,
523 rb_mmtk_before_updating_jit_code,
524 rb_mmtk_after_updating_jit_code,
525 rb_mmtk_number_of_mutators,
526 rb_mmtk_get_mutators,
527 rb_mmtk_scan_gc_roots,
528 rb_mmtk_scan_objspace,
529 rb_mmtk_move_obj_during_marking,
530 rb_mmtk_update_object_references,
531 rb_mmtk_call_gc_mark_children,
532 rb_mmtk_handle_weak_references,
533 rb_mmtk_call_obj_free,
534 rb_mmtk_vm_live_bytes,
535 rb_mmtk_update_global_tables,
536 rb_mmtk_global_tables_count,
537 rb_mmtk_update_finalizer_table,
538 rb_mmtk_special_const_p,
539 rb_mmtk_mutator_thread_panic_handler,
540 rb_mmtk_gc_thread_panic_handler,
541};
542
543// Use max 80% of the available memory by default for MMTk
544#define RB_MMTK_HEAP_LIMIT_PERC 80
545#define RB_MMTK_DEFAULT_HEAP_MIN (1024 * 1024)
546#define RB_MMTK_DEFAULT_HEAP_MAX (rb_mmtk_system_physical_memory() / 100 * RB_MMTK_HEAP_LIMIT_PERC)
547
548enum mmtk_heap_mode {
549 RB_MMTK_DYNAMIC_HEAP,
550 RB_MMTK_FIXED_HEAP
551};
552
553MMTk_Builder *
554rb_mmtk_builder_init(void)
555{
556 MMTk_Builder *builder = mmtk_builder_default();
557 return builder;
558}
559
560void *
561rb_gc_impl_objspace_alloc(void)
562{
563 MMTk_Builder *builder = rb_mmtk_builder_init();
564 MMTk_RubyBindingOptions binding_options = {
565 .suffix_size = RB_GC_OBJ_SUFFIX_SIZE,
566 };
567 mmtk_init_binding(builder, &binding_options, &ruby_upcalls);
568
569 return calloc(1, sizeof(struct objspace));
570}
571
572static void gc_run_finalizers(void *data);
573
574void
575rb_gc_impl_objspace_init(void *objspace_ptr)
576{
577 struct objspace *objspace = objspace_ptr;
578
579 objspace->measure_gc_time = true;
580
581 objspace->finalizer_table = st_init_numtable();
582 objspace->finalizer_postponed_job = rb_postponed_job_preregister(0, gc_run_finalizers, objspace);
583
584 ccan_list_head_init(&objspace->ractor_caches);
585
586 objspace->mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
587 objspace->cond_world_stopped = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
588 objspace->cond_world_started = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
589
590 objspace->event_hook_mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
591}
592
593void
594rb_gc_impl_objspace_free(void *objspace_ptr)
595{
596 free(objspace_ptr);
597}
598
599void *
600rb_gc_impl_ractor_cache_alloc(void *objspace_ptr, void *ractor)
601{
602 struct objspace *objspace = objspace_ptr;
603 if (objspace->live_ractor_cache_count == 0) {
604 mmtk_initialize_collection(ractor);
605 }
606 objspace->live_ractor_cache_count++;
607
608 struct MMTk_ractor_cache *cache = calloc(1, sizeof(struct MMTk_ractor_cache));
609 ccan_list_add(&objspace->ractor_caches, &cache->list_node);
610
611 cache->mutator = mmtk_bind_mutator(cache);
612 cache->bump_pointer = mmtk_get_bump_pointer_allocator(cache->mutator);
613
614 return cache;
615}
616
617void
618rb_gc_impl_ractor_cache_free(void *objspace_ptr, void *cache_ptr)
619{
620 struct objspace *objspace = objspace_ptr;
621 struct MMTk_ractor_cache *cache = cache_ptr;
622
623 ccan_list_del(&cache->list_node);
624
625 mmtk_flush_obj_free_buffer(cache);
626
627 if (ruby_free_at_exit_p()) {
628 MMTK_ASSERT(objspace->live_ractor_cache_count > 0);
629 }
630 else {
631 MMTK_ASSERT(objspace->live_ractor_cache_count > 1);
632 }
633
634 objspace->live_ractor_cache_count--;
635
636 mmtk_destroy_mutator(cache->mutator);
637}
638
639void rb_gc_impl_set_params(void *objspace_ptr) { }
640
641static VALUE gc_verify_internal_consistency(VALUE self) { return Qnil; }
642
643#if SIZEOF_VALUE >= 8
644#define MMTK_HEAP_COUNT 12
645#define MMTK_MAX_OBJ_SIZE 1024
646static size_t heap_sizes[MMTK_HEAP_COUNT + 1] = {
647 32, 40, 64, 80, 96, 128, 160, 256, 512, 640, 768, MMTK_MAX_OBJ_SIZE, 0
648};
649#else
650#define MMTK_HEAP_COUNT 5
651#define MMTK_MAX_OBJ_SIZE 512
652static size_t heap_sizes[MMTK_HEAP_COUNT + 1] = {
653 32, 64, 128, 256, MMTK_MAX_OBJ_SIZE, 0
654};
655#endif
656
657void
658rb_gc_impl_init(void)
659{
660 VALUE gc_constants = rb_hash_new();
661 rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_SIZE")), SIZET2NUM(SIZEOF_VALUE >= 8 ? 64 : 32));
662 rb_hash_aset(gc_constants, ID2SYM(rb_intern("RBASIC_SIZE")), SIZET2NUM(sizeof(struct RBasic)));
663 rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OVERHEAD")), INT2NUM(0));
664 rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), LONG2FIX(MMTK_MAX_OBJ_SIZE));
665 rb_hash_aset(gc_constants, ID2SYM(rb_intern("HEAP_COUNT")), LONG2FIX(MMTK_HEAP_COUNT));
666 // TODO: correctly set RVALUE_OLD_AGE when we have generational GC support
667 rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OLD_AGE")), INT2FIX(0));
668 OBJ_FREEZE(gc_constants);
669 rb_define_const(rb_mGC, "INTERNAL_CONSTANTS", gc_constants);
670
671 // no-ops for compatibility
672 rb_define_singleton_method(rb_mGC, "verify_internal_consistency", gc_verify_internal_consistency, 0);
673
677 rb_define_singleton_method(rb_mGC, "latest_compact_info", rb_f_notimplement, 0);
678 rb_define_singleton_method(rb_mGC, "verify_compaction_references", rb_f_notimplement, -1);
679}
680
681size_t *
682rb_gc_impl_heap_sizes(void *objspace_ptr)
683{
684 return heap_sizes;
685}
686
687int
688rb_mmtk_obj_free_iter_wrapper(VALUE obj, void *data)
689{
690 struct objspace *objspace = data;
691
692 if (!RB_TYPE_P(obj, T_NONE)) {
693 rb_gc_obj_free_vm_weak_references(obj);
694 rb_gc_obj_free(objspace, obj);
695 }
696
697 return 0;
698}
699
700// Shutdown
701static void each_object(struct objspace *objspace, int (*func)(VALUE, void *), void *data);
702
703void
704rb_gc_impl_shutdown_free_objects(void *objspace_ptr)
705{
706 mmtk_set_gc_enabled(false);
707 each_object(objspace_ptr, rb_mmtk_obj_free_iter_wrapper, objspace_ptr);
708 mmtk_set_gc_enabled(true);
709}
710
711// GC
712void
713rb_gc_impl_start(void *objspace_ptr, bool full_mark, bool immediate_mark, bool immediate_sweep, bool compact)
714{
715 mmtk_handle_user_collection_request(rb_gc_get_ractor_newobj_cache(), true, full_mark);
716}
717
718bool
719rb_gc_impl_during_gc_p(void *objspace_ptr)
720{
721 struct objspace *objspace = objspace_ptr;
722 return objspace->world_stopped;
723}
724
725static void
726rb_gc_impl_prepare_heap_i(MMTk_ObjectReference obj, void *d)
727{
728 rb_gc_prepare_heap_process_object((VALUE)obj);
729}
730
731void
732rb_gc_impl_prepare_heap(void *objspace_ptr)
733{
734 mmtk_enumerate_objects(rb_gc_impl_prepare_heap_i, NULL);
735}
736
737void
738rb_gc_impl_gc_enable(void *objspace_ptr)
739{
740 mmtk_set_gc_enabled(true);
741}
742
743void
744rb_gc_impl_gc_disable(void *objspace_ptr, bool finish_current_gc)
745{
746 mmtk_set_gc_enabled(false);
747}
748
749bool
750rb_gc_impl_gc_enabled_p(void *objspace_ptr)
751{
752 return mmtk_gc_enabled_p();
753}
754
755void
756rb_gc_impl_stress_set(void *objspace_ptr, VALUE flag)
757{
758 struct objspace *objspace = objspace_ptr;
759
760 objspace->gc_stress = RTEST(flag);
761}
762
763VALUE
764rb_gc_impl_stress_get(void *objspace_ptr)
765{
766 struct objspace *objspace = objspace_ptr;
767
768 return objspace->gc_stress ? Qtrue : Qfalse;
769}
770
771VALUE
772rb_gc_impl_config_get(void *objspace_ptr)
773{
774 VALUE hash = rb_hash_new();
775
776 rb_hash_aset(hash, ID2SYM(rb_intern_const("mmtk_worker_count")), RB_ULONG2NUM(mmtk_worker_count()));
777 rb_hash_aset(hash, ID2SYM(rb_intern_const("mmtk_plan")), rb_str_new_cstr((const char *)mmtk_plan()));
778 rb_hash_aset(hash, ID2SYM(rb_intern_const("mmtk_heap_mode")), rb_str_new_cstr((const char *)mmtk_heap_mode()));
779 size_t heap_min = mmtk_heap_min();
780 if (heap_min > 0) rb_hash_aset(hash, ID2SYM(rb_intern_const("mmtk_heap_min")), RB_ULONG2NUM(heap_min));
781 rb_hash_aset(hash, ID2SYM(rb_intern_const("mmtk_heap_max")), RB_ULONG2NUM(mmtk_heap_max()));
782
783 return hash;
784}
785
786void
787rb_gc_impl_config_set(void *objspace_ptr, VALUE hash)
788{
789 // TODO
790}
791
792struct rb_gc_vm_context *
793rb_gc_impl_get_vm_context(void *objspace_ptr)
794{
795 struct objspace *objspace = objspace_ptr;
796
797 return &objspace->vm_context;
798}
799
800// Object allocation
801
802static VALUE
803rb_mmtk_alloc_fast_path(struct objspace *objspace, struct MMTk_ractor_cache *ractor_cache, size_t size, size_t align)
804{
805 MMTk_BumpPointer *bump_pointer = ractor_cache->bump_pointer;
806 if (bump_pointer == NULL) return 0;
807
808 uintptr_t cursor = bump_pointer->cursor;
809
810 // Ensure cursor is aligned
811 size_t mask = align - 1;
812 cursor = (cursor + mask) & ~mask;
813
814 cursor += size;
815
816 if (cursor > bump_pointer->limit) {
817 return 0;
818 }
819 else {
820 VALUE obj = cursor - size;
821 bump_pointer->cursor = cursor;
822 return obj;
823 }
824}
825
826static bool
827obj_can_parallel_free_p(VALUE obj)
828{
829 switch (RB_BUILTIN_TYPE(obj)) {
830 case T_ARRAY:
831 case T_BIGNUM:
832 case T_COMPLEX:
833 case T_FLOAT:
834 case T_HASH:
835 case T_OBJECT:
836 case T_RATIONAL:
837 case T_REGEXP:
838 case T_STRING:
839 case T_STRUCT:
840 case T_SYMBOL:
841 return true;
842 default:
843 return false;
844 }
845}
846
847static void
848mmtk_flush_obj_free_buffer(struct MMTk_ractor_cache *cache)
849{
850 if (cache->obj_free_parallel_count > 0) {
851 mmtk_add_obj_free_candidates(cache->obj_free_parallel_buf,
852 cache->obj_free_parallel_count, true);
853 cache->obj_free_parallel_count = 0;
854 }
855 if (cache->obj_free_non_parallel_count > 0) {
856 mmtk_add_obj_free_candidates(cache->obj_free_non_parallel_buf,
857 cache->obj_free_non_parallel_count, false);
858 cache->obj_free_non_parallel_count = 0;
859 }
860}
861
862static inline void
863mmtk_buffer_obj_free_candidate(struct MMTk_ractor_cache *cache, VALUE obj)
864{
865 if (obj_can_parallel_free_p(obj)) {
866 cache->obj_free_parallel_buf[cache->obj_free_parallel_count++] = (MMTk_ObjectReference)obj;
867 if (cache->obj_free_parallel_count >= OBJ_FREE_BUF_CAPACITY) {
868 mmtk_add_obj_free_candidates(cache->obj_free_parallel_buf,
869 cache->obj_free_parallel_count, true);
870 cache->obj_free_parallel_count = 0;
871 }
872 }
873 else {
874 cache->obj_free_non_parallel_buf[cache->obj_free_non_parallel_count++] = (MMTk_ObjectReference)obj;
875 if (cache->obj_free_non_parallel_count >= OBJ_FREE_BUF_CAPACITY) {
876 mmtk_add_obj_free_candidates(cache->obj_free_non_parallel_buf,
877 cache->obj_free_non_parallel_count, false);
878 cache->obj_free_non_parallel_count = 0;
879 }
880 }
881}
882
883VALUE
884rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size)
885{
886#define MMTK_ALLOCATION_SEMANTICS_DEFAULT 0
887 struct objspace *objspace = objspace_ptr;
888 struct MMTk_ractor_cache *ractor_cache = cache_ptr;
889
890 if (alloc_size > MMTK_MAX_OBJ_SIZE) rb_bug("too big");
891 for (int i = 0; i < MMTK_HEAP_COUNT; i++) {
892 if (alloc_size == heap_sizes[i]) break;
893 if (alloc_size < heap_sizes[i]) {
894 alloc_size = heap_sizes[i];
895 break;
896 }
897 }
898
899 if (objspace->gc_stress) {
900 mmtk_handle_user_collection_request(ractor_cache, false, false);
901 }
902
903 // Layout: [hidden size header (sizeof(VALUE))][payload (alloc_size)][suffix (RB_GC_OBJ_SUFFIX_SIZE)]
904 alloc_size += sizeof(VALUE) + RB_GC_OBJ_SUFFIX_SIZE;
905
906 VALUE *alloc_obj = (VALUE *)rb_mmtk_alloc_fast_path(objspace, ractor_cache, alloc_size, MMTk_MIN_OBJ_ALIGN);
907 if (!alloc_obj) {
908 alloc_obj = mmtk_alloc(ractor_cache->mutator, alloc_size, MMTk_MIN_OBJ_ALIGN, 0, MMTK_ALLOCATION_SEMANTICS_DEFAULT);
909 }
910
911 alloc_obj++;
912 alloc_obj[-1] = alloc_size - sizeof(VALUE) - RB_GC_OBJ_SUFFIX_SIZE;
913 alloc_obj[0] = flags;
914 alloc_obj[1] = klass;
915
916 // TODO: implement fast path for mmtk_post_alloc
917 mmtk_post_alloc(ractor_cache->mutator, (void*)alloc_obj, alloc_size, MMTK_ALLOCATION_SEMANTICS_DEFAULT);
918
919 // TODO: only add when object needs obj_free to be called
920 mmtk_buffer_obj_free_candidate(ractor_cache, (VALUE)alloc_obj);
921
922 objspace->total_allocated_objects++;
923
924 return (VALUE)alloc_obj;
925}
926
927size_t
928rb_gc_impl_obj_slot_size(VALUE obj)
929{
930 return ((VALUE *)obj)[-1];
931}
932
933size_t
934rb_gc_impl_heap_id_for_size(void *objspace_ptr, size_t size)
935{
936 for (int i = 0; i < MMTK_HEAP_COUNT; i++) {
937 if (size == heap_sizes[i]) return i;
938 if (size < heap_sizes[i]) return i;
939 }
940
941 rb_bug("size too big");
942}
943
944bool
945rb_gc_impl_size_allocatable_p(size_t size)
946{
947 return size <= MMTK_MAX_OBJ_SIZE;
948}
949
950// Malloc
951void *
952rb_gc_impl_malloc(void *objspace_ptr, size_t size, bool gc_allowed)
953{
954 // TODO: don't use system malloc
955 return malloc(size);
956}
957
958void *
959rb_gc_impl_calloc(void *objspace_ptr, size_t size, bool gc_allowed)
960{
961 // TODO: don't use system calloc
962 return calloc(1, size);
963}
964
965void *
966rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size, bool gc_allowed)
967{
968 // TODO: don't use system realloc
969 return realloc(ptr, new_size);
970}
971
972void
973rb_gc_impl_free(void *objspace_ptr, void *ptr, size_t old_size)
974{
975 // TODO: don't use system free
976 free(ptr);
977}
978
979void rb_gc_impl_adjust_memory_usage(void *objspace_ptr, ssize_t diff) { }
980
981// Marking
982static inline VALUE
983rb_mmtk_call_object_closure(VALUE obj, bool pin)
984{
985 if (RB_UNLIKELY(RB_BUILTIN_TYPE(obj) == T_NONE)) {
986 const size_t info_size = 256;
987 char obj_info_buf[info_size];
988 rb_raw_obj_info(obj_info_buf, info_size, obj);
989
990 char parent_obj_info_buf[info_size];
991 rb_raw_obj_info(parent_obj_info_buf, info_size, marking_parent_object);
992
993 rb_mmtk_gc_thread_bug("try to mark T_NONE object (obj: %s, parent: %s)", obj_info_buf, parent_obj_info_buf);
994 }
995
996 return (VALUE)rb_mmtk_gc_thread_tls->object_closure.c_function(
997 rb_mmtk_gc_thread_tls->object_closure.rust_closure,
998 rb_mmtk_gc_thread_tls->gc_context,
999 (MMTk_ObjectReference)obj,
1000 pin
1001 );
1002}
1003
1004void
1005rb_gc_impl_mark(void *objspace_ptr, VALUE obj)
1006{
1007 if (RB_SPECIAL_CONST_P(obj)) return;
1008
1009 rb_mmtk_call_object_closure(obj, false);
1010}
1011
1012void
1013rb_gc_impl_mark_and_move(void *objspace_ptr, VALUE *ptr)
1014{
1015 if (RB_SPECIAL_CONST_P(*ptr)) return;
1016
1017 VALUE new_obj = rb_mmtk_call_object_closure(*ptr, false);
1018 if (new_obj != *ptr) {
1019 *ptr = new_obj;
1020 }
1021}
1022
1023void
1024rb_gc_impl_mark_and_pin(void *objspace_ptr, VALUE obj)
1025{
1026 if (RB_SPECIAL_CONST_P(obj)) return;
1027
1028 rb_mmtk_call_object_closure(obj, true);
1029}
1030
1031void
1032rb_gc_impl_mark_maybe(void *objspace_ptr, VALUE obj)
1033{
1034 if (rb_gc_impl_pointer_to_heap_p(objspace_ptr, (const void *)obj)) {
1035 rb_gc_impl_mark_and_pin(objspace_ptr, obj);
1036 }
1037}
1038
1039void
1040rb_gc_impl_declare_weak_references(void *objspace_ptr, VALUE obj)
1041{
1043 mmtk_declare_weak_references((MMTk_ObjectReference)obj);
1044}
1045
1046bool
1047rb_gc_impl_handle_weak_references_alive_p(void *objspace_ptr, VALUE obj)
1048{
1049 return mmtk_weak_references_alive_p((MMTk_ObjectReference)obj);
1050}
1051
1052// Compaction
1053void
1054rb_gc_impl_register_pinning_obj(void *objspace_ptr, VALUE obj)
1055{
1056 mmtk_register_pinning_obj((MMTk_ObjectReference)obj);
1057}
1058
1059bool
1060rb_gc_impl_object_moved_p(void *objspace_ptr, VALUE obj)
1061{
1062 return rb_mmtk_call_object_closure(obj, false) != obj;
1063}
1064
1065VALUE
1066rb_gc_impl_location(void *objspace_ptr, VALUE obj)
1067{
1068 return rb_mmtk_call_object_closure(obj, false);
1069}
1070
1071// Write barriers
1072void
1073rb_gc_impl_writebarrier(void *objspace_ptr, VALUE a, VALUE b)
1074{
1075 struct MMTk_ractor_cache *cache = rb_gc_get_ractor_newobj_cache();
1076
1077 if (SPECIAL_CONST_P(b)) return;
1078
1079#ifdef MMTK_DEBUG
1080 if (!rb_gc_impl_pointer_to_heap_p(objspace_ptr, (void *)a)) {
1081 char buff[256];
1082 rb_bug("a: %s is not an object", rb_raw_obj_info(buff, 256, a));
1083 }
1084
1085 if (!rb_gc_impl_pointer_to_heap_p(objspace_ptr, (void *)b)) {
1086 char buff[256];
1087 rb_bug("b: %s is not an object", rb_raw_obj_info(buff, 256, b));
1088 }
1089#endif
1090
1091 MMTK_ASSERT(BUILTIN_TYPE(a) != T_NONE);
1092 MMTK_ASSERT(BUILTIN_TYPE(b) != T_NONE);
1093
1094 mmtk_object_reference_write_post(cache->mutator, (MMTk_ObjectReference)a);
1095}
1096
1097void
1098rb_gc_impl_writebarrier_unprotect(void *objspace_ptr, VALUE obj)
1099{
1100 mmtk_register_wb_unprotected_object((MMTk_ObjectReference)obj);
1101}
1102
1103void
1104rb_gc_impl_writebarrier_remember(void *objspace_ptr, VALUE obj)
1105{
1106 struct MMTk_ractor_cache *cache = rb_gc_get_ractor_newobj_cache();
1107
1108 mmtk_object_reference_write_post(cache->mutator, (MMTk_ObjectReference)obj);
1109}
1110
1111// Heap walking
1112static void
1113each_objects_i(MMTk_ObjectReference obj, void *d)
1114{
1115 rb_darray(VALUE) *objs = d;
1116
1117 rb_darray_append(objs, (VALUE)obj);
1118}
1119
1120static void
1121each_object(struct objspace *objspace, int (*func)(VALUE, void *), void *data)
1122{
1123 rb_darray(VALUE) objs;
1124 rb_darray_make(&objs, 0);
1125
1126 mmtk_enumerate_objects(each_objects_i, &objs);
1127
1128 VALUE *obj_ptr;
1129 rb_darray_foreach(objs, i, obj_ptr) {
1130 if (!mmtk_is_mmtk_object((MMTk_ObjectReference)*obj_ptr)) continue;
1131
1132 if (func(*obj_ptr, data) != 0) {
1133 break;
1134 }
1135 }
1136
1137 rb_darray_free(objs);
1138}
1139
1141 int (*func)(void *, void *, size_t, void *);
1142 void *data;
1143};
1144
1145static int
1146rb_gc_impl_each_objects_i(VALUE obj, void *d)
1147{
1148 struct rb_gc_impl_each_objects_data *data = d;
1149
1150 size_t slot_size = rb_gc_impl_obj_slot_size(obj);
1151
1152 return data->func((void *)obj, (void *)(obj + slot_size), slot_size, data->data);
1153}
1154
1155void
1156rb_gc_impl_each_objects(void *objspace_ptr, int (*func)(void *, void *, size_t, void *), void *data)
1157{
1158 struct rb_gc_impl_each_objects_data each_objects_data = {
1159 .func = func,
1160 .data = data
1161 };
1162
1163 each_object(objspace_ptr, rb_gc_impl_each_objects_i, &each_objects_data);
1164}
1165
1167 void (*func)(VALUE, void *);
1168 void *data;
1169};
1170
1171static int
1172rb_gc_impl_each_object_i(VALUE obj, void *d)
1173{
1174 struct rb_gc_impl_each_object_data *data = d;
1175
1176 data->func(obj, data->data);
1177
1178 return 0;
1179}
1180
1181void
1182rb_gc_impl_each_object(void *objspace_ptr, void (*func)(VALUE, void *), void *data)
1183{
1184 struct rb_gc_impl_each_object_data each_object_data = {
1185 .func = func,
1186 .data = data
1187 };
1188
1189 each_object(objspace_ptr, rb_gc_impl_each_object_i, &each_object_data);
1190}
1191
1192// Finalizers
1193static VALUE
1194gc_run_finalizers_get_final(long i, void *data)
1195{
1196 VALUE table = (VALUE)data;
1197
1198 return RARRAY_AREF(table, i + 1);
1199}
1200
1201static void
1202gc_run_finalizers(void *data)
1203{
1204 struct objspace *objspace = data;
1205
1206 rb_gc_set_pending_interrupt();
1207
1208 while (objspace->finalizer_jobs != NULL) {
1209 struct MMTk_final_job *job = objspace->finalizer_jobs;
1210 objspace->finalizer_jobs = job->next;
1211
1212 switch (job->kind) {
1213 case MMTK_FINAL_JOB_DFREE:
1214 job->as.dfree.func(job->as.dfree.data);
1215 break;
1216 case MMTK_FINAL_JOB_FINALIZE: {
1217 VALUE finalizer_array = job->as.finalize.finalizer_array;
1218
1219 rb_gc_run_obj_finalizer(
1220 RARRAY_AREF(finalizer_array, 0),
1221 RARRAY_LEN(finalizer_array) - 1,
1222 gc_run_finalizers_get_final,
1223 (void *)finalizer_array
1224 );
1225
1226 RB_GC_GUARD(finalizer_array);
1227 break;
1228 }
1229 }
1230
1231 xfree(job);
1232 }
1233
1234 rb_gc_unset_pending_interrupt();
1235}
1236
1237void
1238rb_gc_impl_make_zombie(void *objspace_ptr, VALUE obj, void (*dfree)(void *), void *data)
1239{
1240 if (dfree == NULL) return;
1241
1242 struct objspace *objspace = objspace_ptr;
1243
1244 struct MMTk_final_job *job = xmalloc(sizeof(struct MMTk_final_job));
1245 job->kind = MMTK_FINAL_JOB_DFREE;
1246 job->as.dfree.func = dfree;
1247 job->as.dfree.data = data;
1248
1249 struct MMTk_final_job *prev;
1250 do {
1251 job->next = objspace->finalizer_jobs;
1252 prev = RUBY_ATOMIC_PTR_CAS(objspace->finalizer_jobs, job->next, job);
1253 } while (prev != job->next);
1254
1255 if (!ruby_free_at_exit_p()) {
1256 rb_postponed_job_trigger(objspace->finalizer_postponed_job);
1257 }
1258}
1259
1260VALUE
1261rb_gc_impl_define_finalizer(void *objspace_ptr, VALUE obj, VALUE block)
1262{
1263 struct objspace *objspace = objspace_ptr;
1264 VALUE table;
1265 st_data_t data;
1266
1267 RBASIC(obj)->flags |= FL_FINALIZE;
1268
1269 int lev = RB_GC_VM_LOCK();
1270
1271 if (st_lookup(objspace->finalizer_table, obj, &data)) {
1272 table = (VALUE)data;
1273
1274 /* avoid duplicate block, table is usually small */
1275 {
1276 long len = RARRAY_LEN(table);
1277 long i;
1278
1279 for (i = 0; i < len; i++) {
1280 VALUE recv = RARRAY_AREF(table, i);
1281 if (rb_equal(recv, block)) {
1282 RB_GC_VM_UNLOCK(lev);
1283 return recv;
1284 }
1285 }
1286 }
1287
1288 rb_ary_push(table, block);
1289 }
1290 else {
1291 table = rb_ary_new3(2, rb_obj_id(obj), block);
1292 rb_obj_hide(table);
1293 st_add_direct(objspace->finalizer_table, obj, table);
1294 }
1295
1296 RB_GC_VM_UNLOCK(lev);
1297
1298 return block;
1299}
1300
1301void
1302rb_gc_impl_undefine_finalizer(void *objspace_ptr, VALUE obj)
1303{
1304 struct objspace *objspace = objspace_ptr;
1305
1306 st_data_t data = obj;
1307
1308 int lev = RB_GC_VM_LOCK();
1309 st_delete(objspace->finalizer_table, &data, 0);
1310 RB_GC_VM_UNLOCK(lev);
1311
1312 FL_UNSET(obj, FL_FINALIZE);
1313}
1314
1315void
1316rb_gc_impl_copy_finalizer(void *objspace_ptr, VALUE dest, VALUE obj)
1317{
1318 struct objspace *objspace = objspace_ptr;
1319 VALUE table;
1320 st_data_t data;
1321
1322 if (!FL_TEST(obj, FL_FINALIZE)) return;
1323
1324 int lev = RB_GC_VM_LOCK();
1325 if (RB_LIKELY(st_lookup(objspace->finalizer_table, obj, &data))) {
1326 table = rb_ary_dup((VALUE)data);
1327 RARRAY_ASET(table, 0, rb_obj_id(dest));
1328 st_insert(objspace->finalizer_table, dest, table);
1329 FL_SET(dest, FL_FINALIZE);
1330 }
1331 else {
1332 rb_bug("rb_gc_copy_finalizer: FL_FINALIZE set but not found in finalizer_table: %s", rb_obj_info(obj));
1333 }
1334 RB_GC_VM_UNLOCK(lev);
1335}
1336
1337static int
1338move_finalizer_from_table_i(st_data_t key, st_data_t val, st_data_t arg)
1339{
1340 struct objspace *objspace = (struct objspace *)arg;
1341
1342 make_final_job(objspace, (VALUE)key, (VALUE)val);
1343
1344 return ST_DELETE;
1345}
1346
1347void
1348rb_gc_impl_shutdown_call_finalizer(void *objspace_ptr)
1349{
1350 struct objspace *objspace = objspace_ptr;
1351
1352 while (objspace->finalizer_table->num_entries) {
1353 st_foreach(objspace->finalizer_table, move_finalizer_from_table_i, (st_data_t)objspace);
1354
1355 gc_run_finalizers(objspace);
1356 }
1357
1358 unsigned int lev = RB_GC_VM_LOCK();
1359 {
1360 struct MMTk_ractor_cache *rc;
1361 ccan_list_for_each(&objspace->ractor_caches, rc, list_node) {
1362 mmtk_flush_obj_free_buffer(rc);
1363 }
1364
1365 struct MMTk_RawVecOfObjRef registered_candidates = mmtk_get_all_obj_free_candidates();
1366 for (size_t i = 0; i < registered_candidates.len; i++) {
1367 VALUE obj = (VALUE)registered_candidates.ptr[i];
1368
1369 if (rb_gc_shutdown_call_finalizer_p(obj)) {
1370 rb_gc_obj_free(objspace_ptr, obj);
1371 RBASIC(obj)->flags = 0;
1372 }
1373 }
1374 mmtk_free_raw_vec_of_obj_ref(registered_candidates);
1375 }
1376 RB_GC_VM_UNLOCK(lev);
1377
1378 gc_run_finalizers(objspace);
1379}
1380
1381// Forking
1382
1383void
1384rb_gc_impl_before_fork(void *objspace_ptr)
1385{
1386 struct objspace *objspace = objspace_ptr;
1387
1388 retry:
1389 objspace->fork_hook_vm_lock_lev = RB_GC_VM_LOCK();
1390 rb_gc_vm_barrier();
1391
1392 /* At this point, we know that all the Ractors are paused because of the
1393 * rb_gc_vm_barrier above. Since rb_mmtk_block_for_gc is a barrier point,
1394 * one or more Ractors could be paused there. However, mmtk_before_fork is
1395 * not compatible with that because it assumes that the MMTk workers are idle,
1396 * but the workers are not idle because they are busy working on a GC.
1397 *
1398 * This essentially implements a trylock. It will optimistically lock but will
1399 * release the lock if it detects that any other Ractors are waiting in
1400 * rb_mmtk_block_for_gc.
1401 */
1402 rb_atomic_t mutator_blocking_count = RUBY_ATOMIC_LOAD(objspace->mutator_blocking_count);
1403 if (mutator_blocking_count != 0) {
1404 RB_GC_VM_UNLOCK(objspace->fork_hook_vm_lock_lev);
1405 goto retry;
1406 }
1407
1408 mmtk_before_fork();
1409}
1410
1411void
1412rb_gc_impl_after_fork(void *objspace_ptr, rb_pid_t pid)
1413{
1414 struct objspace *objspace = objspace_ptr;
1415
1416 mmtk_after_fork(rb_gc_get_ractor_newobj_cache());
1417
1418 RB_GC_VM_UNLOCK(objspace->fork_hook_vm_lock_lev);
1419}
1420
1421// Statistics
1422
1423void
1424rb_gc_impl_set_measure_total_time(void *objspace_ptr, VALUE flag)
1425{
1426 struct objspace *objspace = objspace_ptr;
1427
1428 objspace->measure_gc_time = RTEST(flag);
1429}
1430
1431bool
1432rb_gc_impl_get_measure_total_time(void *objspace_ptr)
1433{
1434 struct objspace *objspace = objspace_ptr;
1435
1436 return objspace->measure_gc_time;
1437}
1438
1439unsigned long long
1440rb_gc_impl_get_total_time(void *objspace_ptr)
1441{
1442 struct objspace *objspace = objspace_ptr;
1443
1444 return objspace->total_gc_time;
1445}
1446
1447size_t
1448rb_gc_impl_gc_count(void *objspace_ptr)
1449{
1450 struct objspace *objspace = objspace_ptr;
1451
1452 return objspace->gc_count;
1453}
1454
1455VALUE
1456rb_gc_impl_latest_gc_info(void *objspace_ptr, VALUE hash_or_key)
1457{
1458 VALUE hash = Qnil, key = Qnil;
1459
1460 if (SYMBOL_P(hash_or_key)) {
1461 key = hash_or_key;
1462 }
1463 else if (RB_TYPE_P(hash_or_key, T_HASH)) {
1464 hash = hash_or_key;
1465 }
1466 else {
1467 rb_bug("gc_info_decode: non-hash or symbol given");
1468 }
1469
1470#define SET(name, attr) \
1471 if (key == ID2SYM(rb_intern_const(#name))) \
1472 return (attr); \
1473 else if (hash != Qnil) \
1474 rb_hash_aset(hash, ID2SYM(rb_intern_const(#name)), (attr));
1475
1476 /* Hack to get StackProf working because it calls rb_gc_latest_gc_info with
1477 * the :state key and expects a result. This always returns the :none state. */
1478 SET(state, ID2SYM(rb_intern_const("none")));
1479#undef SET
1480
1481 if (!NIL_P(key)) {
1482 // Matched key should return above
1483 return Qundef;
1484 }
1485
1486 return hash;
1487}
1488
1489enum gc_stat_sym {
1490 gc_stat_sym_count,
1491 gc_stat_sym_moving_gc_count,
1492 gc_stat_sym_time,
1493 gc_stat_sym_total_allocated_objects,
1494 gc_stat_sym_total_bytes,
1495 gc_stat_sym_used_bytes,
1496 gc_stat_sym_free_bytes,
1497 gc_stat_sym_starting_heap_address,
1498 gc_stat_sym_last_heap_address,
1499 gc_stat_sym_weak_references_count,
1500 gc_stat_sym_last
1501};
1502
1503static VALUE gc_stat_symbols[gc_stat_sym_last];
1504
1505static void
1506setup_gc_stat_symbols(void)
1507{
1508 if (gc_stat_symbols[0] == 0) {
1509#define S(s) gc_stat_symbols[gc_stat_sym_##s] = ID2SYM(rb_intern_const(#s))
1510 S(count);
1511 S(moving_gc_count);
1512 S(time);
1513 S(total_allocated_objects);
1514 S(total_bytes);
1515 S(used_bytes);
1516 S(free_bytes);
1517 S(starting_heap_address);
1518 S(last_heap_address);
1519 S(weak_references_count);
1520 }
1521}
1522
1523VALUE
1524rb_gc_impl_stat(void *objspace_ptr, VALUE hash_or_sym)
1525{
1526 struct objspace *objspace = objspace_ptr;
1527 VALUE hash = Qnil, key = Qnil;
1528
1529 setup_gc_stat_symbols();
1530
1531 if (RB_TYPE_P(hash_or_sym, T_HASH)) {
1532 hash = hash_or_sym;
1533 }
1534 else if (SYMBOL_P(hash_or_sym)) {
1535 key = hash_or_sym;
1536 }
1537 else {
1538 rb_bug("non-hash or symbol given");
1539 }
1540
1541#define SET(name, attr) \
1542 if (key == gc_stat_symbols[gc_stat_sym_##name]) \
1543 return SIZET2NUM(attr); \
1544 else if (hash != Qnil) \
1545 rb_hash_aset(hash, gc_stat_symbols[gc_stat_sym_##name], SIZET2NUM(attr));
1546
1547 SET(count, objspace->gc_count);
1548 SET(moving_gc_count, objspace->moving_gc_count);
1549 SET(time, objspace->total_gc_time / (1000 * 1000));
1550 SET(total_allocated_objects, objspace->total_allocated_objects);
1551 SET(total_bytes, mmtk_total_bytes());
1552 SET(used_bytes, mmtk_used_bytes());
1553 SET(free_bytes, mmtk_free_bytes());
1554 SET(starting_heap_address, (size_t)mmtk_starting_heap_address());
1555 SET(last_heap_address, (size_t)mmtk_last_heap_address());
1556 SET(weak_references_count, mmtk_weak_references_count());
1557#undef SET
1558
1559 if (!NIL_P(key)) {
1560 // Matched key should return above
1561 return Qundef;
1562 }
1563
1564 return hash;
1565}
1566
1567VALUE
1568rb_gc_impl_stat_heap(void *objspace_ptr, VALUE heap_name, VALUE hash_or_sym)
1569{
1570 if (FIXNUM_P(heap_name) && SYMBOL_P(hash_or_sym)) {
1571 int heap_idx = FIX2INT(heap_name);
1572 if (heap_idx < 0 || heap_idx >= MMTK_HEAP_COUNT) {
1573 rb_raise(rb_eArgError, "size pool index out of range");
1574 }
1575
1576 if (hash_or_sym == ID2SYM(rb_intern("slot_size"))) {
1577 return SIZET2NUM(heap_sizes[heap_idx]);
1578 }
1579
1580 return Qundef;
1581 }
1582
1583 if (RB_TYPE_P(hash_or_sym, T_HASH)) {
1584 return hash_or_sym;
1585 }
1586
1587 return Qundef;
1588}
1589
1590// Miscellaneous
1591
1592#define RB_GC_OBJECT_METADATA_ENTRY_COUNT 1
1593static struct rb_gc_object_metadata_entry object_metadata_entries[RB_GC_OBJECT_METADATA_ENTRY_COUNT + 1];
1594
1596rb_gc_impl_object_metadata(void *objspace_ptr, VALUE obj)
1597{
1598 static ID ID_object_id;
1599
1600 if (!ID_object_id) {
1601#define I(s) ID_##s = rb_intern(#s);
1602 I(object_id);
1603#undef I
1604 }
1605
1606 size_t n = 0;
1607
1608#define SET_ENTRY(na, v) do { \
1609 MMTK_ASSERT(n <= RB_GC_OBJECT_METADATA_ENTRY_COUNT); \
1610 object_metadata_entries[n].name = ID_##na; \
1611 object_metadata_entries[n].val = v; \
1612 n++; \
1613} while (0)
1614
1615 if (rb_obj_id_p(obj)) SET_ENTRY(object_id, rb_obj_id(obj));
1616
1617 object_metadata_entries[n].name = 0;
1618 object_metadata_entries[n].val = 0;
1619
1620 return object_metadata_entries;
1621}
1622
1623bool
1624rb_gc_impl_pointer_to_heap_p(void *objspace_ptr, const void *ptr)
1625{
1626 if (ptr == NULL) return false;
1627 if ((uintptr_t)ptr % sizeof(void*) != 0) return false;
1628 return mmtk_is_mmtk_object((MMTk_Address)ptr);
1629}
1630
1631bool
1632rb_gc_impl_garbage_object_p(void *objspace_ptr, VALUE obj)
1633{
1634 return false;
1635}
1636
1637void rb_gc_impl_set_event_hook(void *objspace_ptr, const rb_event_flag_t event) { }
1638
1639void
1640rb_gc_impl_copy_attributes(void *objspace_ptr, VALUE dest, VALUE obj)
1641{
1642 if (mmtk_object_wb_unprotected_p((MMTk_ObjectReference)obj)) {
1643 rb_gc_impl_writebarrier_unprotect(objspace_ptr, dest);
1644 }
1645
1646 rb_gc_impl_copy_finalizer(objspace_ptr, dest, obj);
1647}
1648
1649// GC Identification
1650
1651const char *
1652rb_gc_impl_active_gc_name(void)
1653{
1654 return "mmtk";
1655}
Atomic operations.
#define RUBY_ATOMIC_INC(var)
Atomically increments the value pointed by var.
Definition atomic.h:214
#define RUBY_ATOMIC_PTR_CAS(var, oldval, newval)
Identical to RUBY_ATOMIC_CAS, except it expects its arguments are void*.
Definition atomic.h:365
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
Definition atomic.h:69
#define RUBY_ATOMIC_DEC(var)
Atomically decrements the value pointed by var.
Definition atomic.h:223
#define RUBY_ATOMIC_LOAD(var)
Atomic load.
Definition atomic.h:175
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
unsigned int rb_postponed_job_handle_t
The type of a handle returned from rb_postponed_job_preregister and passed to rb_postponed_job_trigge...
Definition debug.h:703
void rb_postponed_job_trigger(rb_postponed_job_handle_t h)
Triggers a pre-registered job registered with rb_postponed_job_preregister, scheduling it for executi...
Definition vm_trace.c:1916
rb_postponed_job_handle_t rb_postponed_job_preregister(unsigned int flags, rb_postponed_job_func_t func, void *data)
Pre-registers a func in Ruby's postponed job preregistration table, returning an opaque handle which ...
Definition vm_trace.c:1882
#define RUBY_INTERNAL_EVENT_FREEOBJ
Object swept.
Definition event.h:94
#define RUBY_INTERNAL_EVENT_GC_START
GC started.
Definition event.h:95
uint32_t rb_event_flag_t
Represents event(s).
Definition event.h:108
static VALUE RB_FL_TEST(VALUE obj, VALUE flags)
Tests if the given flag(s) are set or not.
Definition fl_type.h:430
static void RB_FL_SET(VALUE obj, VALUE flags)
Sets the given flag(s).
Definition fl_type.h:561
@ RUBY_FL_FINALIZE
This flag has something to do with finalisers.
Definition fl_type.h:226
@ RUBY_FL_WEAK_REFERENCE
This object weakly refers to other objects.
Definition fl_type.h:260
#define T_COMPLEX
Old name of RUBY_T_COMPLEX.
Definition value_type.h:59
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define T_FLOAT
Old name of RUBY_T_FLOAT.
Definition value_type.h:64
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
Definition value_type.h:57
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define T_STRUCT
Old name of RUBY_T_STRUCT.
Definition value_type.h:79
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:131
#define T_NONE
Old name of RUBY_T_NONE.
Definition value_type.h:74
#define SIZET2NUM
Old name of RB_SIZE2NUM.
Definition size_t.h:62
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define FIX2INT
Old name of RB_FIX2INT.
Definition int.h:41
#define FL_FINALIZE
Old name of RUBY_FL_FINALIZE.
Definition fl_type.h:61
#define T_RATIONAL
Old name of RUBY_T_RATIONAL.
Definition value_type.h:76
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
#define FL_SET
Old name of RB_FL_SET.
Definition fl_type.h:125
#define rb_ary_new3
Old name of rb_ary_new_from_args.
Definition array.h:658
#define Qtrue
Old name of RUBY_Qtrue.
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define T_OBJECT
Old name of RUBY_T_OBJECT.
Definition value_type.h:75
#define NIL_P
Old name of RB_NIL_P.
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
Definition value_type.h:80
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition value_type.h:85
#define FL_TEST
Old name of RB_FL_TEST.
Definition fl_type.h:127
#define FL_UNSET
Old name of RB_FL_UNSET.
Definition fl_type.h:129
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define SYMBOL_P
Old name of RB_SYMBOL_P.
Definition value_type.h:88
#define T_REGEXP
Old name of RUBY_T_REGEXP.
Definition value_type.h:77
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:95
VALUE rb_mGC
GC module.
Definition gc.c:410
VALUE rb_equal(VALUE lhs, VALUE rhs)
This function is an optimised version of calling #==.
Definition object.c:141
#define RBIMPL_ATTR_FORMAT(x, y, z)
Wraps (or simulates) __attribute__((format))
Definition format.h:33
VALUE rb_ary_dup(VALUE ary)
Duplicates an array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1515
VALUE rb_f_notimplement(int argc, const VALUE *argv, VALUE obj, VALUE marker)
Raises rb_eNotImpError.
Definition vm_method.c:905
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:285
int len
Length of the buffer.
Definition io.h:8
#define RB_ULONG2NUM
Just another name of rb_ulong2num_inline.
Definition long.h:59
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
#define RBIMPL_ATTR_NORETURN()
Wraps (or simulates) [[noreturn]]
Definition noreturn.h:38
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:386
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
int ruby_native_thread_p(void)
Queries if the thread which calls this function is a ruby's thread.
Definition thread.c:5815
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
#define RTEST
This is an old name of RB_TEST.
C99 shim for <stdbool.h>
void * rust_closure
The pointer to the Rust-level closure object.
Definition mmtk.h:49
MMTk_ObjectClosureFunction c_function
The function to be called from C.
Definition mmtk.h:45
Ruby object's base components.
Definition rbasic.h:69
Definition gc_impl.h:15
Definition st.h:79
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
#define SIZEOF_VALUE
Identical to sizeof(VALUE), except it is a macro that can also be used inside of preprocessor directi...
Definition value.h:69
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static enum ruby_value_type RB_BUILTIN_TYPE(VALUE obj)
Queries the type of the object.
Definition value_type.h:182
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376