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