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