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