10#include "ractor_core.h"
11#include "internal/array.h"
12#include "internal/class.h"
13#include "internal/complex.h"
14#include "internal/cont.h"
15#include "internal/error.h"
16#include "internal/gc.h"
17#include "internal/hash.h"
18#include "internal/object.h"
19#include "internal/array.h"
20#include "internal/string.h"
21#include "internal/variable.h"
22#include "eval_intern.h"
23#include "internal/io.h"
24#include "internal/marshal.h"
25#include "internal/ractor.h"
26#include "internal/rational.h"
27#include "internal/re.h"
28#include "internal/struct.h"
29#include "internal/st.h"
30#include "internal/thread.h"
31#include "internal/vm.h"
39static VALUE rb_cRactorSelector;
41VALUE rb_eRactorUnsafeError;
42VALUE rb_eRactorIsolationError;
43static VALUE rb_eRactorError;
44static VALUE rb_eRactorRemoteError;
45static VALUE rb_eRactorMovedError;
46static VALUE rb_eRactorClosedError;
47static VALUE rb_cRactorMovedObject;
49static ID id_marshal_dump, id_marshal_load;
50static ID id_dump, id_load, id_dump_data, id_load_data;
52static void vm_ractor_blocking_cnt_inc(
rb_vm_t *vm,
rb_ractor_t *r,
const char *file,
int line);
55#if RACTOR_CHECK_MODE > 0
56bool rb_ractor_ignore_belonging_flag =
false;
64#if RACTOR_CHECK_MODE > 0
66 if (ec != NULL && r->sync.locked_by == rb_ractor_self(rb_ec_ractor_ptr(ec))) {
67 rb_bug(
"recursive ractor locking");
75#if RACTOR_CHECK_MODE > 0
77 if (ec != NULL && r->sync.locked_by != rb_ractor_self(rb_ec_ractor_ptr(ec))) {
78 rp(r->sync.locked_by);
79 rb_bug(
"ractor lock is not acquired.");
85ractor_lock(
rb_ractor_t *r,
const char *file,
int line)
87 RUBY_DEBUG_LOG2(file, line,
"locking r:%"PRI_SERIALT_PREFIX
"u%s", r->pub.id, rb_current_ractor_raw(
false) == r ?
" (self)" :
"");
89 ASSERT_ractor_unlocking(r);
95 VM_ASSERT(!cr->malloc_gc_disabled);
96 cr->malloc_gc_disabled =
true;
99#if RACTOR_CHECK_MODE > 0
102 r->sync.locked_by = rb_ractor_self(cr);
106 RUBY_DEBUG_LOG2(file, line,
"locked r:%"PRI_SERIALT_PREFIX
"u%s", r->pub.id, rb_current_ractor_raw(
false) == r ?
" (self)" :
"");
110ractor_lock_self(
rb_ractor_t *cr,
const char *file,
int line)
112 VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline()));
113#if RACTOR_CHECK_MODE > 0
114 VM_ASSERT(cr->sync.locked_by != cr->pub.self);
116 ractor_lock(cr, file, line);
120ractor_unlock(
rb_ractor_t *r,
const char *file,
int line)
122 ASSERT_ractor_locking(r);
123#if RACTOR_CHECK_MODE > 0
124 r->sync.locked_by =
Qnil;
130 VM_ASSERT(cr->malloc_gc_disabled);
131 cr->malloc_gc_disabled =
false;
136 RUBY_DEBUG_LOG2(file, line,
"r:%"PRI_SERIALT_PREFIX
"u%s", r->pub.id, rb_current_ractor_raw(
false) == r ?
" (self)" :
"");
140ractor_unlock_self(
rb_ractor_t *cr,
const char *file,
int line)
142 VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline()));
143#if RACTOR_CHECK_MODE > 0
144 VM_ASSERT(cr->sync.locked_by == cr->pub.self);
146 ractor_unlock(cr, file, line);
149#define RACTOR_LOCK(r) ractor_lock(r, __FILE__, __LINE__)
150#define RACTOR_UNLOCK(r) ractor_unlock(r, __FILE__, __LINE__)
151#define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
152#define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
163 RACTOR_UNLOCK_SELF(r);
169ractor_status_str(
enum ractor_status status)
172 case ractor_created:
return "created";
173 case ractor_running:
return "running";
174 case ractor_blocking:
return "blocking";
175 case ractor_terminated:
return "terminated";
177 rb_bug(
"unreachable");
181ractor_status_set(
rb_ractor_t *r,
enum ractor_status status)
183 RUBY_DEBUG_LOG(
"r:%"PRI_SERIALT_PREFIX
"u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
186 if (r->status_ != ractor_created) {
187 VM_ASSERT(r == GET_RACTOR());
192 switch (r->status_) {
194 VM_ASSERT(status == ractor_blocking);
197 VM_ASSERT(status == ractor_blocking||
198 status == ractor_terminated);
200 case ractor_blocking:
201 VM_ASSERT(status == ractor_running);
203 case ractor_terminated:
204 rb_bug(
"unreachable");
212ractor_status_p(
rb_ractor_t *r,
enum ractor_status status)
214 return rb_ractor_status_p(r, status);
219static void ractor_local_storage_mark(
rb_ractor_t *r);
220static void ractor_local_storage_free(
rb_ractor_t *r);
224static size_t ractor_sync_memsize(
const rb_ractor_t *r);
228mark_targeted_hook_list(st_data_t key, st_data_t value, st_data_t _arg)
232 if (hook_list->type == hook_list_type_targeted_iseq) {
233 rb_gc_mark((
VALUE)key);
237 RUBY_ASSERT(hook_list->type == hook_list_type_targeted_def);
238 rb_gc_mark(def->body.bmethod.proc);
240 rb_hook_list_mark(hook_list);
248 rb_gc_mark(th->self);
252 if (th->root_fiber) {
253 VALUE root_fiber_self = rb_fiberptr_self(th->root_fiber);
254 if (root_fiber_self) rb_gc_mark(root_fiber_self);
259 VALUE ec_fiber_self = (th->ec && th->ec->fiber_ptr) ? rb_fiberptr_self(th->ec->fiber_ptr) : 0;
261 rb_gc_mark(ec_fiber_self);
264 rb_execution_context_mark(th->ec);
269 rb_thread_mark_owned_roots(th);
278 rb_gc_mark(r->r_stdin);
279 rb_gc_mark(r->r_stdout);
280 rb_gc_mark(r->r_stderr);
281 rb_gc_mark(r->verbose);
282 rb_gc_mark(r->debug);
289 rb_hook_list_mark(&r->pub.hooks);
290 if (r->pub.targeted_hooks.num_entries) {
291 st_foreach(&r->pub.targeted_hooks, mark_targeted_hook_list, 0);
294 if (r->threads.cnt > 0) {
296 ccan_list_for_each(&r->threads.set, th, lt_node) {
297 VM_ASSERT(th != NULL);
298 ractor_mark_thread(th);
309 if (dying_th) ractor_mark_thread(dying_th);
311 ractor_local_storage_mark(r);
315ractor_mark(
void *ptr)
327 rb_gc_mark(r->sync.default_port_value);
331 if (!rb_gc_multi_objspace_p()) {
332 ractor_mark_unshareable_parts(r);
333 rb_ractor_mark_terminated_join_value(r);
335 else if (rb_gc_during_global_gc_p()) {
340 rb_ractor_mark_terminated_join_value(r);
357 rb_ractor_mark_terminated_join_value(r);
358 rb_gc_mark_vm_stack_values((
long)r->registered_marks_cnt, r->registered_marks);
359 rb_gc_mark_registered_addrs(r,
true);
368 VM_ASSERT(r == rb_current_ractor_raw(
false) || rb_gc_during_global_gc_p());
369 VM_ASSERT(!rb_ractor_status_p(r, ractor_terminated));
370 ractor_mark_unshareable_parts(r);
375 rb_gc_mark_vm_stack_values((
long)r->registered_marks_cnt, r->registered_marks);
382rb_ractor_mark_terminated_join_value(
rb_ractor_t *r)
387 rb_gc_mark_vm_stack_values((
long)numberof(slots), slots);
396 if (src->registered_marks_cnt == 0)
return;
397 size_t need = dst->registered_marks_cnt + src->registered_marks_cnt;
398 if (need > dst->registered_marks_capa) {
399 size_t nc = dst->registered_marks_capa ? dst->registered_marks_capa : 64;
400 while (nc < need) nc *= 2;
401 VALUE *p = realloc(dst->registered_marks, nc *
sizeof(
VALUE));
402 if (!p) rb_bug(
"rb_ractor_absorb_registered_marks: out of memory");
403 dst->registered_marks = p;
404 dst->registered_marks_capa = nc;
406 MEMCPY(dst->registered_marks + dst->registered_marks_cnt,
407 src->registered_marks,
VALUE, src->registered_marks_cnt);
408 dst->registered_marks_cnt = need;
409 src->registered_marks_cnt = 0;
418 if (src->registered_addrs_cnt > 0) {
419 size_t need = dst->registered_addrs_cnt + src->registered_addrs_cnt;
420 if (need > dst->registered_addrs_capa) {
421 size_t nc = dst->registered_addrs_capa ? dst->registered_addrs_capa : 64;
422 while (nc < need) nc *= 2;
425 if (!p) rb_bug(
"rb_ractor_absorb_registered_addrs_without_gc: out of memory");
426 dst->registered_addrs = p;
427 dst->registered_addrs_capa = nc;
429 MEMCPY(dst->registered_addrs + dst->registered_addrs_cnt,
431 dst->registered_addrs_cnt = need;
432 src->registered_addrs_cnt = 0;
433 rb_gc_registered_addrs_enroll_without_gc(vm, dst);
435 rb_gc_registered_addrs_unenroll_without_gc(vm, src);
440free_targeted_hook_lists(st_data_t key, st_data_t val, st_data_t _arg)
443 rb_hook_list_free(hook_list);
448free_targeted_hooks(
st_table *hooks_tbl)
450 st_foreach(hooks_tbl, free_targeted_hook_lists, 0);
456ractor_free(
void *ptr)
459 RUBY_DEBUG_LOG(
"free r:%"PRI_SERIALT_PREFIX
"u", rb_ractor_id(r));
461 free_targeted_hooks(&r->pub.targeted_hooks);
462 rb_thread_sched_destroy(&r->threads.sched);
464 ractor_local_storage_free(r);
465 rb_hook_list_free(&r->pub.hooks);
466 rb_st_free_embedded_table(&r->pub.targeted_hooks);
468 if (r->newobj_cache) {
471 rb_gc_ractor_cache_free(r->newobj_cache);
472 r->newobj_cache = NULL;
479 if (r->objspace && !r->main_ractor) {
480 rb_gc_objspace_disown(r->objspace);
486 if (r->in_terminated_set) {
488 ccan_list_del(&r->vmlr_node);
489 r->in_terminated_set =
false;
496 if (!r->main_ractor) {
497 rb_ractor_absorb_registered_marks(GET_VM()->ractor.main_ractor, r);
498 rb_ractor_absorb_registered_addrs_without_gc(GET_VM()->ractor.main_ractor, r);
502 rb_gc_registered_addrs_unenroll_without_gc(GET_VM(), r);
505 free(r->registered_marks);
506 r->registered_marks = NULL;
507 r->registered_marks_cnt = r->registered_marks_capa = 0;
509 free(r->registered_addrs);
510 r->registered_addrs = NULL;
511 r->registered_addrs_cnt = r->registered_addrs_capa = 0;
513 if (!r->main_ractor) {
519targeted_hook_list_memsize_i(st_data_t key, st_data_t val, st_data_t arg)
521 size_t *size = (
size_t *)arg;
524 *size +=
sizeof(
rb_hook_list_t) + rb_hook_list_memsize(hook_list);
530ractor_memsize(
const void *ptr)
535 size += ractor_sync_memsize(r);
537 size += rb_st_memsize(&r->pub.targeted_hooks) -
sizeof(
struct st_table);
538 st_foreach(&r->pub.targeted_hooks, targeted_hook_list_memsize_i, (st_data_t)&size);
539 size += rb_hook_list_memsize(&r->pub.hooks);
541 if (r->local_storage) {
542 size += st_memsize(r->local_storage);
544 if (r->idkey_local_storage) {
545 size += rb_id_table_memsize(r->idkey_local_storage);
548 size += r->registered_marks_capa *
sizeof(
VALUE);
555ractor_update_references(
void *ptr)
567 ractor_update_references,
575 if (rb_typeddata_is_kind_of(gv, &ractor_data_type)) {
584RACTOR_PTR(
VALUE self)
586 VM_ASSERT(rb_ractor_p(self));
591#define MAIN_RACTOR_ID 1
592static rb_serial_t ractor_last_id = MAIN_RACTOR_ID;
594#include "ractor_sync.c"
605 VM_ASSERT(RB_VM_LOCKED_P() || !rb_multi_ractor_p());
606 return ++ractor_last_id;
612 RUBY_DEBUG_LOG(
"r:%"PRI_SERIALT_PREFIX
"u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
613 VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
618 if (vm->ractor.cnt == 1) {
622 ccan_list_add_tail(&vm->ractor.set, &r->vmlr_node);
625 if (r->newobj_cache) {
626 VM_ASSERT(r == ruby_single_main_ractor);
629 r->newobj_cache = rb_gc_ractor_cache_alloc(r);
634cancel_single_ractor_mode(
void)
637 RUBY_DEBUG_LOG(
"enable multi-ractor mode");
639 ruby_single_main_ractor = NULL;
640 rb_yjit_invalidate_single_ractor();
641 rb_zjit_invalidate_single_ractor();
643 ASSERT_vm_unlocking();
650 VM_ASSERT(ractor_status_p(r, ractor_created));
652 if (rb_multi_ractor_p()) {
655 vm_insert_ractor0(vm, r,
false);
656 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
661 if (cur && cur->creating_child_objspace == r->objspace) {
662 cur->creating_child_objspace = NULL;
668 if (vm->ractor.cnt == 0) {
670 vm_insert_ractor0(vm, r,
true);
671 ractor_status_set(r, ractor_blocking);
672 ractor_status_set(r, ractor_running);
675 cancel_single_ractor_mode();
676 vm_insert_ractor0(vm, r,
true);
677 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
682 if (cur && cur->creating_child_objspace == r->objspace) {
683 cur->creating_child_objspace = NULL;
692 VM_ASSERT(ractor_status_p(cr, ractor_running));
693 VM_ASSERT(vm->ractor.cnt > 1);
694 VM_ASSERT(cr->threads.cnt == 1);
698 RUBY_DEBUG_LOG(
"ractor.cnt:%u-- terminate_waiting:%d",
699 vm->ractor.cnt, vm->ractor.sync.terminate_waiting);
701 VM_ASSERT(vm->ractor.cnt > 0);
702 ccan_list_del(&cr->vmlr_node);
707 if (!rb_gc_multi_objspace_p()) {
709 ccan_list_add(&vm->ractor.terminated_set, &cr->vmlr_node);
710 cr->in_terminated_set =
true;
714 if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
718 rb_gc_ractor_cache_free(cr->newobj_cache);
719 cr->newobj_cache = NULL;
729 rb_gc_objspace_retire(&cr->objspace);
733 ractor_status_set(cr, ractor_terminated);
745 VM_ASSERT(cr != GET_VM()->ractor.main_ractor);
746 VM_ASSERT(th->ec != NULL);
749 const bool fiber_wrapped = fiber && rb_fiberptr_self(fiber) != 0;
754 cr->postmortem = rb_gc_multi_objspace_p() && !rb_thread_event_hooks_registered_p();
755 rb_gc_objspace_postmortem_self();
759 pf->th = (th->self == 0) ? th : NULL;
760 pf->fiber = (fiber_wrapped && rb_fiberptr_self(fiber) == 0) ? fiber : NULL;
766 if (pf->fiber == NULL && pf->th == NULL)
return;
771#ifdef RB_THREAD_LOCAL_SPECIFIER
772 rb_current_ec_set(NULL);
774 native_tls_set(ruby_current_ec_key, NULL);
778 if (pf->fiber) rb_fiber_free_body(pf->fiber);
779 if (pf->th) rb_thread_free_body(pf->th);
783ractor_alloc(
VALUE klass)
789 r->next_ec_serial = 1;
790 VM_ASSERT(ractor_status_p(r, ractor_created));
797 .pub.id = MAIN_RACTOR_ID,
804rb_ractor_main_alloc(
void)
809 ruby_single_main_ractor = r;
814#if defined(HAVE_WORKING_FORK)
822 vm->ractor.blocking_cnt = 0;
825 rb_gc_disable_holders_atfork();
829 th->ractor->creating_child_objspace = NULL;
830 ruby_single_main_ractor = th->ractor;
831 th->ractor->status_ = ractor_created;
833 rb_ractor_living_threads_init(th->ractor);
834 rb_ractor_living_threads_insert(th->ractor, th);
836 VM_ASSERT(vm->ractor.blocking_cnt == 0);
837 VM_ASSERT(vm->ractor.cnt == 1);
843 rb_gc_ractor_cache_free(r->newobj_cache);
844 r->newobj_cache = NULL;
845 r->status_ = ractor_terminated;
847 r->threads.dying_th = NULL;
848 if (!rb_gc_multi_objspace_p()) {
849 ccan_list_del(&r->vmlr_node);
850 ccan_list_add(&vm->ractor.terminated_set, &r->vmlr_node);
851 r->in_terminated_set =
true;
857 rb_gc_objspace_retire(&r->objspace);
859 ractor_sync_terminate_atfork(vm, r);
868 ccan_list_head_init(&r->threads.set);
870 r->threads.blocking_cnt = 0;
871 r->threads.terminating =
false;
873 r->threads.dying_th = NULL;
880 st_init_existing_numtable_with_size(&r->pub.targeted_hooks, 0);
881 r->pub.hooks.type = hook_list_type_ractor_local;
884 rb_thread_sched_init(&r->threads.sched,
false);
885 rb_ractor_living_threads_init(r);
891 enc = rb_enc_get(name);
892 if (!rb_enc_asciicompat(enc)) {
893 rb_raise(rb_eArgError,
"ASCII incompatible encoding (%s)",
910 r->threads.main = th;
911 rb_ractor_living_threads_insert(r, th);
912 rb_ractor_setup_default_port(r);
920 VALUE rv = ractor_alloc(self);
922 ractor_init(r, name, loc);
925 r->pub.id = ractor_next_id();
927 RUBY_DEBUG_LOG(
"r:%"PRI_SERIALT_PREFIX
"u", r->pub.id);
930 r->verbose = cr->verbose;
931 r->debug = cr->debug;
935 r->objspace = rb_gc_objspace_alloc();
937 rb_thread_create_ractor(r, args, block);
948 return ractor_create(rb_current_ec_noinline(), klass, loc, name, args, block);
955 ractor_notify_exit(ec, cr, result, exc);
963 ractor_postmortem_collect(th, pf);
964 ractor_send_exit_tokens(th->ec, th->ractor);
971 ractor_atexit(ec, cr, result,
false);
978 ractor_atexit(ec, cr, ec->errinfo,
true);
988 VM_ASSERT(cr->threads.main != NULL);
989 cr->threads.main = NULL;
996 for (
int i=0; i<
len; i++) {
997 ptr[i] = ractor_receive(ec, ractor_default_port(r), NULL);
1005 for (
int i=0; i<
len; i++) {
1006 ractor_send(ec, ractor_default_port(r),
RARRAY_AREF(args, i),
false);
1011rb_ractor_main_p_(
void)
1013 VM_ASSERT(rb_multi_ractor_p());
1015 return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
1021 return r->threads.cnt;
1026rb_ractor_thread_list(
void)
1032 ccan_list_for_each(&r->threads.set, th, lt_node) {
1033 switch (th->status) {
1034 case THREAD_RUNNABLE:
1035 case THREAD_STOPPED:
1036 case THREAD_STOPPED_FOREVER:
1049 VM_ASSERT(th != NULL);
1053 RUBY_DEBUG_LOG(
"r(%"PRI_SERIALT_PREFIX
"u)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
1054 ccan_list_add_tail(&r->threads.set, &th->lt_node);
1060 if (r->threads.cnt == 1) {
1061 VM_ASSERT(ractor_status_p(r, ractor_created));
1062 vm_insert_ractor(th->vm, r);
1069 ractor_status_set(r, ractor_blocking);
1071 RUBY_DEBUG_LOG2(file, line,
"vm->ractor.blocking_cnt:%d++", vm->ractor.blocking_cnt);
1072 vm->ractor.blocking_cnt++;
1073 VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
1077rb_vm_ractor_blocking_cnt_inc(
rb_vm_t *vm,
rb_ractor_t *cr,
const char *file,
int line)
1079 ASSERT_vm_locking();
1080 VM_ASSERT(GET_RACTOR() == cr);
1081 vm_ractor_blocking_cnt_inc(vm, cr, file, line);
1085rb_vm_ractor_blocking_cnt_dec(
rb_vm_t *vm,
rb_ractor_t *cr,
const char *file,
int line)
1087 ASSERT_vm_locking();
1088 VM_ASSERT(GET_RACTOR() == cr);
1090 RUBY_DEBUG_LOG2(file, line,
"vm->ractor.blocking_cnt:%d--", vm->ractor.blocking_cnt);
1091 VM_ASSERT(vm->ractor.blocking_cnt > 0);
1092 vm->ractor.blocking_cnt--;
1094 ractor_status_set(cr, ractor_running);
1105 ccan_list_del(&th->lt_node);
1113 VM_ASSERT(vm->ractor.cnt > 1);
1114 ccan_list_del(&r->vmlr_node);
1119 VM_ASSERT(r->status_ == ractor_blocking);
1120 VM_ASSERT(vm->ractor.blocking_cnt > 0);
1121 vm->ractor.blocking_cnt--;
1123 rb_gc_ractor_cache_free(r->newobj_cache);
1124 r->newobj_cache = NULL;
1127 rb_gc_objspace_disown(r->objspace);
1130 r->status_ = ractor_terminated;
1138 VM_ASSERT(cr == GET_RACTOR());
1139 RUBY_DEBUG_LOG(
"r->threads.cnt:%d--", cr->threads.cnt);
1141 if (cr->threads.cnt == 1) {
1142 vm_remove_ractor(th->vm, cr);
1147 ccan_list_del(&th->lt_node);
1155rb_ractor_blocking_threads_inc(
rb_ractor_t *cr,
const char *file,
int line)
1157 RUBY_DEBUG_LOG2(file, line,
"cr->threads.blocking_cnt:%d++", cr->threads.blocking_cnt);
1159 VM_ASSERT(cr->threads.cnt > 0);
1160 VM_ASSERT(cr == GET_RACTOR());
1162 cr->threads.blocking_cnt++;
1166rb_ractor_blocking_threads_dec(
rb_ractor_t *cr,
const char *file,
int line)
1168 RUBY_DEBUG_LOG2(file, line,
1169 "r->threads.blocking_cnt:%d--, r->threads.cnt:%u",
1170 cr->threads.blocking_cnt, cr->threads.cnt);
1172 VM_ASSERT(cr == GET_RACTOR());
1174 cr->threads.blocking_cnt--;
1178rb_ractor_vm_barrier_interrupt_running_thread(
rb_ractor_t *r)
1180 VM_ASSERT(r != GET_RACTOR());
1181 ASSERT_ractor_unlocking(r);
1182 ASSERT_vm_locking();
1186 if (ractor_status_p(r, ractor_running)) {
1189 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec);
1197rb_ractor_terminate_interrupt_main_thread(
rb_ractor_t *r)
1199 VM_ASSERT(r != GET_RACTOR());
1200 ASSERT_ractor_unlocking(r);
1201 ASSERT_vm_locking();
1205 if (main_th->status != THREAD_KILLED) {
1206 RUBY_VM_SET_TERMINATE_INTERRUPT(main_th->ec);
1207 rb_threadptr_interrupt(main_th);
1210 RUBY_DEBUG_LOG(
"killed (%p)", (
void *)main_th);
1218ractor_terminal_interrupt_all(
rb_vm_t *vm)
1220 if (vm->ractor.cnt > 1) {
1223 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
1224 if (r != vm->ractor.main_ractor) {
1225 RUBY_DEBUG_LOG(
"r:%"PRI_SERIALT_PREFIX
"u", rb_ractor_id(r));
1226 rb_ractor_terminate_interrupt_main_thread(r);
1236rb_ractor_terminate_all(
void)
1241 RUBY_DEBUG_LOG(
"ractor.cnt:%d", (
int)vm->ractor.cnt);
1243 VM_ASSERT(cr == GET_RACTOR());
1247 ractor_terminal_interrupt_all(vm);
1250 rb_thread_terminate_all(GET_THREAD());
1254 while (vm->ractor.cnt > 1) {
1255 RUBY_DEBUG_LOG(
"terminate_waiting:%d", vm->ractor.sync.terminate_waiting);
1256 vm->ractor.sync.terminate_waiting =
true;
1259 rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
1260 rb_del_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
1261 rb_ractor_sched_wait_terminate(vm, &vm->ractor.sync.terminate_cond, 1000 );
1262 while (vm->ractor.sched.barrier_is_waiting) {
1269 RB_VM_LOCK_ENTER_LEV_NB(&lev);
1271 rb_add_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
1272 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
1274 ractor_terminal_interrupt_all(vm);
1281 rb_gc_objspace_absorb_all_zombies();
1285rb_vm_main_ractor_ec(
rb_vm_t *vm)
1302 if (running_ec) {
return running_ec; }
1303 return vm->ractor.main_thread->ec;
1307ractor_moved_missing(
int argc,
VALUE *argv,
VALUE self)
1309 rb_raise(rb_eRactorMovedError,
"can not send any methods to a moved object");
1411 rb_eRactorIsolationError = rb_define_class_under(
rb_cRactor,
"IsolationError", rb_eRactorError);
1412 rb_eRactorRemoteError = rb_define_class_under(
rb_cRactor,
"RemoteError", rb_eRactorError);
1413 rb_eRactorMovedError = rb_define_class_under(
rb_cRactor,
"MovedError", rb_eRactorError);
1415 rb_eRactorUnsafeError = rb_define_class_under(
rb_cRactor,
"UnsafeError", rb_eRactorError);
1419 rb_define_method(rb_cRactorMovedObject,
"method_missing", ractor_moved_missing, -1);
1422 rb_define_method(rb_cRactorMovedObject,
"__send__", ractor_moved_missing, -1);
1426 rb_define_method(rb_cRactorMovedObject,
"__id__", ractor_moved_missing, -1);
1427 rb_define_method(rb_cRactorMovedObject,
"equal?", ractor_moved_missing, -1);
1428 rb_define_method(rb_cRactorMovedObject,
"instance_eval", ractor_moved_missing, -1);
1429 rb_define_method(rb_cRactorMovedObject,
"instance_exec", ractor_moved_missing, -1);
1447 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
1448 if (r != vm->ractor.main_ractor) {
1449 fprintf(stderr,
"r:%"PRI_SERIALT_PREFIX
"u (%s)\n", r->pub.id, ractor_status_str(r->status_));
1457 if (rb_ractor_main_p()) {
1462 if (UNLIKELY(cr->r_stdin == 0)) {
1463 cr->r_stdin = rb_io_prep_stdin();
1470rb_ractor_stdout(
void)
1472 if (rb_ractor_main_p()) {
1477 if (UNLIKELY(cr->r_stdout == 0)) {
1478 cr->r_stdout = rb_io_prep_stdout();
1480 return cr->r_stdout;
1485rb_ractor_stderr(
void)
1487 if (rb_ractor_main_p()) {
1492 if (UNLIKELY(cr->r_stderr == 0)) {
1493 cr->r_stderr = rb_io_prep_stderr();
1495 return cr->r_stderr;
1502 if (rb_ractor_main_p()) {
1514 if (rb_ractor_main_p()) {
1526 if (rb_ractor_main_p()) {
1539 return &cr->pub.targeted_hooks;
1543rb_obj_set_shareable_no_assert(
VALUE obj)
1550 rb_gc_obj_became_shareable(obj);
1554 bool froze_ivars =
false;
1558 RBASIC_SET_SHAPE_ID(obj, rb_shape_transition_frozen(RBASIC_SHAPE_ID(obj)));
1569 VALUE fields = rb_obj_fields_no_ractor_check(obj);
1570 if (imemo_type_p(fields, imemo_fields)) {
1573 rb_gc_obj_became_shareable(fields);
1575 if (froze_ivars) RBASIC_SET_SHAPE_ID(fields, RBASIC_SHAPE_ID(obj));
1579 rb_imemo_fields_record_shrefs(fields);
1584#ifndef STRICT_VERIFY_SHAREABLE
1585#define STRICT_VERIFY_SHAREABLE 0
1589rb_ractor_verify_shareable(
VALUE obj)
1591#if STRICT_VERIFY_SHAREABLE
1592 rb_gc_verify_shareable(obj);
1602 rb_obj_set_shareable_no_assert(obj);
1614enum obj_traverse_iterator_result {
1620typedef enum obj_traverse_iterator_result (*rb_obj_traverse_enter_func)(
VALUE obj);
1621typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(
VALUE obj);
1622typedef enum obj_traverse_iterator_result (*rb_obj_traverse_final_func)(
VALUE obj);
1624static enum obj_traverse_iterator_result null_leave(
VALUE obj);
1627 rb_obj_traverse_enter_func enter_func;
1628 rb_obj_traverse_leave_func leave_func;
1647 if (obj_traverse_i(key, d->data)) {
1652 if (obj_traverse_i(val, d->data)) {
1661obj_traverse_reachable_i(
VALUE obj,
void *ptr)
1668 if (!rb_objspace_live_object_p(obj)) {
1674 if (obj_traverse_i(obj, d->data)) {
1687 rb_objspace_reachable_objects_from_local(obj, obj_traverse_reachable_i, &d);
1694 if (UNLIKELY(!data->rec)) {
1695 data->rec_hash = rb_ident_hash_new();
1697 data->rec = RHASH_ST_TABLE(data->rec_hash);
1703obj_traverse_ivar_foreach_i(
ID key,
VALUE val, st_data_t ptr)
1707 if (obj_traverse_i(val, d->data)) {
1720 switch (data->enter_func(obj)) {
1721 case traverse_cont:
break;
1722 case traverse_skip:
return 0;
1723 case traverse_stop:
return 1;
1726 if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
1732 if (rb_obj_shape_has_ivars(obj)) {
1738 if (d.stop)
return 1;
1756 rb_ary_cancel_sharing(obj);
1760 if (obj_traverse_i(e, data))
return 1;
1774 if (d.stop)
return 1;
1780 long len = RSTRUCT_LEN_RAW(obj);
1781 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
1783 for (
long i=0; i<
len; i++) {
1784 if (obj_traverse_i(ptr[i], data))
return 1;
1790 if (obj_traverse_i(
RMATCH(obj)->str, data))
return 1;
1794 if (obj_traverse_i(RRATIONAL(obj)->num, data))
return 1;
1795 if (obj_traverse_i(RRATIONAL(obj)->den, data))
return 1;
1798 if (obj_traverse_i(RCOMPLEX(obj)->real, data))
return 1;
1799 if (obj_traverse_i(RCOMPLEX(obj)->imag, data))
return 1;
1804 void *
const ptr = RTYPEDDATA_GET_DATA(obj);
1807 if (!ptr || !
type->function.dmark) {
1810 else if (
type->flags & RUBY_TYPED_DECL_MARKING) {
1811 const size_t *offsets = (
const size_t *)(uintptr_t)
type->function.dmark;
1812 for (; *offsets != RUBY_REF_END; offsets++) {
1813 VALUE ref = *(
VALUE *)((
char *)ptr + *offsets);
1814 if (obj_traverse_i(ref, data))
return 1;
1818 if (obj_traverse_reachable(obj, data))
return 1;
1825 if (obj_traverse_reachable(obj, data))
return 1;
1834 rb_bug(
"unreachable");
1837 if (data->leave_func(obj) == traverse_stop) {
1846 rb_obj_traverse_final_func final_func;
1851obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
1854 if (data->final_func(key)) {
1864rb_obj_traverse(
VALUE obj,
1865 rb_obj_traverse_enter_func enter_func,
1866 rb_obj_traverse_leave_func leave_func,
1867 rb_obj_traverse_final_func final_func)
1870 .enter_func = enter_func,
1871 .leave_func = leave_func,
1875 if (obj_traverse_i(obj, &data))
return 1;
1876 if (final_func && data.rec) {
1878 st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
1885allow_frozen_shareable_p(
VALUE obj)
1895 if (
type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
1904make_shareable_freeze(
VALUE obj)
1907 if (klass ==
rb_cString && BASIC_OP_UNREDEFINED_P(BOP_FREEZE, STRING_REDEFINED_OP_FLAG)) {
1910 else if (klass ==
rb_cArray && BASIC_OP_UNREDEFINED_P(BOP_FREEZE, ARRAY_REDEFINED_OP_FLAG)) {
1913 else if (klass ==
rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_FREEZE, HASH_REDEFINED_OP_FLAG)) {
1914 rb_hash_freeze(obj);
1921static enum obj_traverse_iterator_result
1922make_shareable_check_shareable_freeze(
VALUE obj,
enum obj_traverse_iterator_result result)
1925 make_shareable_freeze(obj);
1928 rb_raise(rb_eRactorError,
"#freeze does not freeze object correctly");
1932 return traverse_skip;
1939static int obj_refer_only_shareables_p(
VALUE obj);
1941static enum obj_traverse_iterator_result
1942make_shareable_check_shareable(
VALUE obj)
1947 return traverse_skip;
1949 else if (!allow_frozen_shareable_p(obj)) {
1951 rb_raise(rb_eRactorError,
1952 "can not make shareable object for %+"PRIsVALUE, obj);
1954 else if (
RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC) {
1955 if (obj_refer_only_shareables_p(obj)) {
1956 make_shareable_check_shareable_freeze(obj, traverse_skip);
1958 return traverse_skip;
1961 rb_raise(rb_eRactorError,
1962 "can not make shareable object for %+"PRIsVALUE
" because it refers unshareable objects", obj);
1966 rb_proc_ractor_make_shareable(obj,
Qundef);
1967 return traverse_cont;
1970 rb_raise(rb_eRactorError,
"can not make shareable object for %+"PRIsVALUE, obj);
1974 switch (
TYPE(obj)) {
1976 return traverse_skip;
1983 shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
1984 attr_index_t capacity = RSHAPE_CAPACITY(shape_id);
1985 attr_index_t free_capacity = capacity - RSHAPE_LEN(shape_id);
1986 if (!rb_shape_has_object_id(shape_id) && capacity && !free_capacity) {
1995 return make_shareable_check_shareable_freeze(obj, traverse_cont);
1998static enum obj_traverse_iterator_result
1999mark_shareable(
VALUE obj)
2002 rb_str_make_independent(obj);
2005 rb_obj_set_shareable_no_assert(obj);
2006 return traverse_cont;
2012 rb_obj_traverse(obj,
2013 make_shareable_check_shareable,
2014 null_leave, mark_shareable);
2023 VALUE copy = ractor_copy(obj);
2028rb_ractor_ensure_shareable(
VALUE obj,
VALUE name)
2031 VALUE message = rb_sprintf(
"cannot assign unshareable object to %"PRIsVALUE,
2039rb_ractor_ensure_main_ractor(
const char *msg)
2041 if (!rb_ractor_main_p()) {
2042 rb_raise(rb_eRactorIsolationError,
"%s", msg);
2046static enum obj_traverse_iterator_result
2047shareable_p_enter(
VALUE obj)
2050 return traverse_skip;
2056 mark_shareable(obj);
2057 return traverse_skip;
2060 allow_frozen_shareable_p(obj)) {
2061 return traverse_cont;
2069 if (obj_refer_only_shareables_p(obj)) {
2070 mark_shareable(obj);
2071 return traverse_skip;
2075 return traverse_stop;
2079rb_ractor_shareable_p_continue(
VALUE obj)
2081 if (rb_obj_traverse(obj,
2082 shareable_p_enter, null_leave,
2091static enum obj_traverse_iterator_result
2092null_leave(
VALUE obj)
2094 return traverse_cont;
2110 rb_obj_traverse_replace_enter_func enter_func;
2111 rb_obj_traverse_replace_leave_func leave_func;
2117 VALUE rec_keepalive;
2130obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp,
int error)
2136obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr,
int exists)
2141 if (obj_traverse_replace_i(*key, data)) {
2145 else if (*key != data->replacement) {
2146 VALUE v = *key = data->replacement;
2150 if (obj_traverse_replace_i(*val, data)) {
2154 else if (*val != data->replacement) {
2155 VALUE v = *val = data->replacement;
2163obj_iv_hash_traverse_replace_foreach_i(st_data_t _key, st_data_t _val, st_data_t _data,
int _x)
2169obj_iv_hash_traverse_replace_i(st_data_t * _key, st_data_t * val, st_data_t ptr,
int exists)
2174 if (obj_traverse_replace_i(*(
VALUE *)val, data)) {
2178 else if (*(
VALUE *)val != data->replacement) {
2189 if (UNLIKELY(!data->rec)) {
2190 data->rec = st_init_numtable();
2197obj_refer_only_shareables_p_i(
VALUE obj,
void *ptr)
2199 int *pcnt = (
int *)ptr;
2201 if (!rb_objspace_live_object_p(obj)) {
2211obj_refer_only_shareables_p(
VALUE obj)
2214 rb_objspace_reachable_objects_from_local(obj, obj_refer_only_shareables_p_i, &cnt);
2221 st_data_t replacement;
2224 data->replacement = obj;
2231 if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, &replacement))) {
2232 data->replacement = (
VALUE)replacement;
2236 switch (data->enter_func(obj, data)) {
2237 case traverse_cont:
break;
2238 case traverse_skip:
return 0;
2239 case traverse_stop:
return 1;
2242 replacement = (st_data_t)data->replacement;
2243 st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, replacement);
2252#define CHECK_AND_REPLACE(parent_obj, v) do { \
2254 if (obj_traverse_replace_i(_val, data)) { return 1; } \
2255 else if (data->replacement != _val) { RB_OBJ_WRITE(parent_obj, &v, data->replacement); } \
2258 if (UNLIKELY(rb_obj_gen_fields_p(obj))) {
2259 VALUE fields_obj = rb_obj_fields_no_ractor_check(obj);
2261 if (UNLIKELY(rb_obj_shape_complex_p(obj))) {
2267 rb_st_foreach_with_replace(
2268 rb_imemo_fields_complex_tbl(fields_obj),
2269 obj_iv_hash_traverse_replace_foreach_i,
2270 obj_iv_hash_traverse_replace_i,
2273 if (d.stop)
return 1;
2276 uint32_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID(obj));
2277 VALUE *fields = rb_imemo_fields_ptr(fields_obj);
2278 for (uint32_t i = 0; i < fields_count; i++) {
2279 CHECK_AND_REPLACE(fields_obj, fields[i]);
2293 rb_str_make_independent(obj);
2298 VALUE fields_obj = ROBJECT_FIELDS_OBJ(obj);
2299 shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj);
2300 if (rb_shape_complex_p(shape_id)) {
2306 rb_st_foreach_with_replace(
2307 rb_imemo_fields_complex_tbl(fields_obj),
2308 obj_iv_hash_traverse_replace_foreach_i,
2309 obj_iv_hash_traverse_replace_i,
2312 if (d.stop)
return 1;
2315 attr_index_t
len = RSHAPE_LEN(shape_id);
2316 VALUE *ptr = rb_imemo_fields_ptr(fields_obj);
2318 for (attr_index_t i = 0; i <
len; i++) {
2319 CHECK_AND_REPLACE(obj, ptr[i]);
2327 rb_ary_cancel_sharing(obj);
2332 if (obj_traverse_replace_i(e, data)) {
2335 else if (e != data->replacement) {
2349 rb_hash_stlike_foreach_with_replace(obj,
2350 obj_hash_traverse_replace_foreach_i,
2351 obj_hash_traverse_replace_i,
2353 if (d.stop)
return 1;
2357 if (obj_traverse_replace_i(ifnone, data)) {
2360 else if (ifnone != data->replacement) {
2368 long len = RSTRUCT_LEN_RAW(obj);
2369 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2371 for (
long i=0; i<
len; i++) {
2372 CHECK_AND_REPLACE(obj, ptr[i]);
2378 CHECK_AND_REPLACE(obj,
RMATCH(obj)->str);
2382 CHECK_AND_REPLACE(obj, RRATIONAL(obj)->num);
2383 CHECK_AND_REPLACE(obj, RRATIONAL(obj)->den);
2386 CHECK_AND_REPLACE(obj, RCOMPLEX(obj)->real);
2387 CHECK_AND_REPLACE(obj, RCOMPLEX(obj)->imag);
2391 if (!data->move && obj_refer_only_shareables_p(obj)) {
2395 rb_raise(rb_eRactorError,
"can not %s %"PRIsVALUE
" object.",
2409 rb_bug(
"unreachable");
2412 data->replacement = (
VALUE)replacement;
2414 if (data->leave_func(obj, data) == traverse_stop) {
2425rb_obj_traverse_replace(
VALUE obj,
2426 rb_obj_traverse_replace_enter_func enter_func,
2427 rb_obj_traverse_replace_leave_func leave_func,
2431 .enter_func = enter_func,
2432 .leave_func = leave_func,
2439 int stopped = obj_traverse_replace_i(obj, &data);
2443 if (data.rec) st_free_table(data.rec);
2450 return data.replacement;
2460enum courier_node_kind {
2462 COURIER_KIND_BACKTRACE,
2463 COURIER_KIND_STRING,
2466 COURIER_KIND_OBJECT,
2467 COURIER_KIND_STRUCT,
2470 COURIER_KIND_REGEXP,
2471 COURIER_KIND_HOOKED,
2478 COURIER_HOOK_MARSHAL_DUMP,
2480 COURIER_HOOK_COMPAT,
2481 COURIER_HOOK_DUMP_DATA,
2485static enum courier_hook
2486courier_hook_of(
VALUE obj)
2488 if (
rb_obj_respond_to(obj, id_marshal_dump, TRUE))
return COURIER_HOOK_MARSHAL_DUMP;
2490 if (rb_marshal_compat_lookup(
CLASS_OF(obj), NULL, NULL))
return COURIER_HOOK_COMPAT;
2492 return COURIER_HOOK_NONE;
2496 enum courier_node_kind kind;
2505 struct {
char *ptr;
long len, capa;
int encidx;
VALUE klass; } str;
2506 struct {
long len; uint32_t *elems;
VALUE klass; } ary;
2507 struct {
long size; uint32_t *kv; uint32_t ifnone_id;
bool compare_by_id;
bool proc_default;
VALUE klass; } hash;
2508 struct {
VALUE klass; } obj;
2509 struct {
long len; uint32_t *elems;
VALUE klass; } strct;
2510 struct { uint32_t regexp_id, str_id;
int num_regs;
void *regs;
VALUE klass; } match;
2511 struct {
void *blob;
int size; } bt;
2512 struct {
VALUE src;
int options;
VALUE klass; } re;
2513 struct {
VALUE klass; uint32_t payload_id;
enum courier_hook hook; } hooked;
2520 uint32_t pathv_id, ecopts_id, wc_pre_ecopts_id, wc_asciicompat_id, timeout_id;
2528#define COURIER_ID_REF_BIT 0x80000000u
2536 uint32_t refs_count;
2564 uint32_t
capa = c->capa ? c->capa * 2 : 8;
2570 ruby_xfree(old_nodes);
2577 uint32_t
capa = c->refs_capa ? c->refs_capa * 2 : 8;
2579 if (c->refs_count > 0)
MEMCPY(refs, c->refs,
VALUE, c->refs_count);
2580 VALUE *old_refs = c->refs;
2582 c->refs_capa =
capa;
2583 ruby_xfree(old_refs);
2589 if (RB_UNLIKELY(c->count == c->capa)) courier_grow_nodes(c);
2594 n->kind = COURIER_KIND_REF;
2610 c->order =
ALLOC_N(uint32_t, nodes);
2615 c->refs_capa = refs;
2627 if (RB_UNLIKELY(c->refs_count == c->refs_capa)) courier_grow_refs(c);
2628 c->refs[c->refs_count] = v;
2629 return COURIER_ID_REF_BIT | c->refs_count++;
2636 if (
id & COURIER_ID_REF_BIT)
return c->refs[
id & ~COURIER_ID_REF_BIT];
2643move_neutralize_source(
VALUE obj)
2648 shape_id_t shape_id = (RBASIC_SHAPE_ID(obj) & SHAPE_ID_CAPACITY_MASK) |
2649 ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_ROBJECT | SHAPE_ID_FL_FROZEN;
2659 bool wipe_body =
true;
2662 if (!STR_EMBED_P(obj) && !rb_str_reembeddable_p(obj)) {
2667 RBASIC_SET_CLASS_RAW(obj, rb_cRactorMovedObject);
2669 RBASIC_SET_FULL_SHAPE_ID(obj, (shape_id & ~SHAPE_ID_LAYOUT_MASK) | SHAPE_ID_LAYOUT_OTHER);
2673 wipe_body = !rb_str_embedded_shared_root_p(obj);
2676 if (!ARY_EMBED_P(obj) && !ARY_SHARED_P(obj) && (ARY_SHARED_ROOT_P(obj) ||
OBJ_FROZEN(obj))) {
2681 RBASIC_SET_CLASS_RAW(obj, rb_cRactorMovedObject);
2683 RBASIC_SET_FULL_SHAPE_ID(obj, (shape_id & ~SHAPE_ID_LAYOUT_MASK) | SHAPE_ID_LAYOUT_OTHER);
2684 if (!ARY_SHARED_ROOT_P(obj)) {
2687 RARRAY(obj)->as.heap.len = 0;
2691 wipe_body = !rb_ary_embedded_shared_root_p(obj);
2703 size_t slot_size = rb_gc_obj_slot_size(obj);
2704 RBASIC_SET_CLASS_RAW(obj, rb_cRactorMovedObject);
2705 RBASIC(obj)->flags = flags;
2706 RBASIC_SET_FULL_SHAPE_ID(obj, shape_id);
2724courier_capture_hash_i(st_data_t key, st_data_t val, st_data_t arg)
2727 uint32_t kid = courier_capture(hc->b, (
VALUE)key);
2728 uint32_t vid = courier_capture(hc->b, (
VALUE)val);
2729 hc->kv[hc->i++] = kid;
2730 hc->kv[hc->i++] = vid;
2743courier_capture_ivar_i(
ID name,
VALUE val, st_data_t arg)
2746 if (oc->n == oc->capa) {
2747 oc->capa = oc->capa ? oc->capa * 2 : 4;
2749 REALLOC_N(oc->vals, uint32_t, oc->capa);
2751 uint32_t vid = courier_capture(oc->b, val);
2752 oc->ids[oc->n] = name;
2753 oc->vals[oc->n] = vid;
2765 rb_ivar_foreach_buffered(obj, courier_capture_ivar_i, (st_data_t)&oc);
2766 b->c->nodes[id].niv = (uint32_t)oc.n;
2767 b->c->nodes[id].iv_ids = oc.ids;
2768 b->c->nodes[id].iv_vals = oc.vals;
2776courier_capture_hooked(
struct courier_build *b,
VALUE obj, uint32_t
id,
enum courier_hook hook)
2782 case COURIER_HOOK_DUMP: {
2791 case COURIER_HOOK_MARSHAL_DUMP:
2792 payload =
rb_funcallv(obj, id_marshal_dump, 0, 0);
2794 case COURIER_HOOK_DUMP_DATA:
2797 case COURIER_HOOK_COMPAT: {
2799 rb_marshal_compat_lookup(klass, &dumper, NULL);
2800 payload = dumper(obj);
2804 rb_bug(
"courier_capture_hooked: no dump protocol");
2807 uint32_t payload_id = courier_capture(b, payload);
2809 b->c->nodes[id].kind = COURIER_KIND_HOOKED;
2810 b->c->nodes[id].u.hooked.klass = klass;
2811 b->c->nodes[id].u.hooked.hook = hook;
2812 b->c->nodes[id].u.hooked.payload_id = payload_id;
2832 return courier_alloc_ref(b->c, obj);
2840 if (st_lookup(b->c->seen, (st_data_t)obj, &existing)) {
2841 return (uint32_t)existing - 1;
2845 return courier_alloc_ref(b->c, obj);
2848 uint32_t
id = courier_alloc_node(b->c);
2849 st_insert(b->c->seen, (st_data_t)obj, (st_data_t)(uintptr_t)(
id + 1));
2853 rb_raise(rb_eRactorError,
"can not move an uninitialized IO");
2857 b->c->nodes[id].frozen = frozen;
2858 courier_capture_ivars(b, obj,
id);
2865 if (!b->copy) rb_str_make_independent(obj);
2866 long len = RSTRING_LEN(obj);
2870 const int termlen = rb_enc_mbminlen(rb_enc_from_index(encidx));
2873 if (!b->copy && !STR_EMBED_P(obj) && rb_str_reembeddable_p(obj)) {
2876 ptr =
RSTRING(obj)->as.heap.ptr;
2884 if (
len) memcpy(ptr, RSTRING_PTR(obj),
len);
2885 memset(ptr +
len, 0, termlen);
2888 b->c->nodes[id].kind = COURIER_KIND_STRING;
2889 b->c->nodes[id].u.str.klass = courier_klass(b, obj);
2890 b->c->nodes[id].u.str.ptr = ptr;
2891 b->c->nodes[id].u.str.len =
len;
2892 b->c->nodes[id].u.str.capa =
capa;
2893 b->c->nodes[id].u.str.encidx = encidx;
2900 for (
long i = 0; i <
len; i++) {
2901 elems[i] = courier_capture(b,
RARRAY_AREF(obj, i));
2903 b->c->nodes[id].kind = COURIER_KIND_ARRAY;
2904 b->c->nodes[id].u.ary.klass = courier_klass(b, obj);
2905 b->c->nodes[id].u.ary.len =
len;
2906 b->c->nodes[id].u.ary.elems = elems;
2910 if (!b->copy && !ARY_EMBED_P(obj) && !ARY_SHARED_P(obj) && !ARY_SHARED_ROOT_P(obj) && !
OBJ_FROZEN(obj)) {
2917 uint32_t ifnone_id = courier_capture(b,
RHASH_IFNONE(obj));
2919 uint32_t *kv = size ?
ALLOC_N(uint32_t, size * 2) : NULL;
2921 rb_hash_stlike_foreach(obj, courier_capture_hash_i, (st_data_t)&hc);
2922 b->c->nodes[id].kind = COURIER_KIND_HASH;
2923 b->c->nodes[id].u.hash.klass = courier_klass(b, obj);
2924 b->c->nodes[id].u.hash.size = size;
2925 b->c->nodes[id].u.hash.kv = kv;
2926 b->c->nodes[id].u.hash.ifnone_id = ifnone_id;
2927 b->c->nodes[id].u.hash.compare_by_id =
RTEST(rb_hash_compare_by_id_p(obj));
2928 b->c->nodes[id].u.hash.proc_default =
FL_TEST_RAW(obj, RHASH_PROC_DEFAULT) != 0;
2930 if (!b->copy) rb_hash_free(obj);
2935 b->c->nodes[id].kind = COURIER_KIND_OBJECT;
2936 b->c->nodes[id].u.obj.klass = courier_klass(b, obj);
2942 for (
long i = 0; i <
len; i++) {
2943 elems[i] = courier_capture(b,
RSTRUCT_GET(obj, (
int)i));
2945 b->c->nodes[id].kind = COURIER_KIND_STRUCT;
2946 b->c->nodes[id].u.strct.len =
len;
2947 b->c->nodes[id].u.strct.elems = elems;
2948 b->c->nodes[id].u.strct.klass = courier_klass(b, obj);
2950 if (!b->copy && RSTRUCT_EMBED_LEN(obj) == 0) {
2951 ruby_xfree((
void *)RSTRUCT_CONST_PTR(obj));
2961 void *regs = rb_match_blob_dump(obj, &re, &st, &nregs, !b->copy);
2962 uint32_t rid = courier_capture(b, re);
2963 uint32_t sid = courier_capture(b, st);
2964 b->c->nodes[id].kind = COURIER_KIND_MATCH;
2965 b->c->nodes[id].u.match.regexp_id = rid;
2966 b->c->nodes[id].u.match.str_id = sid;
2967 b->c->nodes[id].u.match.num_regs = nregs;
2968 b->c->nodes[id].u.match.regs = regs;
2969 b->c->nodes[id].u.match.klass = courier_klass(b, obj);
2975 VM_ASSERT(!b->copy);
2981 uint32_t pathv_id = courier_capture(b, fptr->
pathv);
2982 uint32_t ecopts_id = courier_capture(b, fptr->
encs.
ecopts);
2985 uint32_t timeout_id = courier_capture(b, fptr->
timeout);
2993 fptr->wakeup_mutex =
Qnil;
2995 b->c->nodes[id].kind = COURIER_KIND_IO;
2996 b->c->nodes[id].u.io.fptr = fptr;
2997 b->c->nodes[id].u.io.klass = courier_klass(b, obj);
2998 b->c->nodes[id].u.io.pathv_id = pathv_id;
2999 b->c->nodes[id].u.io.ecopts_id = ecopts_id;
3000 b->c->nodes[id].u.io.wc_pre_ecopts_id = wc_pre_id;
3001 b->c->nodes[id].u.io.wc_asciicompat_id = wc_ac_id;
3002 b->c->nodes[id].u.io.timeout_id = timeout_id;
3013 b->c->nodes[id].kind = COURIER_KIND_REGEXP;
3014 b->c->nodes[id].u.re.klass = courier_klass(b, obj);
3015 b->c->nodes[id].u.re.src = src;
3023 if (b->copy && rb_backtrace_p(obj)) {
3025 void *blob = rb_backtrace_blob_dump(obj, &size);
3026 b->c->nodes[id].kind = COURIER_KIND_BACKTRACE;
3027 b->c->nodes[id].u.bt.blob = blob;
3028 b->c->nodes[id].u.bt.size = size;
3035 enum courier_hook hook = b->copy ? courier_hook_of(obj) : COURIER_HOOK_NONE;
3036 if (hook == COURIER_HOOK_NONE) {
3037 rb_raise(rb_eRactorError,
"can not %s a %"PRIsVALUE
" object",
3040 courier_capture_hooked(b, obj,
id, hook);
3045 if (!b->copy) move_neutralize_source(obj);
3047 b->c->order[b->ordered++] = id;
3054 uint32_t nodes, refs;
3060move_preflight_ivar_i(
ID name,
VALUE val, st_data_t arg)
3067move_preflight_hash_i(st_data_t key, st_data_t val, st_data_t arg)
3086 if (st_lookup(seen, (st_data_t)obj, NULL))
return;
3087 st_insert(seen, (st_data_t)obj, 0);
3094 rb_class_check_singleton_movable(klass);
3103 move_preflight(rm->
regexp, ctx);
3104 move_preflight(rm->
str, ctx);
3113 rb_hash_stlike_foreach(obj, move_preflight_hash_i, (st_data_t)ctx);
3124 rb_raise(rb_eRactorError,
"can not move an uninitialized IO");
3129 rb_raise(rb_eRactorError,
"can not move an IO tied to a writer IO");
3131 if (
RTEST(fptr->wakeup_mutex)) {
3133 rb_raise(rb_eRactorError,
"can not move an IO that is being closed");
3135 move_preflight(fptr->
pathv, ctx);
3139 move_preflight(fptr->
timeout, ctx);
3143 rb_raise(rb_eRactorError,
"can not move a %"PRIsVALUE
" object",
3155 uint32_t nodes, refs;
3162copy_support_val_i(st_data_t val, st_data_t arg)
3165 if (!copy_courier_supported_p((
VALUE)val, ctx)) {
3173copy_support_ivar_i(
ID name,
VALUE val, st_data_t arg)
3175 return copy_support_val_i((st_data_t)val, arg);
3179copy_support_hash_i(st_data_t key, st_data_t val, st_data_t arg)
3181 if (copy_support_val_i(key, arg) == ST_STOP)
return ST_STOP;
3182 return copy_support_val_i(val, arg);
3195 if (st_lookup(seen, (st_data_t)obj, NULL))
return true;
3196 st_insert(seen, (st_data_t)obj, 0);
3208 if (!copy_courier_supported_p(rm->
regexp, ctx))
return false;
3209 if (!copy_courier_supported_p(rm->
str, ctx))
return false;
3214 if (!rb_backtrace_p(obj) && courier_hook_of(obj) == COURIER_HOOK_NONE)
return false;
3218 if (!copy_courier_supported_p(
RARRAY_AREF(obj, i), ctx))
return false;
3222 rb_hash_stlike_foreach(obj, copy_support_hash_i, (st_data_t)ctx);
3223 if (!ctx->ok)
return false;
3224 if (!copy_courier_supported_p(
RHASH_IFNONE(obj), ctx))
return false;
3228 if (!copy_courier_supported_p(
RSTRUCT_GET(obj, (
int)i), ctx))
return false;
3235 if (courier_hook_of(obj) == COURIER_HOOK_NONE)
return false;
3250 bool ok = copy_courier_supported_p(obj, &scan);
3251 st_free_table(scan.seen);
3252 if (!ok)
return NULL;
3256 courier_reserve(c, scan.nodes, scan.refs);
3257 c->seen = st_init_numtable();
3264 enum ruby_tag_type state;
3267 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
3268 c->root = courier_capture(&b, obj);
3271 st_free_table(c->seen);
3274 if (state != TAG_NONE) EC_JUMP_TAG(ec, state);
3287 enum ruby_tag_type state;
3290 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
3291 move_preflight(obj, &scan);
3294 st_free_table(scan.seen);
3295 if (state != TAG_NONE) EC_JUMP_TAG(ec, state);
3299 courier_reserve(c, scan.nodes, scan.refs);
3300 c->seen = st_init_numtable();
3308 enum ruby_tag_type state;
3311 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
3312 c->root = courier_capture(&b, obj);
3315 st_free_table(c->seen);
3317 if (state != TAG_NONE) {
3321 EC_JUMP_TAG(ec, state);
3333 RBASIC_SET_CLASS(shell, klass);
3338 rb_class_take_ownership(klass);
3351 for (uint32_t i = 0; i < c->count; i++) {
3355 case COURIER_KIND_REF:
3358 case COURIER_KIND_STRING:
3362 shell = rb_str_new_owned(n->u.str.ptr, n->u.str.len, n->u.str.capa, n->u.str.encidx);
3363 n->u.str.ptr = NULL;
3364 courier_apply_klass(shell, n->u.str.klass);
3366 case COURIER_KIND_ARRAY:
3368 courier_apply_klass(shell, n->u.ary.klass);
3370 case COURIER_KIND_HASH:
3371 shell = n->u.hash.compare_by_id ? rb_ident_hash_new() : rb_hash_new();
3372 courier_apply_klass(shell, n->u.hash.klass);
3374 case COURIER_KIND_OBJECT:
3378 courier_apply_klass(shell, n->u.obj.klass);
3380 case COURIER_KIND_STRUCT:
3382 courier_apply_klass(shell, n->u.strct.klass);
3384 case COURIER_KIND_MATCH:
3385 shell = rb_match_blob_alloc(
rb_class_real(n->u.match.klass), n->u.match.num_regs);
3386 courier_apply_klass(shell, n->u.match.klass);
3388 case COURIER_KIND_BACKTRACE:
3389 shell = rb_backtrace_blob_load(n->u.bt.blob, n->u.bt.size);
3391 case COURIER_KIND_REGEXP:
3394 shell = rb_reg_init_str(rb_reg_s_alloc(
rb_class_real(n->u.re.klass)), n->u.re.src, n->u.re.options);
3395 courier_apply_klass(shell, n->u.re.klass);
3397 case COURIER_KIND_HOOKED:
3398 if (n->u.hooked.hook == COURIER_HOOK_DUMP) {
3405 courier_apply_klass(shell, n->u.hooked.klass);
3407 case COURIER_KIND_IO:
3409 courier_apply_klass(shell, n->u.io.klass);
3410 RFILE(shell)->fptr = n->u.io.fptr;
3411 n->u.io.fptr->
self = shell;
3412 n->u.io.fptr = NULL;
3415 rb_bug(
"rb_ractor_courier_materialize: bad node kind");
3426 for (uint32_t k = 0; k < c->count; k++) {
3427 uint32_t i = c->order[k];
3431 case COURIER_KIND_ARRAY: {
3434 const long len = n->u.ary.len;
3437 for (
long j = 0; j <
len; j++) {
3438 RARRAY_ASET(shell, j, courier_child(c, shells, n->u.ary.elems[j]));
3443 case COURIER_KIND_HASH:
3444 for (
long j = 0; j < n->u.hash.size; j++) {
3445 rb_hash_aset(shell, courier_child(c, shells, n->u.hash.kv[2 * j]),
3446 courier_child(c, shells, n->u.hash.kv[2 * j + 1]));
3449 VALUE ifnone = courier_child(c, shells, n->u.hash.ifnone_id);
3450 if (n->u.hash.proc_default) {
3451 rb_hash_set_default_proc(shell, ifnone);
3453 else if (ifnone !=
Qnil) {
3454 rb_hash_set_default(shell, ifnone);
3457 case COURIER_KIND_HOOKED: {
3458 VALUE payload = courier_child(c, shells, n->u.hooked.payload_id);
3459 VALUE klass = n->u.hooked.klass;
3461 switch (n->u.hooked.hook) {
3462 case COURIER_HOOK_DUMP:
3464 rb_raise(
rb_eTypeError,
"class %"PRIsVALUE
" needs to have method '_load'", klass);
3472 case COURIER_HOOK_MARSHAL_DUMP:
3473 case COURIER_HOOK_DUMP_DATA:
3474 mid = n->u.hooked.hook == COURIER_HOOK_MARSHAL_DUMP ? id_marshal_load : id_load_data;
3476 rb_raise(
rb_eTypeError,
"instance of %"PRIsVALUE
" needs to have method '%"PRIsVALUE
"'",
3477 klass, rb_id2str(mid));
3481 case COURIER_HOOK_COMPAT: {
3483 rb_marshal_compat_lookup(klass, NULL, &loader);
3484 loader(shell, payload);
3488 rb_bug(
"rb_ractor_courier_materialize: no dump protocol");
3492 case COURIER_KIND_STRUCT:
3493 for (
long j = 0; j < n->u.strct.len; j++) {
3494 RSTRUCT_SET(shell, (
int)j, courier_child(c, shells, n->u.strct.elems[j]));
3497 case COURIER_KIND_MATCH:
3498 rb_match_blob_load(shell, courier_child(c, shells, n->u.match.regexp_id),
3499 courier_child(c, shells, n->u.match.str_id),
3500 n->u.match.num_regs, n->u.match.regs);
3502 case COURIER_KIND_IO: {
3517 for (uint32_t j = 0; j < n->niv; j++) {
3518 rb_ivar_set(shell, n->iv_ids[j], courier_child(c, shells, n->iv_vals[j]));
3523 for (uint32_t i = 0; i < c->count; i++) {
3530 VALUE root = (c->count || c->refs_count) ? courier_child(c, shells, c->root) :
Qnil;
3538 for (uint32_t i = 0; i < c->count; i++) {
3540 ruby_xfree(n->iv_ids);
3541 ruby_xfree(n->iv_vals);
3543 case COURIER_KIND_STRING:
3544 ruby_xfree(n->u.str.ptr);
3546 case COURIER_KIND_ARRAY:
3547 ruby_xfree(n->u.ary.elems);
3549 case COURIER_KIND_HASH:
3550 ruby_xfree(n->u.hash.kv);
3552 case COURIER_KIND_STRUCT:
3553 ruby_xfree(n->u.strct.elems);
3555 case COURIER_KIND_MATCH:
3556 rb_match_blob_free(n->u.match.regs);
3558 case COURIER_KIND_BACKTRACE:
3559 ruby_xfree(n->u.bt.blob);
3561 case COURIER_KIND_IO:
3567 n->u.io.fptr = NULL;
3574 ruby_xfree(c->nodes);
3575 ruby_xfree(c->order);
3576 ruby_xfree(c->refs);
3588 if (c->seen) rb_mark_set(c->seen);
3589 for (uint32_t i = 0; i < c->refs_count; i++) {
3590 rb_gc_mark(c->refs[i]);
3592 for (uint32_t i = 0; i < c->count; i++) {
3594 if (n->kind == COURIER_KIND_REF) {
3595 rb_gc_mark(n->u.ref);
3597 else if (n->kind == COURIER_KIND_OBJECT) {
3598 rb_gc_mark(n->u.obj.klass);
3600 else if (n->kind == COURIER_KIND_STRUCT) {
3601 rb_gc_mark(n->u.strct.klass);
3603 else if (n->kind == COURIER_KIND_MATCH) {
3604 rb_gc_mark(n->u.match.klass);
3606 else if (n->kind == COURIER_KIND_IO) {
3607 rb_gc_mark(n->u.io.klass);
3609 else if (n->kind == COURIER_KIND_STRING) {
3610 rb_gc_mark(n->u.str.klass);
3612 else if (n->kind == COURIER_KIND_BACKTRACE) {
3613 rb_backtrace_blob_mark(n->u.bt.blob, n->u.bt.size);
3615 else if (n->kind == COURIER_KIND_ARRAY) {
3616 rb_gc_mark(n->u.ary.klass);
3618 else if (n->kind == COURIER_KIND_HASH) {
3619 rb_gc_mark(n->u.hash.klass);
3621 else if (n->kind == COURIER_KIND_REGEXP) {
3622 rb_gc_mark(n->u.re.src);
3623 rb_gc_mark(n->u.re.klass);
3625 else if (n->kind == COURIER_KIND_HOOKED) {
3626 rb_gc_mark(n->u.hooked.klass);
3635ractor_native_shallow_copy(
VALUE obj)
3649 rb_obj_copy_ivar(copy, obj);
3652 copy = rb_enc_str_new(RSTRING_PTR(obj), RSTRING_LEN(obj), rb_enc_get(obj));
3658 copy = rb_hash_dup(obj);
3662 rb_struct_init_copy(copy, obj);
3666 rb_match_init_copy(copy, obj);
3671 if (rb_backtrace_p(obj)) {
3672 copy = rb_backtrace_dup(obj);
3684 UNLIKELY(rb_obj_gen_fields_p(obj))) {
3694 RBASIC_SET_SHAPE_ID(copy, rb_obj_shape_transition_frozen(copy));
3699static enum obj_traverse_iterator_result
3703 data->replacement = obj;
3704 return traverse_skip;
3707 VALUE copy = ractor_native_shallow_copy(obj);
3708 if (UNDEF_P(copy))
return traverse_stop;
3709 data->replacement = copy;
3710 return traverse_cont;
3714static enum obj_traverse_iterator_result
3717 return traverse_cont;
3723ractor_copy_native_try(
VALUE obj)
3725 return rb_obj_traverse_replace(obj, copy_enter, copy_leave,
false);
3731ractor_copy(
VALUE obj)
3733 VALUE copy = ractor_copy_native_try(obj);
3734 if (UNDEF_P(copy)) {
3736 ractor_marshal_dump_rescue, obj,
3753} freed_ractor_local_keys;
3757ractor_local_keys_purge(
st_table *local_storage)
3759 for (
int i=0; i<freed_ractor_local_keys.cnt; i++) {
3761 st_data_t val, k = (st_data_t)key;
3762 if (st_delete(local_storage, &k, &val) &&
3764 (*key->type->free)((
void *)val);
3771ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
3774 if (k->type->
mark) (*k->type->
mark)((
void *)val);
3778static enum rb_id_table_iterator_result
3779idkey_local_storage_mark_i(
VALUE val,
void *dmy)
3782 return ID_TABLE_CONTINUE;
3788 if (r->local_storage) {
3789 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
3794 if (rb_gc_single_objspace_p() || rb_gc_during_global_gc_p()) {
3795 ractor_local_keys_purge(r->local_storage);
3799 if (r->idkey_local_storage) {
3800 rb_id_table_foreach_values(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
3803 rb_gc_mark(r->local_storage_store_lock);
3807ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
3810 if (k->type->
free) (*k->type->
free)((
void *)val);
3817 if (r->local_storage) {
3818 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
3819 st_free_table(r->local_storage);
3822 if (r->idkey_local_storage) {
3823 rb_id_table_free(r->idkey_local_storage);
3828rb_ractor_local_storage_value_mark(
void *ptr)
3830 rb_gc_mark((
VALUE)ptr);
3844 rb_ractor_local_storage_value_mark,
3852 key->type =
type ?
type : &ractor_local_storage_type_null;
3853 key->main_cache = (
void *)
Qundef;
3867 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
3868 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
3869 SIZED_REALLOC_N(freed_ractor_local_keys.keys,
rb_ractor_local_key_t, freed_ractor_local_keys.capa, freed_ractor_local_keys.cnt);
3871 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
3878 if (rb_ractor_main_p()) {
3879 if (!UNDEF_P((
VALUE)key->main_cache)) {
3880 *pret = key->main_cache;
3890 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
3904 if (cr->local_storage == NULL) {
3905 cr->local_storage = st_init_numtable();
3908 st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
3910 if (rb_ractor_main_p()) {
3911 key->main_cache = ptr;
3919 if (ractor_local_ref(key, &val)) {
3930 if (ractor_local_ref(key, (
void **)val)) {
3941 ractor_local_set(key, (
void *)val);
3948 if (ractor_local_ref(key, &ret)) {
3959 ractor_local_set(key, ptr);
3962#define DEFAULT_KEYS_CAPA 0x10
3965rb_ractor_finish_marking(
bool full_mark)
3970 if (!(rb_gc_single_objspace_p() || rb_gc_during_global_gc_p())) {
3980 for (
size_t zi = 0; zi < vm->gc.zombie_objspaces_count; zi++) {
3981 rb_ractor_t *owner = vm->gc.zombie_objspaces[zi].owner;
3982 if (owner == NULL || owner->local_storage == NULL)
continue;
3983 ractor_local_keys_purge(owner->local_storage);
3986 for (
int i=0; i<freed_ractor_local_keys.cnt; i++) {
3987 SIZED_FREE(freed_ractor_local_keys.keys[i]);
3989 freed_ractor_local_keys.cnt = 0;
3990 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
3991 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
3992 SIZED_REALLOC_N(freed_ractor_local_keys.keys,
rb_ractor_local_key_t, DEFAULT_KEYS_CAPA, freed_ractor_local_keys.capa);
3997 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
3998 rb_ractor_reap_dead_ports(r);
4000 if (vm->ractor.cnt == 0 && vm->ractor.main_ractor) {
4001 rb_ractor_reap_dead_ports(vm->ractor.main_ractor);
4011 struct rb_id_table *tbl = cr->idkey_local_storage;
4014 if (
id && tbl && rb_id_table_lookup(tbl,
id, &val)) {
4027 struct rb_id_table *tbl = cr->idkey_local_storage;
4030 tbl = cr->idkey_local_storage = rb_id_table_create(2);
4032 rb_id_table_insert(tbl,
id, val);
4044ractor_local_value_store_i(
VALUE ptr)
4049 if (rb_id_table_lookup(data->tbl, data->id, &val)) {
4054 ractor_local_value_set(data->ec,
Qnil, data->sym, val);
4067 .tbl = cr->idkey_local_storage,
4071 if (data.tbl == NULL) {
4072 data.tbl = cr->idkey_local_storage = rb_id_table_create(2);
4074 else if (rb_id_table_lookup(data.tbl, data.id, &val)) {
4079 if (!cr->local_storage_store_lock) {
4092 rb_raise(rb_eRactorIsolationError,
"self should be shareable: %" PRIsVALUE, replace_self);
4096 return rb_proc_ractor_make_shareable(rb_proc_dup(proc), replace_self);
4120RUBY_REFERENCES(cross_ractor_require_refs) = {
4127 "ractor/cross_ractor_require",
4129 RUBY_REFS_LIST_PTR(cross_ractor_require_refs),
4134 0, 0, RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_DECL_MARKING | RUBY_TYPED_EMBEDDABLE
4138require_body(
VALUE crr_obj)
4142 VALUE feature = crr->as.require.feature;
4148 int rb_require_internal_silent(
VALUE fname);
4149 return INT2NUM(rb_require_internal_silent(feature));
4166require_result_send_body(
VALUE ary)
4173 ractor_port_send(ec, port, results,
Qfalse);
4178require_result_send_resuce(
VALUE port,
VALUE errinfo)
4181 ractor_port_send(GET_EC(), port, errinfo,
Qfalse);
4191 const bool silent = crr->silent;
4193 VALUE debug, errinfo;
4196 errinfo = rb_errinfo();
4204 rb_set_errinfo(errinfo);
4209 rb_ary_new_from_args(2, crr->port, rb_ary_new_from_args(2, result, crr->raised ?
Qtrue :
Qfalse)),
4217ractor_require_func(
void *crr_obj)
4219 return ractor_require_protect((
VALUE)crr_obj, require_body);
4223rb_ractor_require(
VALUE feature,
bool silent)
4226 ASSERT_vm_unlocking();
4235 crr->raised =
false;
4236 crr->silent = silent;
4239 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
4240 rb_ractor_interrupt_exec(main_r, ractor_require_func, (
void *)crr_obj, rb_interrupt_exec_flag_value_data);
4243 VALUE results = ractor_port_receive(ec, crr->port,
Qnil);
4244 ractor_port_close(ec, crr->port);
4261 return rb_ractor_require(feature,
false);
4265autoload_load_body(
VALUE crr_obj)
4273ractor_autoload_load_func(
void *crr_obj)
4275 return ractor_require_protect((
VALUE)crr_obj, autoload_load_body);
4279rb_ractor_autoload_load(
VALUE module,
ID name)
4285 RB_OBJ_WRITE(crr_obj, &crr->as.autoload.module, module);
4290 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
4291 rb_ractor_interrupt_exec(main_r, ractor_autoload_load_func, (
void *)crr_obj, rb_interrupt_exec_flag_value_data);
4294 VALUE results = ractor_port_receive(ec, crr->port,
Qnil);
4295 ractor_port_close(ec, crr->port);
4312 return ractor_shareable_proc(ec, arg_self,
false);
4318 return ractor_shareable_proc(ec, arg_self,
true);
4321#include "ractor.rbinc"
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
#define RUBY_ATOMIC_PTR_LOAD(var)
Identical to RUBY_ATOMIC_LOAD, except it expects its arguments are void*.
#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().
static void RB_FL_SET_RAW(VALUE obj, VALUE flags)
This is an implementation detail of RB_FL_SET().
@ RUBY_FL_FREEZE
This flag has something to do with data immutability.
void rb_singleton_class_attached(VALUE klass, VALUE obj)
Attaches a singleton class to its corresponding object.
#define T_COMPLEX
Old name of RUBY_T_COMPLEX.
#define TYPE(_)
Old name of rb_type.
#define FL_SINGLETON
Old name of RUBY_FL_SINGLETON.
#define T_FILE
Old name of RUBY_T_FILE.
#define FL_PROMOTED
Old name of RUBY_FL_PROMOTED.
#define REALLOC_N
Old name of RB_REALLOC_N.
#define ALLOC
Old name of RB_ALLOC.
#define T_STRING
Old name of RUBY_T_STRING.
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
#define T_FLOAT
Old name of RUBY_T_FLOAT.
#define T_IMEMO
Old name of RUBY_T_IMEMO.
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define T_STRUCT
Old name of RUBY_T_STRUCT.
#define SYM2ID
Old name of RB_SYM2ID.
#define T_DATA
Old name of RUBY_T_DATA.
#define ZALLOC
Old name of RB_ZALLOC.
#define FL_SHAREABLE
Old name of RUBY_FL_SHAREABLE.
#define CLASS_OF
Old name of rb_class_of.
#define ENCODING_GET(obj)
Old name of RB_ENCODING_GET.
#define FL_FINALIZE
Old name of RUBY_FL_FINALIZE.
#define T_MODULE
Old name of RUBY_T_MODULE.
#define T_RATIONAL
Old name of RUBY_T_RATIONAL.
#define T_ICLASS
Old name of RUBY_T_ICLASS.
#define T_HASH
Old name of RUBY_T_HASH.
#define ALLOC_N
Old name of RB_ALLOC_N.
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
#define Qtrue
Old name of RUBY_Qtrue.
#define INT2NUM
Old name of RB_INT2NUM.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
#define T_OBJECT
Old name of RUBY_T_OBJECT.
#define NIL_P
Old name of RB_NIL_P.
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
#define T_MATCH
Old name of RUBY_T_MATCH.
#define T_CLASS
Old name of RUBY_T_CLASS.
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
#define FL_FREEZE
Old name of RUBY_FL_FREEZE.
#define CONST_ID
Old name of RUBY_CONST_ID.
#define FL_SET_RAW
Old name of RB_FL_SET_RAW.
#define T_REGEXP
Old name of RUBY_T_REGEXP.
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
VALUE rb_eTypeError
TypeError exception.
VALUE rb_eRuntimeError
RuntimeError exception.
VALUE rb_eStopIteration
StopIteration exception.
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.
VALUE rb_eException
Mother of all exceptions.
VALUE rb_cArray
Array class.
VALUE rb_cObject
Object class.
VALUE rb_obj_alloc(VALUE klass)
Allocates an instance of the given class.
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
VALUE rb_cRactor
Ractor class.
VALUE rb_stdin
STDIN constant.
VALUE rb_cHash
Hash class.
VALUE rb_stderr
STDERR constant.
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
VALUE rb_cBasicObject
BasicObject class.
VALUE rb_class_real(VALUE klass)
Finds a "real" class.
VALUE rb_obj_freeze(VALUE obj)
Same as RB_OBJ_FREEZE(), but returns the given object.
VALUE rb_stdout
STDOUT constant.
VALUE rb_cString
String class.
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
VALUE rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv)
Identical to rb_funcall(), except it takes the method arguments as a C array.
VALUE rb_ary_new_from_values(long n, const VALUE *elts)
Identical to rb_ary_new_from_args(), except how objects are passed.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_new_capa(long capa)
Identical to rb_ary_new(), except it additionally specifies how many rooms of objects it should alloc...
VALUE rb_ary_resize(VALUE ary, long len)
Expands or shrinks the passed array to the passed length.
VALUE rb_ary_pop(VALUE ary)
Destructively deletes an element from the end of the passed array and returns what was deleted.
VALUE rb_ary_hidden_new(long capa)
Allocates a hidden (no class) 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_freeze(VALUE obj)
Freeze an array, preventing further modifications.
VALUE rb_marshal_load(VALUE port)
Deserialises a previous output of rb_marshal_dump() into a network of objects.
VALUE rb_block_proc(void)
Constructs a Proc object from implicitly passed components.
VALUE rb_block_lambda(void)
Identical to rb_proc_new(), except it returns a lambda.
VALUE rb_obj_is_proc(VALUE recv)
Queries if the given object is a proc.
int rb_reg_options(VALUE re)
Queries the options of the passed regular expression.
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
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_ivar_set(VALUE obj, ID name, VALUE val)
Identical to rb_iv_set(), except it accepts the name as an ID instead of a C string.
VALUE rb_autoload_load(VALUE space, ID name)
Kicks the autoload procedure as if it was "touched".
VALUE rb_class_name(VALUE obj)
Queries the name of the given object's class.
void rb_free_generic_ivar(VALUE obj)
Frees the list of instance variables.
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
int rb_obj_respond_to(VALUE obj, ID mid, int private_p)
Identical to rb_respond_to(), except it additionally takes the visibility parameter.
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
int capa
Designed capacity of the buffer.
int rb_io_fptr_finalize(rb_io_t *fptr)
Destroys the given IO.
int len
Length of the buffer.
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.
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...
struct rb_ractor_local_key_struct * rb_ractor_local_key_t
(Opaque) struct that holds a ractor-local storage key.
void * rb_ractor_local_storage_ptr(rb_ractor_local_key_t key)
Identical to rb_ractor_local_storage_value() except the return type.
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.
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().
#define RB_OBJ_SET_SHAREABLE(obj)
Wrapper of rb_obj_set_shareable().
VALUE rb_ractor_stdin(void)
Queries the standard input of the current Ractor that is calling this function.
static bool rb_ractor_shareable_p(VALUE obj)
Queries if multiple Ractors can share the passed object or not.
void rb_ractor_stderr_set(VALUE io)
Assigns an IO to the standard error of the Ractor that is calling this function.
void rb_ractor_local_storage_value_set(rb_ractor_local_key_t key, VALUE val)
Associates the passed value to the passed key.
bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
Queries the key.
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
VALUE rb_obj_set_shareable(VALUE obj)
Marks the passed object as shareable, without any check.
rb_ractor_local_key_t rb_ractor_local_storage_value_newkey(void)
Issues a new key.
void rb_ractor_stdout_set(VALUE io)
Assigns an IO to the standard output of the Ractor that is calling this function.
void rb_ractor_stdin_set(VALUE io)
Assigns an IO to the standard input of the Ractor that is calling this function.
VALUE rb_ractor_local_storage_value(rb_ractor_local_key_t key)
Queries the key.
VALUE rb_yield(VALUE val)
Yields the block.
rb_block_call_func * rb_block_call_func_t
Shorthand type that represents an iterator-written-in-C function pointer.
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
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.
void rb_copy_generic_ivar(VALUE clone, VALUE obj)
Copies the list of instance variables.
#define RARRAY_LEN
Just another name of rb_array_len.
#define RARRAY(obj)
Convenient casting macro.
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
#define RARRAY_AREF(a, i)
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
#define RBASIC(obj)
Convenient casting macro.
#define RUBY_DEFAULT_FREE
This is a value you can set to RData::dfree.
#define RFILE(obj)
Convenient casting macro.
#define RHASH_SET_IFNONE(h, ifnone)
Destructively updates the default value of the hash.
#define RHASH_SIZE(h)
Queries the size of the hash.
#define RMATCH(obj)
Convenient casting macro.
static VALUE RREGEXP_SRC(VALUE rexp)
Convenient getter function.
#define RSTRING(obj)
Convenient casting macro.
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
static long RSTRUCT_LEN(VALUE st)
Returns the number of struct members.
static VALUE RSTRUCT_SET(VALUE st, int k, VALUE v)
Resembles Struct#[]=.
static VALUE RSTRUCT_GET(VALUE st, int k)
Resembles Struct#[].
#define RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
#define DATA_PTR(obj)
Convenient casting macro for backward compatibility.
static const rb_data_type_t * RTYPEDDATA_TYPE(VALUE obj)
Queries for the type of given object.
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
#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...
#define FilePathValue(v)
Ensures that the parameter object is a path.
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
#define RTEST
This is an old name of RB_TEST.
Ruby object's base components.
Regular expression execution context.
VALUE regexp
The expression of this match.
VALUE str
The target string that the match was made against.
This is the struct that holds necessary info for a struct.
VALUE ecopts
Flags as Ruby hash.
Ruby's IO, metadata and buffers.
struct rb_io_encoding encs
Decomposed encoding flags.
VALUE self
The IO's Ruby level counterpart.
VALUE write_lock
This is a Ruby level mutex.
VALUE timeout
The timeout associated with this IO when performing blocking operations.
VALUE writeconv_pre_ecopts
Value of ::rb_io_t::rb_io_enc_t::ecopts stored right before initialising rb_io_t::writeconv.
VALUE tied_io_for_writing
Duplex IO object, if set.
VALUE writeconv_asciicompat
This is, when set, an instance of rb_cString which holds the "common" encoding.
VALUE pathv
pathname for file
Type that defines a ractor-local storage.
void(* free)(void *ptr)
A function to destruct a ractor-local storage.
void(* mark)(void *ptr)
A function to mark a ractor-local storage.
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_signal(rb_nativethread_cond_t *cond)
Signals a condition variable.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.
static enum ruby_value_type RB_BUILTIN_TYPE(VALUE obj)
Queries the type of the object.
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.