Ruby 4.0.5p0 (2026-05-20 revision 64336ffd0ee9e1f4c05891695a3d7b49cb709721)
mmtk.c
1#include <pthread.h>
2#include <stdbool.h>
3
4#include "ruby/assert.h"
5#include "ruby/atomic.h"
6#include "ruby/debug.h"
7
8#include "gc/gc.h"
9#include "gc/gc_impl.h"
10#include "gc/mmtk/mmtk.h"
11
12#include "ccan/list/list.h"
13#include "darray.h"
14
15#ifdef __APPLE__
16#include <sys/sysctl.h>
17#endif
18
19struct objspace {
20 bool measure_gc_time;
21 bool gc_stress;
22
23 size_t gc_count;
24 size_t total_gc_time;
25 size_t total_allocated_objects;
26
27 st_table *finalizer_table;
28 struct MMTk_final_job *finalizer_jobs;
29 rb_postponed_job_handle_t finalizer_postponed_job;
30
31 struct ccan_list_head ractor_caches;
32 unsigned long live_ractor_cache_count;
33
34 pthread_mutex_t mutex;
35 rb_atomic_t mutator_blocking_count;
36 bool world_stopped;
37 pthread_cond_t cond_world_stopped;
38 pthread_cond_t cond_world_started;
39 size_t start_the_world_count;
40
41 struct rb_gc_vm_context vm_context;
42
43 unsigned int fork_hook_vm_lock_lev;
44};
45
47 struct ccan_list_node list_node;
48
49 MMTk_Mutator *mutator;
50 bool gc_mutator_p;
51
52 MMTk_BumpPointer *bump_pointer;
53};
54
56 struct MMTk_final_job *next;
57 enum {
58 MMTK_FINAL_JOB_DFREE,
59 MMTK_FINAL_JOB_FINALIZE,
60 } kind;
61 union {
62 struct {
63 void (*func)(void *);
64 void *data;
65 } dfree;
66 struct {
67 /* HACK: we store the object ID on the 0th element of this array. */
68 VALUE finalizer_array;
69 } finalize;
70 } as;
71};
72
73#ifdef RB_THREAD_LOCAL_SPECIFIER
74RB_THREAD_LOCAL_SPECIFIER struct MMTk_GCThreadTLS *rb_mmtk_gc_thread_tls;
75#else
76# error We currently need language-supported TLS
77#endif
78
79#include <pthread.h>
80
81static void
82rb_mmtk_init_gc_worker_thread(MMTk_VMWorkerThread gc_thread_tls)
83{
84 rb_mmtk_gc_thread_tls = gc_thread_tls;
85}
86
87static bool
88rb_mmtk_is_mutator(void)
89{
90 return ruby_native_thread_p();
91}
92
93static void
94rb_mmtk_stop_the_world(void)
95{
96 struct objspace *objspace = rb_gc_get_objspace();
97
98 int err;
99 if ((err = pthread_mutex_lock(&objspace->mutex)) != 0) {
100 rb_bug("ERROR: cannot lock objspace->mutex: %s", strerror(err));
101 }
102
103 while (!objspace->world_stopped) {
104 pthread_cond_wait(&objspace->cond_world_stopped, &objspace->mutex);
105 }
106
107 if ((err = pthread_mutex_unlock(&objspace->mutex)) != 0) {
108 rb_bug("ERROR: cannot release objspace->mutex: %s", strerror(err));
109 }
110}
111
112static void
113rb_mmtk_resume_mutators(void)
114{
115 struct objspace *objspace = rb_gc_get_objspace();
116
117 int err;
118 if ((err = pthread_mutex_lock(&objspace->mutex)) != 0) {
119 rb_bug("ERROR: cannot lock objspace->mutex: %s", strerror(err));
120 }
121
122 objspace->world_stopped = false;
123 objspace->gc_count++;
124 pthread_cond_broadcast(&objspace->cond_world_started);
125
126 if ((err = pthread_mutex_unlock(&objspace->mutex)) != 0) {
127 rb_bug("ERROR: cannot release objspace->mutex: %s", strerror(err));
128 }
129}
130
131static void
132rb_mmtk_block_for_gc(MMTk_VMMutatorThread mutator)
133{
134 struct objspace *objspace = rb_gc_get_objspace();
135
136 size_t starting_gc_count = objspace->gc_count;
137 RUBY_ATOMIC_INC(objspace->mutator_blocking_count);
138 int lock_lev = RB_GC_VM_LOCK();
139 RUBY_ATOMIC_DEC(objspace->mutator_blocking_count);
140 int err;
141 if ((err = pthread_mutex_lock(&objspace->mutex)) != 0) {
142 rb_bug("ERROR: cannot lock objspace->mutex: %s", strerror(err));
143 }
144
145 if (objspace->gc_count == starting_gc_count) {
146 rb_gc_event_hook(0, RUBY_INTERNAL_EVENT_GC_START);
147
148 rb_gc_initialize_vm_context(&objspace->vm_context);
149
150 mutator->gc_mutator_p = true;
151
152 struct timespec gc_start_time;
153 if (objspace->measure_gc_time) {
154 clock_gettime(CLOCK_MONOTONIC, &gc_start_time);
155 }
156
157 rb_gc_save_machine_context();
158
159 rb_gc_vm_barrier();
160
161 objspace->world_stopped = true;
162
163 pthread_cond_broadcast(&objspace->cond_world_stopped);
164
165 // Wait for GC end
166 while (objspace->world_stopped) {
167 pthread_cond_wait(&objspace->cond_world_started, &objspace->mutex);
168 }
169
170 if (objspace->measure_gc_time) {
171 struct timespec gc_end_time;
172 clock_gettime(CLOCK_MONOTONIC, &gc_end_time);
173
174 objspace->total_gc_time +=
175 (gc_end_time.tv_sec - gc_start_time.tv_sec) * (1000 * 1000 * 1000) +
176 (gc_end_time.tv_nsec - gc_start_time.tv_nsec);
177 }
178 }
179
180 if ((err = pthread_mutex_unlock(&objspace->mutex)) != 0) {
181 rb_bug("ERROR: cannot release objspace->mutex: %s", strerror(err));
182 }
183 RB_GC_VM_UNLOCK(lock_lev);
184}
185
186static size_t
187rb_mmtk_number_of_mutators(void)
188{
189 struct objspace *objspace = rb_gc_get_objspace();
190 return objspace->live_ractor_cache_count;
191}
192
193static void
194rb_mmtk_get_mutators(void (*visit_mutator)(MMTk_Mutator *mutator, void *data), void *data)
195{
196 struct objspace *objspace = rb_gc_get_objspace();
197 struct MMTk_ractor_cache *ractor_cache;
198
199 ccan_list_for_each(&objspace->ractor_caches, ractor_cache, list_node) {
200 visit_mutator(ractor_cache->mutator, data);
201 }
202}
203
204static void
205rb_mmtk_scan_gc_roots(void)
206{
207 struct objspace *objspace = rb_gc_get_objspace();
208
209 // FIXME: Make `rb_gc_mark_roots` aware that the current thread may not have EC.
210 // See: https://github.com/ruby/mmtk/issues/22
211 rb_gc_worker_thread_set_vm_context(&objspace->vm_context);
212 rb_gc_mark_roots(objspace, NULL);
213 rb_gc_worker_thread_unset_vm_context(&objspace->vm_context);
214}
215
216static int
217pin_value(st_data_t key, st_data_t value, st_data_t data)
218{
219 rb_gc_impl_mark_and_pin((void *)data, (VALUE)value);
220
221 return ST_CONTINUE;
222}
223
224static void
225rb_mmtk_scan_objspace(void)
226{
227 struct objspace *objspace = rb_gc_get_objspace();
228
229 if (objspace->finalizer_table != NULL) {
230 st_foreach(objspace->finalizer_table, pin_value, (st_data_t)objspace);
231 }
232
233 struct MMTk_final_job *job = objspace->finalizer_jobs;
234 while (job != NULL) {
235 switch (job->kind) {
236 case MMTK_FINAL_JOB_DFREE:
237 break;
238 case MMTK_FINAL_JOB_FINALIZE:
239 rb_gc_impl_mark(objspace, job->as.finalize.finalizer_array);
240 break;
241 default:
242 rb_bug("rb_mmtk_scan_objspace: unknown final job type %d", job->kind);
243 }
244
245 job = job->next;
246 }
247}
248
249static void
250rb_mmtk_scan_object_ruby_style(MMTk_ObjectReference object)
251{
252 rb_gc_mark_children(rb_gc_get_objspace(), (VALUE)object);
253}
254
255static void
256rb_mmtk_call_gc_mark_children(MMTk_ObjectReference object)
257{
258 rb_gc_mark_children(rb_gc_get_objspace(), (VALUE)object);
259}
260
261static void
262rb_mmtk_call_obj_free(MMTk_ObjectReference object)
263{
264 VALUE obj = (VALUE)object;
265 struct objspace *objspace = rb_gc_get_objspace();
266
267 if (RB_UNLIKELY(rb_gc_event_hook_required_p(RUBY_INTERNAL_EVENT_FREEOBJ))) {
268 rb_gc_worker_thread_set_vm_context(&objspace->vm_context);
269 rb_gc_event_hook(obj, RUBY_INTERNAL_EVENT_FREEOBJ);
270 rb_gc_worker_thread_unset_vm_context(&objspace->vm_context);
271 }
272
273 rb_gc_obj_free(objspace, obj);
274}
275
276static size_t
277rb_mmtk_vm_live_bytes(void)
278{
279 return 0;
280}
281
282static void
283make_final_job(struct objspace *objspace, VALUE obj, VALUE table)
284{
286 RUBY_ASSERT(mmtk_is_reachable((MMTk_ObjectReference)table));
288
290
291 struct MMTk_final_job *job = xmalloc(sizeof(struct MMTk_final_job));
292 job->next = objspace->finalizer_jobs;
293 job->kind = MMTK_FINAL_JOB_FINALIZE;
294 job->as.finalize.finalizer_array = table;
295
296 objspace->finalizer_jobs = job;
297}
298
299static int
300rb_mmtk_update_finalizer_table_i(st_data_t key, st_data_t value, st_data_t data)
301{
303 RUBY_ASSERT(mmtk_is_reachable((MMTk_ObjectReference)value));
305
306 struct objspace *objspace = (struct objspace *)data;
307
308 if (!mmtk_is_reachable((MMTk_ObjectReference)key)) {
309 make_final_job(objspace, (VALUE)key, (VALUE)value);
310
311 rb_postponed_job_trigger(objspace->finalizer_postponed_job);
312
313 return ST_DELETE;
314 }
315
316 return ST_CONTINUE;
317}
318
319static void
320rb_mmtk_update_finalizer_table(void)
321{
322 struct objspace *objspace = rb_gc_get_objspace();
323
324 // TODO: replace with st_foreach_with_replace when GC is moving
325 st_foreach(objspace->finalizer_table, rb_mmtk_update_finalizer_table_i, (st_data_t)objspace);
326}
327
328static int
329rb_mmtk_update_table_i(VALUE val, void *data)
330{
331 if (!mmtk_is_reachable((MMTk_ObjectReference)val)) {
332 return ST_DELETE;
333 }
334
335 return ST_CONTINUE;
336}
337
338static int
339rb_mmtk_global_tables_count(void)
340{
341 return RB_GC_VM_WEAK_TABLE_COUNT;
342}
343
344static void
345rb_mmtk_update_global_tables(int table)
346{
347 RUBY_ASSERT(table < RB_GC_VM_WEAK_TABLE_COUNT);
348
349 rb_gc_vm_weak_table_foreach(rb_mmtk_update_table_i, NULL, NULL, true, (enum rb_gc_vm_weak_tables)table);
350}
351
352static bool
353rb_mmtk_special_const_p(MMTk_ObjectReference object)
354{
355 VALUE obj = (VALUE)object;
356
357 return RB_SPECIAL_CONST_P(obj);
358}
359
360static void
361rb_mmtk_mutator_thread_panic_handler(void)
362{
363 rb_bug("Ruby mutator thread panicked");
364}
365
366// Bootup
367MMTk_RubyUpcalls ruby_upcalls = {
368 rb_mmtk_init_gc_worker_thread,
369 rb_mmtk_is_mutator,
370 rb_mmtk_stop_the_world,
371 rb_mmtk_resume_mutators,
372 rb_mmtk_block_for_gc,
373 rb_mmtk_number_of_mutators,
374 rb_mmtk_get_mutators,
375 rb_mmtk_scan_gc_roots,
376 rb_mmtk_scan_objspace,
377 rb_mmtk_scan_object_ruby_style,
378 rb_mmtk_call_gc_mark_children,
379 rb_mmtk_call_obj_free,
380 rb_mmtk_vm_live_bytes,
381 rb_mmtk_update_global_tables,
382 rb_mmtk_global_tables_count,
383 rb_mmtk_update_finalizer_table,
384 rb_mmtk_special_const_p,
385 rb_mmtk_mutator_thread_panic_handler,
386};
387
388// Use max 80% of the available memory by default for MMTk
389#define RB_MMTK_HEAP_LIMIT_PERC 80
390#define RB_MMTK_DEFAULT_HEAP_MIN (1024 * 1024)
391#define RB_MMTK_DEFAULT_HEAP_MAX (rb_mmtk_system_physical_memory() / 100 * RB_MMTK_HEAP_LIMIT_PERC)
392
393enum mmtk_heap_mode {
394 RB_MMTK_DYNAMIC_HEAP,
395 RB_MMTK_FIXED_HEAP
396};
397
398MMTk_Builder *
399rb_mmtk_builder_init(void)
400{
401 MMTk_Builder *builder = mmtk_builder_default();
402 return builder;
403}
404
405void *
406rb_gc_impl_objspace_alloc(void)
407{
408 MMTk_Builder *builder = rb_mmtk_builder_init();
409 mmtk_init_binding(builder, NULL, &ruby_upcalls, (MMTk_ObjectReference)Qundef);
410
411 return calloc(1, sizeof(struct objspace));
412}
413
414static void gc_run_finalizers(void *data);
415
416void
417rb_gc_impl_objspace_init(void *objspace_ptr)
418{
419 struct objspace *objspace = objspace_ptr;
420
421 objspace->measure_gc_time = true;
422
423 objspace->finalizer_table = st_init_numtable();
424 objspace->finalizer_postponed_job = rb_postponed_job_preregister(0, gc_run_finalizers, objspace);
425
426 ccan_list_head_init(&objspace->ractor_caches);
427
428 objspace->mutex = (pthread_mutex_t)PTHREAD_MUTEX_INITIALIZER;
429 objspace->cond_world_stopped = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
430 objspace->cond_world_started = (pthread_cond_t)PTHREAD_COND_INITIALIZER;
431}
432
433void
434rb_gc_impl_objspace_free(void *objspace_ptr)
435{
436 free(objspace_ptr);
437}
438
439void *
440rb_gc_impl_ractor_cache_alloc(void *objspace_ptr, void *ractor)
441{
442 struct objspace *objspace = objspace_ptr;
443 if (objspace->live_ractor_cache_count == 0) {
444 mmtk_initialize_collection(ractor);
445 }
446 objspace->live_ractor_cache_count++;
447
448 struct MMTk_ractor_cache *cache = malloc(sizeof(struct MMTk_ractor_cache));
449 ccan_list_add(&objspace->ractor_caches, &cache->list_node);
450
451 cache->mutator = mmtk_bind_mutator(cache);
452 cache->bump_pointer = mmtk_get_bump_pointer_allocator(cache->mutator);
453
454 return cache;
455}
456
457void
458rb_gc_impl_ractor_cache_free(void *objspace_ptr, void *cache_ptr)
459{
460 struct objspace *objspace = objspace_ptr;
461 struct MMTk_ractor_cache *cache = cache_ptr;
462
463 ccan_list_del(&cache->list_node);
464
465 RUBY_ASSERT(objspace->live_ractor_cache_count > 1);
466 objspace->live_ractor_cache_count--;
467
468 mmtk_destroy_mutator(cache->mutator);
469}
470
471void rb_gc_impl_set_params(void *objspace_ptr) { }
472
473static VALUE gc_verify_internal_consistency(VALUE self) { return Qnil; }
474
475#define MMTK_HEAP_COUNT 6
476#define MMTK_MAX_OBJ_SIZE 640
477
478static size_t heap_sizes[MMTK_HEAP_COUNT + 1] = {
479 32, 40, 80, 160, 320, MMTK_MAX_OBJ_SIZE, 0
480};
481
482void
483rb_gc_impl_init(void)
484{
485 VALUE gc_constants = rb_hash_new();
486 rb_hash_aset(gc_constants, ID2SYM(rb_intern("BASE_SLOT_SIZE")), SIZET2NUM(sizeof(VALUE) * 5));
487 rb_hash_aset(gc_constants, ID2SYM(rb_intern("RBASIC_SIZE")), SIZET2NUM(sizeof(struct RBasic)));
488 rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVALUE_OVERHEAD")), INT2NUM(0));
489 rb_hash_aset(gc_constants, ID2SYM(rb_intern("RVARGC_MAX_ALLOCATE_SIZE")), LONG2FIX(MMTK_MAX_OBJ_SIZE));
490 // Pretend we have 5 size pools
491 rb_hash_aset(gc_constants, ID2SYM(rb_intern("SIZE_POOL_COUNT")), LONG2FIX(5));
492 OBJ_FREEZE(gc_constants);
493 rb_define_const(rb_mGC, "INTERNAL_CONSTANTS", gc_constants);
494
495 // no-ops for compatibility
496 rb_define_singleton_method(rb_mGC, "verify_internal_consistency", gc_verify_internal_consistency, 0);
497
501 rb_define_singleton_method(rb_mGC, "latest_compact_info", rb_f_notimplement, 0);
502 rb_define_singleton_method(rb_mGC, "verify_compaction_references", rb_f_notimplement, -1);
503}
504
505size_t *
506rb_gc_impl_heap_sizes(void *objspace_ptr)
507{
508 return heap_sizes;
509}
510
511int
512rb_mmtk_obj_free_iter_wrapper(VALUE obj, void *data)
513{
514 struct objspace *objspace = data;
515
516 if (!RB_TYPE_P(obj, T_NONE)) {
517 rb_gc_obj_free_vm_weak_references(obj);
518 rb_gc_obj_free(objspace, obj);
519 }
520
521 return 0;
522}
523
524// Shutdown
525static void each_object(struct objspace *objspace, int (*func)(VALUE, void *), void *data);
526
527void
528rb_gc_impl_shutdown_free_objects(void *objspace_ptr)
529{
530 mmtk_set_gc_enabled(false);
531 each_object(objspace_ptr, rb_mmtk_obj_free_iter_wrapper, objspace_ptr);
532 mmtk_set_gc_enabled(true);
533}
534
535// GC
536void
537rb_gc_impl_start(void *objspace_ptr, bool full_mark, bool immediate_mark, bool immediate_sweep, bool compact)
538{
539 mmtk_handle_user_collection_request(rb_gc_get_ractor_newobj_cache(), true, full_mark);
540}
541
542bool
543rb_gc_impl_during_gc_p(void *objspace_ptr)
544{
545 // TODO
546 return false;
547}
548
549static void
550rb_gc_impl_prepare_heap_i(MMTk_ObjectReference obj, void *d)
551{
552 rb_gc_prepare_heap_process_object((VALUE)obj);
553}
554
555void
556rb_gc_impl_prepare_heap(void *objspace_ptr)
557{
558 mmtk_enumerate_objects(rb_gc_impl_prepare_heap_i, NULL);
559}
560
561void
562rb_gc_impl_gc_enable(void *objspace_ptr)
563{
564 mmtk_set_gc_enabled(true);
565}
566
567void
568rb_gc_impl_gc_disable(void *objspace_ptr, bool finish_current_gc)
569{
570 mmtk_set_gc_enabled(false);
571}
572
573bool
574rb_gc_impl_gc_enabled_p(void *objspace_ptr)
575{
576 return mmtk_gc_enabled_p();
577}
578
579void
580rb_gc_impl_stress_set(void *objspace_ptr, VALUE flag)
581{
582 struct objspace *objspace = objspace_ptr;
583
584 objspace->gc_stress = RTEST(flag);
585}
586
587VALUE
588rb_gc_impl_stress_get(void *objspace_ptr)
589{
590 struct objspace *objspace = objspace_ptr;
591
592 return objspace->gc_stress ? Qtrue : Qfalse;
593}
594
595VALUE
596rb_gc_impl_config_get(void *objspace_ptr)
597{
598 VALUE hash = rb_hash_new();
599
600 rb_hash_aset(hash, ID2SYM(rb_intern_const("mmtk_worker_count")), RB_ULONG2NUM(mmtk_worker_count()));
601 rb_hash_aset(hash, ID2SYM(rb_intern_const("mmtk_plan")), rb_str_new_cstr((const char *)mmtk_plan()));
602 rb_hash_aset(hash, ID2SYM(rb_intern_const("mmtk_heap_mode")), rb_str_new_cstr((const char *)mmtk_heap_mode()));
603 size_t heap_min = mmtk_heap_min();
604 if (heap_min > 0) rb_hash_aset(hash, ID2SYM(rb_intern_const("mmtk_heap_min")), RB_ULONG2NUM(heap_min));
605 rb_hash_aset(hash, ID2SYM(rb_intern_const("mmtk_heap_max")), RB_ULONG2NUM(mmtk_heap_max()));
606
607 return hash;
608}
609
610void
611rb_gc_impl_config_set(void *objspace_ptr, VALUE hash)
612{
613 // TODO
614}
615
616// Object allocation
617
618static VALUE
619rb_mmtk_alloc_fast_path(struct objspace *objspace, struct MMTk_ractor_cache *ractor_cache, size_t size)
620{
621 MMTk_BumpPointer *bump_pointer = ractor_cache->bump_pointer;
622 if (bump_pointer == NULL) return 0;
623
624 uintptr_t new_cursor = bump_pointer->cursor + size;
625
626 if (new_cursor > bump_pointer->limit) {
627 return 0;
628 }
629 else {
630 VALUE obj = (VALUE)bump_pointer->cursor;
631 bump_pointer->cursor = new_cursor;
632 return obj;
633 }
634}
635
636VALUE
637rb_gc_impl_new_obj(void *objspace_ptr, void *cache_ptr, VALUE klass, VALUE flags, bool wb_protected, size_t alloc_size)
638{
639#define MMTK_ALLOCATION_SEMANTICS_DEFAULT 0
640 struct objspace *objspace = objspace_ptr;
641 struct MMTk_ractor_cache *ractor_cache = cache_ptr;
642
643 if (alloc_size > MMTK_MAX_OBJ_SIZE) rb_bug("too big");
644 for (int i = 0; i < MMTK_HEAP_COUNT; i++) {
645 if (alloc_size == heap_sizes[i]) break;
646 if (alloc_size < heap_sizes[i]) {
647 alloc_size = heap_sizes[i];
648 break;
649 }
650 }
651
652 if (objspace->gc_stress) {
653 mmtk_handle_user_collection_request(ractor_cache, false, false);
654 }
655
656 alloc_size += sizeof(VALUE);
657
658 VALUE *alloc_obj = (VALUE *)rb_mmtk_alloc_fast_path(objspace, ractor_cache, alloc_size);
659 if (!alloc_obj) {
660 alloc_obj = mmtk_alloc(ractor_cache->mutator, alloc_size, MMTk_MIN_OBJ_ALIGN, 0, MMTK_ALLOCATION_SEMANTICS_DEFAULT);
661 }
662
663 alloc_obj++;
664 alloc_obj[-1] = alloc_size - sizeof(VALUE);
665 alloc_obj[0] = flags;
666 alloc_obj[1] = klass;
667
668 // TODO: implement fast path for mmtk_post_alloc
669 mmtk_post_alloc(ractor_cache->mutator, (void*)alloc_obj, alloc_size, MMTK_ALLOCATION_SEMANTICS_DEFAULT);
670
671 // TODO: only add when object needs obj_free to be called
672 mmtk_add_obj_free_candidate(alloc_obj);
673
674 objspace->total_allocated_objects++;
675
676 return (VALUE)alloc_obj;
677}
678
679size_t
680rb_gc_impl_obj_slot_size(VALUE obj)
681{
682 return ((VALUE *)obj)[-1];
683}
684
685size_t
686rb_gc_impl_heap_id_for_size(void *objspace_ptr, size_t size)
687{
688 for (int i = 0; i < MMTK_HEAP_COUNT; i++) {
689 if (size == heap_sizes[i]) return i;
690 if (size < heap_sizes[i]) return i;
691 }
692
693 rb_bug("size too big");
694}
695
696bool
697rb_gc_impl_size_allocatable_p(size_t size)
698{
699 return size <= MMTK_MAX_OBJ_SIZE;
700}
701
702// Malloc
703void *
704rb_gc_impl_malloc(void *objspace_ptr, size_t size, bool gc_allowed)
705{
706 // TODO: don't use system malloc
707 return malloc(size);
708}
709
710void *
711rb_gc_impl_calloc(void *objspace_ptr, size_t size, bool gc_allowed)
712{
713 // TODO: don't use system calloc
714 return calloc(1, size);
715}
716
717void *
718rb_gc_impl_realloc(void *objspace_ptr, void *ptr, size_t new_size, size_t old_size, bool gc_allowed)
719{
720 // TODO: don't use system realloc
721 return realloc(ptr, new_size);
722}
723
724void
725rb_gc_impl_free(void *objspace_ptr, void *ptr, size_t old_size)
726{
727 // TODO: don't use system free
728 free(ptr);
729}
730
731void rb_gc_impl_adjust_memory_usage(void *objspace_ptr, ssize_t diff) { }
732
733// Marking
734void
735rb_gc_impl_mark(void *objspace_ptr, VALUE obj)
736{
737 if (RB_SPECIAL_CONST_P(obj)) return;
738
739 rb_mmtk_gc_thread_tls->object_closure.c_function(rb_mmtk_gc_thread_tls->object_closure.rust_closure,
740 rb_mmtk_gc_thread_tls->gc_context,
741 (MMTk_ObjectReference)obj,
742 false);
743}
744
745void
746rb_gc_impl_mark_and_move(void *objspace_ptr, VALUE *ptr)
747{
748 if (RB_SPECIAL_CONST_P(*ptr)) return;
749
750 // TODO: make it movable
751 rb_gc_impl_mark(objspace_ptr, *ptr);
752}
753
754void
755rb_gc_impl_mark_and_pin(void *objspace_ptr, VALUE obj)
756{
757 if (RB_SPECIAL_CONST_P(obj)) return;
758
759 // TODO: also pin
760 rb_gc_impl_mark(objspace_ptr, obj);
761}
762
763void
764rb_gc_impl_mark_maybe(void *objspace_ptr, VALUE obj)
765{
766 if (rb_gc_impl_pointer_to_heap_p(objspace_ptr, (const void *)obj)) {
767 rb_gc_impl_mark_and_pin(objspace_ptr, obj);
768 }
769}
770
771void
772rb_gc_impl_mark_weak(void *objspace_ptr, VALUE *ptr)
773{
774 mmtk_mark_weak((MMTk_ObjectReference *)ptr);
775}
776
777void
778rb_gc_impl_remove_weak(void *objspace_ptr, VALUE parent_obj, VALUE *ptr)
779{
780 mmtk_remove_weak((MMTk_ObjectReference *)ptr);
781}
782
783// Compaction
784bool
785rb_gc_impl_object_moved_p(void *objspace_ptr, VALUE obj)
786{
787 rb_bug("unimplemented");
788}
789
790VALUE
791rb_gc_impl_location(void *objspace_ptr, VALUE value)
792{
793 rb_bug("unimplemented");
794}
795
796// Write barriers
797void
798rb_gc_impl_writebarrier(void *objspace_ptr, VALUE a, VALUE b)
799{
800 struct MMTk_ractor_cache *cache = rb_gc_get_ractor_newobj_cache();
801
802 if (SPECIAL_CONST_P(b)) return;
803
804 mmtk_object_reference_write_post(cache->mutator, (MMTk_ObjectReference)a);
805}
806
807void
808rb_gc_impl_writebarrier_unprotect(void *objspace_ptr, VALUE obj)
809{
810 mmtk_register_wb_unprotected_object((MMTk_ObjectReference)obj);
811}
812
813void
814rb_gc_impl_writebarrier_remember(void *objspace_ptr, VALUE obj)
815{
816 struct MMTk_ractor_cache *cache = rb_gc_get_ractor_newobj_cache();
817
818 mmtk_object_reference_write_post(cache->mutator, (MMTk_ObjectReference)obj);
819}
820
821// Heap walking
822static void
823each_objects_i(MMTk_ObjectReference obj, void *d)
824{
825 rb_darray(VALUE) *objs = d;
826
827 rb_darray_append(objs, (VALUE)obj);
828}
829
830static void
831each_object(struct objspace *objspace, int (*func)(VALUE, void *), void *data)
832{
833 rb_darray(VALUE) objs;
834 rb_darray_make(&objs, 0);
835
836 mmtk_enumerate_objects(each_objects_i, &objs);
837
838 VALUE *obj_ptr;
839 rb_darray_foreach(objs, i, obj_ptr) {
840 if (!mmtk_is_mmtk_object((MMTk_ObjectReference)*obj_ptr)) continue;
841
842 if (func(*obj_ptr, data) != 0) {
843 break;
844 }
845 }
846
847 rb_darray_free(objs);
848}
849
851 int (*func)(void *, void *, size_t, void *);
852 void *data;
853};
854
855static int
856rb_gc_impl_each_objects_i(VALUE obj, void *d)
857{
858 struct rb_gc_impl_each_objects_data *data = d;
859
860 size_t slot_size = rb_gc_impl_obj_slot_size(obj);
861
862 return data->func((void *)obj, (void *)(obj + slot_size), slot_size, data->data);
863}
864
865void
866rb_gc_impl_each_objects(void *objspace_ptr, int (*func)(void *, void *, size_t, void *), void *data)
867{
868 struct rb_gc_impl_each_objects_data each_objects_data = {
869 .func = func,
870 .data = data
871 };
872
873 each_object(objspace_ptr, rb_gc_impl_each_objects_i, &each_objects_data);
874}
875
877 void (*func)(VALUE, void *);
878 void *data;
879};
880
881static int
882rb_gc_impl_each_object_i(VALUE obj, void *d)
883{
884 struct rb_gc_impl_each_object_data *data = d;
885
886 data->func(obj, data->data);
887
888 return 0;
889}
890
891void
892rb_gc_impl_each_object(void *objspace_ptr, void (*func)(VALUE, void *), void *data)
893{
894 struct rb_gc_impl_each_object_data each_object_data = {
895 .func = func,
896 .data = data
897 };
898
899 each_object(objspace_ptr, rb_gc_impl_each_object_i, &each_object_data);
900}
901
902// Finalizers
903static VALUE
904gc_run_finalizers_get_final(long i, void *data)
905{
906 VALUE table = (VALUE)data;
907
908 return RARRAY_AREF(table, i + 1);
909}
910
911static void
912gc_run_finalizers(void *data)
913{
914 struct objspace *objspace = data;
915
916 rb_gc_set_pending_interrupt();
917
918 while (objspace->finalizer_jobs != NULL) {
919 struct MMTk_final_job *job = objspace->finalizer_jobs;
920 objspace->finalizer_jobs = job->next;
921
922 switch (job->kind) {
923 case MMTK_FINAL_JOB_DFREE:
924 job->as.dfree.func(job->as.dfree.data);
925 break;
926 case MMTK_FINAL_JOB_FINALIZE: {
927 VALUE finalizer_array = job->as.finalize.finalizer_array;
928
929 rb_gc_run_obj_finalizer(
930 RARRAY_AREF(finalizer_array, 0),
931 RARRAY_LEN(finalizer_array) - 1,
932 gc_run_finalizers_get_final,
933 (void *)finalizer_array
934 );
935
936 RB_GC_GUARD(finalizer_array);
937 break;
938 }
939 }
940
941 xfree(job);
942 }
943
944 rb_gc_unset_pending_interrupt();
945}
946
947void
948rb_gc_impl_make_zombie(void *objspace_ptr, VALUE obj, void (*dfree)(void *), void *data)
949{
950 if (dfree == NULL) return;
951
952 struct objspace *objspace = objspace_ptr;
953
954 struct MMTk_final_job *job = xmalloc(sizeof(struct MMTk_final_job));
955 job->kind = MMTK_FINAL_JOB_DFREE;
956 job->as.dfree.func = dfree;
957 job->as.dfree.data = data;
958
959 struct MMTk_final_job *prev;
960 do {
961 job->next = objspace->finalizer_jobs;
962 prev = RUBY_ATOMIC_PTR_CAS(objspace->finalizer_jobs, job->next, job);
963 } while (prev != job->next);
964
965 if (!ruby_free_at_exit_p()) {
966 rb_postponed_job_trigger(objspace->finalizer_postponed_job);
967 }
968}
969
970VALUE
971rb_gc_impl_define_finalizer(void *objspace_ptr, VALUE obj, VALUE block)
972{
973 struct objspace *objspace = objspace_ptr;
974 VALUE table;
975 st_data_t data;
976
977 RBASIC(obj)->flags |= FL_FINALIZE;
978
979 int lev = RB_GC_VM_LOCK();
980
981 if (st_lookup(objspace->finalizer_table, obj, &data)) {
982 table = (VALUE)data;
983
984 /* avoid duplicate block, table is usually small */
985 {
986 long len = RARRAY_LEN(table);
987 long i;
988
989 for (i = 0; i < len; i++) {
990 VALUE recv = RARRAY_AREF(table, i);
991 if (rb_equal(recv, block)) {
992 RB_GC_VM_UNLOCK(lev);
993 return recv;
994 }
995 }
996 }
997
998 rb_ary_push(table, block);
999 }
1000 else {
1001 table = rb_ary_new3(2, rb_obj_id(obj), block);
1002 rb_obj_hide(table);
1003 st_add_direct(objspace->finalizer_table, obj, table);
1004 }
1005
1006 RB_GC_VM_UNLOCK(lev);
1007
1008 return block;
1009}
1010
1011void
1012rb_gc_impl_undefine_finalizer(void *objspace_ptr, VALUE obj)
1013{
1014 struct objspace *objspace = objspace_ptr;
1015
1016 st_data_t data = obj;
1017
1018 int lev = RB_GC_VM_LOCK();
1019 st_delete(objspace->finalizer_table, &data, 0);
1020 RB_GC_VM_UNLOCK(lev);
1021
1022 FL_UNSET(obj, FL_FINALIZE);
1023}
1024
1025void
1026rb_gc_impl_copy_finalizer(void *objspace_ptr, VALUE dest, VALUE obj)
1027{
1028 struct objspace *objspace = objspace_ptr;
1029 VALUE table;
1030 st_data_t data;
1031
1032 if (!FL_TEST(obj, FL_FINALIZE)) return;
1033
1034 int lev = RB_GC_VM_LOCK();
1035 if (RB_LIKELY(st_lookup(objspace->finalizer_table, obj, &data))) {
1036 table = rb_ary_dup((VALUE)data);
1037 RARRAY_ASET(table, 0, rb_obj_id(dest));
1038 st_insert(objspace->finalizer_table, dest, table);
1039 FL_SET(dest, FL_FINALIZE);
1040 }
1041 else {
1042 rb_bug("rb_gc_copy_finalizer: FL_FINALIZE set but not found in finalizer_table: %s", rb_obj_info(obj));
1043 }
1044 RB_GC_VM_UNLOCK(lev);
1045}
1046
1047static int
1048move_finalizer_from_table_i(st_data_t key, st_data_t val, st_data_t arg)
1049{
1050 struct objspace *objspace = (struct objspace *)arg;
1051
1052 make_final_job(objspace, (VALUE)key, (VALUE)val);
1053
1054 return ST_DELETE;
1055}
1056
1057void
1058rb_gc_impl_shutdown_call_finalizer(void *objspace_ptr)
1059{
1060 struct objspace *objspace = objspace_ptr;
1061
1062 while (objspace->finalizer_table->num_entries) {
1063 st_foreach(objspace->finalizer_table, move_finalizer_from_table_i, (st_data_t)objspace);
1064
1065 gc_run_finalizers(objspace);
1066 }
1067
1068 unsigned int lev = RB_GC_VM_LOCK();
1069 {
1070 struct MMTk_RawVecOfObjRef registered_candidates = mmtk_get_all_obj_free_candidates();
1071 for (size_t i = 0; i < registered_candidates.len; i++) {
1072 VALUE obj = (VALUE)registered_candidates.ptr[i];
1073
1074 if (rb_gc_shutdown_call_finalizer_p(obj)) {
1075 rb_gc_obj_free(objspace_ptr, obj);
1076 RBASIC(obj)->flags = 0;
1077 }
1078 }
1079 mmtk_free_raw_vec_of_obj_ref(registered_candidates);
1080 }
1081 RB_GC_VM_UNLOCK(lev);
1082
1083 gc_run_finalizers(objspace);
1084}
1085
1086// Forking
1087
1088void
1089rb_gc_impl_before_fork(void *objspace_ptr)
1090{
1091 struct objspace *objspace = objspace_ptr;
1092
1093 retry:
1094 objspace->fork_hook_vm_lock_lev = RB_GC_VM_LOCK();
1095 rb_gc_vm_barrier();
1096
1097 /* At this point, we know that all the Ractors are paused because of the
1098 * rb_gc_vm_barrier above. Since rb_mmtk_block_for_gc is a barrier point,
1099 * one or more Ractors could be paused there. However, mmtk_before_fork is
1100 * not compatible with that because it assumes that the MMTk workers are idle,
1101 * but the workers are not idle because they are busy working on a GC.
1102 *
1103 * This essentially implements a trylock. It will optimistically lock but will
1104 * release the lock if it detects that any other Ractors are waiting in
1105 * rb_mmtk_block_for_gc.
1106 */
1107 rb_atomic_t mutator_blocking_count = RUBY_ATOMIC_LOAD(objspace->mutator_blocking_count);
1108 if (mutator_blocking_count != 0) {
1109 RB_GC_VM_UNLOCK(objspace->fork_hook_vm_lock_lev);
1110 goto retry;
1111 }
1112
1113 mmtk_before_fork();
1114}
1115
1116void
1117rb_gc_impl_after_fork(void *objspace_ptr, rb_pid_t pid)
1118{
1119 struct objspace *objspace = objspace_ptr;
1120
1121 mmtk_after_fork(rb_gc_get_ractor_newobj_cache());
1122
1123 RB_GC_VM_UNLOCK(objspace->fork_hook_vm_lock_lev);
1124}
1125
1126// Statistics
1127
1128void
1129rb_gc_impl_set_measure_total_time(void *objspace_ptr, VALUE flag)
1130{
1131 struct objspace *objspace = objspace_ptr;
1132
1133 objspace->measure_gc_time = RTEST(flag);
1134}
1135
1136bool
1137rb_gc_impl_get_measure_total_time(void *objspace_ptr)
1138{
1139 struct objspace *objspace = objspace_ptr;
1140
1141 return objspace->measure_gc_time;
1142}
1143
1144unsigned long long
1145rb_gc_impl_get_total_time(void *objspace_ptr)
1146{
1147 struct objspace *objspace = objspace_ptr;
1148
1149 return objspace->total_gc_time;
1150}
1151
1152size_t
1153rb_gc_impl_gc_count(void *objspace_ptr)
1154{
1155 struct objspace *objspace = objspace_ptr;
1156
1157 return objspace->gc_count;
1158}
1159
1160VALUE
1161rb_gc_impl_latest_gc_info(void *objspace_ptr, VALUE hash_or_key)
1162{
1163 VALUE hash = Qnil, key = Qnil;
1164
1165 if (SYMBOL_P(hash_or_key)) {
1166 key = hash_or_key;
1167 }
1168 else if (RB_TYPE_P(hash_or_key, T_HASH)) {
1169 hash = hash_or_key;
1170 }
1171 else {
1172 rb_bug("gc_info_decode: non-hash or symbol given");
1173 }
1174
1175#define SET(name, attr) \
1176 if (key == ID2SYM(rb_intern_const(#name))) \
1177 return (attr); \
1178 else if (hash != Qnil) \
1179 rb_hash_aset(hash, ID2SYM(rb_intern_const(#name)), (attr));
1180
1181 /* Hack to get StackProf working because it calls rb_gc_latest_gc_info with
1182 * the :state key and expects a result. This always returns the :none state. */
1183 SET(state, ID2SYM(rb_intern_const("none")));
1184#undef SET
1185
1186 if (!NIL_P(key)) {
1187 // Matched key should return above
1188 return Qundef;
1189 }
1190
1191 return hash;
1192}
1193
1194enum gc_stat_sym {
1195 gc_stat_sym_count,
1196 gc_stat_sym_time,
1197 gc_stat_sym_total_allocated_objects,
1198 gc_stat_sym_total_bytes,
1199 gc_stat_sym_used_bytes,
1200 gc_stat_sym_free_bytes,
1201 gc_stat_sym_starting_heap_address,
1202 gc_stat_sym_last_heap_address,
1203 gc_stat_sym_last
1204};
1205
1206static VALUE gc_stat_symbols[gc_stat_sym_last];
1207
1208static void
1209setup_gc_stat_symbols(void)
1210{
1211 if (gc_stat_symbols[0] == 0) {
1212#define S(s) gc_stat_symbols[gc_stat_sym_##s] = ID2SYM(rb_intern_const(#s))
1213 S(count);
1214 S(time);
1215 S(total_allocated_objects);
1216 S(total_bytes);
1217 S(used_bytes);
1218 S(free_bytes);
1219 S(starting_heap_address);
1220 S(last_heap_address);
1221 }
1222}
1223
1224VALUE
1225rb_gc_impl_stat(void *objspace_ptr, VALUE hash_or_sym)
1226{
1227 struct objspace *objspace = objspace_ptr;
1228 VALUE hash = Qnil, key = Qnil;
1229
1230 setup_gc_stat_symbols();
1231
1232 if (RB_TYPE_P(hash_or_sym, T_HASH)) {
1233 hash = hash_or_sym;
1234 }
1235 else if (SYMBOL_P(hash_or_sym)) {
1236 key = hash_or_sym;
1237 }
1238 else {
1239 rb_bug("non-hash or symbol given");
1240 }
1241
1242#define SET(name, attr) \
1243 if (key == gc_stat_symbols[gc_stat_sym_##name]) \
1244 return SIZET2NUM(attr); \
1245 else if (hash != Qnil) \
1246 rb_hash_aset(hash, gc_stat_symbols[gc_stat_sym_##name], SIZET2NUM(attr));
1247
1248 SET(count, objspace->gc_count);
1249 SET(time, objspace->total_gc_time / (1000 * 1000));
1250 SET(total_allocated_objects, objspace->total_allocated_objects);
1251 SET(total_bytes, mmtk_total_bytes());
1252 SET(used_bytes, mmtk_used_bytes());
1253 SET(free_bytes, mmtk_free_bytes());
1254 SET(starting_heap_address, (size_t)mmtk_starting_heap_address());
1255 SET(last_heap_address, (size_t)mmtk_last_heap_address());
1256#undef SET
1257
1258 if (!NIL_P(key)) {
1259 // Matched key should return above
1260 return Qundef;
1261 }
1262
1263 return hash;
1264}
1265
1266VALUE
1267rb_gc_impl_stat_heap(void *objspace_ptr, VALUE heap_name, VALUE hash_or_sym)
1268{
1269 if (RB_TYPE_P(hash_or_sym, T_HASH)) {
1270 return hash_or_sym;
1271 }
1272 else {
1273 return Qundef;
1274 }
1275}
1276
1277// Miscellaneous
1278
1279#define RB_GC_OBJECT_METADATA_ENTRY_COUNT 1
1280static struct rb_gc_object_metadata_entry object_metadata_entries[RB_GC_OBJECT_METADATA_ENTRY_COUNT + 1];
1281
1283rb_gc_impl_object_metadata(void *objspace_ptr, VALUE obj)
1284{
1285 static ID ID_object_id;
1286
1287 if (!ID_object_id) {
1288#define I(s) ID_##s = rb_intern(#s);
1289 I(object_id);
1290#undef I
1291 }
1292
1293 size_t n = 0;
1294
1295#define SET_ENTRY(na, v) do { \
1296 RUBY_ASSERT(n <= RB_GC_OBJECT_METADATA_ENTRY_COUNT); \
1297 object_metadata_entries[n].name = ID_##na; \
1298 object_metadata_entries[n].val = v; \
1299 n++; \
1300} while (0)
1301
1302 if (rb_obj_id_p(obj)) SET_ENTRY(object_id, rb_obj_id(obj));
1303
1304 object_metadata_entries[n].name = 0;
1305 object_metadata_entries[n].val = 0;
1306
1307 return object_metadata_entries;
1308}
1309
1310bool
1311rb_gc_impl_pointer_to_heap_p(void *objspace_ptr, const void *ptr)
1312{
1313 if (ptr == NULL) return false;
1314 if ((uintptr_t)ptr % sizeof(void*) != 0) return false;
1315 return mmtk_is_mmtk_object((MMTk_Address)ptr);
1316}
1317
1318bool
1319rb_gc_impl_garbage_object_p(void *objspace_ptr, VALUE obj)
1320{
1321 return false;
1322}
1323
1324void rb_gc_impl_set_event_hook(void *objspace_ptr, const rb_event_flag_t event) { }
1325
1326void
1327rb_gc_impl_copy_attributes(void *objspace_ptr, VALUE dest, VALUE obj)
1328{
1329 if (mmtk_object_wb_unprotected_p((MMTk_ObjectReference)obj)) {
1330 rb_gc_impl_writebarrier_unprotect(objspace_ptr, dest);
1331 }
1332
1333 rb_gc_impl_copy_finalizer(objspace_ptr, dest, obj);
1334}
1335
1336// GC Identification
1337
1338const char *
1339rb_gc_impl_active_gc_name(void)
1340{
1341 return "mmtk";
1342}
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
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:1952
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:1918
#define RUBY_INTERNAL_EVENT_FREEOBJ
Object swept.
Definition event.h:94
#define RUBY_INTERNAL_EVENT_GC_START
GC started.
Definition event.h:95
uint32_t rb_event_flag_t
Represents event(s).
Definition event.h:108
static VALUE RB_FL_TEST(VALUE obj, VALUE flags)
Tests if the given flag(s) are set or not.
Definition fl_type.h:489
static void RB_FL_UNSET(VALUE obj, VALUE flags)
Clears the given flag(s).
Definition fl_type.h:675
@ RUBY_FL_FINALIZE
This flag has something to do with finalisers.
Definition fl_type.h:238
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define Qundef
Old name of RUBY_Qundef.
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:134
#define T_NONE
Old name of RUBY_T_NONE.
Definition value_type.h:74
#define SIZET2NUM
Old name of RB_SIZE2NUM.
Definition size_t.h:62
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define FL_FINALIZE
Old name of RUBY_FL_FINALIZE.
Definition fl_type.h:61
#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:128
#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 NIL_P
Old name of RB_NIL_P.
#define FL_TEST
Old name of RB_FL_TEST.
Definition fl_type.h:130
#define FL_UNSET
Old name of RB_FL_UNSET.
Definition fl_type.h:132
#define SYMBOL_P
Old name of RB_SYMBOL_P.
Definition value_type.h:88
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:100
VALUE rb_mGC
GC module.
Definition gc.c:424
VALUE rb_equal(VALUE lhs, VALUE rhs)
This function is an optimised version of calling #==.
Definition object.c:176
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.
VALUE rb_hash_new(void)
Creates a new, empty hash object.
Definition hash.c:1464
#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:808
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
bool ruby_free_at_exit_p(void)
Returns whether the Ruby VM will free all memory at shutdown.
Definition vm.c:4807
#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
int st_foreach(st_table *q, int_type *w, st_data_t e)
Iteration over the given table.
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:386
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
int ruby_native_thread_p(void)
Queries if the thread which calls this function is a ruby's thread.
Definition thread.c:5806
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:50
MMTk_ObjectClosureFunction c_function
The function to be called from C.
Definition mmtk.h:46
Ruby object's base components.
Definition rbasic.h:69
Definition gc_impl.h:15
Definition st.h:79
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
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