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