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