Ruby: ractor.c Source File

Ruby 4.0.6p0 (2026-07-14 revision 03b6d3f8898a28604fe6cb00eae3226b821168f4)

ractor.c
1// Ractor implementation
2
3#include "ruby/ruby.h"
4#include "ruby/thread.h"
5#include "ruby/ractor.h"
6#include "ruby/re.h"
7#include "ruby/thread_native.h"
8#include "vm_core.h"
9#include "vm_sync.h"
10#include "ractor_core.h"
11#include "internal/complex.h"
12#include "internal/error.h"
13#include "internal/gc.h"
14#include "internal/hash.h"
15#include "internal/object.h"
16#include "internal/ractor.h"
17#include "internal/rational.h"
18#include "internal/struct.h"
19#include "internal/thread.h"
20#include "variable.h"
21#include "yjit.h"
22#include "zjit.h"
23
24 VALUE rb_cRactor;
25static VALUE rb_cRactorSelector;
26
27 VALUE rb_eRactorUnsafeError;
28 VALUE rb_eRactorIsolationError;
29static VALUE rb_eRactorError;
30static VALUE rb_eRactorRemoteError;
31static VALUE rb_eRactorMovedError;
32static VALUE rb_eRactorClosedError;
33static VALUE rb_cRactorMovedObject;
34
35static void vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line);
36
37
38#if RACTOR_CHECK_MODE > 0
39bool rb_ractor_ignore_belonging_flag = false;
40#endif
41
42// Ractor locking
43
44static void
45ASSERT_ractor_unlocking(rb_ractor_t *r)
46{
47#if RACTOR_CHECK_MODE > 0
48 const rb_execution_context_t *ec = rb_current_ec_noinline();
49 if (ec != NULL && r->sync.locked_by == rb_ractor_self(rb_ec_ractor_ptr(ec))) {
50 rb_bug("recursive ractor locking");
51 }
52#endif
53}
54
55static void
56ASSERT_ractor_locking(rb_ractor_t *r)
57{
58#if RACTOR_CHECK_MODE > 0
59 const rb_execution_context_t *ec = rb_current_ec_noinline();
60 if (ec != NULL && r->sync.locked_by != rb_ractor_self(rb_ec_ractor_ptr(ec))) {
61 rp(r->sync.locked_by);
62 rb_bug("ractor lock is not acquired.");
63 }
64#endif
65}
66
67static void
68ractor_lock(rb_ractor_t *r, const char *file, int line)
69{
70 RUBY_DEBUG_LOG2(file, line, "locking r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
71
72 ASSERT_ractor_unlocking(r);
73 rb_native_mutex_lock(&r->sync.lock);
74
75 const rb_execution_context_t *ec = rb_current_ec_noinline();
76 if (ec) {
77 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
78 VM_ASSERT(!cr->malloc_gc_disabled);
79 cr->malloc_gc_disabled = true;
80 }
81
82#if RACTOR_CHECK_MODE > 0
83 if (ec != NULL) {
84 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
85 r->sync.locked_by = rb_ractor_self(cr);
86 }
87#endif
88
89 RUBY_DEBUG_LOG2(file, line, "locked r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
90}
91
92static void
93ractor_lock_self(rb_ractor_t *cr, const char *file, int line)
94{
95 VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline()));
96#if RACTOR_CHECK_MODE > 0
97 VM_ASSERT(cr->sync.locked_by != cr->pub.self);
98#endif
99 ractor_lock(cr, file, line);
100}
101
102static void
103ractor_unlock(rb_ractor_t *r, const char *file, int line)
104{
105 ASSERT_ractor_locking(r);
106#if RACTOR_CHECK_MODE > 0
107 r->sync.locked_by = Qnil;
108#endif
109
110 const rb_execution_context_t *ec = rb_current_ec_noinline();
111 if (ec) {
112 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
113 VM_ASSERT(cr->malloc_gc_disabled);
114 cr->malloc_gc_disabled = false;
115 }
116
117 rb_native_mutex_unlock(&r->sync.lock);
118
119 RUBY_DEBUG_LOG2(file, line, "r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
120}
121
122static void
123ractor_unlock_self(rb_ractor_t *cr, const char *file, int line)
124{
125 VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline()));
126#if RACTOR_CHECK_MODE > 0
127 VM_ASSERT(cr->sync.locked_by == cr->pub.self);
128#endif
129 ractor_unlock(cr, file, line);
130}
131
132#define RACTOR_LOCK(r) ractor_lock(r, __FILE__, __LINE__)
133#define RACTOR_UNLOCK(r) ractor_unlock(r, __FILE__, __LINE__)
134#define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
135#define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
136
137void
138rb_ractor_lock_self(rb_ractor_t *r)
139{
140 RACTOR_LOCK_SELF(r);
141}
142
143void
144rb_ractor_unlock_self(rb_ractor_t *r)
145{
146 RACTOR_UNLOCK_SELF(r);
147}
148
149// Ractor status
150
151static const char *
152ractor_status_str(enum ractor_status status)
153{
154 switch (status) {
155 case ractor_created: return "created";
156 case ractor_running: return "running";
157 case ractor_blocking: return "blocking";
158 case ractor_terminated: return "terminated";
159 }
160 rb_bug("unreachable");
161}
162
163static void
164ractor_status_set(rb_ractor_t *r, enum ractor_status status)
165{
166 RUBY_DEBUG_LOG("r:%u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
167
168 // check 1
169 if (r->status_ != ractor_created) {
170 VM_ASSERT(r == GET_RACTOR()); // only self-modification is allowed.
171 ASSERT_vm_locking();
172 }
173
174 // check2: transition check. assume it will be vanished on non-debug build.
175 switch (r->status_) {
176 case ractor_created:
177 VM_ASSERT(status == ractor_blocking);
178 break;
179 case ractor_running:
180 VM_ASSERT(status == ractor_blocking||
181 status == ractor_terminated);
182 break;
183 case ractor_blocking:
184 VM_ASSERT(status == ractor_running);
185 break;
186 case ractor_terminated:
187 rb_bug("unreachable");
188 break;
189 }
190
191 r->status_ = status;
192}
193
194static bool
195ractor_status_p(rb_ractor_t *r, enum ractor_status status)
196{
197 return rb_ractor_status_p(r, status);
198}
199
200// Ractor data/mark/free
201
202static void ractor_local_storage_mark(rb_ractor_t *r);
203static void ractor_local_storage_free(rb_ractor_t *r);
204
205static void ractor_sync_mark(rb_ractor_t *r);
206static void ractor_sync_free(rb_ractor_t *r);
207static size_t ractor_sync_memsize(const rb_ractor_t *r);
208static void ractor_sync_init(rb_ractor_t *r);
209
210static int
211mark_targeted_hook_list(st_data_t key, st_data_t value, st_data_t _arg)
212{
213 rb_hook_list_t *hook_list = (rb_hook_list_t*)value;
214
215 if (hook_list->type == hook_list_type_targeted_iseq) {
216 rb_gc_mark((VALUE)key);
217 }
218 else {
219 rb_method_definition_t *def = (rb_method_definition_t*)key;
220 RUBY_ASSERT(hook_list->type == hook_list_type_targeted_def);
221 rb_gc_mark(def->body.bmethod.proc);
222 }
223 rb_hook_list_mark(hook_list);
224
225 return ST_CONTINUE;
226}
227
228static void
229ractor_mark(void *ptr)
230{
231 rb_ractor_t *r = (rb_ractor_t *)ptr;
232 bool checking_shareable = rb_gc_checking_shareable();
233
234 rb_gc_mark(r->loc);
235 rb_gc_mark(r->name);
236
237 if (!checking_shareable) {
238 // may unshareable objects
239 rb_gc_mark(r->r_stdin);
240 rb_gc_mark(r->r_stdout);
241 rb_gc_mark(r->r_stderr);
242 rb_gc_mark(r->verbose);
243 rb_gc_mark(r->debug);
244
245 // mark received messages
246 ractor_sync_mark(r);
247
248 rb_hook_list_mark(&r->pub.hooks);
249 if (r->pub.targeted_hooks) {
250 st_foreach(r->pub.targeted_hooks, mark_targeted_hook_list, 0);
251 }
252
253 if (r->threads.cnt > 0) {
254 rb_thread_t *th = 0;
255 ccan_list_for_each(&r->threads.set, th, lt_node) {
256 VM_ASSERT(th != NULL);
257 rb_gc_mark(th->self);
258 }
259 }
260
261 ractor_local_storage_mark(r);
262 }
263}
264
265static int
266free_targeted_hook_lists(st_data_t key, st_data_t val, st_data_t _arg)
267{
268 rb_hook_list_t *hook_list = (rb_hook_list_t*)val;
269 rb_hook_list_free(hook_list);
270 return ST_DELETE;
271}
272
273static void
274free_targeted_hooks(st_table *hooks_tbl)
275{
276 st_foreach(hooks_tbl, free_targeted_hook_lists, 0);
277}
278
279static void
280ractor_free(void *ptr)
281{
282 rb_ractor_t *r = (rb_ractor_t *)ptr;
283 RUBY_DEBUG_LOG("free r:%d", rb_ractor_id(r));
284 free_targeted_hooks(r->pub.targeted_hooks);
285 rb_native_mutex_destroy(&r->sync.lock);
286#ifdef RUBY_THREAD_WIN32_H
287 rb_native_cond_destroy(&r->sync.wakeup_cond);
288#endif
289 ractor_local_storage_free(r);
290 rb_hook_list_free(&r->pub.hooks);
291 st_free_table(r->pub.targeted_hooks);
292
293 if (r->newobj_cache) {
294 RUBY_ASSERT(r == ruby_single_main_ractor);
295
296 rb_gc_ractor_cache_free(r->newobj_cache);
297 r->newobj_cache = NULL;
298 }
299
300 ractor_sync_free(r);
301 ruby_xfree(r);
302}
303
304static size_t
305ractor_memsize(const void *ptr)
306{
307 rb_ractor_t *r = (rb_ractor_t *)ptr;
308
309 // TODO: more correct?
310 return sizeof(rb_ractor_t) + ractor_sync_memsize(r);
311}
312
313static const rb_data_type_t ractor_data_type = {
314 "ractor",
315 {
316 ractor_mark,
317 ractor_free,
318 ractor_memsize,
319 NULL, // update
320 },
321 0, 0, RUBY_TYPED_FREE_IMMEDIATELY /* | RUBY_TYPED_WB_PROTECTED */
322};
323
324bool
325rb_ractor_p(VALUE gv)
326{
327 if (rb_typeddata_is_kind_of(gv, &ractor_data_type)) {
328 return true;
329 }
330 else {
331 return false;
332 }
333}
334
335static inline rb_ractor_t *
336RACTOR_PTR(VALUE self)
337{
338 VM_ASSERT(rb_ractor_p(self));
339 rb_ractor_t *r = DATA_PTR(self);
340 return r;
341}
342
343static rb_atomic_t ractor_last_id;
344
345#if RACTOR_CHECK_MODE > 0
346uint32_t
347rb_ractor_current_id(void)
348{
349 if (GET_THREAD()->ractor == NULL) {
350 return 1; // main ractor
351 }
352 else {
353 return rb_ractor_id(GET_RACTOR());
354 }
355}
356#endif
357
358#include "ractor_sync.c"
359
360// creation/termination
361
362static uint32_t
363ractor_next_id(void)
364{
365 uint32_t id;
366
367 id = (uint32_t)(RUBY_ATOMIC_FETCH_ADD(ractor_last_id, 1) + 1);
368
369 return id;
370}
371
372static void
373vm_insert_ractor0(rb_vm_t *vm, rb_ractor_t *r, bool single_ractor_mode)
374{
375 RUBY_DEBUG_LOG("r:%u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
376 VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
377
378 ccan_list_add_tail(&vm->ractor.set, &r->vmlr_node);
379 vm->ractor.cnt++;
380
381 if (r->newobj_cache) {
382 VM_ASSERT(r == ruby_single_main_ractor);
383 }
384 else {
385 r->newobj_cache = rb_gc_ractor_cache_alloc(r);
386 }
387}
388
389static void
390cancel_single_ractor_mode(void)
391{
392 // enable multi-ractor mode
393 RUBY_DEBUG_LOG("enable multi-ractor mode");
394
395 ruby_single_main_ractor = NULL;
396 rb_funcall(rb_cRactor, rb_intern("_activated"), 0);
397}
398
399static void
400vm_insert_ractor(rb_vm_t *vm, rb_ractor_t *r)
401{
402 VM_ASSERT(ractor_status_p(r, ractor_created));
403
404 if (rb_multi_ractor_p()) {
405 RB_VM_LOCK();
406 {
407 vm_insert_ractor0(vm, r, false);
408 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
409 }
410 RB_VM_UNLOCK();
411 }
412 else {
413 if (vm->ractor.cnt == 0) {
414 // main ractor
415 vm_insert_ractor0(vm, r, true);
416 ractor_status_set(r, ractor_blocking);
417 ractor_status_set(r, ractor_running);
418 }
419 else {
420 cancel_single_ractor_mode();
421 vm_insert_ractor0(vm, r, true);
422 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
423 }
424 }
425}
426
427static void
428vm_remove_ractor(rb_vm_t *vm, rb_ractor_t *cr)
429{
430 VM_ASSERT(ractor_status_p(cr, ractor_running));
431 VM_ASSERT(vm->ractor.cnt > 1);
432 VM_ASSERT(cr->threads.cnt == 1);
433
434 RB_VM_LOCK();
435 {
436 RUBY_DEBUG_LOG("ractor.cnt:%u-- terminate_waiting:%d",
437 vm->ractor.cnt, vm->ractor.sync.terminate_waiting);
438
439 VM_ASSERT(vm->ractor.cnt > 0);
440 ccan_list_del(&cr->vmlr_node);
441
442 if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
443 rb_native_cond_signal(&vm->ractor.sync.terminate_cond);
444 }
445 vm->ractor.cnt--;
446
447 rb_gc_ractor_cache_free(cr->newobj_cache);
448 cr->newobj_cache = NULL;
449
450 ractor_status_set(cr, ractor_terminated);
451 }
452 RB_VM_UNLOCK();
453}
454
455static VALUE
456ractor_alloc(VALUE klass)
457{
458 rb_ractor_t *r;
459 VALUE rv = TypedData_Make_Struct(klass, rb_ractor_t, &ractor_data_type, r);
460 FL_SET_RAW(rv, RUBY_FL_SHAREABLE);
461 r->pub.self = rv;
462 r->next_ec_serial = 1;
463 VM_ASSERT(ractor_status_p(r, ractor_created));
464 return rv;
465}
466
467rb_ractor_t *
468rb_ractor_main_alloc(void)
469{
470 rb_ractor_t *r = ruby_mimcalloc(1, sizeof(rb_ractor_t));
471 if (r == NULL) {
472 fprintf(stderr, "[FATAL] failed to allocate memory for main ractor\n");
473 exit(EXIT_FAILURE);
474 }
475 r->pub.id = ++ractor_last_id;
476 r->loc = Qnil;
477 r->name = Qnil;
478 r->pub.self = Qnil;
479 r->newobj_cache = rb_gc_ractor_cache_alloc(r);
480 r->next_ec_serial = 1;
481 ruby_single_main_ractor = r;
482
483 return r;
484}
485
486#if defined(HAVE_WORKING_FORK)
487// Set up the main Ractor for the VM after fork.
488// Puts us in "single Ractor mode"
489void
490rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th)
491{
492 // initialize as a main ractor
493 vm->ractor.cnt = 0;
494 vm->ractor.blocking_cnt = 0;
495 ruby_single_main_ractor = th->ractor;
496 th->ractor->status_ = ractor_created;
497
498 rb_ractor_living_threads_init(th->ractor);
499 rb_ractor_living_threads_insert(th->ractor, th);
500
501 VM_ASSERT(vm->ractor.blocking_cnt == 0);
502 VM_ASSERT(vm->ractor.cnt == 1);
503}
504
505void
506rb_ractor_terminate_atfork(rb_vm_t *vm, rb_ractor_t *r)
507{
508 rb_gc_ractor_cache_free(r->newobj_cache);
509 r->newobj_cache = NULL;
510 r->status_ = ractor_terminated;
511 ractor_sync_terminate_atfork(vm, r);
512}
513#endif
514
515void rb_thread_sched_init(struct rb_thread_sched *, bool atfork);
516
517void
518rb_ractor_living_threads_init(rb_ractor_t *r)
519{
520 ccan_list_head_init(&r->threads.set);
521 r->threads.cnt = 0;
522 r->threads.blocking_cnt = 0;
523}
524
525static void
526ractor_init(rb_ractor_t *r, VALUE name, VALUE loc)
527{
528 ractor_sync_init(r);
529 r->pub.targeted_hooks = st_init_numtable();
530 r->pub.hooks.type = hook_list_type_ractor_local;
531
532 // thread management
533 rb_thread_sched_init(&r->threads.sched, false);
534 rb_ractor_living_threads_init(r);
535
536 // naming
537 if (!NIL_P(name)) {
538 rb_encoding *enc;
539 StringValueCStr(name);
540 enc = rb_enc_get(name);
541 if (!rb_enc_asciicompat(enc)) {
542 rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
543 rb_enc_name(enc));
544 }
545 name = RB_OBJ_SET_SHAREABLE(rb_str_new_frozen(name));
546 }
547
548 if (!SPECIAL_CONST_P(loc)) RB_OBJ_SET_SHAREABLE(loc);
549 r->loc = loc;
550 r->name = name;
551}
552
553void
554rb_ractor_main_setup(rb_vm_t *vm, rb_ractor_t *r, rb_thread_t *th)
555{
556 VALUE rv = r->pub.self = TypedData_Wrap_Struct(rb_cRactor, &ractor_data_type, r);
557 FL_SET_RAW(r->pub.self, RUBY_FL_SHAREABLE);
558 ractor_init(r, Qnil, Qnil);
559 r->threads.main = th;
560 rb_ractor_living_threads_insert(r, th);
561
562 RB_GC_GUARD(rv);
563}
564
565static VALUE
566ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VALUE args, VALUE block)
567{
568 VALUE rv = ractor_alloc(self);
569 rb_ractor_t *r = RACTOR_PTR(rv);
570 ractor_init(r, name, loc);
571
572 r->pub.id = ractor_next_id();
573 RUBY_DEBUG_LOG("r:%u", r->pub.id);
574
575 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
576 r->verbose = cr->verbose;
577 r->debug = cr->debug;
578
579 rb_yjit_before_ractor_spawn();
580 rb_zjit_before_ractor_spawn();
581 rb_thread_create_ractor(r, args, block);
582
583 RB_GC_GUARD(rv);
584 return rv;
585}
586
587#if 0
588static VALUE
589ractor_create_func(VALUE klass, VALUE loc, VALUE name, VALUE args, rb_block_call_func_t func)
590{
591 VALUE block = rb_proc_new(func, Qnil);
592 return ractor_create(rb_current_ec_noinline(), klass, loc, name, args, block);
593}
594#endif
595
596static void
597ractor_atexit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE result, bool exc)
598{
599 ractor_notify_exit(ec, cr, result, exc);
600}
601
602void
603rb_ractor_atexit(rb_execution_context_t *ec, VALUE result)
604{
605 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
606 ractor_atexit(ec, cr, result, false);
607}
608
609void
610rb_ractor_atexit_exception(rb_execution_context_t *ec)
611{
612 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
613 ractor_atexit(ec, cr, ec->errinfo, true);
614}
615
616void
617rb_ractor_teardown(rb_execution_context_t *ec)
618{
619 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
620
621 // sync with rb_ractor_terminate_interrupt_main_thread()
622 RB_VM_LOCKING() {
623 VM_ASSERT(cr->threads.main != NULL);
624 cr->threads.main = NULL;
625 }
626}
627
628void
629rb_ractor_receive_parameters(rb_execution_context_t *ec, rb_ractor_t *r, int len, VALUE *ptr)
630{
631 for (int i=0; i<len; i++) {
632 ptr[i] = ractor_receive(ec, ractor_default_port(r));
633 }
634}
635
636void
637rb_ractor_send_parameters(rb_execution_context_t *ec, rb_ractor_t *r, VALUE args)
638{
639 int len = RARRAY_LENINT(args);
640 for (int i=0; i<len; i++) {
641 ractor_send(ec, ractor_default_port(r), RARRAY_AREF(args, i), false);
642 }
643}
644
645bool
646rb_ractor_main_p_(void)
647{
648 VM_ASSERT(rb_multi_ractor_p());
649 rb_execution_context_t *ec = GET_EC();
650 return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
651}
652
653int
654rb_ractor_living_thread_num(const rb_ractor_t *r)
655{
656 return r->threads.cnt;
657}
658
659// only for current ractor
660 VALUE
661rb_ractor_thread_list(void)
662{
663 rb_ractor_t *r = GET_RACTOR();
664 rb_thread_t *th = 0;
665 VALUE ary = rb_ary_new();
666
667 ccan_list_for_each(&r->threads.set, th, lt_node) {
668 switch (th->status) {
669 case THREAD_RUNNABLE:
670 case THREAD_STOPPED:
671 case THREAD_STOPPED_FOREVER:
672 rb_ary_push(ary, th->self);
673 default:
674 break;
675 }
676 }
677
678 return ary;
679}
680
681void
682rb_ractor_living_threads_insert(rb_ractor_t *r, rb_thread_t *th)
683{
684 VM_ASSERT(th != NULL);
685
686 RACTOR_LOCK(r);
687 {
688 RUBY_DEBUG_LOG("r(%d)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
689 ccan_list_add_tail(&r->threads.set, &th->lt_node);
690 r->threads.cnt++;
691 }
692 RACTOR_UNLOCK(r);
693
694 // first thread for a ractor
695 if (r->threads.cnt == 1) {
696 VM_ASSERT(ractor_status_p(r, ractor_created));
697 vm_insert_ractor(th->vm, r);
698 }
699}
700
701static void
702vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line)
703{
704 ractor_status_set(r, ractor_blocking);
705
706 RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d++", vm->ractor.blocking_cnt);
707 vm->ractor.blocking_cnt++;
708 VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
709}
710
711void
712rb_vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
713{
714 ASSERT_vm_locking();
715 VM_ASSERT(GET_RACTOR() == cr);
716 vm_ractor_blocking_cnt_inc(vm, cr, file, line);
717}
718
719void
720rb_vm_ractor_blocking_cnt_dec(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
721{
722 ASSERT_vm_locking();
723 VM_ASSERT(GET_RACTOR() == cr);
724
725 RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d--", vm->ractor.blocking_cnt);
726 VM_ASSERT(vm->ractor.blocking_cnt > 0);
727 vm->ractor.blocking_cnt--;
728
729 ractor_status_set(cr, ractor_running);
730}
731
732static void
733ractor_check_blocking(rb_ractor_t *cr, unsigned int remained_thread_cnt, const char *file, int line)
734{
735 VM_ASSERT(cr == GET_RACTOR());
736
737 RUBY_DEBUG_LOG2(file, line,
738 "cr->threads.cnt:%u cr->threads.blocking_cnt:%u vm->ractor.cnt:%u vm->ractor.blocking_cnt:%u",
739 cr->threads.cnt, cr->threads.blocking_cnt,
740 GET_VM()->ractor.cnt, GET_VM()->ractor.blocking_cnt);
741
742 VM_ASSERT(cr->threads.cnt >= cr->threads.blocking_cnt + 1);
743
744 if (remained_thread_cnt > 0 &&
745 // will be block
746 cr->threads.cnt == cr->threads.blocking_cnt + 1) {
747 // change ractor status: running -> blocking
748 rb_vm_t *vm = GET_VM();
749
750 RB_VM_LOCKING() {
751 rb_vm_ractor_blocking_cnt_inc(vm, cr, file, line);
752 }
753 }
754}
755
756void rb_threadptr_remove(rb_thread_t *th);
757
758void
759rb_ractor_living_threads_remove(rb_ractor_t *cr, rb_thread_t *th)
760{
761 VM_ASSERT(cr == GET_RACTOR());
762 RUBY_DEBUG_LOG("r->threads.cnt:%d--", cr->threads.cnt);
763 ractor_check_blocking(cr, cr->threads.cnt - 1, __FILE__, __LINE__);
764
765 rb_threadptr_remove(th);
766
767 if (cr->threads.cnt == 1) {
768 vm_remove_ractor(th->vm, cr);
769 }
770 else {
771 RACTOR_LOCK(cr);
772 {
773 ccan_list_del(&th->lt_node);
774 cr->threads.cnt--;
775 }
776 RACTOR_UNLOCK(cr);
777 }
778}
779
780void
781rb_ractor_blocking_threads_inc(rb_ractor_t *cr, const char *file, int line)
782{
783 RUBY_DEBUG_LOG2(file, line, "cr->threads.blocking_cnt:%d++", cr->threads.blocking_cnt);
784
785 VM_ASSERT(cr->threads.cnt > 0);
786 VM_ASSERT(cr == GET_RACTOR());
787
788 ractor_check_blocking(cr, cr->threads.cnt, __FILE__, __LINE__);
789 cr->threads.blocking_cnt++;
790}
791
792void
793rb_ractor_blocking_threads_dec(rb_ractor_t *cr, const char *file, int line)
794{
795 RUBY_DEBUG_LOG2(file, line,
796 "r->threads.blocking_cnt:%d--, r->threads.cnt:%u",
797 cr->threads.blocking_cnt, cr->threads.cnt);
798
799 VM_ASSERT(cr == GET_RACTOR());
800
801 if (cr->threads.cnt == cr->threads.blocking_cnt) {
802 rb_vm_t *vm = GET_VM();
803
804 RB_VM_LOCKING() {
805 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
806 }
807 }
808
809 cr->threads.blocking_cnt--;
810}
811
812void
813rb_ractor_vm_barrier_interrupt_running_thread(rb_ractor_t *r)
814{
815 VM_ASSERT(r != GET_RACTOR());
816 ASSERT_ractor_unlocking(r);
817 ASSERT_vm_locking();
818
819 RACTOR_LOCK(r);
820 {
821 if (ractor_status_p(r, ractor_running)) {
822 rb_execution_context_t *ec = r->threads.running_ec;
823 if (ec) {
824 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec);
825 }
826 }
827 }
828 RACTOR_UNLOCK(r);
829}
830
831void
832rb_ractor_terminate_interrupt_main_thread(rb_ractor_t *r)
833{
834 VM_ASSERT(r != GET_RACTOR());
835 ASSERT_ractor_unlocking(r);
836 ASSERT_vm_locking();
837
838 rb_thread_t *main_th = r->threads.main;
839 if (main_th) {
840 if (main_th->status != THREAD_KILLED) {
841 RUBY_VM_SET_TERMINATE_INTERRUPT(main_th->ec);
842 rb_threadptr_interrupt(main_th);
843 }
844 else {
845 RUBY_DEBUG_LOG("killed (%p)", (void *)main_th);
846 }
847 }
848}
849
850void rb_thread_terminate_all(rb_thread_t *th); // thread.c
851
852static void
853ractor_terminal_interrupt_all(rb_vm_t *vm)
854{
855 if (vm->ractor.cnt > 1) {
856 // send terminate notification to all ractors
857 rb_ractor_t *r = 0;
858 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
859 if (r != vm->ractor.main_ractor) {
860 RUBY_DEBUG_LOG("r:%d", rb_ractor_id(r));
861 rb_ractor_terminate_interrupt_main_thread(r);
862 }
863 }
864 }
865}
866
867void rb_add_running_thread(rb_thread_t *th);
868void rb_del_running_thread(rb_thread_t *th);
869
870void
871rb_ractor_terminate_all(void)
872{
873 rb_vm_t *vm = GET_VM();
874 rb_ractor_t *cr = vm->ractor.main_ractor;
875
876 RUBY_DEBUG_LOG("ractor.cnt:%d", (int)vm->ractor.cnt);
877
878 VM_ASSERT(cr == GET_RACTOR()); // only main-ractor's main-thread should kick it.
879
880 RB_VM_LOCK();
881 {
882 ractor_terminal_interrupt_all(vm); // kill all ractors
883 }
884 RB_VM_UNLOCK();
885 rb_thread_terminate_all(GET_THREAD()); // kill other threads in main-ractor and wait
886
887 RB_VM_LOCK();
888 {
889 while (vm->ractor.cnt > 1) {
890 RUBY_DEBUG_LOG("terminate_waiting:%d", vm->ractor.sync.terminate_waiting);
891 vm->ractor.sync.terminate_waiting = true;
892
893 // wait for 1sec
894 rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
895 rb_del_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
896 rb_vm_cond_timedwait(vm, &vm->ractor.sync.terminate_cond, 1000 /* ms */);
897#ifdef RUBY_THREAD_PTHREAD_H
898 while (vm->ractor.sched.barrier_waiting) {
899 // A barrier is waiting. Threads relinquish the VM lock before joining the barrier and
900 // since we just acquired the VM lock back, we're blocking other threads from joining it.
901 // We loop until the barrier is over. We can't join this barrier because our thread isn't added to
902 // running_threads until the call below to `rb_add_running_thread`.
903 RB_VM_UNLOCK();
904 unsigned int lev;
905 RB_VM_LOCK_ENTER_LEV_NB(&lev);
906 }
907#endif
908 rb_add_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
909 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
910
911 ractor_terminal_interrupt_all(vm);
912 }
913 }
914 RB_VM_UNLOCK();
915}
916
917rb_execution_context_t *
918rb_vm_main_ractor_ec(rb_vm_t *vm)
919{
920 /* This code needs to carefully work around two bugs:
921 * - Bug #20016: When M:N threading is enabled, running_ec is NULL if no thread is
922 * actually currently running (as opposed to without M:N threading, when
923 * running_ec will still point to the _last_ thread which ran)
924 * - Bug #20197: If the main thread is sleeping, setting its postponed job
925 * interrupt flag is pointless; it won't look at the flag until it stops sleeping
926 * for some reason. It would be better to set the flag on the running ec, which
927 * will presumably look at it soon.
928 *
929 * Solution: use running_ec if it's set, otherwise fall back to the main thread ec.
930 * This is still susceptible to some rare race conditions (what if the last thread
931 * to run just entered a long-running sleep?), but seems like the best balance of
932 * robustness and complexity.
933 */
934 rb_execution_context_t *running_ec = vm->ractor.main_ractor->threads.running_ec;
935 if (running_ec) { return running_ec; }
936 return vm->ractor.main_thread->ec;
937}
938
939static VALUE
940ractor_moved_missing(int argc, VALUE *argv, VALUE self)
941{
942 rb_raise(rb_eRactorMovedError, "can not send any methods to a moved object");
943}
944
945/*
946 * Document-class: Ractor::Error
947 *
948 * The parent class of Ractor-related error classes.
949 */
950
951/*
952 * Document-class: Ractor::ClosedError
953 *
954 * Raised when an attempt is made to send a message to a closed port,
955 * or to retrieve a message from a closed and empty port.
956 * Ports may be closed explicitly with Ractor::Port#close
957 * and are closed implicitly when a Ractor terminates.
958 *
959 * port = Ractor::Port.new
960 * port.close
961 * port << "test" # Ractor::ClosedError
962 * port.receive # Ractor::ClosedError
963 *
964 * ClosedError is a descendant of StopIteration, so the closing of a port will break
965 * out of loops without propagating the error.
966 */
967
968/*
969 * Document-class: Ractor::IsolationError
970 *
971 * Raised on attempt to make a Ractor-unshareable object
972 * Ractor-shareable.
973 */
974
975/*
976 * Document-class: Ractor::RemoteError
977 *
978 * Raised on Ractor#join or Ractor#value if there was an uncaught exception in the Ractor.
979 * Its +cause+ will contain the original exception, and +ractor+ is the original ractor
980 * it was raised in.
981 *
982 * r = Ractor.new { raise "Something weird happened" }
983 *
984 * begin
985 * r.value
986 * rescue => e
987 * p e # => #<Ractor::RemoteError: thrown by remote Ractor.>
988 * p e.ractor == r # => true
989 * p e.cause # => #<RuntimeError: Something weird happened>
990 * end
991 *
992 */
993
994/*
995 * Document-class: Ractor::MovedError
996 *
997 * Raised on an attempt to access an object which was moved in Ractor#send or Ractor::Port#send.
998 *
999 * r = Ractor.new { sleep }
1000 *
1001 * ary = [1, 2, 3]
1002 * r.send(ary, move: true)
1003 * ary.inspect
1004 * # Ractor::MovedError (can not send any methods to a moved object)
1005 *
1006 */
1007
1008/*
1009 * Document-class: Ractor::MovedObject
1010 *
1011 * A special object which replaces any value that was moved to another ractor in Ractor#send
1012 * or Ractor::Port#send. Any attempt to access the object results in Ractor::MovedError.
1013 *
1014 * r = Ractor.new { receive }
1015 *
1016 * ary = [1, 2, 3]
1017 * r.send(ary, move: true)
1018 * p Ractor::MovedObject === ary
1019 * # => true
1020 * ary.inspect
1021 * # Ractor::MovedError (can not send any methods to a moved object)
1022 */
1023
1024/*
1025 * Document-class: Ractor::UnsafeError
1026 *
1027 * Raised when Ractor-unsafe C-methods is invoked by a non-main Ractor.
1028 */
1029
1030// Main docs are in ractor.rb, but without this clause there are weird artifacts
1031// in their rendering.
1032/*
1033 * Document-class: Ractor
1034 *
1035 */
1036
1037void
1038Init_Ractor(void)
1039{
1040 rb_cRactor = rb_define_class("Ractor", rb_cObject);
1041 rb_undef_alloc_func(rb_cRactor);
1042
1043 rb_eRactorError = rb_define_class_under(rb_cRactor, "Error", rb_eRuntimeError);
1044 rb_eRactorIsolationError = rb_define_class_under(rb_cRactor, "IsolationError", rb_eRactorError);
1045 rb_eRactorRemoteError = rb_define_class_under(rb_cRactor, "RemoteError", rb_eRactorError);
1046 rb_eRactorMovedError = rb_define_class_under(rb_cRactor, "MovedError", rb_eRactorError);
1047 rb_eRactorClosedError = rb_define_class_under(rb_cRactor, "ClosedError", rb_eStopIteration);
1048 rb_eRactorUnsafeError = rb_define_class_under(rb_cRactor, "UnsafeError", rb_eRactorError);
1049
1050 rb_cRactorMovedObject = rb_define_class_under(rb_cRactor, "MovedObject", rb_cBasicObject);
1051 rb_undef_alloc_func(rb_cRactorMovedObject);
1052 rb_define_method(rb_cRactorMovedObject, "method_missing", ractor_moved_missing, -1);
1053
1054 // override methods defined in BasicObject
1055 rb_define_method(rb_cRactorMovedObject, "__send__", ractor_moved_missing, -1);
1056 rb_define_method(rb_cRactorMovedObject, "!", ractor_moved_missing, -1);
1057 rb_define_method(rb_cRactorMovedObject, "==", ractor_moved_missing, -1);
1058 rb_define_method(rb_cRactorMovedObject, "!=", ractor_moved_missing, -1);
1059 rb_define_method(rb_cRactorMovedObject, "__id__", ractor_moved_missing, -1);
1060 rb_define_method(rb_cRactorMovedObject, "equal?", ractor_moved_missing, -1);
1061 rb_define_method(rb_cRactorMovedObject, "instance_eval", ractor_moved_missing, -1);
1062 rb_define_method(rb_cRactorMovedObject, "instance_exec", ractor_moved_missing, -1);
1063
1064 Init_RactorPort();
1065}
1066
1067void
1068rb_ractor_dump(void)
1069{
1070 rb_vm_t *vm = GET_VM();
1071 rb_ractor_t *r = 0;
1072
1073 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
1074 if (r != vm->ractor.main_ractor) {
1075 fprintf(stderr, "r:%u (%s)\n", r->pub.id, ractor_status_str(r->status_));
1076 }
1077 }
1078}
1079
1080 VALUE
1081rb_ractor_stdin(void)
1082{
1083 if (rb_ractor_main_p()) {
1084 return rb_stdin;
1085 }
1086 else {
1087 rb_ractor_t *cr = GET_RACTOR();
1088 return cr->r_stdin;
1089 }
1090}
1091
1092 VALUE
1093rb_ractor_stdout(void)
1094{
1095 if (rb_ractor_main_p()) {
1096 return rb_stdout;
1097 }
1098 else {
1099 rb_ractor_t *cr = GET_RACTOR();
1100 return cr->r_stdout;
1101 }
1102}
1103
1104 VALUE
1105rb_ractor_stderr(void)
1106{
1107 if (rb_ractor_main_p()) {
1108 return rb_stderr;
1109 }
1110 else {
1111 rb_ractor_t *cr = GET_RACTOR();
1112 return cr->r_stderr;
1113 }
1114}
1115
1116void
1117rb_ractor_stdin_set(VALUE in)
1118{
1119 if (rb_ractor_main_p()) {
1120 rb_stdin = in;
1121 }
1122 else {
1123 rb_ractor_t *cr = GET_RACTOR();
1124 RB_OBJ_WRITE(cr->pub.self, &cr->r_stdin, in);
1125 }
1126}
1127
1128void
1129rb_ractor_stdout_set(VALUE out)
1130{
1131 if (rb_ractor_main_p()) {
1132 rb_stdout = out;
1133 }
1134 else {
1135 rb_ractor_t *cr = GET_RACTOR();
1136 RB_OBJ_WRITE(cr->pub.self, &cr->r_stdout, out);
1137 }
1138}
1139
1140void
1141rb_ractor_stderr_set(VALUE err)
1142{
1143 if (rb_ractor_main_p()) {
1144 rb_stderr = err;
1145 }
1146 else {
1147 rb_ractor_t *cr = GET_RACTOR();
1148 RB_OBJ_WRITE(cr->pub.self, &cr->r_stderr, err);
1149 }
1150}
1151
1152rb_hook_list_t *
1153rb_ractor_hooks(rb_ractor_t *cr)
1154{
1155 return &cr->pub.hooks;
1156}
1157
1158st_table *
1159rb_ractor_targeted_hooks(rb_ractor_t *cr)
1160{
1161 return cr->pub.targeted_hooks;
1162}
1163
1164static void
1165rb_obj_set_shareable_no_assert(VALUE obj)
1166{
1167 FL_SET_RAW(obj, FL_SHAREABLE);
1168
1169 if (rb_obj_gen_fields_p(obj)) {
1170 VALUE fields = rb_obj_fields_no_ractor_check(obj);
1171 if (imemo_type_p(fields, imemo_fields)) {
1172 // no recursive mark
1173 FL_SET_RAW(fields, FL_SHAREABLE);
1174 }
1175 }
1176}
1177
1178#ifndef STRICT_VERIFY_SHAREABLE
1179#define STRICT_VERIFY_SHAREABLE 0
1180#endif
1181
1182bool
1183rb_ractor_verify_shareable(VALUE obj)
1184{
1185#if STRICT_VERIFY_SHAREABLE
1186 rb_gc_verify_shareable(obj);
1187#endif
1188 return true;
1189}
1190
1191 VALUE
1192rb_obj_set_shareable(VALUE obj)
1193{
1194 RUBY_ASSERT(!RB_SPECIAL_CONST_P(obj));
1195
1196 rb_obj_set_shareable_no_assert(obj);
1197 RUBY_ASSERT(rb_ractor_verify_shareable(obj));
1198
1199 return obj;
1200}
1201
1203
1204// 2: stop search
1205// 1: skip child
1206// 0: continue
1207
1208enum obj_traverse_iterator_result {
1209 traverse_cont,
1210 traverse_skip,
1211 traverse_stop,
1212};
1213
1214typedef enum obj_traverse_iterator_result (*rb_obj_traverse_enter_func)(VALUE obj);
1215typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(VALUE obj);
1216typedef enum obj_traverse_iterator_result (*rb_obj_traverse_final_func)(VALUE obj);
1217
1218static enum obj_traverse_iterator_result null_leave(VALUE obj);
1219
1220 struct obj_traverse_data {
1221 rb_obj_traverse_enter_func enter_func;
1222 rb_obj_traverse_leave_func leave_func;
1223
1224 st_table *rec;
1225 VALUE rec_hash;
1226};
1227
1228
1229 struct obj_traverse_callback_data {
1230 bool stop;
1231 struct obj_traverse_data *data;
1232};
1233
1234static int obj_traverse_i(VALUE obj, struct obj_traverse_data *data);
1235
1236static int
1237obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
1238{
1239 struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
1240
1241 if (obj_traverse_i(key, d->data)) {
1242 d->stop = true;
1243 return ST_STOP;
1244 }
1245
1246 if (obj_traverse_i(val, d->data)) {
1247 d->stop = true;
1248 return ST_STOP;
1249 }
1250
1251 return ST_CONTINUE;
1252}
1253
1254static void
1255obj_traverse_reachable_i(VALUE obj, void *ptr)
1256{
1257 struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
1258
1259 if (obj_traverse_i(obj, d->data)) {
1260 d->stop = true;
1261 }
1262}
1263
1264static struct st_table *
1265obj_traverse_rec(struct obj_traverse_data *data)
1266{
1267 if (UNLIKELY(!data->rec)) {
1268 data->rec_hash = rb_ident_hash_new();
1269 data->rec = RHASH_ST_TABLE(data->rec_hash);
1270 }
1271 return data->rec;
1272}
1273
1274static int
1275obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr)
1276{
1277 struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
1278
1279 if (obj_traverse_i(val, d->data)) {
1280 d->stop = true;
1281 return ST_STOP;
1282 }
1283
1284 return ST_CONTINUE;
1285}
1286
1287static int
1288obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
1289{
1290 if (RB_SPECIAL_CONST_P(obj)) return 0;
1291
1292 switch (data->enter_func(obj)) {
1293 case traverse_cont: break;
1294 case traverse_skip: return 0; // skip children
1295 case traverse_stop: return 1; // stop search
1296 }
1297
1298 if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
1299 // already traversed
1300 return 0;
1301 }
1302 RB_OBJ_WRITTEN(data->rec_hash, Qundef, obj);
1303
1304 struct obj_traverse_callback_data d = {
1305 .stop = false,
1306 .data = data,
1307 };
1308 rb_ivar_foreach(obj, obj_traverse_ivar_foreach_i, (st_data_t)&d);
1309 if (d.stop) return 1;
1310
1311 switch (BUILTIN_TYPE(obj)) {
1312 // no child node
1313 case T_STRING:
1314 case T_FLOAT:
1315 case T_BIGNUM:
1316 case T_REGEXP:
1317 case T_FILE:
1318 case T_SYMBOL:
1319 break;
1320
1321 case T_OBJECT:
1322 /* Instance variables already traversed. */
1323 break;
1324
1325 case T_ARRAY:
1326 {
1327 rb_ary_cancel_sharing(obj);
1328
1329 for (int i = 0; i < RARRAY_LENINT(obj); i++) {
1330 VALUE e = rb_ary_entry(obj, i);
1331 if (obj_traverse_i(e, data)) return 1;
1332 }
1333 }
1334 break;
1335
1336 case T_HASH:
1337 {
1338 if (obj_traverse_i(RHASH_IFNONE(obj), data)) return 1;
1339
1340 struct obj_traverse_callback_data d = {
1341 .stop = false,
1342 .data = data,
1343 };
1344 rb_hash_foreach(obj, obj_hash_traverse_i, (VALUE)&d);
1345 if (d.stop) return 1;
1346 }
1347 break;
1348
1349 case T_STRUCT:
1350 {
1351 long len = RSTRUCT_LEN(obj);
1352 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
1353
1354 for (long i=0; i<len; i++) {
1355 if (obj_traverse_i(ptr[i], data)) return 1;
1356 }
1357 }
1358 break;
1359
1360 case T_MATCH:
1361 if (obj_traverse_i(RMATCH(obj)->str, data)) return 1;
1362 break;
1363
1364 case T_RATIONAL:
1365 if (obj_traverse_i(RRATIONAL(obj)->num, data)) return 1;
1366 if (obj_traverse_i(RRATIONAL(obj)->den, data)) return 1;
1367 break;
1368 case T_COMPLEX:
1369 if (obj_traverse_i(RCOMPLEX(obj)->real, data)) return 1;
1370 if (obj_traverse_i(RCOMPLEX(obj)->imag, data)) return 1;
1371 break;
1372
1373 case T_DATA:
1374 case T_IMEMO:
1375 {
1376 struct obj_traverse_callback_data d = {
1377 .stop = false,
1378 .data = data,
1379 };
1380 RB_VM_LOCKING_NO_BARRIER() {
1381 rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
1382 }
1383 if (d.stop) return 1;
1384 }
1385 break;
1386
1387 // unreachable
1388 case T_CLASS:
1389 case T_MODULE:
1390 case T_ICLASS:
1391 default:
1392 rp(obj);
1393 rb_bug("unreachable");
1394 }
1395
1396 if (data->leave_func(obj) == traverse_stop) {
1397 return 1;
1398 }
1399 else {
1400 return 0;
1401 }
1402}
1403
1404 struct rb_obj_traverse_final_data {
1405 rb_obj_traverse_final_func final_func;
1406 int stopped;
1407};
1408
1409static int
1410obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
1411{
1412 struct rb_obj_traverse_final_data *data = (void *)arg;
1413 if (data->final_func(key)) {
1414 data->stopped = 1;
1415 return ST_STOP;
1416 }
1417 return ST_CONTINUE;
1418}
1419
1420// 0: traverse all
1421// 1: stopped
1422static int
1423rb_obj_traverse(VALUE obj,
1424 rb_obj_traverse_enter_func enter_func,
1425 rb_obj_traverse_leave_func leave_func,
1426 rb_obj_traverse_final_func final_func)
1427{
1428 struct obj_traverse_data data = {
1429 .enter_func = enter_func,
1430 .leave_func = leave_func,
1431 .rec = NULL,
1432 };
1433
1434 if (obj_traverse_i(obj, &data)) return 1;
1435 if (final_func && data.rec) {
1436 struct rb_obj_traverse_final_data f = {final_func, 0};
1437 st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
1438 return f.stopped;
1439 }
1440 return 0;
1441}
1442
1443static int
1444allow_frozen_shareable_p(VALUE obj)
1445{
1446 if (!RB_TYPE_P(obj, T_DATA)) {
1447 return true;
1448 }
1449 else if (RTYPEDDATA_P(obj)) {
1450 const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
1451 if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
1452 return true;
1453 }
1454 }
1455
1456 return false;
1457}
1458
1459static enum obj_traverse_iterator_result
1460make_shareable_check_shareable_freeze(VALUE obj, enum obj_traverse_iterator_result result)
1461{
1462 if (!RB_OBJ_FROZEN_RAW(obj)) {
1463 rb_funcall(obj, idFreeze, 0);
1464
1465 if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
1466 rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
1467 }
1468
1469 if (RB_OBJ_SHAREABLE_P(obj)) {
1470 return traverse_skip;
1471 }
1472 }
1473
1474 return result;
1475}
1476
1477static int obj_refer_only_shareables_p(VALUE obj);
1478
1479static enum obj_traverse_iterator_result
1480make_shareable_check_shareable(VALUE obj)
1481{
1482 VM_ASSERT(!SPECIAL_CONST_P(obj));
1483
1484 if (rb_ractor_shareable_p(obj)) {
1485 return traverse_skip;
1486 }
1487 else if (!allow_frozen_shareable_p(obj)) {
1488 VM_ASSERT(RB_TYPE_P(obj, T_DATA));
1489 const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
1490
1491 if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC) {
1492 if (obj_refer_only_shareables_p(obj)) {
1493 make_shareable_check_shareable_freeze(obj, traverse_skip);
1494 RB_OBJ_SET_SHAREABLE(obj);
1495 return traverse_skip;
1496 }
1497 else {
1498 rb_raise(rb_eRactorError,
1499 "can not make shareable object for %+"PRIsVALUE" because it refers unshareable objects", obj);
1500 }
1501 }
1502 else if (rb_obj_is_proc(obj)) {
1503 rb_proc_ractor_make_shareable(obj, Qundef);
1504 return traverse_cont;
1505 }
1506 else {
1507 rb_raise(rb_eRactorError, "can not make shareable object for %+"PRIsVALUE, obj);
1508 }
1509 }
1510
1511 switch (TYPE(obj)) {
1512 case T_IMEMO:
1513 return traverse_skip;
1514 case T_OBJECT:
1515 {
1516 // If a T_OBJECT is shared and has no free capacity, we can't safely store the object_id inline,
1517 // as it would require to move the object content into an external buffer.
1518 // This is only a problem for T_OBJECT, given other types have external fields and can do RCU.
1519 // To avoid this issue, we proactively create the object_id.
1520 shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
1521 attr_index_t capacity = RSHAPE_CAPACITY(shape_id);
1522 attr_index_t free_capacity = capacity - RSHAPE_LEN(shape_id);
1523 if (!rb_shape_has_object_id(shape_id) && capacity && !free_capacity) {
1524 rb_obj_id(obj);
1525 }
1526 }
1527 break;
1528 default:
1529 break;
1530 }
1531
1532 return make_shareable_check_shareable_freeze(obj, traverse_cont);
1533}
1534
1535static enum obj_traverse_iterator_result
1536mark_shareable(VALUE obj)
1537{
1538 if (RB_TYPE_P(obj, T_STRING)) {
1539 rb_str_make_independent(obj);
1540 }
1541
1542 rb_obj_set_shareable_no_assert(obj);
1543 return traverse_cont;
1544}
1545
1546 VALUE
1547rb_ractor_make_shareable(VALUE obj)
1548{
1549 rb_obj_traverse(obj,
1550 make_shareable_check_shareable,
1551 null_leave, mark_shareable);
1552 return obj;
1553}
1554
1555 VALUE
1556rb_ractor_make_shareable_copy(VALUE obj)
1557{
1558 VALUE copy = ractor_copy(obj);
1559 return rb_ractor_make_shareable(copy);
1560}
1561
1562 VALUE
1563rb_ractor_ensure_shareable(VALUE obj, VALUE name)
1564{
1565 if (!rb_ractor_shareable_p(obj)) {
1566 VALUE message = rb_sprintf("cannot assign unshareable object to %"PRIsVALUE,
1567 name);
1568 rb_exc_raise(rb_exc_new_str(rb_eRactorIsolationError, message));
1569 }
1570 return obj;
1571}
1572
1573void
1574rb_ractor_ensure_main_ractor(const char *msg)
1575{
1576 if (!rb_ractor_main_p()) {
1577 rb_raise(rb_eRactorIsolationError, "%s", msg);
1578 }
1579}
1580
1581static enum obj_traverse_iterator_result
1582shareable_p_enter(VALUE obj)
1583{
1584 if (RB_OBJ_SHAREABLE_P(obj)) {
1585 return traverse_skip;
1586 }
1587 else if (RB_TYPE_P(obj, T_CLASS) ||
1588 RB_TYPE_P(obj, T_MODULE) ||
1589 RB_TYPE_P(obj, T_ICLASS)) {
1590 // TODO: remove it
1591 mark_shareable(obj);
1592 return traverse_skip;
1593 }
1594 else if (RB_OBJ_FROZEN_RAW(obj) &&
1595 allow_frozen_shareable_p(obj)) {
1596 return traverse_cont;
1597 }
1598
1599 return traverse_stop; // fail
1600}
1601
1602bool
1603rb_ractor_shareable_p_continue(VALUE obj)
1604{
1605 if (rb_obj_traverse(obj,
1606 shareable_p_enter, null_leave,
1607 mark_shareable)) {
1608 return false;
1609 }
1610 else {
1611 return true;
1612 }
1613}
1614
1615#if RACTOR_CHECK_MODE > 0
1616void
1617rb_ractor_setup_belonging(VALUE obj)
1618{
1619 rb_ractor_setup_belonging_to(obj, rb_ractor_current_id());
1620}
1621
1622static enum obj_traverse_iterator_result
1623reset_belonging_enter(VALUE obj)
1624{
1625 if (rb_ractor_shareable_p(obj)) {
1626 return traverse_skip;
1627 }
1628 else {
1629 rb_ractor_setup_belonging(obj);
1630 return traverse_cont;
1631 }
1632}
1633#endif
1634
1635static enum obj_traverse_iterator_result
1636null_leave(VALUE obj)
1637{
1638 return traverse_cont;
1639}
1640
1641static VALUE
1642ractor_reset_belonging(VALUE obj)
1643{
1644#if RACTOR_CHECK_MODE > 0
1645 rb_obj_traverse(obj, reset_belonging_enter, null_leave, NULL);
1646#endif
1647 return obj;
1648}
1649
1650
1652
1653// 2: stop search
1654// 1: skip child
1655// 0: continue
1656
1657struct obj_traverse_replace_data;
1658static int obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data);
1659typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_enter_func)(VALUE obj, struct obj_traverse_replace_data *data);
1660typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_leave_func)(VALUE obj, struct obj_traverse_replace_data *data);
1661
1662 struct obj_traverse_replace_data {
1663 rb_obj_traverse_replace_enter_func enter_func;
1664 rb_obj_traverse_replace_leave_func leave_func;
1665
1666 st_table *rec;
1667 VALUE rec_hash;
1668
1669 VALUE replacement;
1670 bool move;
1671};
1672
1673 struct obj_traverse_replace_callback_data {
1674 bool stop;
1675 VALUE src;
1676 struct obj_traverse_replace_data *data;
1677};
1678
1679static int
1680obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp, int error)
1681{
1682 return ST_REPLACE;
1683}
1684
1685static int
1686obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr, int exists)
1687{
1688 struct obj_traverse_replace_callback_data *d = (struct obj_traverse_replace_callback_data *)ptr;
1689 struct obj_traverse_replace_data *data = d->data;
1690
1691 if (obj_traverse_replace_i(*key, data)) {
1692 d->stop = true;
1693 return ST_STOP;
1694 }
1695 else if (*key != data->replacement) {
1696 VALUE v = *key = data->replacement;
1697 RB_OBJ_WRITTEN(d->src, Qundef, v);
1698 }
1699
1700 if (obj_traverse_replace_i(*val, data)) {
1701 d->stop = true;
1702 return ST_STOP;
1703 }
1704 else if (*val != data->replacement) {
1705 VALUE v = *val = data->replacement;
1706 RB_OBJ_WRITTEN(d->src, Qundef, v);
1707 }
1708
1709 return ST_CONTINUE;
1710}
1711
1712static int
1713obj_iv_hash_traverse_replace_foreach_i(st_data_t _key, st_data_t _val, st_data_t _data, int _x)
1714{
1715 return ST_REPLACE;
1716}
1717
1718static int
1719obj_iv_hash_traverse_replace_i(st_data_t * _key, st_data_t * val, st_data_t ptr, int exists)
1720{
1721 struct obj_traverse_replace_callback_data *d = (struct obj_traverse_replace_callback_data *)ptr;
1722 struct obj_traverse_replace_data *data = d->data;
1723
1724 if (obj_traverse_replace_i(*(VALUE *)val, data)) {
1725 d->stop = true;
1726 return ST_STOP;
1727 }
1728 else if (*(VALUE *)val != data->replacement) {
1729 VALUE v = *(VALUE *)val = data->replacement;
1730 RB_OBJ_WRITTEN(d->src, Qundef, v);
1731 }
1732
1733 return ST_CONTINUE;
1734}
1735
1736static struct st_table *
1737obj_traverse_replace_rec(struct obj_traverse_replace_data *data)
1738{
1739 if (UNLIKELY(!data->rec)) {
1740 data->rec_hash = rb_ident_hash_new();
1741 data->rec = RHASH_ST_TABLE(data->rec_hash);
1742 }
1743 return data->rec;
1744}
1745
1746static void
1747obj_refer_only_shareables_p_i(VALUE obj, void *ptr)
1748{
1749 int *pcnt = (int *)ptr;
1750
1751 if (!rb_ractor_shareable_p(obj)) {
1752 ++*pcnt;
1753 }
1754}
1755
1756static int
1757obj_refer_only_shareables_p(VALUE obj)
1758{
1759 int cnt = 0;
1760 RB_VM_LOCKING_NO_BARRIER() {
1761 rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
1762 }
1763 return cnt == 0;
1764}
1765
1766static int
1767obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
1768{
1769 st_data_t replacement;
1770
1771 if (RB_SPECIAL_CONST_P(obj)) {
1772 data->replacement = obj;
1773 return 0;
1774 }
1775
1776 switch (data->enter_func(obj, data)) {
1777 case traverse_cont: break;
1778 case traverse_skip: return 0; // skip children
1779 case traverse_stop: return 1; // stop search
1780 }
1781
1782 replacement = (st_data_t)data->replacement;
1783
1784 if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, &replacement))) {
1785 data->replacement = (VALUE)replacement;
1786 return 0;
1787 }
1788 else {
1789 st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, replacement);
1790 RB_OBJ_WRITTEN(data->rec_hash, Qundef, obj);
1791 RB_OBJ_WRITTEN(data->rec_hash, Qundef, replacement);
1792 }
1793
1794 if (!data->move) {
1795 obj = replacement;
1796 }
1797
1798#define CHECK_AND_REPLACE(parent_obj, v) do { \
1799 VALUE _val = (v); \
1800 if (obj_traverse_replace_i(_val, data)) { return 1; } \
1801 else if (data->replacement != _val) { RB_OBJ_WRITE(parent_obj, &v, data->replacement); } \
1802} while (0)
1803
1804 if (UNLIKELY(rb_obj_gen_fields_p(obj))) {
1805 VALUE fields_obj = rb_obj_fields_no_ractor_check(obj);
1806
1807 if (UNLIKELY(rb_shape_obj_too_complex_p(obj))) {
1808 struct obj_traverse_replace_callback_data d = {
1809 .stop = false,
1810 .data = data,
1811 .src = fields_obj,
1812 };
1813 rb_st_foreach_with_replace(
1814 rb_imemo_fields_complex_tbl(fields_obj),
1815 obj_iv_hash_traverse_replace_foreach_i,
1816 obj_iv_hash_traverse_replace_i,
1817 (st_data_t)&d
1818 );
1819 if (d.stop) return 1;
1820 }
1821 else {
1822 uint32_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID(obj));
1823 VALUE *fields = rb_imemo_fields_ptr(fields_obj);
1824 for (uint32_t i = 0; i < fields_count; i++) {
1825 CHECK_AND_REPLACE(fields_obj, fields[i]);
1826 }
1827 }
1828 }
1829
1830 switch (BUILTIN_TYPE(obj)) {
1831 // no child node
1832 case T_FLOAT:
1833 case T_BIGNUM:
1834 case T_REGEXP:
1835 case T_FILE:
1836 case T_SYMBOL:
1837 break;
1838 case T_STRING:
1839 rb_str_make_independent(obj);
1840 break;
1841
1842 case T_OBJECT:
1843 {
1844 if (rb_shape_obj_too_complex_p(obj)) {
1845 struct obj_traverse_replace_callback_data d = {
1846 .stop = false,
1847 .data = data,
1848 .src = obj,
1849 };
1850 rb_st_foreach_with_replace(
1851 ROBJECT_FIELDS_HASH(obj),
1852 obj_iv_hash_traverse_replace_foreach_i,
1853 obj_iv_hash_traverse_replace_i,
1854 (st_data_t)&d
1855 );
1856 if (d.stop) return 1;
1857 }
1858 else {
1859 uint32_t len = ROBJECT_FIELDS_COUNT_NOT_COMPLEX(obj);
1860 VALUE *ptr = ROBJECT_FIELDS(obj);
1861
1862 for (uint32_t i = 0; i < len; i++) {
1863 CHECK_AND_REPLACE(obj, ptr[i]);
1864 }
1865 }
1866 }
1867 break;
1868
1869 case T_ARRAY:
1870 {
1871 rb_ary_cancel_sharing(obj);
1872
1873 for (int i = 0; i < RARRAY_LENINT(obj); i++) {
1874 VALUE e = rb_ary_entry(obj, i);
1875
1876 if (obj_traverse_replace_i(e, data)) {
1877 return 1;
1878 }
1879 else if (e != data->replacement) {
1880 RARRAY_ASET(obj, i, data->replacement);
1881 }
1882 }
1883 RB_GC_GUARD(obj);
1884 }
1885 break;
1886 case T_HASH:
1887 {
1888 struct obj_traverse_replace_callback_data d = {
1889 .stop = false,
1890 .data = data,
1891 .src = obj,
1892 };
1893 rb_hash_stlike_foreach_with_replace(obj,
1894 obj_hash_traverse_replace_foreach_i,
1895 obj_hash_traverse_replace_i,
1896 (VALUE)&d);
1897 if (d.stop) return 1;
1898 // TODO: rehash here?
1899
1900 VALUE ifnone = RHASH_IFNONE(obj);
1901 if (obj_traverse_replace_i(ifnone, data)) {
1902 return 1;
1903 }
1904 else if (ifnone != data->replacement) {
1905 RHASH_SET_IFNONE(obj, data->replacement);
1906 }
1907 }
1908 break;
1909
1910 case T_STRUCT:
1911 {
1912 long len = RSTRUCT_LEN(obj);
1913 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
1914
1915 for (long i=0; i<len; i++) {
1916 CHECK_AND_REPLACE(obj, ptr[i]);
1917 }
1918 }
1919 break;
1920
1921 case T_MATCH:
1922 CHECK_AND_REPLACE(obj, RMATCH(obj)->str);
1923 break;
1924
1925 case T_RATIONAL:
1926 CHECK_AND_REPLACE(obj, RRATIONAL(obj)->num);
1927 CHECK_AND_REPLACE(obj, RRATIONAL(obj)->den);
1928 break;
1929 case T_COMPLEX:
1930 CHECK_AND_REPLACE(obj, RCOMPLEX(obj)->real);
1931 CHECK_AND_REPLACE(obj, RCOMPLEX(obj)->imag);
1932 break;
1933
1934 case T_DATA:
1935 if (!data->move && obj_refer_only_shareables_p(obj)) {
1936 break;
1937 }
1938 else {
1939 rb_raise(rb_eRactorError, "can not %s %"PRIsVALUE" object.",
1940 data->move ? "move" : "copy", rb_class_of(obj));
1941 }
1942
1943 case T_IMEMO:
1944 // not supported yet
1945 return 1;
1946
1947 // unreachable
1948 case T_CLASS:
1949 case T_MODULE:
1950 case T_ICLASS:
1951 default:
1952 rp(obj);
1953 rb_bug("unreachable");
1954 }
1955
1956 data->replacement = (VALUE)replacement;
1957
1958 if (data->leave_func(obj, data) == traverse_stop) {
1959 return 1;
1960 }
1961 else {
1962 return 0;
1963 }
1964}
1965
1966// 0: traverse all
1967// 1: stopped
1968static VALUE
1969rb_obj_traverse_replace(VALUE obj,
1970 rb_obj_traverse_replace_enter_func enter_func,
1971 rb_obj_traverse_replace_leave_func leave_func,
1972 bool move)
1973{
1974 struct obj_traverse_replace_data data = {
1975 .enter_func = enter_func,
1976 .leave_func = leave_func,
1977 .rec = NULL,
1978 .replacement = Qundef,
1979 .move = move,
1980 };
1981
1982 if (obj_traverse_replace_i(obj, &data)) {
1983 return Qundef;
1984 }
1985 else {
1986 return data.replacement;
1987 }
1988}
1989
1990static const bool wb_protected_types[RUBY_T_MASK] = {
1991 [T_OBJECT] = RGENGC_WB_PROTECTED_OBJECT,
1992 [T_HASH] = RGENGC_WB_PROTECTED_HASH,
1993 [T_ARRAY] = RGENGC_WB_PROTECTED_ARRAY,
1994 [T_STRING] = RGENGC_WB_PROTECTED_STRING,
1995 [T_STRUCT] = RGENGC_WB_PROTECTED_STRUCT,
1996 [T_COMPLEX] = RGENGC_WB_PROTECTED_COMPLEX,
1997 [T_REGEXP] = RGENGC_WB_PROTECTED_REGEXP,
1998 [T_MATCH] = RGENGC_WB_PROTECTED_MATCH,
1999 [T_FLOAT] = RGENGC_WB_PROTECTED_FLOAT,
2000 [T_RATIONAL] = RGENGC_WB_PROTECTED_RATIONAL,
2001};
2002
2003static enum obj_traverse_iterator_result
2004move_enter(VALUE obj, struct obj_traverse_replace_data *data)
2005{
2006 if (rb_ractor_shareable_p(obj)) {
2007 data->replacement = obj;
2008 return traverse_skip;
2009 }
2010 else {
2011 VALUE type = RB_BUILTIN_TYPE(obj);
2012 size_t slot_size = rb_gc_obj_slot_size(obj);
2013 type |= wb_protected_types[type] ? FL_WB_PROTECTED : 0;
2014 NEWOBJ_OF(moved, struct RBasic, 0, type, slot_size, 0);
2015 MEMZERO(&moved[1], char, slot_size - sizeof(*moved));
2016 data->replacement = (VALUE)moved;
2017 return traverse_cont;
2018 }
2019}
2020
2021static enum obj_traverse_iterator_result
2022move_leave(VALUE obj, struct obj_traverse_replace_data *data)
2023{
2024 // Copy flags
2025 VALUE ignored_flags = RUBY_FL_PROMOTED;
2026 RBASIC(data->replacement)->flags = (RBASIC(obj)->flags & ~ignored_flags) | (RBASIC(data->replacement)->flags & ignored_flags);
2027 // Copy contents without the flags
2028 memcpy(
2029 (char *)data->replacement + sizeof(VALUE),
2030 (char *)obj + sizeof(VALUE),
2031 rb_gc_obj_slot_size(obj) - sizeof(VALUE)
2032 );
2033
2034 // We've copied obj's references to the replacement
2035 rb_gc_writebarrier_remember(data->replacement);
2036
2037 void rb_replace_generic_ivar(VALUE clone, VALUE obj); // variable.c
2038 if (UNLIKELY(rb_obj_gen_fields_p(obj))) {
2039 rb_replace_generic_ivar(data->replacement, obj);
2040 }
2041
2042 rb_gc_obj_id_moved(data->replacement);
2043
2044 VALUE flags = T_OBJECT | FL_FREEZE | (RBASIC(obj)->flags & FL_PROMOTED);
2045
2046 // Avoid mutations using bind_call, etc.
2047 MEMZERO((char *)obj, char, sizeof(struct RBasic));
2048 RBASIC(obj)->flags = flags;
2049 RBASIC_SET_CLASS_RAW(obj, rb_cRactorMovedObject);
2050 return traverse_cont;
2051}
2052
2053static VALUE
2054ractor_move(VALUE obj)
2055{
2056 VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave, true);
2057 if (!UNDEF_P(val)) {
2058 return val;
2059 }
2060 else {
2061 rb_raise(rb_eRactorError, "can not move the object");
2062 }
2063}
2064
2065static enum obj_traverse_iterator_result
2066copy_enter(VALUE obj, struct obj_traverse_replace_data *data)
2067{
2068 if (rb_ractor_shareable_p(obj)) {
2069 data->replacement = obj;
2070 return traverse_skip;
2071 }
2072 else {
2073 data->replacement = rb_obj_clone(obj);
2074 return traverse_cont;
2075 }
2076}
2077
2078static enum obj_traverse_iterator_result
2079copy_leave(VALUE obj, struct obj_traverse_replace_data *data)
2080{
2081 return traverse_cont;
2082}
2083
2084static VALUE
2085ractor_copy(VALUE obj)
2086{
2087 VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave, false);
2088 if (!UNDEF_P(val)) {
2089 return val;
2090 }
2091 else {
2092 rb_raise(rb_eRactorError, "can not copy the object");
2093 }
2094}
2095
2096// Ractor local storage
2097
2098 struct rb_ractor_local_key_struct {
2099 const struct rb_ractor_local_storage_type *type;
2100 void *main_cache;
2101};
2102
2103 static struct freed_ractor_local_keys_struct {
2104 int cnt;
2105 int capa;
2106 rb_ractor_local_key_t *keys;
2107} freed_ractor_local_keys;
2108
2109static int
2110ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
2111{
2112 struct rb_ractor_local_key_struct *k = (struct rb_ractor_local_key_struct *)key;
2113 if (k->type->mark) (*k->type->mark)((void *)val);
2114 return ST_CONTINUE;
2115}
2116
2117static enum rb_id_table_iterator_result
2118idkey_local_storage_mark_i(VALUE val, void *dmy)
2119{
2120 rb_gc_mark(val);
2121 return ID_TABLE_CONTINUE;
2122}
2123
2124static void
2125ractor_local_storage_mark(rb_ractor_t *r)
2126{
2127 if (r->local_storage) {
2128 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
2129
2130 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
2131 rb_ractor_local_key_t key = freed_ractor_local_keys.keys[i];
2132 st_data_t val, k = (st_data_t)key;
2133 if (st_delete(r->local_storage, &k, &val) &&
2134 (key = (rb_ractor_local_key_t)k)->type->free) {
2135 (*key->type->free)((void *)val);
2136 }
2137 }
2138 }
2139
2140 if (r->idkey_local_storage) {
2141 rb_id_table_foreach_values(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
2142 }
2143
2144 rb_gc_mark(r->local_storage_store_lock);
2145}
2146
2147static int
2148ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
2149{
2150 struct rb_ractor_local_key_struct *k = (struct rb_ractor_local_key_struct *)key;
2151 if (k->type->free) (*k->type->free)((void *)val);
2152 return ST_CONTINUE;
2153}
2154
2155static void
2156ractor_local_storage_free(rb_ractor_t *r)
2157{
2158 if (r->local_storage) {
2159 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
2160 st_free_table(r->local_storage);
2161 }
2162
2163 if (r->idkey_local_storage) {
2164 rb_id_table_free(r->idkey_local_storage);
2165 }
2166}
2167
2168static void
2169rb_ractor_local_storage_value_mark(void *ptr)
2170{
2171 rb_gc_mark((VALUE)ptr);
2172}
2173
2174static const struct rb_ractor_local_storage_type ractor_local_storage_type_null = {
2175 NULL,
2176 NULL,
2177};
2178
2179 const struct rb_ractor_local_storage_type rb_ractor_local_storage_type_free = {
2180 NULL,
2181 ruby_xfree,
2182};
2183
2184static const struct rb_ractor_local_storage_type ractor_local_storage_type_value = {
2185 rb_ractor_local_storage_value_mark,
2186 NULL,
2187};
2188
2189 rb_ractor_local_key_t
2190rb_ractor_local_storage_ptr_newkey(const struct rb_ractor_local_storage_type *type)
2191{
2192 rb_ractor_local_key_t key = ALLOC(struct rb_ractor_local_key_struct);
2193 key->type = type ? type : &ractor_local_storage_type_null;
2194 key->main_cache = (void *)Qundef;
2195 return key;
2196}
2197
2198 rb_ractor_local_key_t
2199rb_ractor_local_storage_value_newkey(void)
2200{
2201 return rb_ractor_local_storage_ptr_newkey(&ractor_local_storage_type_value);
2202}
2203
2204void
2205rb_ractor_local_storage_delkey(rb_ractor_local_key_t key)
2206{
2207 RB_VM_LOCKING() {
2208 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
2209 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
2210 REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, freed_ractor_local_keys.capa);
2211 }
2212 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
2213 }
2214}
2215
2216static bool
2217ractor_local_ref(rb_ractor_local_key_t key, void **pret)
2218{
2219 if (rb_ractor_main_p()) {
2220 if (!UNDEF_P((VALUE)key->main_cache)) {
2221 *pret = key->main_cache;
2222 return true;
2223 }
2224 else {
2225 return false;
2226 }
2227 }
2228 else {
2229 rb_ractor_t *cr = GET_RACTOR();
2230
2231 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
2232 return true;
2233 }
2234 else {
2235 return false;
2236 }
2237 }
2238}
2239
2240static void
2241ractor_local_set(rb_ractor_local_key_t key, void *ptr)
2242{
2243 rb_ractor_t *cr = GET_RACTOR();
2244
2245 if (cr->local_storage == NULL) {
2246 cr->local_storage = st_init_numtable();
2247 }
2248
2249 st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
2250
2251 if (rb_ractor_main_p()) {
2252 key->main_cache = ptr;
2253 }
2254}
2255
2256 VALUE
2257rb_ractor_local_storage_value(rb_ractor_local_key_t key)
2258{
2259 void *val;
2260 if (ractor_local_ref(key, &val)) {
2261 return (VALUE)val;
2262 }
2263 else {
2264 return Qnil;
2265 }
2266}
2267
2268bool
2269rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
2270{
2271 if (ractor_local_ref(key, (void **)val)) {
2272 return true;
2273 }
2274 else {
2275 return false;
2276 }
2277}
2278
2279void
2280rb_ractor_local_storage_value_set(rb_ractor_local_key_t key, VALUE val)
2281{
2282 ractor_local_set(key, (void *)val);
2283}
2284
2285void *
2286rb_ractor_local_storage_ptr(rb_ractor_local_key_t key)
2287{
2288 void *ret;
2289 if (ractor_local_ref(key, &ret)) {
2290 return ret;
2291 }
2292 else {
2293 return NULL;
2294 }
2295}
2296
2297void
2298rb_ractor_local_storage_ptr_set(rb_ractor_local_key_t key, void *ptr)
2299{
2300 ractor_local_set(key, ptr);
2301}
2302
2303#define DEFAULT_KEYS_CAPA 0x10
2304
2305void
2306rb_ractor_finish_marking(void)
2307{
2308 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
2309 ruby_xfree(freed_ractor_local_keys.keys[i]);
2310 }
2311 freed_ractor_local_keys.cnt = 0;
2312 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
2313 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
2314 REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, DEFAULT_KEYS_CAPA);
2315 }
2316}
2317
2318static VALUE
2319ractor_local_value(rb_execution_context_t *ec, VALUE self, VALUE sym)
2320{
2321 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2322 ID id = rb_check_id(&sym);
2323 struct rb_id_table *tbl = cr->idkey_local_storage;
2324 VALUE val;
2325
2326 if (id && tbl && rb_id_table_lookup(tbl, id, &val)) {
2327 return val;
2328 }
2329 else {
2330 return Qnil;
2331 }
2332}
2333
2334static VALUE
2335ractor_local_value_set(rb_execution_context_t *ec, VALUE self, VALUE sym, VALUE val)
2336{
2337 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2338 ID id = SYM2ID(rb_to_symbol(sym));
2339 struct rb_id_table *tbl = cr->idkey_local_storage;
2340
2341 if (tbl == NULL) {
2342 tbl = cr->idkey_local_storage = rb_id_table_create(2);
2343 }
2344 rb_id_table_insert(tbl, id, val);
2345 return val;
2346}
2347
2348 struct ractor_local_storage_store_data {
2349 rb_execution_context_t *ec;
2350 struct rb_id_table *tbl;
2351 ID id;
2352 VALUE sym;
2353};
2354
2355static VALUE
2356ractor_local_value_store_i(VALUE ptr)
2357{
2358 VALUE val;
2359 struct ractor_local_storage_store_data *data = (struct ractor_local_storage_store_data *)ptr;
2360
2361 if (rb_id_table_lookup(data->tbl, data->id, &val)) {
2362 // after synchronization, we found already registered entry
2363 }
2364 else {
2365 val = rb_yield(Qnil);
2366 ractor_local_value_set(data->ec, Qnil, data->sym, val);
2367 }
2368 return val;
2369}
2370
2371static VALUE
2372ractor_local_value_store_if_absent(rb_execution_context_t *ec, VALUE self, VALUE sym)
2373{
2374 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2375 struct ractor_local_storage_store_data data = {
2376 .ec = ec,
2377 .sym = sym,
2378 .id = SYM2ID(rb_to_symbol(sym)),
2379 .tbl = cr->idkey_local_storage,
2380 };
2381 VALUE val;
2382
2383 if (data.tbl == NULL) {
2384 data.tbl = cr->idkey_local_storage = rb_id_table_create(2);
2385 }
2386 else if (rb_id_table_lookup(data.tbl, data.id, &val)) {
2387 // already set
2388 return val;
2389 }
2390
2391 if (!cr->local_storage_store_lock) {
2392 cr->local_storage_store_lock = rb_mutex_new();
2393 }
2394
2395 return rb_mutex_synchronize(cr->local_storage_store_lock, ractor_local_value_store_i, (VALUE)&data);
2396}
2397
2398// shareable_proc
2399
2400static VALUE
2401ractor_shareable_proc(rb_execution_context_t *ec, VALUE replace_self, bool is_lambda)
2402{
2403 if (!rb_ractor_shareable_p(replace_self)) {
2404 rb_raise(rb_eRactorIsolationError, "self should be shareable: %" PRIsVALUE, replace_self);
2405 }
2406 else {
2407 VALUE proc = is_lambda ? rb_block_lambda() : rb_block_proc();
2408 return rb_proc_ractor_make_shareable(rb_proc_dup(proc), replace_self);
2409 }
2410}
2411
2412// Ractor#require
2413
2414 struct cross_ractor_require {
2415 VALUE port;
2416 bool raised;
2417
2418 union {
2419 struct {
2420 VALUE feature;
2421 } require;
2422
2423 struct {
2424 VALUE module;
2425 ID name;
2426 } autoload;
2427 } as;
2428
2429 bool silent;
2430};
2431
2432RUBY_REFERENCES(cross_ractor_require_refs) = {
2433 RUBY_REF_EDGE(struct cross_ractor_require, port),
2434 RUBY_REF_EDGE(struct cross_ractor_require, as.require.feature),
2435 RUBY_REF_END
2436};
2437
2438static const rb_data_type_t cross_ractor_require_data_type = {
2439 "ractor/cross_ractor_require",
2440 {
2441 RUBY_REFS_LIST_PTR(cross_ractor_require_refs),
2442 RUBY_DEFAULT_FREE,
2443 NULL, // memsize
2444 NULL, // compact
2445 },
2446 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_DECL_MARKING | RUBY_TYPED_EMBEDDABLE
2447};
2448
2449static VALUE
2450require_body(VALUE crr_obj)
2451{
2452 struct cross_ractor_require *crr;
2453 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2454 VALUE feature = crr->as.require.feature;
2455
2456 ID require;
2457 CONST_ID(require, "require");
2458
2459 if (crr->silent) {
2460 int rb_require_internal_silent(VALUE fname);
2461 return INT2NUM(rb_require_internal_silent(feature));
2462 }
2463 else {
2464 return rb_funcallv(Qnil, require, 1, &feature);
2465 }
2466}
2467
2468static VALUE
2469require_rescue(VALUE crr_obj, VALUE errinfo)
2470{
2471 struct cross_ractor_require *crr;
2472 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2473 crr->raised = true;
2474 return errinfo;
2475}
2476
2477static VALUE
2478require_result_send_body(VALUE ary)
2479{
2480 VALUE port = RARRAY_AREF(ary, 0);
2481 VALUE results = RARRAY_AREF(ary, 1);
2482
2483 rb_execution_context_t *ec = GET_EC();
2484
2485 ractor_port_send(ec, port, results, Qfalse);
2486 return Qnil;
2487}
2488
2489static VALUE
2490require_result_send_resuce(VALUE port, VALUE errinfo)
2491{
2492 // TODO: need rescue?
2493 ractor_port_send(GET_EC(), port, errinfo, Qfalse);
2494 return Qnil;
2495}
2496
2497static VALUE
2498ractor_require_protect(VALUE crr_obj, VALUE (*func)(VALUE))
2499{
2500 struct cross_ractor_require *crr;
2501 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2502
2503 const bool silent = crr->silent;
2504
2505 VALUE debug, errinfo;
2506 if (silent) {
2507 debug = ruby_debug;
2508 errinfo = rb_errinfo();
2509 }
2510
2511 // get normal result or raised exception (with crr->raised == true)
2512 VALUE result = rb_rescue2(func, crr_obj, require_rescue, crr_obj, rb_eException, 0);
2513
2514 if (silent) {
2515 ruby_debug = debug;
2516 rb_set_errinfo(errinfo);
2517 }
2518
2519 rb_rescue2(require_result_send_body,
2520 // [port, [result, raised]]
2521 rb_ary_new_from_args(2, crr->port, rb_ary_new_from_args(2, result, crr->raised ? Qtrue : Qfalse)),
2522 require_result_send_resuce, rb_eException, crr->port);
2523
2524 RB_GC_GUARD(crr_obj);
2525 return Qnil;
2526}
2527
2528static VALUE
2529ractor_require_func(void *crr_obj)
2530{
2531 return ractor_require_protect((VALUE)crr_obj, require_body);
2532}
2533
2534 VALUE
2535rb_ractor_require(VALUE feature, bool silent)
2536{
2537 // We're about to block on the main ractor, so if we're holding the global lock we'll deadlock.
2538 ASSERT_vm_unlocking();
2539
2540 struct cross_ractor_require *crr;
2541 VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2542 RB_OBJ_SET_SHAREABLE(crr_obj); // TODO: internal data?
2543
2544 // Convert feature to proper file path and make it shareable as fstring
2545 RB_OBJ_WRITE(crr_obj, &crr->as.require.feature, rb_fstring(FilePathValue(feature)));
2546 RB_OBJ_WRITE(crr_obj, &crr->port, rb_ractor_make_shareable(ractor_port_new(GET_RACTOR())));
2547 crr->raised = false;
2548 crr->silent = silent;
2549
2550 rb_execution_context_t *ec = GET_EC();
2551 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
2552 rb_ractor_interrupt_exec(main_r, ractor_require_func, (void *)crr_obj, rb_interrupt_exec_flag_value_data);
2553
2554 // wait for require done
2555 VALUE results = ractor_port_receive(ec, crr->port);
2556 ractor_port_close(ec, crr->port);
2557
2558 VALUE exc = rb_ary_pop(results);
2559 VALUE result = rb_ary_pop(results);
2560 RB_GC_GUARD(crr_obj);
2561
2562 if (RTEST(exc)) {
2563 rb_exc_raise(result);
2564 }
2565 else {
2566 return result;
2567 }
2568}
2569
2570static VALUE
2571ractor_require(rb_execution_context_t *ec, VALUE self, VALUE feature)
2572{
2573 return rb_ractor_require(feature, false);
2574}
2575
2576static VALUE
2577autoload_load_body(VALUE crr_obj)
2578{
2579 struct cross_ractor_require *crr;
2580 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2581 return rb_autoload_load(crr->as.autoload.module, crr->as.autoload.name);
2582}
2583
2584static VALUE
2585ractor_autoload_load_func(void *crr_obj)
2586{
2587 return ractor_require_protect((VALUE)crr_obj, autoload_load_body);
2588}
2589
2590 VALUE
2591rb_ractor_autoload_load(VALUE module, ID name)
2592{
2593 struct cross_ractor_require *crr;
2594 VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2595 RB_OBJ_SET_SHAREABLE(crr_obj); // TODO: internal data?
2596
2597 RB_OBJ_WRITE(crr_obj, &crr->as.autoload.module, module);
2598 RB_OBJ_WRITE(crr_obj, &crr->as.autoload.name, name);
2599 RB_OBJ_WRITE(crr_obj, &crr->port, rb_ractor_make_shareable(ractor_port_new(GET_RACTOR())));
2600
2601 rb_execution_context_t *ec = GET_EC();
2602 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
2603 rb_ractor_interrupt_exec(main_r, ractor_autoload_load_func, (void *)crr_obj, rb_interrupt_exec_flag_value_data);
2604
2605 // wait for require done
2606 VALUE results = ractor_port_receive(ec, crr->port);
2607 ractor_port_close(ec, crr->port);
2608
2609 VALUE exc = rb_ary_pop(results);
2610 VALUE result = rb_ary_pop(results);
2611 RB_GC_GUARD(crr_obj);
2612
2613 if (RTEST(exc)) {
2614 rb_exc_raise(result);
2615 }
2616 else {
2617 return result;
2618 }
2619}
2620
2621#include "ractor.rbinc"
RUBY_ASSERT
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
rb_atomic_t
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
Definition atomic.h:69
RUBY_ATOMIC_FETCH_ADD
#define RUBY_ATOMIC_FETCH_ADD(var, val)
Atomically replaces the value pointed by var with the result of addition of val to the old value of v...
Definition atomic.h:118
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition cxxanyargs.hpp:670
RB_OBJ_FROZEN_RAW
static VALUE RB_OBJ_FROZEN_RAW(VALUE obj)
This is an implementation detail of RB_OBJ_FROZEN().
Definition fl_type.h:877
RUBY_FL_PROMOTED
@ RUBY_FL_PROMOTED
Ruby objects are "generational".
Definition fl_type.h:217
RUBY_FL_SHAREABLE
@ RUBY_FL_SHAREABLE
This flag has something to do with Ractor.
Definition fl_type.h:280
rb_define_class
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition class.c:1477
rb_define_class_under
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1508
T_COMPLEX
#define T_COMPLEX
Old name of RUBY_T_COMPLEX.
Definition value_type.h:59
TYPE
#define TYPE(_)
Old name of rb_type.
Definition value_type.h:108
T_FILE
#define T_FILE
Old name of RUBY_T_FILE.
Definition value_type.h:62
FL_PROMOTED
#define FL_PROMOTED
Old name of RUBY_FL_PROMOTED.
Definition fl_type.h:60
REALLOC_N
#define REALLOC_N
Old name of RB_REALLOC_N.
Definition memory.h:403
ALLOC
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
T_STRING
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
Qundef
#define Qundef
Old name of RUBY_Qundef.
Definition special_consts.h:62
T_FLOAT
#define T_FLOAT
Old name of RUBY_T_FLOAT.
Definition value_type.h:64
T_IMEMO
#define T_IMEMO
Old name of RUBY_T_IMEMO.
Definition value_type.h:67
T_BIGNUM
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
Definition value_type.h:57
SPECIAL_CONST_P
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
Definition special_consts.h:56
T_STRUCT
#define T_STRUCT
Old name of RUBY_T_STRUCT.
Definition value_type.h:79
SYM2ID
#define SYM2ID
Old name of RB_SYM2ID.
Definition symbol.h:45
T_DATA
#define T_DATA
Old name of RUBY_T_DATA.
Definition value_type.h:60
FL_SHAREABLE
#define FL_SHAREABLE
Old name of RUBY_FL_SHAREABLE.
Definition fl_type.h:63
T_MODULE
#define T_MODULE
Old name of RUBY_T_MODULE.
Definition value_type.h:70
T_RATIONAL
#define T_RATIONAL
Old name of RUBY_T_RATIONAL.
Definition value_type.h:76
T_ICLASS
#define T_ICLASS
Old name of RUBY_T_ICLASS.
Definition value_type.h:66
T_HASH
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
Qtrue
#define Qtrue
Old name of RUBY_Qtrue.
Definition special_consts.h:61
INT2NUM
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
Qnil
#define Qnil
Old name of RUBY_Qnil.
Definition special_consts.h:60
Qfalse
#define Qfalse
Old name of RUBY_Qfalse.
Definition special_consts.h:59
T_ARRAY
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
T_OBJECT
#define T_OBJECT
Old name of RUBY_T_OBJECT.
Definition value_type.h:75
NIL_P
#define NIL_P
Old name of RB_NIL_P.
Definition special_consts.h:55
FL_WB_PROTECTED
#define FL_WB_PROTECTED
Old name of RUBY_FL_WB_PROTECTED.
Definition fl_type.h:59
T_SYMBOL
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
Definition value_type.h:80
T_MATCH
#define T_MATCH
Old name of RUBY_T_MATCH.
Definition value_type.h:69
T_CLASS
#define T_CLASS
Old name of RUBY_T_CLASS.
Definition value_type.h:58
BUILTIN_TYPE
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition value_type.h:85
FL_FREEZE
#define FL_FREEZE
Old name of RUBY_FL_FREEZE.
Definition fl_type.h:66
CONST_ID
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
FL_SET_RAW
#define FL_SET_RAW
Old name of RB_FL_SET_RAW.
Definition fl_type.h:129
T_REGEXP
#define T_REGEXP
Old name of RUBY_T_REGEXP.
Definition value_type.h:77
ruby_debug
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
Definition error.h:486
rb_exc_raise
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:664
rb_typeddata_is_kind_of
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Checks if the given object is of given kind.
Definition error.c:1381
rb_eRuntimeError
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1429
rb_eStopIteration
VALUE rb_eStopIteration
StopIteration exception.
Definition enumerator.c:181
rb_exc_new_str
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of C's.
Definition error.c:1482
rb_eException
VALUE rb_eException
Mother of all exceptions.
Definition error.c:1423
rb_errinfo
VALUE rb_errinfo(void)
This is the same as $! in Ruby.
Definition eval.c:2056
rb_cRactor
VALUE rb_cRactor
Ractor class.
Definition ractor.c:24
rb_stdin
VALUE rb_stdin
STDIN constant.
Definition io.c:201
rb_stderr
VALUE rb_stderr
STDERR constant.
Definition io.c:201
rb_class_of
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
Definition globals.h:174
rb_cBasicObject
VALUE rb_cBasicObject
BasicObject class.
Definition object.c:59
rb_obj_clone
VALUE rb_obj_clone(VALUE obj)
Produces a shallow copy of the given object.
Definition object.c:527
rb_stdout
VALUE rb_stdout
STDOUT constant.
Definition io.c:201
RB_OBJ_WRITTEN
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:615
RB_OBJ_WRITE
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:603
rb_funcall
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1117
RGENGC_WB_PROTECTED_STRUCT
#define RGENGC_WB_PROTECTED_STRUCT
This is a compile-time flag to enable/disable write barrier for struct RStruct.
Definition gc.h:468
RGENGC_WB_PROTECTED_STRING
#define RGENGC_WB_PROTECTED_STRING
This is a compile-time flag to enable/disable write barrier for struct RString.
Definition gc.h:479
RGENGC_WB_PROTECTED_HASH
#define RGENGC_WB_PROTECTED_HASH
This is a compile-time flag to enable/disable write barrier for struct RHash.
Definition gc.h:457
RGENGC_WB_PROTECTED_MATCH
#define RGENGC_WB_PROTECTED_MATCH
This is a compile-time flag to enable/disable write barrier for struct RMatch.
Definition gc.h:512
RGENGC_WB_PROTECTED_ARRAY
#define RGENGC_WB_PROTECTED_ARRAY
This is a compile-time flag to enable/disable write barrier for struct RArray.
Definition gc.h:446
RGENGC_WB_PROTECTED_COMPLEX
#define RGENGC_WB_PROTECTED_COMPLEX
This is a compile-time flag to enable/disable write barrier for struct RComplex.
Definition gc.h:545
RGENGC_WB_PROTECTED_FLOAT
#define RGENGC_WB_PROTECTED_FLOAT
This is a compile-time flag to enable/disable write barrier for struct RFloat.
Definition gc.h:534
RGENGC_WB_PROTECTED_RATIONAL
#define RGENGC_WB_PROTECTED_RATIONAL
This is a compile-time flag to enable/disable write barrier for struct RRational.
Definition gc.h:556
RGENGC_WB_PROTECTED_REGEXP
#define RGENGC_WB_PROTECTED_REGEXP
This is a compile-time flag to enable/disable write barrier for struct RRegexp.
Definition gc.h:501
RGENGC_WB_PROTECTED_OBJECT
#define RGENGC_WB_PROTECTED_OBJECT
This is a compile-time flag to enable/disable write barrier for struct RObject.
Definition gc.h:490
rb_ary_new
VALUE rb_ary_new(void)
Allocates a new, empty array.
rb_ary_pop
VALUE rb_ary_pop(VALUE ary)
Destructively deletes an element from the end of the passed array and returns what was deleted.
rb_ary_push
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
rb_ary_entry
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
rb_block_proc
VALUE rb_block_proc(void)
Constructs a Proc object from implicitly passed components.
Definition proc.c:983
rb_block_lambda
VALUE rb_block_lambda(void)
Identical to rb_proc_new(), except it returns a lambda.
Definition proc.c:1002
rb_obj_is_proc
VALUE rb_obj_is_proc(VALUE recv)
Queries if the given object is a proc.
Definition proc.c:120
rb_str_new_frozen
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition string.c:1518
rb_mutex_new
VALUE rb_mutex_new(void)
Creates a mutex.
Definition thread_sync.c:184
rb_mutex_synchronize
VALUE rb_mutex_synchronize(VALUE mutex, VALUE(*func)(VALUE arg), VALUE arg)
Obtains the lock, runs the passed function, and releases the lock when it completes.
Definition thread_sync.c:649
rb_autoload_load
VALUE rb_autoload_load(VALUE space, ID name)
Kicks the autoload procedure as if it was "touched".
Definition variable.c:3335
rb_undef_alloc_func
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1711
rb_check_id
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition symbol.c:1133
rb_to_symbol
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
Definition string.c:12674
len
int len
Length of the buffer.
Definition io.h:8
ractor.h
rb_ractor_local_storage_type_free
const struct rb_ractor_local_storage_type rb_ractor_local_storage_type_free
A type of ractor-local storage that destructs itself using ruby_xfree.
Definition ractor.c:2179
rb_ractor_make_shareable_copy
VALUE rb_ractor_make_shareable_copy(VALUE obj)
Identical to rb_ractor_make_shareable(), except it returns a (deep) copy of the passed one instead of...
Definition ractor.c:1556
rb_ractor_local_key_t
struct rb_ractor_local_key_struct * rb_ractor_local_key_t
(Opaque) struct that holds a ractor-local storage key.
Definition ractor.h:42
rb_ractor_local_storage_ptr
void * rb_ractor_local_storage_ptr(rb_ractor_local_key_t key)
Identical to rb_ractor_local_storage_value() except the return type.
Definition ractor.c:2286
rb_ractor_local_storage_ptr_set
void rb_ractor_local_storage_ptr_set(rb_ractor_local_key_t key, void *ptr)
Identical to rb_ractor_local_storage_value_set() except the parameter type.
Definition ractor.c:2298
rb_ractor_local_storage_ptr_newkey
rb_ractor_local_key_t rb_ractor_local_storage_ptr_newkey(const struct rb_ractor_local_storage_type *type)
Extended version of rb_ractor_local_storage_value_newkey().
Definition ractor.c:2190
rb_ractor_stderr
VALUE rb_ractor_stderr(void)
Queries the standard error of the current Ractor that is calling this function.
Definition ractor.c:1105
rb_ractor_stdin
VALUE rb_ractor_stdin(void)
Queries the standard input of the current Ractor that is calling this function.
Definition ractor.c:1081
rb_ractor_shareable_p
static bool rb_ractor_shareable_p(VALUE obj)
Queries if multiple Ractors can share the passed object or not.
Definition ractor.h:249
rb_ractor_stderr_set
void rb_ractor_stderr_set(VALUE io)
Assigns an IO to the standard error of the Ractor that is calling this function.
Definition ractor.c:1141
rb_ractor_local_storage_value_set
void rb_ractor_local_storage_value_set(rb_ractor_local_key_t key, VALUE val)
Associates the passed value to the passed key.
Definition ractor.c:2280
rb_ractor_local_storage_value_lookup
bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
Queries the key.
Definition ractor.c:2269
rb_ractor_stdout
VALUE rb_ractor_stdout(void)
Queries the standard output of the current Ractor that is calling this function.
Definition ractor.c:1093
RB_OBJ_SHAREABLE_P
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
Definition ractor.h:235
rb_ractor_make_shareable
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
Definition ractor.c:1547
rb_ractor_local_storage_value_newkey
rb_ractor_local_key_t rb_ractor_local_storage_value_newkey(void)
Issues a new key.
Definition ractor.c:2199
rb_ractor_stdout_set
void rb_ractor_stdout_set(VALUE io)
Assigns an IO to the standard output of the Ractor that is calling this function.
Definition ractor.c:1129
rb_ractor_stdin_set
void rb_ractor_stdin_set(VALUE io)
Assigns an IO to the standard input of the Ractor that is calling this function.
Definition ractor.c:1117
rb_ractor_local_storage_value
VALUE rb_ractor_local_storage_value(rb_ractor_local_key_t key)
Queries the key.
Definition ractor.c:2257
re.h
thread.h
rb_yield
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1372
rb_block_call_func_t
rb_block_call_func * rb_block_call_func_t
Shorthand type that represents an iterator-written-in-C function pointer.
Definition iterator.h:88
MEMZERO
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition memory.h:360
RB_GC_GUARD
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
ruby::backward::cxxanyargs::rb_proc_new
VALUE rb_proc_new(type *q, VALUE w)
Creates a rb_cProc instance.
Definition cxxanyargs.hpp:394
ruby::backward::cxxanyargs::type
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition cxxanyargs.hpp:56
ruby::backward::cxxanyargs::st_foreach
int st_foreach(st_table *q, int_type *w, st_data_t e)
Iteration over the given table.
Definition cxxanyargs.hpp:432
ruby::backward::cxxanyargs::rb_hash_foreach
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
Definition cxxanyargs.hpp:482
ruby::backward::cxxanyargs::rb_ivar_foreach
void rb_ivar_foreach(VALUE q, int_type *w, VALUE e)
Iteration over each instance variable of the object.
Definition cxxanyargs.hpp:498
ruby::backward::cxxanyargs::rb_rescue2
VALUE rb_rescue2(type *q, VALUE w, type *e, VALUE r,...)
An equivalent of rescue clause.
Definition cxxanyargs.hpp:284
RARRAY_LENINT
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:281
RARRAY_ASET
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:386
RARRAY_AREF
#define RARRAY_AREF(a, i)
Definition rarray.h:403
RBASIC
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
DATA_PTR
#define DATA_PTR(obj)
Convenient getter macro.
Definition rdata.h:67
RUBY_DEFAULT_FREE
#define RUBY_DEFAULT_FREE
This is a value you can set to RData::dfree.
Definition rdata.h:78
RHASH_SET_IFNONE
#define RHASH_SET_IFNONE(h, ifnone)
Destructively updates the default value of the hash.
Definition rhash.h:92
RHASH_IFNONE
#define RHASH_IFNONE(h)
Definition rhash.h:59
RMATCH
#define RMATCH(obj)
Convenient casting macro.
Definition rmatch.h:37
ROBJECT_FIELDS
static VALUE * ROBJECT_FIELDS(VALUE obj)
Queries the instance variables.
Definition robject.h:128
StringValueCStr
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
RTYPEDDATA_P
static bool RTYPEDDATA_P(VALUE obj)
Checks whether the passed object is RTypedData or RData.
Definition rtypeddata.h:575
TypedData_Get_Struct
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:649
TypedData_Wrap_Struct
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:461
rb_data_type_t
struct rb_data_type_struct rb_data_type_t
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:205
TypedData_Make_Struct
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:508
RTYPEDDATA_TYPE
static const struct rb_data_type_struct * RTYPEDDATA_TYPE(VALUE obj)
Queries for the type of given object.
Definition rtypeddata.h:598
ruby.h
FilePathValue
#define FilePathValue(v)
Ensures that the parameter object is a path.
Definition ruby.h:90
RB_SPECIAL_CONST_P
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
Definition special_consts.h:327
RTEST
#define RTEST
This is an old name of RB_TEST.
Definition special_consts.h:51
RBasic
Ruby object's base components.
Definition rbasic.h:69
cross_ractor_require
Definition ractor.c:2414
freed_ractor_local_keys_struct
Definition ractor.c:2103
obj_traverse_callback_data
Definition ractor.c:1229
obj_traverse_data
Definition ractor.c:1220
obj_traverse_replace_callback_data
Definition ractor.c:1673
obj_traverse_replace_data
Definition ractor.c:1662
ractor_local_storage_store_data
Definition ractor.c:2348
rb_id_table
Definition id_table.c:41
rb_obj_traverse_final_data
Definition ractor.c:1404
rb_ractor_local_key_struct
Definition ractor.c:2098
rb_ractor_local_storage_type
Type that defines a ractor-local storage.
Definition ractor.h:21
rb_ractor_local_storage_type::free
void(* free)(void *ptr)
A function to destruct a ractor-local storage.
Definition ractor.h:37
rb_ractor_local_storage_type::mark
void(* mark)(void *ptr)
A function to mark a ractor-local storage.
Definition ractor.h:29
rb_thread_sched
Definition thread_none.h:16
st_table
Definition st.h:79
thread_native.h
rb_native_mutex_lock
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_lock.
rb_native_mutex_unlock
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_unlock.
rb_native_mutex_destroy
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_destroy.
rb_native_cond_destroy
void rb_native_cond_destroy(rb_nativethread_cond_t *cond)
Destroys the passed condition variable.
rb_native_cond_signal
void rb_native_cond_signal(rb_nativethread_cond_t *cond)
Signals a condition variable.
ID
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
VALUE
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
RB_BUILTIN_TYPE
static enum ruby_value_type RB_BUILTIN_TYPE(VALUE obj)
Queries the type of the object.
Definition value_type.h:182
RB_TYPE_P
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
RUBY_T_MASK
@ RUBY_T_MASK
Bitmask of ruby_value_type.
Definition value_type.h:145