Ruby 4.1.0dev (2026-04-27 revision 1de3deb5cd1b9a7a37fe01c401eb6e1dc4e49de7)
ractor.c (1de3deb5cd1b9a7a37fe01c401eb6e1dc4e49de7)
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 rb_obj_hide(data->rec_hash);
1274 data->rec = RHASH_ST_TABLE(data->rec_hash);
1275 }
1276 return data->rec;
1277}
1278
1279static int
1280obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr)
1281{
1283
1284 if (obj_traverse_i(val, d->data)) {
1285 d->stop = true;
1286 return ST_STOP;
1287 }
1288
1289 return ST_CONTINUE;
1290}
1291
1292static int
1293obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
1294{
1295 if (RB_SPECIAL_CONST_P(obj)) return 0;
1296
1297 switch (data->enter_func(obj)) {
1298 case traverse_cont: break;
1299 case traverse_skip: return 0; // skip children
1300 case traverse_stop: return 1; // stop search
1301 }
1302
1303 if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
1304 // already traversed
1305 return 0;
1306 }
1307 RB_OBJ_WRITTEN(data->rec_hash, Qundef, obj);
1308
1309 struct obj_traverse_callback_data d = {
1310 .stop = false,
1311 .data = data,
1312 };
1313 rb_ivar_foreach(obj, obj_traverse_ivar_foreach_i, (st_data_t)&d);
1314 if (d.stop) return 1;
1315
1316 switch (BUILTIN_TYPE(obj)) {
1317 // no child node
1318 case T_STRING:
1319 case T_FLOAT:
1320 case T_BIGNUM:
1321 case T_REGEXP:
1322 case T_FILE:
1323 case T_SYMBOL:
1324 break;
1325
1326 case T_OBJECT:
1327 /* Instance variables already traversed. */
1328 break;
1329
1330 case T_ARRAY:
1331 {
1332 rb_ary_cancel_sharing(obj);
1333
1334 for (int i = 0; i < RARRAY_LENINT(obj); i++) {
1335 VALUE e = rb_ary_entry(obj, i);
1336 if (obj_traverse_i(e, data)) return 1;
1337 }
1338 }
1339 break;
1340
1341 case T_HASH:
1342 {
1343 if (obj_traverse_i(RHASH_IFNONE(obj), data)) return 1;
1344
1345 struct obj_traverse_callback_data d = {
1346 .stop = false,
1347 .data = data,
1348 };
1349 rb_hash_foreach(obj, obj_hash_traverse_i, (VALUE)&d);
1350 if (d.stop) return 1;
1351 }
1352 break;
1353
1354 case T_STRUCT:
1355 {
1356 long len = RSTRUCT_LEN_RAW(obj);
1357 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
1358
1359 for (long i=0; i<len; i++) {
1360 if (obj_traverse_i(ptr[i], data)) return 1;
1361 }
1362 }
1363 break;
1364
1365 case T_MATCH:
1366 if (obj_traverse_i(RMATCH(obj)->str, data)) return 1;
1367 break;
1368
1369 case T_RATIONAL:
1370 if (obj_traverse_i(RRATIONAL(obj)->num, data)) return 1;
1371 if (obj_traverse_i(RRATIONAL(obj)->den, data)) return 1;
1372 break;
1373 case T_COMPLEX:
1374 if (obj_traverse_i(RCOMPLEX(obj)->real, data)) return 1;
1375 if (obj_traverse_i(RCOMPLEX(obj)->imag, data)) return 1;
1376 break;
1377
1378 case T_DATA:
1379 case T_IMEMO:
1380 {
1381 struct obj_traverse_callback_data d = {
1382 .stop = false,
1383 .data = data,
1384 };
1385 RB_VM_LOCKING_NO_BARRIER() {
1386 rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
1387 }
1388 if (d.stop) return 1;
1389 }
1390 break;
1391
1392 // unreachable
1393 case T_CLASS:
1394 case T_MODULE:
1395 case T_ICLASS:
1396 default:
1397 rp(obj);
1398 rb_bug("unreachable");
1399 }
1400
1401 if (data->leave_func(obj) == traverse_stop) {
1402 return 1;
1403 }
1404 else {
1405 return 0;
1406 }
1407}
1408
1410 rb_obj_traverse_final_func final_func;
1411 int stopped;
1412};
1413
1414static int
1415obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
1416{
1417 struct rb_obj_traverse_final_data *data = (void *)arg;
1418 if (data->final_func(key)) {
1419 data->stopped = 1;
1420 return ST_STOP;
1421 }
1422 return ST_CONTINUE;
1423}
1424
1425// 0: traverse all
1426// 1: stopped
1427static int
1428rb_obj_traverse(VALUE obj,
1429 rb_obj_traverse_enter_func enter_func,
1430 rb_obj_traverse_leave_func leave_func,
1431 rb_obj_traverse_final_func final_func)
1432{
1433 struct obj_traverse_data data = {
1434 .enter_func = enter_func,
1435 .leave_func = leave_func,
1436 .rec = NULL,
1437 };
1438
1439 if (obj_traverse_i(obj, &data)) return 1;
1440 if (final_func && data.rec) {
1441 struct rb_obj_traverse_final_data f = {final_func, 0};
1442 st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
1443 return f.stopped;
1444 }
1445 return 0;
1446}
1447
1448static int
1449allow_frozen_shareable_p(VALUE obj)
1450{
1451 if (!RB_TYPE_P(obj, T_DATA)) {
1452 return true;
1453 }
1454 else if (RTYPEDDATA_P(obj)) {
1455 const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
1456 if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
1457 return true;
1458 }
1459 }
1460
1461 return false;
1462}
1463
1464static enum obj_traverse_iterator_result
1465make_shareable_check_shareable_freeze(VALUE obj, enum obj_traverse_iterator_result result)
1466{
1467 if (!RB_OBJ_FROZEN_RAW(obj)) {
1468 rb_funcall(obj, idFreeze, 0);
1469
1470 if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
1471 rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
1472 }
1473
1474 if (RB_OBJ_SHAREABLE_P(obj)) {
1475 return traverse_skip;
1476 }
1477 }
1478
1479 return result;
1480}
1481
1482static int obj_refer_only_shareables_p(VALUE obj);
1483
1484static enum obj_traverse_iterator_result
1485make_shareable_check_shareable(VALUE obj)
1486{
1487 VM_ASSERT(!SPECIAL_CONST_P(obj));
1488
1489 if (rb_ractor_shareable_p(obj)) {
1490 return traverse_skip;
1491 }
1492 else if (!allow_frozen_shareable_p(obj)) {
1493 VM_ASSERT(RB_TYPE_P(obj, T_DATA));
1494 const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
1495
1496 if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC) {
1497 if (obj_refer_only_shareables_p(obj)) {
1498 make_shareable_check_shareable_freeze(obj, traverse_skip);
1499 RB_OBJ_SET_SHAREABLE(obj);
1500 return traverse_skip;
1501 }
1502 else {
1503 rb_raise(rb_eRactorError,
1504 "can not make shareable object for %+"PRIsVALUE" because it refers unshareable objects", obj);
1505 }
1506 }
1507 else if (rb_obj_is_proc(obj)) {
1508 rb_proc_ractor_make_shareable(obj, Qundef);
1509 return traverse_cont;
1510 }
1511 else {
1512 rb_raise(rb_eRactorError, "can not make shareable object for %+"PRIsVALUE, obj);
1513 }
1514 }
1515
1516 switch (TYPE(obj)) {
1517 case T_IMEMO:
1518 return traverse_skip;
1519 case T_OBJECT:
1520 {
1521 // If a T_OBJECT is shared and has no free capacity, we can't safely store the object_id inline,
1522 // as it would require to move the object content into an external buffer.
1523 // This is only a problem for T_OBJECT, given other types have external fields and can do RCU.
1524 // To avoid this issue, we proactively create the object_id.
1525 shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
1526 attr_index_t capacity = RSHAPE_CAPACITY(shape_id);
1527 attr_index_t free_capacity = capacity - RSHAPE_LEN(shape_id);
1528 if (!rb_shape_has_object_id(shape_id) && capacity && !free_capacity) {
1529 rb_obj_id(obj);
1530 }
1531 }
1532 break;
1533 default:
1534 break;
1535 }
1536
1537 return make_shareable_check_shareable_freeze(obj, traverse_cont);
1538}
1539
1540static enum obj_traverse_iterator_result
1541mark_shareable(VALUE obj)
1542{
1543 if (RB_TYPE_P(obj, T_STRING)) {
1544 rb_str_make_independent(obj);
1545 }
1546
1547 rb_obj_set_shareable_no_assert(obj);
1548 return traverse_cont;
1549}
1550
1551VALUE
1553{
1554 rb_obj_traverse(obj,
1555 make_shareable_check_shareable,
1556 null_leave, mark_shareable);
1557 return obj;
1558}
1559
1560VALUE
1562{
1563 VALUE copy = ractor_copy(obj);
1564 return rb_ractor_make_shareable(copy);
1565}
1566
1567VALUE
1568rb_ractor_ensure_shareable(VALUE obj, VALUE name)
1569{
1570 if (!rb_ractor_shareable_p(obj)) {
1571 VALUE message = rb_sprintf("cannot assign unshareable object to %"PRIsVALUE,
1572 name);
1573 rb_exc_raise(rb_exc_new_str(rb_eRactorIsolationError, message));
1574 }
1575 return obj;
1576}
1577
1578void
1579rb_ractor_ensure_main_ractor(const char *msg)
1580{
1581 if (!rb_ractor_main_p()) {
1582 rb_raise(rb_eRactorIsolationError, "%s", msg);
1583 }
1584}
1585
1586static enum obj_traverse_iterator_result
1587shareable_p_enter(VALUE obj)
1588{
1589 if (RB_OBJ_SHAREABLE_P(obj)) {
1590 return traverse_skip;
1591 }
1592 else if (RB_TYPE_P(obj, T_CLASS) ||
1593 RB_TYPE_P(obj, T_MODULE) ||
1594 RB_TYPE_P(obj, T_ICLASS)) {
1595 // TODO: remove it
1596 mark_shareable(obj);
1597 return traverse_skip;
1598 }
1599 else if (RB_OBJ_FROZEN_RAW(obj) &&
1600 allow_frozen_shareable_p(obj)) {
1601 return traverse_cont;
1602 }
1603
1604 return traverse_stop; // fail
1605}
1606
1607bool
1608rb_ractor_shareable_p_continue(VALUE obj)
1609{
1610 if (rb_obj_traverse(obj,
1611 shareable_p_enter, null_leave,
1612 mark_shareable)) {
1613 return false;
1614 }
1615 else {
1616 return true;
1617 }
1618}
1619
1620#if RACTOR_CHECK_MODE > 0
1621void
1622rb_ractor_setup_belonging(VALUE obj)
1623{
1624 rb_ractor_setup_belonging_to(obj, rb_ractor_current_id());
1625}
1626
1627static enum obj_traverse_iterator_result
1628reset_belonging_enter(VALUE obj)
1629{
1630 if (rb_ractor_shareable_p(obj)) {
1631 return traverse_skip;
1632 }
1633 else {
1634 rb_ractor_setup_belonging(obj);
1635 return traverse_cont;
1636 }
1637}
1638#endif
1639
1640static enum obj_traverse_iterator_result
1641null_leave(VALUE obj)
1642{
1643 return traverse_cont;
1644}
1645
1646static VALUE
1647ractor_reset_belonging(VALUE obj)
1648{
1649#if RACTOR_CHECK_MODE > 0
1650 rb_obj_traverse(obj, reset_belonging_enter, null_leave, NULL);
1651#endif
1652 return obj;
1653}
1654
1655
1657
1658// 2: stop search
1659// 1: skip child
1660// 0: continue
1661
1663static int obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data);
1664typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_enter_func)(VALUE obj, struct obj_traverse_replace_data *data);
1665typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_leave_func)(VALUE obj, struct obj_traverse_replace_data *data);
1666
1668 rb_obj_traverse_replace_enter_func enter_func;
1669 rb_obj_traverse_replace_leave_func leave_func;
1670
1671 st_table *rec;
1672 VALUE rec_hash;
1673
1674 VALUE replacement;
1675 bool move;
1676};
1677
1679 bool stop;
1680 VALUE src;
1681 struct obj_traverse_replace_data *data;
1682};
1683
1684static int
1685obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp, int error)
1686{
1687 return ST_REPLACE;
1688}
1689
1690static int
1691obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr, int exists)
1692{
1694 struct obj_traverse_replace_data *data = d->data;
1695
1696 if (obj_traverse_replace_i(*key, data)) {
1697 d->stop = true;
1698 return ST_STOP;
1699 }
1700 else if (*key != data->replacement) {
1701 VALUE v = *key = data->replacement;
1702 RB_OBJ_WRITTEN(d->src, Qundef, v);
1703 }
1704
1705 if (obj_traverse_replace_i(*val, data)) {
1706 d->stop = true;
1707 return ST_STOP;
1708 }
1709 else if (*val != data->replacement) {
1710 VALUE v = *val = data->replacement;
1711 RB_OBJ_WRITTEN(d->src, Qundef, v);
1712 }
1713
1714 return ST_CONTINUE;
1715}
1716
1717static int
1718obj_iv_hash_traverse_replace_foreach_i(st_data_t _key, st_data_t _val, st_data_t _data, int _x)
1719{
1720 return ST_REPLACE;
1721}
1722
1723static int
1724obj_iv_hash_traverse_replace_i(st_data_t * _key, st_data_t * val, st_data_t ptr, int exists)
1725{
1727 struct obj_traverse_replace_data *data = d->data;
1728
1729 if (obj_traverse_replace_i(*(VALUE *)val, data)) {
1730 d->stop = true;
1731 return ST_STOP;
1732 }
1733 else if (*(VALUE *)val != data->replacement) {
1734 VALUE v = *(VALUE *)val = data->replacement;
1735 RB_OBJ_WRITTEN(d->src, Qundef, v);
1736 }
1737
1738 return ST_CONTINUE;
1739}
1740
1741static struct st_table *
1742obj_traverse_replace_rec(struct obj_traverse_replace_data *data)
1743{
1744 if (UNLIKELY(!data->rec)) {
1745 data->rec_hash = rb_ident_hash_new();
1746 data->rec = RHASH_ST_TABLE(data->rec_hash);
1747 }
1748 return data->rec;
1749}
1750
1751static void
1752obj_refer_only_shareables_p_i(VALUE obj, void *ptr)
1753{
1754 int *pcnt = (int *)ptr;
1755
1756 if (!rb_ractor_shareable_p(obj)) {
1757 ++*pcnt;
1758 }
1759}
1760
1761static int
1762obj_refer_only_shareables_p(VALUE obj)
1763{
1764 int cnt = 0;
1765 RB_VM_LOCKING_NO_BARRIER() {
1766 rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
1767 }
1768 return cnt == 0;
1769}
1770
1771static int
1772obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
1773{
1774 st_data_t replacement;
1775
1776 if (RB_SPECIAL_CONST_P(obj)) {
1777 data->replacement = obj;
1778 return 0;
1779 }
1780
1781 switch (data->enter_func(obj, data)) {
1782 case traverse_cont: break;
1783 case traverse_skip: return 0; // skip children
1784 case traverse_stop: return 1; // stop search
1785 }
1786
1787 replacement = (st_data_t)data->replacement;
1788
1789 if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, &replacement))) {
1790 data->replacement = (VALUE)replacement;
1791 return 0;
1792 }
1793 else {
1794 st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, replacement);
1795 RB_OBJ_WRITTEN(data->rec_hash, Qundef, obj);
1796 RB_OBJ_WRITTEN(data->rec_hash, Qundef, replacement);
1797 }
1798
1799 if (!data->move) {
1800 obj = replacement;
1801 }
1802
1803#define CHECK_AND_REPLACE(parent_obj, v) do { \
1804 VALUE _val = (v); \
1805 if (obj_traverse_replace_i(_val, data)) { return 1; } \
1806 else if (data->replacement != _val) { RB_OBJ_WRITE(parent_obj, &v, data->replacement); } \
1807} while (0)
1808
1809 if (UNLIKELY(rb_obj_gen_fields_p(obj))) {
1810 VALUE fields_obj = rb_obj_fields_no_ractor_check(obj);
1811
1812 if (UNLIKELY(rb_shape_obj_too_complex_p(obj))) {
1814 .stop = false,
1815 .data = data,
1816 .src = fields_obj,
1817 };
1818 rb_st_foreach_with_replace(
1819 rb_imemo_fields_complex_tbl(fields_obj),
1820 obj_iv_hash_traverse_replace_foreach_i,
1821 obj_iv_hash_traverse_replace_i,
1822 (st_data_t)&d
1823 );
1824 if (d.stop) return 1;
1825 }
1826 else {
1827 uint32_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID(obj));
1828 VALUE *fields = rb_imemo_fields_ptr(fields_obj);
1829 for (uint32_t i = 0; i < fields_count; i++) {
1830 CHECK_AND_REPLACE(fields_obj, fields[i]);
1831 }
1832 }
1833 }
1834
1835 switch (BUILTIN_TYPE(obj)) {
1836 // no child node
1837 case T_FLOAT:
1838 case T_BIGNUM:
1839 case T_REGEXP:
1840 case T_FILE:
1841 case T_SYMBOL:
1842 break;
1843 case T_STRING:
1844 rb_str_make_independent(obj);
1845 break;
1846
1847 case T_OBJECT:
1848 {
1849 if (rb_shape_obj_too_complex_p(obj)) {
1851 .stop = false,
1852 .data = data,
1853 .src = obj,
1854 };
1855 rb_st_foreach_with_replace(
1856 ROBJECT_FIELDS_HASH(obj),
1857 obj_iv_hash_traverse_replace_foreach_i,
1858 obj_iv_hash_traverse_replace_i,
1859 (st_data_t)&d
1860 );
1861 if (d.stop) return 1;
1862 }
1863 else {
1864 uint32_t len = ROBJECT_FIELDS_COUNT_NOT_COMPLEX(obj);
1865 VALUE *ptr = ROBJECT_FIELDS(obj);
1866
1867 for (uint32_t i = 0; i < len; i++) {
1868 CHECK_AND_REPLACE(obj, ptr[i]);
1869 }
1870 }
1871 }
1872 break;
1873
1874 case T_ARRAY:
1875 {
1876 rb_ary_cancel_sharing(obj);
1877
1878 for (int i = 0; i < RARRAY_LENINT(obj); i++) {
1879 VALUE e = rb_ary_entry(obj, i);
1880
1881 if (obj_traverse_replace_i(e, data)) {
1882 return 1;
1883 }
1884 else if (e != data->replacement) {
1885 RARRAY_ASET(obj, i, data->replacement);
1886 }
1887 }
1888 RB_GC_GUARD(obj);
1889 }
1890 break;
1891 case T_HASH:
1892 {
1894 .stop = false,
1895 .data = data,
1896 .src = obj,
1897 };
1898 rb_hash_stlike_foreach_with_replace(obj,
1899 obj_hash_traverse_replace_foreach_i,
1900 obj_hash_traverse_replace_i,
1901 (VALUE)&d);
1902 if (d.stop) return 1;
1903 // TODO: rehash here?
1904
1905 VALUE ifnone = RHASH_IFNONE(obj);
1906 if (obj_traverse_replace_i(ifnone, data)) {
1907 return 1;
1908 }
1909 else if (ifnone != data->replacement) {
1910 RHASH_SET_IFNONE(obj, data->replacement);
1911 }
1912 }
1913 break;
1914
1915 case T_STRUCT:
1916 {
1917 long len = RSTRUCT_LEN_RAW(obj);
1918 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
1919
1920 for (long i=0; i<len; i++) {
1921 CHECK_AND_REPLACE(obj, ptr[i]);
1922 }
1923 }
1924 break;
1925
1926 case T_MATCH:
1927 CHECK_AND_REPLACE(obj, RMATCH(obj)->str);
1928 break;
1929
1930 case T_RATIONAL:
1931 CHECK_AND_REPLACE(obj, RRATIONAL(obj)->num);
1932 CHECK_AND_REPLACE(obj, RRATIONAL(obj)->den);
1933 break;
1934 case T_COMPLEX:
1935 CHECK_AND_REPLACE(obj, RCOMPLEX(obj)->real);
1936 CHECK_AND_REPLACE(obj, RCOMPLEX(obj)->imag);
1937 break;
1938
1939 case T_DATA:
1940 if (!data->move && obj_refer_only_shareables_p(obj)) {
1941 break;
1942 }
1943 else {
1944 rb_raise(rb_eRactorError, "can not %s %"PRIsVALUE" object.",
1945 data->move ? "move" : "copy", rb_class_of(obj));
1946 }
1947
1948 case T_IMEMO:
1949 // not supported yet
1950 return 1;
1951
1952 // unreachable
1953 case T_CLASS:
1954 case T_MODULE:
1955 case T_ICLASS:
1956 default:
1957 rp(obj);
1958 rb_bug("unreachable");
1959 }
1960
1961 data->replacement = (VALUE)replacement;
1962
1963 if (data->leave_func(obj, data) == traverse_stop) {
1964 return 1;
1965 }
1966 else {
1967 return 0;
1968 }
1969}
1970
1971// 0: traverse all
1972// 1: stopped
1973static VALUE
1974rb_obj_traverse_replace(VALUE obj,
1975 rb_obj_traverse_replace_enter_func enter_func,
1976 rb_obj_traverse_replace_leave_func leave_func,
1977 bool move)
1978{
1979 struct obj_traverse_replace_data data = {
1980 .enter_func = enter_func,
1981 .leave_func = leave_func,
1982 .rec = NULL,
1983 .replacement = Qundef,
1984 .move = move,
1985 };
1986
1987 if (obj_traverse_replace_i(obj, &data)) {
1988 return Qundef;
1989 }
1990 else {
1991 return data.replacement;
1992 }
1993}
1994
1995static const bool wb_protected_types[RUBY_T_MASK] = {
2006};
2007
2008static enum obj_traverse_iterator_result
2009move_enter(VALUE obj, struct obj_traverse_replace_data *data)
2010{
2011 if (rb_ractor_shareable_p(obj)) {
2012 data->replacement = obj;
2013 return traverse_skip;
2014 }
2015 else {
2016 VALUE type = RB_BUILTIN_TYPE(obj);
2017 size_t slot_size = rb_gc_obj_slot_size(obj);
2018 type |= wb_protected_types[type] ? FL_WB_PROTECTED : 0;
2019 NEWOBJ_OF(moved, struct RBasic, 0, type, slot_size, 0);
2020 MEMZERO(&moved[1], char, slot_size - sizeof(*moved));
2021 data->replacement = (VALUE)moved;
2022 return traverse_cont;
2023 }
2024}
2025
2026static enum obj_traverse_iterator_result
2027move_leave(VALUE obj, struct obj_traverse_replace_data *data)
2028{
2029 // Copy flags
2030 VALUE ignored_flags = RUBY_FL_PROMOTED;
2031 RBASIC(data->replacement)->flags = (RBASIC(obj)->flags & ~ignored_flags) | (RBASIC(data->replacement)->flags & ignored_flags);
2032 // Copy contents without the flags
2033 memcpy(
2034 (char *)data->replacement + sizeof(VALUE),
2035 (char *)obj + sizeof(VALUE),
2036 rb_gc_obj_slot_size(obj) - sizeof(VALUE)
2037 );
2038
2039 // We've copied obj's references to the replacement
2040 rb_gc_writebarrier_remember(data->replacement);
2041
2042 void rb_replace_generic_ivar(VALUE clone, VALUE obj); // variable.c
2043 if (UNLIKELY(rb_obj_gen_fields_p(obj))) {
2044 rb_replace_generic_ivar(data->replacement, obj);
2045 }
2046
2047 rb_gc_obj_id_moved(data->replacement);
2048
2049 VALUE flags = T_OBJECT | FL_FREEZE | (RBASIC(obj)->flags & FL_PROMOTED);
2050
2051 // Avoid mutations using bind_call, etc.
2052 MEMZERO((char *)obj, char, sizeof(struct RBasic));
2053 RBASIC(obj)->flags = flags;
2054 RBASIC_SET_CLASS_RAW(obj, rb_cRactorMovedObject);
2055 return traverse_cont;
2056}
2057
2058static VALUE
2059ractor_move(VALUE obj)
2060{
2061 VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave, true);
2062 if (!UNDEF_P(val)) {
2063 return val;
2064 }
2065 else {
2066 rb_raise(rb_eRactorError, "can not move the object");
2067 }
2068}
2069
2070static VALUE
2071ractor_call_clone_try(VALUE obj)
2072{
2073 return rb_funcall(obj, idClone, 0);
2074}
2075
2076static VALUE
2077ractor_call_clone_rescue(VALUE obj, VALUE exc)
2078{
2079 rb_raise(rb_eRactorError, "can't clone unshareable instance of %"PRIsVALUE, rb_class_of(obj));
2081}
2082
2083static VALUE
2084ractor_obj_clone(VALUE obj)
2085{
2086 VALUE clone = rb_rescue(ractor_call_clone_try, obj, ractor_call_clone_rescue, obj);
2087
2088 if (obj == clone) {
2089 rb_raise(rb_eRactorError, "#clone returned self");
2090 }
2091
2092 return clone;
2093}
2094
2095static enum obj_traverse_iterator_result
2096copy_enter(VALUE obj, struct obj_traverse_replace_data *data)
2097{
2098 if (rb_ractor_shareable_p(obj)) {
2099 data->replacement = obj;
2100 return traverse_skip;
2101 }
2102 else {
2103 data->replacement = ractor_obj_clone(obj);
2104 return traverse_cont;
2105 }
2106}
2107
2108static enum obj_traverse_iterator_result
2109copy_leave(VALUE obj, struct obj_traverse_replace_data *data)
2110{
2111 return traverse_cont;
2112}
2113
2114static VALUE
2115ractor_copy(VALUE obj)
2116{
2117 VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave, false);
2118 if (!UNDEF_P(val)) {
2119 return val;
2120 }
2121 else {
2122 rb_raise(rb_eRactorError, "can not copy the object");
2123 }
2124}
2125
2126// Ractor local storage
2127
2129 const struct rb_ractor_local_storage_type *type;
2130 void *main_cache;
2131};
2132
2134 int cnt;
2135 int capa;
2137} freed_ractor_local_keys;
2138
2139static int
2140ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
2141{
2143 if (k->type->mark) (*k->type->mark)((void *)val);
2144 return ST_CONTINUE;
2145}
2146
2147static enum rb_id_table_iterator_result
2148idkey_local_storage_mark_i(VALUE val, void *dmy)
2149{
2150 rb_gc_mark(val);
2151 return ID_TABLE_CONTINUE;
2152}
2153
2154static void
2155ractor_local_storage_mark(rb_ractor_t *r)
2156{
2157 if (r->local_storage) {
2158 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
2159
2160 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
2161 rb_ractor_local_key_t key = freed_ractor_local_keys.keys[i];
2162 st_data_t val, k = (st_data_t)key;
2163 if (st_delete(r->local_storage, &k, &val) &&
2164 (key = (rb_ractor_local_key_t)k)->type->free) {
2165 (*key->type->free)((void *)val);
2166 }
2167 }
2168 }
2169
2170 if (r->idkey_local_storage) {
2171 rb_id_table_foreach_values(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
2172 }
2173
2174 rb_gc_mark(r->local_storage_store_lock);
2175}
2176
2177static int
2178ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
2179{
2181 if (k->type->free) (*k->type->free)((void *)val);
2182 return ST_CONTINUE;
2183}
2184
2185static void
2186ractor_local_storage_free(rb_ractor_t *r)
2187{
2188 if (r->local_storage) {
2189 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
2190 st_free_table(r->local_storage);
2191 }
2192
2193 if (r->idkey_local_storage) {
2194 rb_id_table_free(r->idkey_local_storage);
2195 }
2196}
2197
2198static void
2199rb_ractor_local_storage_value_mark(void *ptr)
2200{
2201 rb_gc_mark((VALUE)ptr);
2202}
2203
2204static const struct rb_ractor_local_storage_type ractor_local_storage_type_null = {
2205 NULL,
2206 NULL,
2207};
2208
2210 NULL,
2211 ruby_xfree,
2212};
2213
2214static const struct rb_ractor_local_storage_type ractor_local_storage_type_value = {
2215 rb_ractor_local_storage_value_mark,
2216 NULL,
2217};
2218
2221{
2223 key->type = type ? type : &ractor_local_storage_type_null;
2224 key->main_cache = (void *)Qundef;
2225 return key;
2226}
2227
2230{
2231 return rb_ractor_local_storage_ptr_newkey(&ractor_local_storage_type_value);
2232}
2233
2234void
2235rb_ractor_local_storage_delkey(rb_ractor_local_key_t key)
2236{
2237 RB_VM_LOCKING() {
2238 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
2239 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
2240 SIZED_REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, freed_ractor_local_keys.capa, freed_ractor_local_keys.cnt);
2241 }
2242 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
2243 }
2244}
2245
2246static bool
2247ractor_local_ref(rb_ractor_local_key_t key, void **pret)
2248{
2249 if (rb_ractor_main_p()) {
2250 if (!UNDEF_P((VALUE)key->main_cache)) {
2251 *pret = key->main_cache;
2252 return true;
2253 }
2254 else {
2255 return false;
2256 }
2257 }
2258 else {
2259 rb_ractor_t *cr = GET_RACTOR();
2260
2261 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
2262 return true;
2263 }
2264 else {
2265 return false;
2266 }
2267 }
2268}
2269
2270static void
2271ractor_local_set(rb_ractor_local_key_t key, void *ptr)
2272{
2273 rb_ractor_t *cr = GET_RACTOR();
2274
2275 if (cr->local_storage == NULL) {
2276 cr->local_storage = st_init_numtable();
2277 }
2278
2279 st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
2280
2281 if (rb_ractor_main_p()) {
2282 key->main_cache = ptr;
2283 }
2284}
2285
2286VALUE
2288{
2289 void *val;
2290 if (ractor_local_ref(key, &val)) {
2291 return (VALUE)val;
2292 }
2293 else {
2294 return Qnil;
2295 }
2296}
2297
2298bool
2300{
2301 if (ractor_local_ref(key, (void **)val)) {
2302 return true;
2303 }
2304 else {
2305 return false;
2306 }
2307}
2308
2309void
2311{
2312 ractor_local_set(key, (void *)val);
2313}
2314
2315void *
2317{
2318 void *ret;
2319 if (ractor_local_ref(key, &ret)) {
2320 return ret;
2321 }
2322 else {
2323 return NULL;
2324 }
2325}
2326
2327void
2329{
2330 ractor_local_set(key, ptr);
2331}
2332
2333#define DEFAULT_KEYS_CAPA 0x10
2334
2335void
2336rb_ractor_finish_marking(void)
2337{
2338 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
2339 SIZED_FREE(freed_ractor_local_keys.keys[i]);
2340 }
2341 freed_ractor_local_keys.cnt = 0;
2342 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
2343 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
2344 SIZED_REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, DEFAULT_KEYS_CAPA, freed_ractor_local_keys.capa);
2345 }
2346}
2347
2348static VALUE
2349ractor_local_value(rb_execution_context_t *ec, VALUE self, VALUE sym)
2350{
2351 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2352 ID id = rb_check_id(&sym);
2353 struct rb_id_table *tbl = cr->idkey_local_storage;
2354 VALUE val;
2355
2356 if (id && tbl && rb_id_table_lookup(tbl, id, &val)) {
2357 return val;
2358 }
2359 else {
2360 return Qnil;
2361 }
2362}
2363
2364static VALUE
2365ractor_local_value_set(rb_execution_context_t *ec, VALUE self, VALUE sym, VALUE val)
2366{
2367 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2368 ID id = SYM2ID(rb_to_symbol(sym));
2369 struct rb_id_table *tbl = cr->idkey_local_storage;
2370
2371 if (tbl == NULL) {
2372 tbl = cr->idkey_local_storage = rb_id_table_create(2);
2373 }
2374 rb_id_table_insert(tbl, id, val);
2375 return val;
2376}
2377
2380 struct rb_id_table *tbl;
2381 ID id;
2382 VALUE sym;
2383};
2384
2385static VALUE
2386ractor_local_value_store_i(VALUE ptr)
2387{
2388 VALUE val;
2390
2391 if (rb_id_table_lookup(data->tbl, data->id, &val)) {
2392 // after synchronization, we found already registered entry
2393 }
2394 else {
2395 val = rb_yield(Qnil);
2396 ractor_local_value_set(data->ec, Qnil, data->sym, val);
2397 }
2398 return val;
2399}
2400
2401static VALUE
2402ractor_local_value_store_if_absent(rb_execution_context_t *ec, VALUE self, VALUE sym)
2403{
2404 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2405 struct ractor_local_storage_store_data data = {
2406 .ec = ec,
2407 .sym = sym,
2408 .id = SYM2ID(rb_to_symbol(sym)),
2409 .tbl = cr->idkey_local_storage,
2410 };
2411 VALUE val;
2412
2413 if (data.tbl == NULL) {
2414 data.tbl = cr->idkey_local_storage = rb_id_table_create(2);
2415 }
2416 else if (rb_id_table_lookup(data.tbl, data.id, &val)) {
2417 // already set
2418 return val;
2419 }
2420
2421 if (!cr->local_storage_store_lock) {
2422 cr->local_storage_store_lock = rb_mutex_new();
2423 }
2424
2425 return rb_mutex_synchronize(cr->local_storage_store_lock, ractor_local_value_store_i, (VALUE)&data);
2426}
2427
2428// shareable_proc
2429
2430static VALUE
2431ractor_shareable_proc(rb_execution_context_t *ec, VALUE replace_self, bool is_lambda)
2432{
2433 if (!rb_ractor_shareable_p(replace_self)) {
2434 rb_raise(rb_eRactorIsolationError, "self should be shareable: %" PRIsVALUE, replace_self);
2435 }
2436 else {
2437 VALUE proc = is_lambda ? rb_block_lambda() : rb_block_proc();
2438 return rb_proc_ractor_make_shareable(rb_proc_dup(proc), replace_self);
2439 }
2440}
2441
2442// Ractor#require
2443
2445 VALUE port;
2446 bool raised;
2447
2448 union {
2449 struct {
2450 VALUE feature;
2451 } require;
2452
2453 struct {
2454 VALUE module;
2455 ID name;
2456 } autoload;
2457 } as;
2458
2459 bool silent;
2460};
2461
2462RUBY_REFERENCES(cross_ractor_require_refs) = {
2463 RUBY_REF_EDGE(struct cross_ractor_require, port),
2464 RUBY_REF_EDGE(struct cross_ractor_require, as.require.feature),
2465 RUBY_REF_END
2466};
2467
2468static const rb_data_type_t cross_ractor_require_data_type = {
2469 "ractor/cross_ractor_require",
2470 {
2471 RUBY_REFS_LIST_PTR(cross_ractor_require_refs),
2473 NULL, // memsize
2474 NULL, // compact
2475 },
2476 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_DECL_MARKING | RUBY_TYPED_EMBEDDABLE
2477};
2478
2479static VALUE
2480require_body(VALUE crr_obj)
2481{
2482 struct cross_ractor_require *crr;
2483 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2484 VALUE feature = crr->as.require.feature;
2485
2486 ID require;
2487 CONST_ID(require, "require");
2488
2489 if (crr->silent) {
2490 int rb_require_internal_silent(VALUE fname);
2491 return INT2NUM(rb_require_internal_silent(feature));
2492 }
2493 else {
2494 return rb_funcallv(Qnil, require, 1, &feature);
2495 }
2496}
2497
2498static VALUE
2499require_rescue(VALUE crr_obj, VALUE errinfo)
2500{
2501 struct cross_ractor_require *crr;
2502 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2503 crr->raised = true;
2504 return errinfo;
2505}
2506
2507static VALUE
2508require_result_send_body(VALUE ary)
2509{
2510 VALUE port = RARRAY_AREF(ary, 0);
2511 VALUE results = RARRAY_AREF(ary, 1);
2512
2513 rb_execution_context_t *ec = GET_EC();
2514
2515 ractor_port_send(ec, port, results, Qfalse);
2516 return Qnil;
2517}
2518
2519static VALUE
2520require_result_send_resuce(VALUE port, VALUE errinfo)
2521{
2522 // TODO: need rescue?
2523 ractor_port_send(GET_EC(), port, errinfo, Qfalse);
2524 return Qnil;
2525}
2526
2527static VALUE
2528ractor_require_protect(VALUE crr_obj, VALUE (*func)(VALUE))
2529{
2530 struct cross_ractor_require *crr;
2531 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2532
2533 const bool silent = crr->silent;
2534
2535 VALUE debug, errinfo;
2536 if (silent) {
2537 debug = ruby_debug;
2538 errinfo = rb_errinfo();
2539 }
2540
2541 // get normal result or raised exception (with crr->raised == true)
2542 VALUE result = rb_rescue2(func, crr_obj, require_rescue, crr_obj, rb_eException, 0);
2543
2544 if (silent) {
2545 ruby_debug = debug;
2546 rb_set_errinfo(errinfo);
2547 }
2548
2549 rb_rescue2(require_result_send_body,
2550 // [port, [result, raised]]
2551 rb_ary_new_from_args(2, crr->port, rb_ary_new_from_args(2, result, crr->raised ? Qtrue : Qfalse)),
2552 require_result_send_resuce, rb_eException, crr->port);
2553
2554 RB_GC_GUARD(crr_obj);
2555 return Qnil;
2556}
2557
2558static VALUE
2559ractor_require_func(void *crr_obj)
2560{
2561 return ractor_require_protect((VALUE)crr_obj, require_body);
2562}
2563
2564VALUE
2565rb_ractor_require(VALUE feature, bool silent)
2566{
2567 // We're about to block on the main ractor, so if we're holding the global lock we'll deadlock.
2568 ASSERT_vm_unlocking();
2569
2570 struct cross_ractor_require *crr;
2571 VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2572 RB_OBJ_SET_SHAREABLE(crr_obj); // TODO: internal data?
2573
2574 // Convert feature to proper file path and make it shareable as fstring
2575 RB_OBJ_WRITE(crr_obj, &crr->as.require.feature, rb_fstring(FilePathValue(feature)));
2576 RB_OBJ_WRITE(crr_obj, &crr->port, rb_ractor_make_shareable(ractor_port_new(GET_RACTOR())));
2577 crr->raised = false;
2578 crr->silent = silent;
2579
2580 rb_execution_context_t *ec = GET_EC();
2581 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
2582 rb_ractor_interrupt_exec(main_r, ractor_require_func, (void *)crr_obj, rb_interrupt_exec_flag_value_data);
2583
2584 // wait for require done
2585 VALUE results = ractor_port_receive(ec, crr->port);
2586 ractor_port_close(ec, crr->port);
2587
2588 VALUE exc = rb_ary_pop(results);
2589 VALUE result = rb_ary_pop(results);
2590 RB_GC_GUARD(crr_obj);
2591
2592 if (RTEST(exc)) {
2593 rb_exc_raise(result);
2594 }
2595 else {
2596 return result;
2597 }
2598}
2599
2600static VALUE
2601ractor_require(rb_execution_context_t *ec, VALUE self, VALUE feature)
2602{
2603 return rb_ractor_require(feature, false);
2604}
2605
2606static VALUE
2607autoload_load_body(VALUE crr_obj)
2608{
2609 struct cross_ractor_require *crr;
2610 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2611 return rb_autoload_load(crr->as.autoload.module, crr->as.autoload.name);
2612}
2613
2614static VALUE
2615ractor_autoload_load_func(void *crr_obj)
2616{
2617 return ractor_require_protect((VALUE)crr_obj, autoload_load_body);
2618}
2619
2620VALUE
2621rb_ractor_autoload_load(VALUE module, ID name)
2622{
2623 struct cross_ractor_require *crr;
2624 VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2625 RB_OBJ_SET_SHAREABLE(crr_obj); // TODO: internal data?
2626
2627 RB_OBJ_WRITE(crr_obj, &crr->as.autoload.module, module);
2628 RB_OBJ_WRITE(crr_obj, &crr->as.autoload.name, name);
2629 RB_OBJ_WRITE(crr_obj, &crr->port, rb_ractor_make_shareable(ractor_port_new(GET_RACTOR())));
2630
2631 rb_execution_context_t *ec = GET_EC();
2632 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
2633 rb_ractor_interrupt_exec(main_r, ractor_autoload_load_func, (void *)crr_obj, rb_interrupt_exec_flag_value_data);
2634
2635 // wait for require done
2636 VALUE results = ractor_port_receive(ec, crr->port);
2637 ractor_port_close(ec, crr->port);
2638
2639 VALUE exc = rb_ary_pop(results);
2640 VALUE result = rb_ary_pop(results);
2641 RB_GC_GUARD(crr_obj);
2642
2643 if (RTEST(exc)) {
2644 rb_exc_raise(result);
2645 }
2646 else {
2647 return result;
2648 }
2649}
2650
2651#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:1498
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1529
#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_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:95
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:1525
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:3350
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:12728
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:2209
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:1561
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:2316
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:2328
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:2220
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:2310
bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
Queries the key.
Definition ractor.c:2299
#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:1552
rb_ractor_local_key_t rb_ractor_local_storage_value_newkey(void)
Issues a new key.
Definition ractor.c:2229
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:2287
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