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