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