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