Ruby 4.1.0dev (2026-03-05 revision 8a87cebd1874f8f9f68af8928191ee3f0d97bb28)
ractor.c (8a87cebd1874f8f9f68af8928191ee3f0d97bb28)
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/thread.h"
20#include "variable.h"
21#include "yjit.h"
22#include "zjit.h"
23
25static VALUE rb_cRactorSelector;
26
27VALUE rb_eRactorUnsafeError;
28VALUE rb_eRactorIsolationError;
29static VALUE rb_eRactorError;
30static VALUE rb_eRactorRemoteError;
31static VALUE rb_eRactorMovedError;
32static VALUE rb_eRactorClosedError;
33static VALUE rb_cRactorMovedObject;
34
35static void vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line);
36
37
38#if RACTOR_CHECK_MODE > 0
39bool rb_ractor_ignore_belonging_flag = false;
40#endif
41
42// Ractor locking
43
44static void
45ASSERT_ractor_unlocking(rb_ractor_t *r)
46{
47#if RACTOR_CHECK_MODE > 0
48 const rb_execution_context_t *ec = rb_current_ec_noinline();
49 if (ec != NULL && r->sync.locked_by == rb_ractor_self(rb_ec_ractor_ptr(ec))) {
50 rb_bug("recursive ractor locking");
51 }
52#endif
53}
54
55static void
56ASSERT_ractor_locking(rb_ractor_t *r)
57{
58#if RACTOR_CHECK_MODE > 0
59 const rb_execution_context_t *ec = rb_current_ec_noinline();
60 if (ec != NULL && r->sync.locked_by != rb_ractor_self(rb_ec_ractor_ptr(ec))) {
61 rp(r->sync.locked_by);
62 rb_bug("ractor lock is not acquired.");
63 }
64#endif
65}
66
67static void
68ractor_lock(rb_ractor_t *r, const char *file, int line)
69{
70 RUBY_DEBUG_LOG2(file, line, "locking r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
71
72 ASSERT_ractor_unlocking(r);
73 rb_native_mutex_lock(&r->sync.lock);
74
75 const rb_execution_context_t *ec = rb_current_ec_noinline();
76 if (ec) {
77 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
78 VM_ASSERT(!cr->malloc_gc_disabled);
79 cr->malloc_gc_disabled = true;
80 }
81
82#if RACTOR_CHECK_MODE > 0
83 if (ec != NULL) {
84 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
85 r->sync.locked_by = rb_ractor_self(cr);
86 }
87#endif
88
89 RUBY_DEBUG_LOG2(file, line, "locked r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
90}
91
92static void
93ractor_lock_self(rb_ractor_t *cr, const char *file, int line)
94{
95 VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline()));
96#if RACTOR_CHECK_MODE > 0
97 VM_ASSERT(cr->sync.locked_by != cr->pub.self);
98#endif
99 ractor_lock(cr, file, line);
100}
101
102static void
103ractor_unlock(rb_ractor_t *r, const char *file, int line)
104{
105 ASSERT_ractor_locking(r);
106#if RACTOR_CHECK_MODE > 0
107 r->sync.locked_by = Qnil;
108#endif
109
110 const rb_execution_context_t *ec = rb_current_ec_noinline();
111 if (ec) {
112 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
113 VM_ASSERT(cr->malloc_gc_disabled);
114 cr->malloc_gc_disabled = false;
115 }
116
117 rb_native_mutex_unlock(&r->sync.lock);
118
119 RUBY_DEBUG_LOG2(file, line, "r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
120}
121
122static void
123ractor_unlock_self(rb_ractor_t *cr, const char *file, int line)
124{
125 VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline()));
126#if RACTOR_CHECK_MODE > 0
127 VM_ASSERT(cr->sync.locked_by == cr->pub.self);
128#endif
129 ractor_unlock(cr, file, line);
130}
131
132#define RACTOR_LOCK(r) ractor_lock(r, __FILE__, __LINE__)
133#define RACTOR_UNLOCK(r) ractor_unlock(r, __FILE__, __LINE__)
134#define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
135#define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
136
137void
138rb_ractor_lock_self(rb_ractor_t *r)
139{
140 RACTOR_LOCK_SELF(r);
141}
142
143void
144rb_ractor_unlock_self(rb_ractor_t *r)
145{
146 RACTOR_UNLOCK_SELF(r);
147}
148
149// Ractor status
150
151static const char *
152ractor_status_str(enum ractor_status status)
153{
154 switch (status) {
155 case ractor_created: return "created";
156 case ractor_running: return "running";
157 case ractor_blocking: return "blocking";
158 case ractor_terminated: return "terminated";
159 }
160 rb_bug("unreachable");
161}
162
163static void
164ractor_status_set(rb_ractor_t *r, enum ractor_status status)
165{
166 RUBY_DEBUG_LOG("r:%u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
167
168 // check 1
169 if (r->status_ != ractor_created) {
170 VM_ASSERT(r == GET_RACTOR()); // only self-modification is allowed.
171 ASSERT_vm_locking();
172 }
173
174 // check2: transition check. assume it will be vanished on non-debug build.
175 switch (r->status_) {
176 case ractor_created:
177 VM_ASSERT(status == ractor_blocking);
178 break;
179 case ractor_running:
180 VM_ASSERT(status == ractor_blocking||
181 status == ractor_terminated);
182 break;
183 case ractor_blocking:
184 VM_ASSERT(status == ractor_running);
185 break;
186 case ractor_terminated:
187 rb_bug("unreachable");
188 break;
189 }
190
191 r->status_ = status;
192}
193
194static bool
195ractor_status_p(rb_ractor_t *r, enum ractor_status status)
196{
197 return rb_ractor_status_p(r, status);
198}
199
200// Ractor data/mark/free
201
202static void ractor_local_storage_mark(rb_ractor_t *r);
203static void ractor_local_storage_free(rb_ractor_t *r);
204
205static void ractor_sync_mark(rb_ractor_t *r);
206static void ractor_sync_free(rb_ractor_t *r);
207static size_t ractor_sync_memsize(const rb_ractor_t *r);
208static void ractor_sync_init(rb_ractor_t *r);
209
210static int
211mark_targeted_hook_list(st_data_t key, st_data_t value, st_data_t _arg)
212{
213 rb_hook_list_t *hook_list = (rb_hook_list_t*)value;
214
215 if (hook_list->type == hook_list_type_targeted_iseq) {
216 rb_gc_mark((VALUE)key);
217 }
218 else {
220 RUBY_ASSERT(hook_list->type == hook_list_type_targeted_def);
221 rb_gc_mark(def->body.bmethod.proc);
222 }
223 rb_hook_list_mark(hook_list);
224
225 return ST_CONTINUE;
226}
227
228static void
229ractor_mark(void *ptr)
230{
231 rb_ractor_t *r = (rb_ractor_t *)ptr;
232 bool checking_shareable = rb_gc_checking_shareable();
233
234 rb_gc_mark(r->loc);
235 rb_gc_mark(r->name);
236
237 if (!checking_shareable) {
238 // may unshareable objects
239 rb_gc_mark(r->r_stdin);
240 rb_gc_mark(r->r_stdout);
241 rb_gc_mark(r->r_stderr);
242 rb_gc_mark(r->verbose);
243 rb_gc_mark(r->debug);
244
245 // mark received messages
246 ractor_sync_mark(r);
247
248 rb_hook_list_mark(&r->pub.hooks);
249 if (r->pub.targeted_hooks) {
250 st_foreach(r->pub.targeted_hooks, mark_targeted_hook_list, 0);
251 }
252
253 if (r->threads.cnt > 0) {
254 rb_thread_t *th = 0;
255 ccan_list_for_each(&r->threads.set, th, lt_node) {
256 VM_ASSERT(th != NULL);
257 rb_gc_mark(th->self);
258 }
259 }
260
261 ractor_local_storage_mark(r);
262 }
263}
264
265static int
266free_targeted_hook_lists(st_data_t key, st_data_t val, st_data_t _arg)
267{
268 rb_hook_list_t *hook_list = (rb_hook_list_t*)val;
269 rb_hook_list_free(hook_list);
270 return ST_DELETE;
271}
272
273static void
274free_targeted_hooks(st_table *hooks_tbl)
275{
276 st_foreach(hooks_tbl, free_targeted_hook_lists, 0);
277}
278
279static void
280ractor_free(void *ptr)
281{
282 rb_ractor_t *r = (rb_ractor_t *)ptr;
283 RUBY_DEBUG_LOG("free r:%d", rb_ractor_id(r));
284 free_targeted_hooks(r->pub.targeted_hooks);
285 rb_native_mutex_destroy(&r->sync.lock);
286#ifdef RUBY_THREAD_WIN32_H
287 rb_native_cond_destroy(&r->sync.wakeup_cond);
288#endif
289 ractor_local_storage_free(r);
290 rb_hook_list_free(&r->pub.hooks);
291 st_free_table(r->pub.targeted_hooks);
292
293 if (r->newobj_cache) {
294 RUBY_ASSERT(r == ruby_single_main_ractor);
295
296 rb_gc_ractor_cache_free(r->newobj_cache);
297 r->newobj_cache = NULL;
298 }
299
300 ractor_sync_free(r);
301 if (!r->main_ractor) {
302 SIZED_FREE(r);
303 }
304}
305
306static size_t
307ractor_memsize(const void *ptr)
308{
309 rb_ractor_t *r = (rb_ractor_t *)ptr;
310
311 // TODO: more correct?
312 return sizeof(rb_ractor_t) + ractor_sync_memsize(r);
313}
314
315static const rb_data_type_t ractor_data_type = {
316 "ractor",
317 {
318 ractor_mark,
319 ractor_free,
320 ractor_memsize,
321 NULL, // update
322 },
323 0, 0, RUBY_TYPED_FREE_IMMEDIATELY /* | RUBY_TYPED_WB_PROTECTED */
324};
325
326bool
327rb_ractor_p(VALUE gv)
328{
329 if (rb_typeddata_is_kind_of(gv, &ractor_data_type)) {
330 return true;
331 }
332 else {
333 return false;
334 }
335}
336
337static inline rb_ractor_t *
338RACTOR_PTR(VALUE self)
339{
340 VM_ASSERT(rb_ractor_p(self));
341 rb_ractor_t *r = DATA_PTR(self);
342 return r;
343}
344
345static rb_atomic_t ractor_last_id;
346
347#if RACTOR_CHECK_MODE > 0
348uint32_t
349rb_ractor_current_id(void)
350{
351 if (GET_THREAD()->ractor == NULL) {
352 return 1; // main ractor
353 }
354 else {
355 return rb_ractor_id(GET_RACTOR());
356 }
357}
358#endif
359
360#include "ractor_sync.c"
361
362// creation/termination
363
364static uint32_t
365ractor_next_id(void)
366{
367 uint32_t id;
368
369 id = (uint32_t)(RUBY_ATOMIC_FETCH_ADD(ractor_last_id, 1) + 1);
370
371 return id;
372}
373
374static void
375vm_insert_ractor0(rb_vm_t *vm, rb_ractor_t *r, bool single_ractor_mode)
376{
377 RUBY_DEBUG_LOG("r:%u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
378 VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
379
380 ccan_list_add_tail(&vm->ractor.set, &r->vmlr_node);
381 vm->ractor.cnt++;
382
383 if (r->newobj_cache) {
384 VM_ASSERT(r == ruby_single_main_ractor);
385 }
386 else {
387 r->newobj_cache = rb_gc_ractor_cache_alloc(r);
388 }
389}
390
391static void
392cancel_single_ractor_mode(void)
393{
394 // enable multi-ractor mode
395 RUBY_DEBUG_LOG("enable multi-ractor mode");
396
397 ruby_single_main_ractor = NULL;
398 rb_funcall(rb_cRactor, rb_intern("_activated"), 0);
399}
400
401static void
402vm_insert_ractor(rb_vm_t *vm, rb_ractor_t *r)
403{
404 VM_ASSERT(ractor_status_p(r, ractor_created));
405
406 if (rb_multi_ractor_p()) {
407 RB_VM_LOCK();
408 {
409 vm_insert_ractor0(vm, r, false);
410 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
411 }
412 RB_VM_UNLOCK();
413 }
414 else {
415 if (vm->ractor.cnt == 0) {
416 // main ractor
417 vm_insert_ractor0(vm, r, true);
418 ractor_status_set(r, ractor_blocking);
419 ractor_status_set(r, ractor_running);
420 }
421 else {
422 cancel_single_ractor_mode();
423 vm_insert_ractor0(vm, r, true);
424 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
425 }
426 }
427}
428
429static void
430vm_remove_ractor(rb_vm_t *vm, rb_ractor_t *cr)
431{
432 VM_ASSERT(ractor_status_p(cr, ractor_running));
433 VM_ASSERT(vm->ractor.cnt > 1);
434 VM_ASSERT(cr->threads.cnt == 1);
435
436 RB_VM_LOCK();
437 {
438 RUBY_DEBUG_LOG("ractor.cnt:%u-- terminate_waiting:%d",
439 vm->ractor.cnt, vm->ractor.sync.terminate_waiting);
440
441 VM_ASSERT(vm->ractor.cnt > 0);
442 ccan_list_del(&cr->vmlr_node);
443
444 if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
445 rb_native_cond_signal(&vm->ractor.sync.terminate_cond);
446 }
447 vm->ractor.cnt--;
448
449 rb_gc_ractor_cache_free(cr->newobj_cache);
450 cr->newobj_cache = NULL;
451
452 ractor_status_set(cr, ractor_terminated);
453 }
454 RB_VM_UNLOCK();
455}
456
457static VALUE
458ractor_alloc(VALUE klass)
459{
460 rb_ractor_t *r;
461 VALUE rv = TypedData_Make_Struct(klass, rb_ractor_t, &ractor_data_type, r);
463 r->pub.self = rv;
464 r->next_ec_serial = 1;
465 VM_ASSERT(ractor_status_p(r, ractor_created));
466 return rv;
467}
468
469static rb_ractor_t _main_ractor;
470
472rb_ractor_main_alloc(void)
473{
474 rb_ractor_t *r = &_main_ractor;
475 if (r == NULL) {
476 fprintf(stderr, "[FATAL] failed to allocate memory for main ractor\n");
477 exit(EXIT_FAILURE);
478 }
479 r->pub.id = ++ractor_last_id;
480 r->loc = Qnil;
481 r->name = Qnil;
482 r->pub.self = Qnil;
483 r->newobj_cache = rb_gc_ractor_cache_alloc(r);
484 r->next_ec_serial = 1;
485 r->main_ractor = true;
486 ruby_single_main_ractor = r;
487
488 return r;
489}
490
491#if defined(HAVE_WORKING_FORK)
492// Set up the main Ractor for the VM after fork.
493// Puts us in "single Ractor mode"
494void
495rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th)
496{
497 // initialize as a main ractor
498 vm->ractor.cnt = 0;
499 vm->ractor.blocking_cnt = 0;
500 ruby_single_main_ractor = th->ractor;
501 th->ractor->status_ = ractor_created;
502
503 rb_ractor_living_threads_init(th->ractor);
504 rb_ractor_living_threads_insert(th->ractor, th);
505
506 VM_ASSERT(vm->ractor.blocking_cnt == 0);
507 VM_ASSERT(vm->ractor.cnt == 1);
508}
509
510void
511rb_ractor_terminate_atfork(rb_vm_t *vm, rb_ractor_t *r)
512{
513 rb_gc_ractor_cache_free(r->newobj_cache);
514 r->newobj_cache = NULL;
515 r->status_ = ractor_terminated;
516 ractor_sync_terminate_atfork(vm, r);
517}
518#endif
519
520void rb_thread_sched_init(struct rb_thread_sched *, bool atfork);
521
522void
523rb_ractor_living_threads_init(rb_ractor_t *r)
524{
525 ccan_list_head_init(&r->threads.set);
526 r->threads.cnt = 0;
527 r->threads.blocking_cnt = 0;
528}
529
530static void
531ractor_init(rb_ractor_t *r, VALUE name, VALUE loc)
532{
533 ractor_sync_init(r);
534 r->pub.targeted_hooks = st_init_numtable();
535 r->pub.hooks.type = hook_list_type_ractor_local;
536
537 // thread management
538 rb_thread_sched_init(&r->threads.sched, false);
539 rb_ractor_living_threads_init(r);
540
541 // naming
542 if (!NIL_P(name)) {
543 rb_encoding *enc;
544 StringValueCStr(name);
545 enc = rb_enc_get(name);
546 if (!rb_enc_asciicompat(enc)) {
547 rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
548 rb_enc_name(enc));
549 }
550 name = RB_OBJ_SET_SHAREABLE(rb_str_new_frozen(name));
551 }
552
553 if (!SPECIAL_CONST_P(loc)) RB_OBJ_SET_SHAREABLE(loc);
554 r->loc = loc;
555 r->name = name;
556}
557
558void
559rb_ractor_main_setup(rb_vm_t *vm, rb_ractor_t *r, rb_thread_t *th)
560{
561 VALUE rv = r->pub.self = TypedData_Wrap_Struct(rb_cRactor, &ractor_data_type, r);
562 FL_SET_RAW(r->pub.self, RUBY_FL_SHAREABLE);
563 ractor_init(r, Qnil, Qnil);
564 r->threads.main = th;
565 rb_ractor_living_threads_insert(r, th);
566
567 RB_GC_GUARD(rv);
568}
569
570static VALUE
571ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VALUE args, VALUE block)
572{
573 VALUE rv = ractor_alloc(self);
574 rb_ractor_t *r = RACTOR_PTR(rv);
575 ractor_init(r, name, loc);
576
577 r->pub.id = ractor_next_id();
578 RUBY_DEBUG_LOG("r:%u", r->pub.id);
579
580 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
581 r->verbose = cr->verbose;
582 r->debug = cr->debug;
583
584 rb_yjit_before_ractor_spawn();
585 rb_zjit_before_ractor_spawn();
586 rb_thread_create_ractor(r, args, block);
587
588 RB_GC_GUARD(rv);
589 return rv;
590}
591
592#if 0
593static VALUE
594ractor_create_func(VALUE klass, VALUE loc, VALUE name, VALUE args, rb_block_call_func_t func)
595{
596 VALUE block = rb_proc_new(func, Qnil);
597 return ractor_create(rb_current_ec_noinline(), klass, loc, name, args, block);
598}
599#endif
600
601static void
602ractor_atexit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE result, bool exc)
603{
604 ractor_notify_exit(ec, cr, result, exc);
605}
606
607void
608rb_ractor_atexit(rb_execution_context_t *ec, VALUE result)
609{
610 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
611 ractor_atexit(ec, cr, result, false);
612}
613
614void
615rb_ractor_atexit_exception(rb_execution_context_t *ec)
616{
617 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
618 ractor_atexit(ec, cr, ec->errinfo, true);
619}
620
621void
622rb_ractor_teardown(rb_execution_context_t *ec)
623{
624 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
625
626 // sync with rb_ractor_terminate_interrupt_main_thread()
627 RB_VM_LOCKING() {
628 VM_ASSERT(cr->threads.main != NULL);
629 cr->threads.main = NULL;
630 }
631}
632
633void
634rb_ractor_receive_parameters(rb_execution_context_t *ec, rb_ractor_t *r, int len, VALUE *ptr)
635{
636 for (int i=0; i<len; i++) {
637 ptr[i] = ractor_receive(ec, ractor_default_port(r));
638 }
639}
640
641void
642rb_ractor_send_parameters(rb_execution_context_t *ec, rb_ractor_t *r, VALUE args)
643{
644 int len = RARRAY_LENINT(args);
645 for (int i=0; i<len; i++) {
646 ractor_send(ec, ractor_default_port(r), RARRAY_AREF(args, i), false);
647 }
648}
649
650bool
651rb_ractor_main_p_(void)
652{
653 VM_ASSERT(rb_multi_ractor_p());
654 rb_execution_context_t *ec = GET_EC();
655 return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
656}
657
658int
659rb_ractor_living_thread_num(const rb_ractor_t *r)
660{
661 return r->threads.cnt;
662}
663
664// only for current ractor
665VALUE
666rb_ractor_thread_list(void)
667{
668 rb_ractor_t *r = GET_RACTOR();
669 rb_thread_t *th = 0;
670 VALUE ary = rb_ary_new();
671
672 ccan_list_for_each(&r->threads.set, th, lt_node) {
673 switch (th->status) {
674 case THREAD_RUNNABLE:
675 case THREAD_STOPPED:
676 case THREAD_STOPPED_FOREVER:
677 rb_ary_push(ary, th->self);
678 default:
679 break;
680 }
681 }
682
683 return ary;
684}
685
686void
687rb_ractor_living_threads_insert(rb_ractor_t *r, rb_thread_t *th)
688{
689 VM_ASSERT(th != NULL);
690
691 RACTOR_LOCK(r);
692 {
693 RUBY_DEBUG_LOG("r(%d)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
694 ccan_list_add_tail(&r->threads.set, &th->lt_node);
695 r->threads.cnt++;
696 }
697 RACTOR_UNLOCK(r);
698
699 // first thread for a ractor
700 if (r->threads.cnt == 1) {
701 VM_ASSERT(ractor_status_p(r, ractor_created));
702 vm_insert_ractor(th->vm, r);
703 }
704}
705
706static void
707vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line)
708{
709 ractor_status_set(r, ractor_blocking);
710
711 RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d++", vm->ractor.blocking_cnt);
712 vm->ractor.blocking_cnt++;
713 VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
714}
715
716void
717rb_vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
718{
719 ASSERT_vm_locking();
720 VM_ASSERT(GET_RACTOR() == cr);
721 vm_ractor_blocking_cnt_inc(vm, cr, file, line);
722}
723
724void
725rb_vm_ractor_blocking_cnt_dec(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
726{
727 ASSERT_vm_locking();
728 VM_ASSERT(GET_RACTOR() == cr);
729
730 RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d--", vm->ractor.blocking_cnt);
731 VM_ASSERT(vm->ractor.blocking_cnt > 0);
732 vm->ractor.blocking_cnt--;
733
734 ractor_status_set(cr, ractor_running);
735}
736
737static void
738ractor_check_blocking(rb_ractor_t *cr, unsigned int remained_thread_cnt, const char *file, int line)
739{
740 VM_ASSERT(cr == GET_RACTOR());
741
742 RUBY_DEBUG_LOG2(file, line,
743 "cr->threads.cnt:%u cr->threads.blocking_cnt:%u vm->ractor.cnt:%u vm->ractor.blocking_cnt:%u",
744 cr->threads.cnt, cr->threads.blocking_cnt,
745 GET_VM()->ractor.cnt, GET_VM()->ractor.blocking_cnt);
746
747 VM_ASSERT(cr->threads.cnt >= cr->threads.blocking_cnt + 1);
748
749 if (remained_thread_cnt > 0 &&
750 // will be block
751 cr->threads.cnt == cr->threads.blocking_cnt + 1) {
752 // change ractor status: running -> blocking
753 rb_vm_t *vm = GET_VM();
754
755 RB_VM_LOCKING() {
756 rb_vm_ractor_blocking_cnt_inc(vm, cr, file, line);
757 }
758 }
759}
760
761void rb_threadptr_remove(rb_thread_t *th);
762
763void
764rb_ractor_living_threads_remove(rb_ractor_t *cr, rb_thread_t *th)
765{
766 VM_ASSERT(cr == GET_RACTOR());
767 RUBY_DEBUG_LOG("r->threads.cnt:%d--", cr->threads.cnt);
768 ractor_check_blocking(cr, cr->threads.cnt - 1, __FILE__, __LINE__);
769
770 rb_threadptr_remove(th);
771
772 if (cr->threads.cnt == 1) {
773 vm_remove_ractor(th->vm, cr);
774 }
775 else {
776 RACTOR_LOCK(cr);
777 {
778 ccan_list_del(&th->lt_node);
779 cr->threads.cnt--;
780 }
781 RACTOR_UNLOCK(cr);
782 }
783}
784
785void
786rb_ractor_blocking_threads_inc(rb_ractor_t *cr, const char *file, int line)
787{
788 RUBY_DEBUG_LOG2(file, line, "cr->threads.blocking_cnt:%d++", cr->threads.blocking_cnt);
789
790 VM_ASSERT(cr->threads.cnt > 0);
791 VM_ASSERT(cr == GET_RACTOR());
792
793 ractor_check_blocking(cr, cr->threads.cnt, __FILE__, __LINE__);
794 cr->threads.blocking_cnt++;
795}
796
797void
798rb_ractor_blocking_threads_dec(rb_ractor_t *cr, const char *file, int line)
799{
800 RUBY_DEBUG_LOG2(file, line,
801 "r->threads.blocking_cnt:%d--, r->threads.cnt:%u",
802 cr->threads.blocking_cnt, cr->threads.cnt);
803
804 VM_ASSERT(cr == GET_RACTOR());
805
806 if (cr->threads.cnt == cr->threads.blocking_cnt) {
807 rb_vm_t *vm = GET_VM();
808
809 RB_VM_LOCKING() {
810 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
811 }
812 }
813
814 cr->threads.blocking_cnt--;
815}
816
817void
818rb_ractor_vm_barrier_interrupt_running_thread(rb_ractor_t *r)
819{
820 VM_ASSERT(r != GET_RACTOR());
821 ASSERT_ractor_unlocking(r);
822 ASSERT_vm_locking();
823
824 RACTOR_LOCK(r);
825 {
826 if (ractor_status_p(r, ractor_running)) {
827 rb_execution_context_t *ec = r->threads.running_ec;
828 if (ec) {
829 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec);
830 }
831 }
832 }
833 RACTOR_UNLOCK(r);
834}
835
836void
837rb_ractor_terminate_interrupt_main_thread(rb_ractor_t *r)
838{
839 VM_ASSERT(r != GET_RACTOR());
840 ASSERT_ractor_unlocking(r);
841 ASSERT_vm_locking();
842
843 rb_thread_t *main_th = r->threads.main;
844 if (main_th) {
845 if (main_th->status != THREAD_KILLED) {
846 RUBY_VM_SET_TERMINATE_INTERRUPT(main_th->ec);
847 rb_threadptr_interrupt(main_th);
848 }
849 else {
850 RUBY_DEBUG_LOG("killed (%p)", (void *)main_th);
851 }
852 }
853}
854
855void rb_thread_terminate_all(rb_thread_t *th); // thread.c
856
857static void
858ractor_terminal_interrupt_all(rb_vm_t *vm)
859{
860 if (vm->ractor.cnt > 1) {
861 // send terminate notification to all ractors
862 rb_ractor_t *r = 0;
863 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
864 if (r != vm->ractor.main_ractor) {
865 RUBY_DEBUG_LOG("r:%d", rb_ractor_id(r));
866 rb_ractor_terminate_interrupt_main_thread(r);
867 }
868 }
869 }
870}
871
872void rb_add_running_thread(rb_thread_t *th);
873void rb_del_running_thread(rb_thread_t *th);
874
875void
876rb_ractor_terminate_all(void)
877{
878 rb_vm_t *vm = GET_VM();
879 rb_ractor_t *cr = vm->ractor.main_ractor;
880
881 RUBY_DEBUG_LOG("ractor.cnt:%d", (int)vm->ractor.cnt);
882
883 VM_ASSERT(cr == GET_RACTOR()); // only main-ractor's main-thread should kick it.
884
885 RB_VM_LOCK();
886 {
887 ractor_terminal_interrupt_all(vm); // kill all ractors
888 }
889 RB_VM_UNLOCK();
890 rb_thread_terminate_all(GET_THREAD()); // kill other threads in main-ractor and wait
891
892 RB_VM_LOCK();
893 {
894 while (vm->ractor.cnt > 1) {
895 RUBY_DEBUG_LOG("terminate_waiting:%d", vm->ractor.sync.terminate_waiting);
896 vm->ractor.sync.terminate_waiting = true;
897
898 // wait for 1sec
899 rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
900 rb_del_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
901 rb_vm_cond_timedwait(vm, &vm->ractor.sync.terminate_cond, 1000 /* ms */);
902#ifdef RUBY_THREAD_PTHREAD_H
903 while (vm->ractor.sched.barrier_waiting) {
904 // A barrier is waiting. Threads relinquish the VM lock before joining the barrier and
905 // since we just acquired the VM lock back, we're blocking other threads from joining it.
906 // We loop until the barrier is over. We can't join this barrier because our thread isn't added to
907 // running_threads until the call below to `rb_add_running_thread`.
908 RB_VM_UNLOCK();
909 unsigned int lev;
910 RB_VM_LOCK_ENTER_LEV_NB(&lev);
911 }
912#endif
913 rb_add_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
914 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
915
916 ractor_terminal_interrupt_all(vm);
917 }
918 }
919 RB_VM_UNLOCK();
920}
921
923rb_vm_main_ractor_ec(rb_vm_t *vm)
924{
925 /* This code needs to carefully work around two bugs:
926 * - Bug #20016: When M:N threading is enabled, running_ec is NULL if no thread is
927 * actually currently running (as opposed to without M:N threading, when
928 * running_ec will still point to the _last_ thread which ran)
929 * - Bug #20197: If the main thread is sleeping, setting its postponed job
930 * interrupt flag is pointless; it won't look at the flag until it stops sleeping
931 * for some reason. It would be better to set the flag on the running ec, which
932 * will presumably look at it soon.
933 *
934 * Solution: use running_ec if it's set, otherwise fall back to the main thread ec.
935 * This is still susceptible to some rare race conditions (what if the last thread
936 * to run just entered a long-running sleep?), but seems like the best balance of
937 * robustness and complexity.
938 */
939 rb_execution_context_t *running_ec = vm->ractor.main_ractor->threads.running_ec;
940 if (running_ec) { return running_ec; }
941 return vm->ractor.main_thread->ec;
942}
943
944static VALUE
945ractor_moved_missing(int argc, VALUE *argv, VALUE self)
946{
947 rb_raise(rb_eRactorMovedError, "can not send any methods to a moved object");
948}
949
950/*
951 * Document-class: Ractor::Error
952 *
953 * The parent class of Ractor-related error classes.
954 */
955
956/*
957 * Document-class: Ractor::ClosedError
958 *
959 * Raised when an attempt is made to send a message to a closed port,
960 * or to retrieve a message from a closed and empty port.
961 * Ports may be closed explicitly with Ractor::Port#close
962 * and are closed implicitly when a Ractor terminates.
963 *
964 * port = Ractor::Port.new
965 * port.close
966 * port << "test" # Ractor::ClosedError
967 * port.receive # Ractor::ClosedError
968 *
969 * ClosedError is a descendant of StopIteration, so the closing of a port will break
970 * out of loops without propagating the error.
971 */
972
973/*
974 * Document-class: Ractor::IsolationError
975 *
976 * Raised on attempt to make a Ractor-unshareable object
977 * Ractor-shareable.
978 */
979
980/*
981 * Document-class: Ractor::RemoteError
982 *
983 * Raised on Ractor#join or Ractor#value if there was an uncaught exception in the Ractor.
984 * Its +cause+ will contain the original exception, and +ractor+ is the original ractor
985 * it was raised in.
986 *
987 * r = Ractor.new { raise "Something weird happened" }
988 *
989 * begin
990 * r.value
991 * rescue => e
992 * p e # => #<Ractor::RemoteError: thrown by remote Ractor.>
993 * p e.ractor == r # => true
994 * p e.cause # => #<RuntimeError: Something weird happened>
995 * end
996 *
997 */
998
999/*
1000 * Document-class: Ractor::MovedError
1001 *
1002 * Raised on an attempt to access an object which was moved in Ractor#send or Ractor::Port#send.
1003 *
1004 * r = Ractor.new { sleep }
1005 *
1006 * ary = [1, 2, 3]
1007 * r.send(ary, move: true)
1008 * ary.inspect
1009 * # Ractor::MovedError (can not send any methods to a moved object)
1010 *
1011 */
1012
1013/*
1014 * Document-class: Ractor::MovedObject
1015 *
1016 * A special object which replaces any value that was moved to another ractor in Ractor#send
1017 * or Ractor::Port#send. Any attempt to access the object results in Ractor::MovedError.
1018 *
1019 * r = Ractor.new { receive }
1020 *
1021 * ary = [1, 2, 3]
1022 * r.send(ary, move: true)
1023 * p Ractor::MovedObject === ary
1024 * # => true
1025 * ary.inspect
1026 * # Ractor::MovedError (can not send any methods to a moved object)
1027 */
1028
1029/*
1030 * Document-class: Ractor::UnsafeError
1031 *
1032 * Raised when Ractor-unsafe C-methods is invoked by a non-main Ractor.
1033 */
1034
1035// Main docs are in ractor.rb, but without this clause there are weird artifacts
1036// in their rendering.
1037/*
1038 * Document-class: Ractor
1039 *
1040 */
1041
1042void
1043Init_Ractor(void)
1044{
1047
1048 rb_eRactorError = rb_define_class_under(rb_cRactor, "Error", rb_eRuntimeError);
1049 rb_eRactorIsolationError = rb_define_class_under(rb_cRactor, "IsolationError", rb_eRactorError);
1050 rb_eRactorRemoteError = rb_define_class_under(rb_cRactor, "RemoteError", rb_eRactorError);
1051 rb_eRactorMovedError = rb_define_class_under(rb_cRactor, "MovedError", rb_eRactorError);
1052 rb_eRactorClosedError = rb_define_class_under(rb_cRactor, "ClosedError", rb_eStopIteration);
1053 rb_eRactorUnsafeError = rb_define_class_under(rb_cRactor, "UnsafeError", rb_eRactorError);
1054
1055 rb_cRactorMovedObject = rb_define_class_under(rb_cRactor, "MovedObject", rb_cBasicObject);
1056 rb_undef_alloc_func(rb_cRactorMovedObject);
1057 rb_define_method(rb_cRactorMovedObject, "method_missing", ractor_moved_missing, -1);
1058
1059 // override methods defined in BasicObject
1060 rb_define_method(rb_cRactorMovedObject, "__send__", 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, "!=", ractor_moved_missing, -1);
1064 rb_define_method(rb_cRactorMovedObject, "__id__", ractor_moved_missing, -1);
1065 rb_define_method(rb_cRactorMovedObject, "equal?", ractor_moved_missing, -1);
1066 rb_define_method(rb_cRactorMovedObject, "instance_eval", ractor_moved_missing, -1);
1067 rb_define_method(rb_cRactorMovedObject, "instance_exec", ractor_moved_missing, -1);
1068
1069 Init_RactorPort();
1070}
1071
1072void
1073rb_ractor_dump(void)
1074{
1075 rb_vm_t *vm = GET_VM();
1076 rb_ractor_t *r = 0;
1077
1078 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
1079 if (r != vm->ractor.main_ractor) {
1080 fprintf(stderr, "r:%u (%s)\n", r->pub.id, ractor_status_str(r->status_));
1081 }
1082 }
1083}
1084
1085VALUE
1087{
1088 if (rb_ractor_main_p()) {
1089 return rb_stdin;
1090 }
1091 else {
1092 rb_ractor_t *cr = GET_RACTOR();
1093 return cr->r_stdin;
1094 }
1095}
1096
1097VALUE
1098rb_ractor_stdout(void)
1099{
1100 if (rb_ractor_main_p()) {
1101 return rb_stdout;
1102 }
1103 else {
1104 rb_ractor_t *cr = GET_RACTOR();
1105 return cr->r_stdout;
1106 }
1107}
1108
1109VALUE
1110rb_ractor_stderr(void)
1111{
1112 if (rb_ractor_main_p()) {
1113 return rb_stderr;
1114 }
1115 else {
1116 rb_ractor_t *cr = GET_RACTOR();
1117 return cr->r_stderr;
1118 }
1119}
1120
1121void
1123{
1124 if (rb_ractor_main_p()) {
1125 rb_stdin = in;
1126 }
1127 else {
1128 rb_ractor_t *cr = GET_RACTOR();
1129 RB_OBJ_WRITE(cr->pub.self, &cr->r_stdin, in);
1130 }
1131}
1132
1133void
1135{
1136 if (rb_ractor_main_p()) {
1137 rb_stdout = out;
1138 }
1139 else {
1140 rb_ractor_t *cr = GET_RACTOR();
1141 RB_OBJ_WRITE(cr->pub.self, &cr->r_stdout, out);
1142 }
1143}
1144
1145void
1147{
1148 if (rb_ractor_main_p()) {
1149 rb_stderr = err;
1150 }
1151 else {
1152 rb_ractor_t *cr = GET_RACTOR();
1153 RB_OBJ_WRITE(cr->pub.self, &cr->r_stderr, err);
1154 }
1155}
1156
1158rb_ractor_hooks(rb_ractor_t *cr)
1159{
1160 return &cr->pub.hooks;
1161}
1162
1163st_table *
1164rb_ractor_targeted_hooks(rb_ractor_t *cr)
1165{
1166 return cr->pub.targeted_hooks;
1167}
1168
1169static void
1170rb_obj_set_shareable_no_assert(VALUE obj)
1171{
1173
1174 if (rb_obj_gen_fields_p(obj)) {
1175 VALUE fields = rb_obj_fields_no_ractor_check(obj);
1176 if (imemo_type_p(fields, imemo_fields)) {
1177 // no recursive mark
1178 FL_SET_RAW(fields, FL_SHAREABLE);
1179 }
1180 }
1181}
1182
1183#ifndef STRICT_VERIFY_SHAREABLE
1184#define STRICT_VERIFY_SHAREABLE 0
1185#endif
1186
1187bool
1188rb_ractor_verify_shareable(VALUE obj)
1189{
1190#if STRICT_VERIFY_SHAREABLE
1191 rb_gc_verify_shareable(obj);
1192#endif
1193 return true;
1194}
1195
1196VALUE
1197rb_obj_set_shareable(VALUE obj)
1198{
1200
1201 rb_obj_set_shareable_no_assert(obj);
1202 RUBY_ASSERT(rb_ractor_verify_shareable(obj));
1203
1204 return obj;
1205}
1206
1208
1209// 2: stop search
1210// 1: skip child
1211// 0: continue
1212
1213enum obj_traverse_iterator_result {
1214 traverse_cont,
1215 traverse_skip,
1216 traverse_stop,
1217};
1218
1219typedef enum obj_traverse_iterator_result (*rb_obj_traverse_enter_func)(VALUE obj);
1220typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(VALUE obj);
1221typedef enum obj_traverse_iterator_result (*rb_obj_traverse_final_func)(VALUE obj);
1222
1223static enum obj_traverse_iterator_result null_leave(VALUE obj);
1224
1226 rb_obj_traverse_enter_func enter_func;
1227 rb_obj_traverse_leave_func leave_func;
1228
1229 st_table *rec;
1230 VALUE rec_hash;
1231};
1232
1233
1235 bool stop;
1236 struct obj_traverse_data *data;
1237};
1238
1239static int obj_traverse_i(VALUE obj, struct obj_traverse_data *data);
1240
1241static int
1242obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
1243{
1245
1246 if (obj_traverse_i(key, d->data)) {
1247 d->stop = true;
1248 return ST_STOP;
1249 }
1250
1251 if (obj_traverse_i(val, d->data)) {
1252 d->stop = true;
1253 return ST_STOP;
1254 }
1255
1256 return ST_CONTINUE;
1257}
1258
1259static void
1260obj_traverse_reachable_i(VALUE obj, void *ptr)
1261{
1263
1264 if (obj_traverse_i(obj, d->data)) {
1265 d->stop = true;
1266 }
1267}
1268
1269static struct st_table *
1270obj_traverse_rec(struct obj_traverse_data *data)
1271{
1272 if (UNLIKELY(!data->rec)) {
1273 data->rec_hash = rb_ident_hash_new();
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 enum obj_traverse_iterator_result
2071copy_enter(VALUE obj, struct obj_traverse_replace_data *data)
2072{
2073 if (rb_ractor_shareable_p(obj)) {
2074 data->replacement = obj;
2075 return traverse_skip;
2076 }
2077 else {
2078 if (!rb_get_alloc_func(rb_obj_class(obj))) {
2079 rb_raise(rb_eRactorError, "can not copy unshareable object %+"PRIsVALUE, obj);
2080 }
2081 data->replacement = rb_obj_clone(obj);
2082 return traverse_cont;
2083 }
2084}
2085
2086static enum obj_traverse_iterator_result
2087copy_leave(VALUE obj, struct obj_traverse_replace_data *data)
2088{
2089 return traverse_cont;
2090}
2091
2092static VALUE
2093ractor_copy(VALUE obj)
2094{
2095 VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave, false);
2096 if (!UNDEF_P(val)) {
2097 return val;
2098 }
2099 else {
2100 rb_raise(rb_eRactorError, "can not copy the object");
2101 }
2102}
2103
2104// Ractor local storage
2105
2107 const struct rb_ractor_local_storage_type *type;
2108 void *main_cache;
2109};
2110
2112 int cnt;
2113 int capa;
2115} freed_ractor_local_keys;
2116
2117static int
2118ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
2119{
2121 if (k->type->mark) (*k->type->mark)((void *)val);
2122 return ST_CONTINUE;
2123}
2124
2125static enum rb_id_table_iterator_result
2126idkey_local_storage_mark_i(VALUE val, void *dmy)
2127{
2128 rb_gc_mark(val);
2129 return ID_TABLE_CONTINUE;
2130}
2131
2132static void
2133ractor_local_storage_mark(rb_ractor_t *r)
2134{
2135 if (r->local_storage) {
2136 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
2137
2138 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
2139 rb_ractor_local_key_t key = freed_ractor_local_keys.keys[i];
2140 st_data_t val, k = (st_data_t)key;
2141 if (st_delete(r->local_storage, &k, &val) &&
2142 (key = (rb_ractor_local_key_t)k)->type->free) {
2143 (*key->type->free)((void *)val);
2144 }
2145 }
2146 }
2147
2148 if (r->idkey_local_storage) {
2149 rb_id_table_foreach_values(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
2150 }
2151
2152 rb_gc_mark(r->local_storage_store_lock);
2153}
2154
2155static int
2156ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
2157{
2159 if (k->type->free) (*k->type->free)((void *)val);
2160 return ST_CONTINUE;
2161}
2162
2163static void
2164ractor_local_storage_free(rb_ractor_t *r)
2165{
2166 if (r->local_storage) {
2167 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
2168 st_free_table(r->local_storage);
2169 }
2170
2171 if (r->idkey_local_storage) {
2172 rb_id_table_free(r->idkey_local_storage);
2173 }
2174}
2175
2176static void
2177rb_ractor_local_storage_value_mark(void *ptr)
2178{
2179 rb_gc_mark((VALUE)ptr);
2180}
2181
2182static const struct rb_ractor_local_storage_type ractor_local_storage_type_null = {
2183 NULL,
2184 NULL,
2185};
2186
2188 NULL,
2189 ruby_xfree,
2190};
2191
2192static const struct rb_ractor_local_storage_type ractor_local_storage_type_value = {
2193 rb_ractor_local_storage_value_mark,
2194 NULL,
2195};
2196
2199{
2201 key->type = type ? type : &ractor_local_storage_type_null;
2202 key->main_cache = (void *)Qundef;
2203 return key;
2204}
2205
2208{
2209 return rb_ractor_local_storage_ptr_newkey(&ractor_local_storage_type_value);
2210}
2211
2212void
2213rb_ractor_local_storage_delkey(rb_ractor_local_key_t key)
2214{
2215 RB_VM_LOCKING() {
2216 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
2217 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
2218 SIZED_REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, freed_ractor_local_keys.capa, freed_ractor_local_keys.cnt);
2219 }
2220 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
2221 }
2222}
2223
2224static bool
2225ractor_local_ref(rb_ractor_local_key_t key, void **pret)
2226{
2227 if (rb_ractor_main_p()) {
2228 if (!UNDEF_P((VALUE)key->main_cache)) {
2229 *pret = key->main_cache;
2230 return true;
2231 }
2232 else {
2233 return false;
2234 }
2235 }
2236 else {
2237 rb_ractor_t *cr = GET_RACTOR();
2238
2239 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
2240 return true;
2241 }
2242 else {
2243 return false;
2244 }
2245 }
2246}
2247
2248static void
2249ractor_local_set(rb_ractor_local_key_t key, void *ptr)
2250{
2251 rb_ractor_t *cr = GET_RACTOR();
2252
2253 if (cr->local_storage == NULL) {
2254 cr->local_storage = st_init_numtable();
2255 }
2256
2257 st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
2258
2259 if (rb_ractor_main_p()) {
2260 key->main_cache = ptr;
2261 }
2262}
2263
2264VALUE
2266{
2267 void *val;
2268 if (ractor_local_ref(key, &val)) {
2269 return (VALUE)val;
2270 }
2271 else {
2272 return Qnil;
2273 }
2274}
2275
2276bool
2278{
2279 if (ractor_local_ref(key, (void **)val)) {
2280 return true;
2281 }
2282 else {
2283 return false;
2284 }
2285}
2286
2287void
2289{
2290 ractor_local_set(key, (void *)val);
2291}
2292
2293void *
2295{
2296 void *ret;
2297 if (ractor_local_ref(key, &ret)) {
2298 return ret;
2299 }
2300 else {
2301 return NULL;
2302 }
2303}
2304
2305void
2307{
2308 ractor_local_set(key, ptr);
2309}
2310
2311#define DEFAULT_KEYS_CAPA 0x10
2312
2313void
2314rb_ractor_finish_marking(void)
2315{
2316 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
2317 SIZED_FREE(freed_ractor_local_keys.keys[i]);
2318 }
2319 freed_ractor_local_keys.cnt = 0;
2320 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
2321 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
2322 SIZED_REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, DEFAULT_KEYS_CAPA, freed_ractor_local_keys.capa);
2323 }
2324}
2325
2326static VALUE
2327ractor_local_value(rb_execution_context_t *ec, VALUE self, VALUE sym)
2328{
2329 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2330 ID id = rb_check_id(&sym);
2331 struct rb_id_table *tbl = cr->idkey_local_storage;
2332 VALUE val;
2333
2334 if (id && tbl && rb_id_table_lookup(tbl, id, &val)) {
2335 return val;
2336 }
2337 else {
2338 return Qnil;
2339 }
2340}
2341
2342static VALUE
2343ractor_local_value_set(rb_execution_context_t *ec, VALUE self, VALUE sym, VALUE val)
2344{
2345 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2346 ID id = SYM2ID(rb_to_symbol(sym));
2347 struct rb_id_table *tbl = cr->idkey_local_storage;
2348
2349 if (tbl == NULL) {
2350 tbl = cr->idkey_local_storage = rb_id_table_create(2);
2351 }
2352 rb_id_table_insert(tbl, id, val);
2353 return val;
2354}
2355
2358 struct rb_id_table *tbl;
2359 ID id;
2360 VALUE sym;
2361};
2362
2363static VALUE
2364ractor_local_value_store_i(VALUE ptr)
2365{
2366 VALUE val;
2368
2369 if (rb_id_table_lookup(data->tbl, data->id, &val)) {
2370 // after synchronization, we found already registered entry
2371 }
2372 else {
2373 val = rb_yield(Qnil);
2374 ractor_local_value_set(data->ec, Qnil, data->sym, val);
2375 }
2376 return val;
2377}
2378
2379static VALUE
2380ractor_local_value_store_if_absent(rb_execution_context_t *ec, VALUE self, VALUE sym)
2381{
2382 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2383 struct ractor_local_storage_store_data data = {
2384 .ec = ec,
2385 .sym = sym,
2386 .id = SYM2ID(rb_to_symbol(sym)),
2387 .tbl = cr->idkey_local_storage,
2388 };
2389 VALUE val;
2390
2391 if (data.tbl == NULL) {
2392 data.tbl = cr->idkey_local_storage = rb_id_table_create(2);
2393 }
2394 else if (rb_id_table_lookup(data.tbl, data.id, &val)) {
2395 // already set
2396 return val;
2397 }
2398
2399 if (!cr->local_storage_store_lock) {
2400 cr->local_storage_store_lock = rb_mutex_new();
2401 }
2402
2403 return rb_mutex_synchronize(cr->local_storage_store_lock, ractor_local_value_store_i, (VALUE)&data);
2404}
2405
2406// shareable_proc
2407
2408static VALUE
2409ractor_shareable_proc(rb_execution_context_t *ec, VALUE replace_self, bool is_lambda)
2410{
2411 if (!rb_ractor_shareable_p(replace_self)) {
2412 rb_raise(rb_eRactorIsolationError, "self should be shareable: %" PRIsVALUE, replace_self);
2413 }
2414 else {
2415 VALUE proc = is_lambda ? rb_block_lambda() : rb_block_proc();
2416 return rb_proc_ractor_make_shareable(rb_proc_dup(proc), replace_self);
2417 }
2418}
2419
2420// Ractor#require
2421
2423 VALUE port;
2424 bool raised;
2425
2426 union {
2427 struct {
2428 VALUE feature;
2429 } require;
2430
2431 struct {
2432 VALUE module;
2433 ID name;
2434 } autoload;
2435 } as;
2436
2437 bool silent;
2438};
2439
2440RUBY_REFERENCES(cross_ractor_require_refs) = {
2441 RUBY_REF_EDGE(struct cross_ractor_require, port),
2442 RUBY_REF_EDGE(struct cross_ractor_require, as.require.feature),
2443 RUBY_REF_END
2444};
2445
2446static const rb_data_type_t cross_ractor_require_data_type = {
2447 "ractor/cross_ractor_require",
2448 {
2449 RUBY_REFS_LIST_PTR(cross_ractor_require_refs),
2451 NULL, // memsize
2452 NULL, // compact
2453 },
2454 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_DECL_MARKING | RUBY_TYPED_EMBEDDABLE
2455};
2456
2457static VALUE
2458require_body(VALUE crr_obj)
2459{
2460 struct cross_ractor_require *crr;
2461 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2462 VALUE feature = crr->as.require.feature;
2463
2464 ID require;
2465 CONST_ID(require, "require");
2466
2467 if (crr->silent) {
2468 int rb_require_internal_silent(VALUE fname);
2469 return INT2NUM(rb_require_internal_silent(feature));
2470 }
2471 else {
2472 return rb_funcallv(Qnil, require, 1, &feature);
2473 }
2474}
2475
2476static VALUE
2477require_rescue(VALUE crr_obj, VALUE errinfo)
2478{
2479 struct cross_ractor_require *crr;
2480 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2481 crr->raised = true;
2482 return errinfo;
2483}
2484
2485static VALUE
2486require_result_send_body(VALUE ary)
2487{
2488 VALUE port = RARRAY_AREF(ary, 0);
2489 VALUE results = RARRAY_AREF(ary, 1);
2490
2491 rb_execution_context_t *ec = GET_EC();
2492
2493 ractor_port_send(ec, port, results, Qfalse);
2494 return Qnil;
2495}
2496
2497static VALUE
2498require_result_send_resuce(VALUE port, VALUE errinfo)
2499{
2500 // TODO: need rescue?
2501 ractor_port_send(GET_EC(), port, errinfo, Qfalse);
2502 return Qnil;
2503}
2504
2505static VALUE
2506ractor_require_protect(VALUE crr_obj, VALUE (*func)(VALUE))
2507{
2508 struct cross_ractor_require *crr;
2509 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2510
2511 const bool silent = crr->silent;
2512
2513 VALUE debug, errinfo;
2514 if (silent) {
2515 debug = ruby_debug;
2516 errinfo = rb_errinfo();
2517 }
2518
2519 // get normal result or raised exception (with crr->raised == true)
2520 VALUE result = rb_rescue2(func, crr_obj, require_rescue, crr_obj, rb_eException, 0);
2521
2522 if (silent) {
2523 ruby_debug = debug;
2524 rb_set_errinfo(errinfo);
2525 }
2526
2527 rb_rescue2(require_result_send_body,
2528 // [port, [result, raised]]
2529 rb_ary_new_from_args(2, crr->port, rb_ary_new_from_args(2, result, crr->raised ? Qtrue : Qfalse)),
2530 require_result_send_resuce, rb_eException, crr->port);
2531
2532 RB_GC_GUARD(crr_obj);
2533 return Qnil;
2534}
2535
2536static VALUE
2537ractor_require_func(void *crr_obj)
2538{
2539 return ractor_require_protect((VALUE)crr_obj, require_body);
2540}
2541
2542VALUE
2543rb_ractor_require(VALUE feature, bool silent)
2544{
2545 // We're about to block on the main ractor, so if we're holding the global lock we'll deadlock.
2546 ASSERT_vm_unlocking();
2547
2548 struct cross_ractor_require *crr;
2549 VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2550 RB_OBJ_SET_SHAREABLE(crr_obj); // TODO: internal data?
2551
2552 // Convert feature to proper file path and make it shareable as fstring
2553 RB_OBJ_WRITE(crr_obj, &crr->as.require.feature, rb_fstring(FilePathValue(feature)));
2554 RB_OBJ_WRITE(crr_obj, &crr->port, rb_ractor_make_shareable(ractor_port_new(GET_RACTOR())));
2555 crr->raised = false;
2556 crr->silent = silent;
2557
2558 rb_execution_context_t *ec = GET_EC();
2559 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
2560 rb_ractor_interrupt_exec(main_r, ractor_require_func, (void *)crr_obj, rb_interrupt_exec_flag_value_data);
2561
2562 // wait for require done
2563 VALUE results = ractor_port_receive(ec, crr->port);
2564 ractor_port_close(ec, crr->port);
2565
2566 VALUE exc = rb_ary_pop(results);
2567 VALUE result = rb_ary_pop(results);
2568 RB_GC_GUARD(crr_obj);
2569
2570 if (RTEST(exc)) {
2571 rb_exc_raise(result);
2572 }
2573 else {
2574 return result;
2575 }
2576}
2577
2578static VALUE
2579ractor_require(rb_execution_context_t *ec, VALUE self, VALUE feature)
2580{
2581 return rb_ractor_require(feature, false);
2582}
2583
2584static VALUE
2585autoload_load_body(VALUE crr_obj)
2586{
2587 struct cross_ractor_require *crr;
2588 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2589 return rb_autoload_load(crr->as.autoload.module, crr->as.autoload.name);
2590}
2591
2592static VALUE
2593ractor_autoload_load_func(void *crr_obj)
2594{
2595 return ractor_require_protect((VALUE)crr_obj, autoload_load_body);
2596}
2597
2598VALUE
2599rb_ractor_autoload_load(VALUE module, ID name)
2600{
2601 struct cross_ractor_require *crr;
2602 VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
2603 RB_OBJ_SET_SHAREABLE(crr_obj); // TODO: internal data?
2604
2605 RB_OBJ_WRITE(crr_obj, &crr->as.autoload.module, module);
2606 RB_OBJ_WRITE(crr_obj, &crr->as.autoload.name, name);
2607 RB_OBJ_WRITE(crr_obj, &crr->port, rb_ractor_make_shareable(ractor_port_new(GET_RACTOR())));
2608
2609 rb_execution_context_t *ec = GET_EC();
2610 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
2611 rb_ractor_interrupt_exec(main_r, ractor_autoload_load_func, (void *)crr_obj, rb_interrupt_exec_flag_value_data);
2612
2613 // wait for require done
2614 VALUE results = ractor_port_receive(ec, crr->port);
2615 ractor_port_close(ec, crr->port);
2616
2617 VALUE exc = rb_ary_pop(results);
2618 VALUE result = rb_ary_pop(results);
2619 RB_GC_GUARD(crr_obj);
2620
2621 if (RTEST(exc)) {
2622 rb_exc_raise(result);
2623 }
2624 else {
2625 return result;
2626 }
2627}
2628
2629#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:1596
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1627
#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 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:660
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1416
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:1469
VALUE rb_eException
Mother of all exceptions.
Definition error.c:1410
VALUE rb_cObject
Object class.
Definition object.c:61
VALUE rb_cRactor
Ractor class.
Definition ractor.c:24
VALUE rb_stdin
STDIN constant.
Definition io.c:201
VALUE rb_stderr
STDERR constant.
Definition io.c:201
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
Definition globals.h:174
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:264
VALUE rb_cBasicObject
BasicObject class.
Definition object.c:59
VALUE rb_obj_clone(VALUE obj)
Produces a shallow copy of the given object.
Definition object.c:527
VALUE rb_stdout
STDOUT constant.
Definition io.c:201
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:615
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:603
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1117
#define RGENGC_WB_PROTECTED_STRUCT
This is a compile-time flag to enable/disable write barrier for struct RStruct.
Definition gc.h:468
#define RGENGC_WB_PROTECTED_STRING
This is a compile-time flag to enable/disable write barrier for struct RString.
Definition gc.h:479
#define RGENGC_WB_PROTECTED_HASH
This is a compile-time flag to enable/disable write barrier for struct RHash.
Definition gc.h:457
#define RGENGC_WB_PROTECTED_MATCH
This is a compile-time flag to enable/disable write barrier for struct RMatch.
Definition gc.h:512
#define RGENGC_WB_PROTECTED_ARRAY
This is a compile-time flag to enable/disable write barrier for struct RArray.
Definition gc.h:446
#define RGENGC_WB_PROTECTED_COMPLEX
This is a compile-time flag to enable/disable write barrier for struct RComplex.
Definition gc.h:545
#define RGENGC_WB_PROTECTED_FLOAT
This is a compile-time flag to enable/disable write barrier for struct RFloat.
Definition gc.h:534
#define RGENGC_WB_PROTECTED_RATIONAL
This is a compile-time flag to enable/disable write barrier for struct RRational.
Definition gc.h:556
#define RGENGC_WB_PROTECTED_REGEXP
This is a compile-time flag to enable/disable write barrier for struct RRegexp.
Definition gc.h:501
#define RGENGC_WB_PROTECTED_OBJECT
This is a compile-time flag to enable/disable write barrier for struct RObject.
Definition gc.h:490
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_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:3285
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1705
rb_alloc_func_t rb_get_alloc_func(VALUE klass)
Queries the allocator function of a class.
Definition vm_method.c:1711
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition symbol.c:1171
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:2187
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:2294
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:2306
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:2198
VALUE rb_ractor_stdin(void)
Queries the standard input of the current Ractor that is calling this function.
Definition ractor.c:1086
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:1146
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:2288
bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
Queries the key.
Definition ractor.c:2277
#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:2207
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:1134
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:1122
VALUE rb_ractor_local_storage_value(rb_ractor_local_key_t key)
Queries the key.
Definition ractor.c:2265
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1372
rb_block_call_func * rb_block_call_func_t
Shorthand type that represents an iterator-written-in-C function pointer.
Definition iterator.h:88
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition memory.h:360
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
VALUE rb_proc_new(type *q, VALUE w)
Creates a rb_cProc instance.
VALUE type(ANYARGS)
ANYARGS-ed function type.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
void rb_ivar_foreach(VALUE q, int_type *w, VALUE e)
Iteration over each instance variable of the object.
VALUE rb_rescue2(type *q, VALUE w, type *e, VALUE r,...)
An equivalent of rescue clause.
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:281
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:386
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
#define DATA_PTR(obj)
Convenient getter macro.
Definition rdata.h:67
#define RUBY_DEFAULT_FREE
This is a value you can set to RData::dfree.
Definition rdata.h:78
#define RHASH_SET_IFNONE(h, ifnone)
Destructively updates the default value of the hash.
Definition rhash.h:92
#define RHASH_IFNONE(h)
Definition rhash.h:59
#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:652
#define RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
Definition rtypeddata.h:119
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:736
static const rb_data_type_t * RTYPEDDATA_TYPE(VALUE obj)
Queries for the type of given object.
Definition rtypeddata.h:670
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:514
#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:561
#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:211
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