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