Ruby 4.1.0dev (2026-10-28 revision 1b06b950bb460d20a9dcecb8b97d363adbf4a358)
ractor.c (1b06b950bb460d20a9dcecb8b97d363adbf4a358)
1// Ractor implementation
2
3#include "ruby/ruby.h"
4#include "ruby/thread.h"
5#include "ruby/ractor.h"
6#include "ruby/re.h"
8#include "vm_core.h"
9#include "vm_sync.h"
10#include "ractor_core.h"
11#include "internal/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"
32#include "ruby/encoding.h"
33#include "variable.h"
34#include "shape.h"
35#include "yjit.h"
36#include "zjit.h"
37
39static VALUE rb_cRactorSelector;
40
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;
48
49static ID id_marshal_dump, id_marshal_load;
50static ID id_dump, id_load, id_dump_data, id_load_data;
51
52static void vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line);
53
54
55#if RACTOR_CHECK_MODE > 0
56bool rb_ractor_ignore_belonging_flag = false;
57#endif
58
59// Ractor locking
60
61static void
62ASSERT_ractor_unlocking(rb_ractor_t *r)
63{
64#if RACTOR_CHECK_MODE > 0
65 const rb_execution_context_t *ec = rb_current_ec_noinline();
66 if (ec != NULL && r->sync.locked_by == rb_ractor_self(rb_ec_ractor_ptr(ec))) {
67 rb_bug("recursive ractor locking");
68 }
69#endif
70}
71
72static void
73ASSERT_ractor_locking(rb_ractor_t *r)
74{
75#if RACTOR_CHECK_MODE > 0
76 const rb_execution_context_t *ec = rb_current_ec_noinline();
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.");
80 }
81#endif
82}
83
84static void
85ractor_lock(rb_ractor_t *r, const char *file, int line)
86{
87 RUBY_DEBUG_LOG2(file, line, "locking r:%"PRI_SERIALT_PREFIX"u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
88
89 ASSERT_ractor_unlocking(r);
90 rb_native_mutex_lock(&r->sync.lock);
91
92 const rb_execution_context_t *ec = rb_current_ec_noinline();
93 if (ec) {
94 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
95 VM_ASSERT(!cr->malloc_gc_disabled);
96 cr->malloc_gc_disabled = true;
97 }
98
99#if RACTOR_CHECK_MODE > 0
100 if (ec != NULL) {
101 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
102 r->sync.locked_by = rb_ractor_self(cr);
103 }
104#endif
105
106 RUBY_DEBUG_LOG2(file, line, "locked r:%"PRI_SERIALT_PREFIX"u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
107}
108
109static void
110ractor_lock_self(rb_ractor_t *cr, const char *file, int line)
111{
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);
115#endif
116 ractor_lock(cr, file, line);
117}
118
119static void
120ractor_unlock(rb_ractor_t *r, const char *file, int line)
121{
122 ASSERT_ractor_locking(r);
123#if RACTOR_CHECK_MODE > 0
124 r->sync.locked_by = Qnil;
125#endif
126
127 const rb_execution_context_t *ec = rb_current_ec_noinline();
128 if (ec) {
129 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
130 VM_ASSERT(cr->malloc_gc_disabled);
131 cr->malloc_gc_disabled = false;
132 }
133
134 rb_native_mutex_unlock(&r->sync.lock);
135
136 RUBY_DEBUG_LOG2(file, line, "r:%"PRI_SERIALT_PREFIX"u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
137}
138
139static void
140ractor_unlock_self(rb_ractor_t *cr, const char *file, int line)
141{
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);
145#endif
146 ractor_unlock(cr, file, line);
147}
148
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__)
153
154void
155rb_ractor_lock_self(rb_ractor_t *r)
156{
157 RACTOR_LOCK_SELF(r);
158}
159
160void
161rb_ractor_unlock_self(rb_ractor_t *r)
162{
163 RACTOR_UNLOCK_SELF(r);
164}
165
166// Ractor status
167
168static const char *
169ractor_status_str(enum ractor_status status)
170{
171 switch (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";
176 }
177 rb_bug("unreachable");
178}
179
180static void
181ractor_status_set(rb_ractor_t *r, enum ractor_status status)
182{
183 RUBY_DEBUG_LOG("r:%"PRI_SERIALT_PREFIX"u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
184
185 // check 1
186 if (r->status_ != ractor_created) {
187 VM_ASSERT(r == GET_RACTOR()); // only self-modification is allowed.
188 ASSERT_vm_locking();
189 }
190
191 // check2: transition check. assume it will be vanished on non-debug build.
192 switch (r->status_) {
193 case ractor_created:
194 VM_ASSERT(status == ractor_blocking);
195 break;
196 case ractor_running:
197 VM_ASSERT(status == ractor_blocking||
198 status == ractor_terminated);
199 break;
200 case ractor_blocking:
201 VM_ASSERT(status == ractor_running);
202 break;
203 case ractor_terminated:
204 rb_bug("unreachable");
205 break;
206 }
207
208 r->status_ = status;
209}
210
211static bool
212ractor_status_p(rb_ractor_t *r, enum ractor_status status)
213{
214 return rb_ractor_status_p(r, status);
215}
216
217// Ractor data/mark/free
218
219static void ractor_local_storage_mark(rb_ractor_t *r);
220static void ractor_local_storage_free(rb_ractor_t *r);
221
222static void ractor_sync_mark(const struct rb_gc_mark_ctx *ctx, rb_ractor_t *r);
223static void ractor_sync_free(rb_ractor_t *r);
224static size_t ractor_sync_memsize(const rb_ractor_t *r);
225static void ractor_sync_init(rb_ractor_t *r);
226
227static int
228mark_targeted_hook_list(st_data_t key, st_data_t value, st_data_t _arg)
229{
230 rb_hook_list_t *hook_list = (rb_hook_list_t*)value;
231
232 if (hook_list->type == hook_list_type_targeted_iseq) {
233 rb_gc_mark((VALUE)key);
234 }
235 else {
237 RUBY_ASSERT(hook_list->type == hook_list_type_targeted_def);
238 rb_gc_mark(def->body.bmethod.proc);
239 }
240 rb_hook_list_mark(hook_list);
241
242 return ST_CONTINUE;
243}
244
245static void
246ractor_mark_thread(const struct rb_gc_mark_ctx *ctx, rb_thread_t *th)
247{
248 rb_gc_mark_ctx(ctx, th->self);
249
250 /* A thread's ec lives inside the root fiber struct and is freed with that
251 * fiber's wrapper object, so keep the fiber wrappers alive from here too. */
252 if (th->root_fiber) {
253 VALUE root_fiber_self = rb_fiberptr_self(th->root_fiber);
254 if (root_fiber_self) rb_gc_mark_ctx(ctx, root_fiber_self);
255 }
256 /* The ec sits inside its fiber, so marking that fiber's wrapper scans the ec
257 * as well. Only when there is no wrapper yet (mid-creation, teardown) does
258 * the ec need marking of its own. */
259 VALUE ec_fiber_self = (th->ec && th->ec->fiber_ptr) ? rb_fiberptr_self(th->ec->fiber_ptr) : 0;
260 if (ec_fiber_self) {
261 rb_gc_mark_ctx(ctx, ec_fiber_self);
262 }
263 else if (th->ec) {
264 rb_execution_context_mark_ctx(ctx, th->ec);
265 }
266
267 /* Root the thread's remaining possessions directly as well; thgroup in
268 * particular has no other root. */
269 rb_thread_mark_owned_roots(ctx, th);
270}
271
272static void
273ractor_mark_unshareable_parts(const struct rb_gc_mark_ctx *ctx, rb_ractor_t *r)
274{
275 /* A single VALUE slot written by the owner in one word, so any GC reads it safely.
276 * Its target belongs to another Ractor, so containment makes a foreign marker skip
277 * it. */
278 rb_gc_mark_ctx(ctx, r->r_stdin);
279 rb_gc_mark_ctx(ctx, r->r_stdout);
280 rb_gc_mark_ctx(ctx, r->r_stderr);
281 rb_gc_mark_ctx(ctx, r->verbose);
282 rb_gc_mark_ctx(ctx, r->debug);
283
284 // mark the received messages (the structures the owner mutates guard themselves)
285 ractor_sync_mark(ctx, r);
286
287 /* Structures the owner mutates while running follow. */
288
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);
292 }
293
294 if (r->threads.cnt > 0) {
295 rb_thread_t *th = 0;
296 ccan_list_for_each(&r->threads.set, th, lt_node) {
297 VM_ASSERT(th != NULL);
298 ractor_mark_thread(ctx, th);
299 }
300 }
301
302 /* A thread in the MN termination epilogue has left the set but is still
303 * running on its coroutine stack; it stays a root until its last use.
304 * Read once: the epilogue clears the slot concurrently. The thread is
305 * past rb_fiber_close/thread_cleanup_func by then -- the same state
306 * thread_mark walks whenever a terminated Thread's wrapper is still
307 * referenced, and ractor_mark_thread performs the same marks. */
308 rb_thread_t *dying_th = RUBY_ATOMIC_PTR_LOAD(r->threads.dying_th);
309 if (dying_th) ractor_mark_thread(ctx, dying_th);
310
311 ractor_local_storage_mark(r);
312}
313
314static void
315ractor_mark(void *ptr)
316{
317 rb_ractor_t *r = (rb_ractor_t *)ptr;
318 const struct rb_gc_mark_ctx ctx = rb_gc_current_mark_ctx();
319
320 /* Only the wrapper's direct references: following an unshareable object from the
321 * shareable wrapper would break the shref rule. Unshareable roots are marked by the
322 * root scan (rb_ractor_mark_local_roots); zombie_objspaces covers the terminated. */
323 rb_gc_mark_ctx(&ctx, r->loc);
324 rb_gc_mark_ctx(&ctx, r->name);
325 /* The default port is shareable, so following it breaks no rule. Other Ractors
326 * still send/value through it after termination, and once a terminated Ractor left
327 * both the set and zombie_objspaces (orphan-merged) this marker is its only cover. */
328 rb_gc_mark_ctx(&ctx, r->sync.default_port_value);
329 /* A single-objspace impl (mmtk) has no zombie_objspaces and no pin/shref bits, so
330 * the root scan cannot reach a terminated Ractor's queue, in-flight payloads or
331 * join value; and no shref rule forbids following them from the wrapper. */
332 if (!rb_gc_multi_objspace_p()) {
333 ractor_mark_unshareable_parts(&ctx, r);
334 rb_ractor_mark_terminated_join_value(r);
335 }
336 else if (rb_gc_during_global_gc_p()) {
337 /* The join value is only of use to whoever can still call Ractor#value, which
338 * means holding this wrapper, so mark it as the wrapper's child rather than as a
339 * root. A global GC stops the world and marks every objspace together, which is
340 * what lets the shareable wrapper reach an unshareable value at all. */
341 rb_ractor_mark_terminated_join_value(r);
342 }
343}
344
345/* Mark the GC roots reachable from Ractor r's C structs. A local GC cannot rely on the
346 * heap Ractor and Thread wrapper objects, which may live in another objspace, so this
347 * Ractor's own possessions are rooted directly from here. */
348void
349rb_ractor_mark_local_roots(const struct rb_gc_mark_ctx *ctx, rb_ractor_t *r)
350{
351 if (r->postmortem) {
352 /* The final self collection: everything else -- the Thread and Fiber
353 * wrappers, stdio, stack leftovers -- is what it exists to reclaim.
354 * Skipping the walk below cannot drop dying_th: postmortem runs on the
355 * Ractor's last thread, which can only run after any predecessor's
356 * epilogue cleared the slot (under the same scheduler lock). */
357 VM_ASSERT(RUBY_ATOMIC_PTR_LOAD(r->threads.dying_th) == NULL);
358 rb_ractor_mark_terminated_join_value(r);
359 rb_gc_mark_vm_stack_values((long)r->registered_marks_cnt, r->registered_marks);
360 rb_gc_mark_registered_addrs(r, true);
361 return;
362 }
363
364 rb_gc_mark_ctx(ctx, r->loc);
365 rb_gc_mark_ctx(ctx, r->name);
366 /* Only the root scan calls this: a local GC for itself, a global GC for the whole
367 * set under the barrier. A terminated Ractor has left the set; zombie_objspaces
368 * covers it instead. */
369 VM_ASSERT(r == rb_current_ractor_raw(false) || rb_gc_during_global_gc_p());
370 VM_ASSERT(!rb_ractor_status_p(r, ractor_terminated));
371 ractor_mark_unshareable_parts(ctx, r);
372
373 /* This Ractor's rb_gc_register_mark_object pins, treated conservatively: a local GC
374 * marks only its own residents and leaves foreign or shareable entries to their
375 * owner or to the global GC. */
376 rb_gc_mark_vm_stack_values((long)r->registered_marks_cnt, r->registered_marks);
377}
378
379/* Mark and pin a terminated, unfreed Ractor's return value (legacy); the global GC
380 * calls this via zombie_objspaces. Pinned because compaction does not update C-struct
381 * slots. The default port is covered by the mutual wrapper/port marking instead. */
382void
383rb_ractor_mark_terminated_join_value(rb_ractor_t *r)
384{
385 VALUE slots[] = {
386 r->sync.legacy,
387 };
388 rb_gc_mark_vm_stack_values((long)numberof(slots), slots);
389}
390
391/* Move src's rb_gc_register_mark_object pins to dst. Called before merging src's
392 * objspace into dst, so a pinned object never loses its root in between. An absorb can
393 * run during a GC sweep, so plain realloc keeps it from re-entering GC. */
394void
395rb_ractor_absorb_registered_marks(rb_ractor_t *dst, rb_ractor_t *src)
396{
397 if (src->registered_marks_cnt == 0) return;
398 size_t need = dst->registered_marks_cnt + src->registered_marks_cnt;
399 if (need > dst->registered_marks_capa) {
400 size_t nc = dst->registered_marks_capa ? dst->registered_marks_capa : 64;
401 while (nc < need) nc *= 2;
402 VALUE *p = realloc(dst->registered_marks, nc * sizeof(VALUE));
403 if (!p) rb_bug("rb_ractor_absorb_registered_marks: out of memory");
404 dst->registered_marks = p;
405 dst->registered_marks_capa = nc;
406 }
407 MEMCPY(dst->registered_marks + dst->registered_marks_cnt,
408 src->registered_marks, VALUE, src->registered_marks_cnt);
409 dst->registered_marks_cnt = need;
410 src->registered_marks_cnt = 0;
411}
412
413void
414rb_ractor_absorb_registered_addrs_without_gc(rb_ractor_t *dst, rb_ractor_t *src)
415{
416 rb_vm_t *vm = GET_VM();
417
418 rb_native_mutex_lock(&vm->gc.registered_addrs.lock);
419 if (src->registered_addrs_cnt > 0) {
420 size_t need = dst->registered_addrs_cnt + src->registered_addrs_cnt;
421 if (need > dst->registered_addrs_capa) {
422 size_t nc = dst->registered_addrs_capa ? dst->registered_addrs_capa : 64;
423 while (nc < need) nc *= 2;
424 struct rb_ractor_registered_addr *p =
425 realloc(dst->registered_addrs, nc * sizeof(struct rb_ractor_registered_addr));
426 if (!p) rb_bug("rb_ractor_absorb_registered_addrs_without_gc: out of memory");
427 dst->registered_addrs = p;
428 dst->registered_addrs_capa = nc;
429 }
430 MEMCPY(dst->registered_addrs + dst->registered_addrs_cnt,
431 src->registered_addrs, struct rb_ractor_registered_addr, src->registered_addrs_cnt);
432 dst->registered_addrs_cnt = need;
433 src->registered_addrs_cnt = 0;
434 rb_gc_registered_addrs_enroll_without_gc(vm, dst);
435 }
436 rb_gc_registered_addrs_unenroll_without_gc(vm, src);
437 rb_native_mutex_unlock(&vm->gc.registered_addrs.lock);
438}
439
440static int
441free_targeted_hook_lists(st_data_t key, st_data_t val, st_data_t _arg)
442{
443 rb_hook_list_t *hook_list = (rb_hook_list_t*)val;
444 rb_hook_list_free(hook_list);
445 return ST_DELETE;
446}
447
448static void
449free_targeted_hooks(st_table *hooks_tbl)
450{
451 st_foreach(hooks_tbl, free_targeted_hook_lists, 0);
452}
453
454void rb_thread_sched_destroy(struct rb_thread_sched *);
455
456static void
457ractor_free(void *ptr)
458{
459 rb_ractor_t *r = (rb_ractor_t *)ptr;
460 RUBY_DEBUG_LOG("free r:%"PRI_SERIALT_PREFIX"u", rb_ractor_id(r));
461
462 free_targeted_hooks(&r->pub.targeted_hooks);
463 rb_thread_sched_destroy(&r->threads.sched);
464 rb_native_mutex_destroy(&r->sync.lock);
465 ractor_local_storage_free(r);
466 rb_hook_list_free(&r->pub.hooks);
467 rb_st_free_embedded_table(&r->pub.targeted_hooks);
468
469 if (r->newobj_cache) {
470 RUBY_ASSERT(r == ruby_single_main_ractor);
471
472 rb_gc_ractor_cache_free(r->newobj_cache);
473 r->newobj_cache = NULL;
474 }
475
476 /* Died unjoined and the handle is collected: nobody can inherit it. We are in a
477 * sweep under the global GC barrier, so disown the zombie_objspaces entry and post
478 * the merge to main. main itself only gets here in the free-at-exit walk: leave it
479 * and its objspace to VM destruct. */
480 if (r->objspace && !r->main_ractor) {
481 rb_gc_objspace_disown(r->objspace);
482 r->objspace = NULL;
483 }
484
485 ractor_sync_free(r);
486
487 if (r->in_terminated_set) {
488 rb_native_mutex_lock(&GET_VM()->gc.registered_addrs.lock);
489 ccan_list_del(&r->vmlr_node);
490 r->in_terminated_set = false;
491 rb_native_mutex_unlock(&GET_VM()->gc.registered_addrs.lock);
492 }
493
494 /* An orphan (unjoined) Ractor hands its rb_gc_register_mark_object pins to main
495 * before its objspace is absorbed; the join path does the same for the joiner.
496 * Both happen before the objspace merge, so no window has unmoved registrations. */
497 if (!r->main_ractor) {
498 rb_ractor_absorb_registered_marks(GET_VM()->ractor.main_ractor, r);
499 rb_ractor_absorb_registered_addrs_without_gc(GET_VM()->ractor.main_ractor, r);
500 }
501 else {
502 rb_native_mutex_lock(&GET_VM()->gc.registered_addrs.lock);
503 rb_gc_registered_addrs_unenroll_without_gc(GET_VM(), r);
504 rb_native_mutex_unlock(&GET_VM()->gc.registered_addrs.lock);
505 }
506 free(r->registered_marks);
507 r->registered_marks = NULL;
508 r->registered_marks_cnt = r->registered_marks_capa = 0;
509
510 free(r->registered_addrs);
511 r->registered_addrs = NULL;
512 r->registered_addrs_cnt = r->registered_addrs_capa = 0;
513
514 if (!r->main_ractor) {
515 SIZED_FREE(r);
516 }
517}
518
519static int
520targeted_hook_list_memsize_i(st_data_t key, st_data_t val, st_data_t arg)
521{
522 size_t *size = (size_t *)arg;
523 rb_hook_list_t *hook_list = (rb_hook_list_t *)val;
524
525 *size += sizeof(rb_hook_list_t) + rb_hook_list_memsize(hook_list);
526
527 return ST_CONTINUE;
528}
529
530static size_t
531ractor_memsize(const void *ptr)
532{
533 rb_ractor_t *r = (rb_ractor_t *)ptr;
534 size_t size = sizeof(rb_ractor_t);
535
536 size += ractor_sync_memsize(r);
537
538 size += rb_st_memsize(&r->pub.targeted_hooks) - sizeof(struct st_table);
539 st_foreach(&r->pub.targeted_hooks, targeted_hook_list_memsize_i, (st_data_t)&size);
540 size += rb_hook_list_memsize(&r->pub.hooks);
541
542 if (r->local_storage) {
543 size += st_memsize(r->local_storage);
544 }
545 if (r->idkey_local_storage) {
546 size += rb_id_table_memsize(r->idkey_local_storage);
547 }
548
549 size += r->registered_marks_capa * sizeof(VALUE);
550 size += r->registered_addrs_capa * sizeof(struct rb_ractor_registered_addr);
551
552 return size;
553}
554
555static void
556ractor_update_references(void *ptr)
557{
558 /* registered_marks are pinned (marked by rb_gc_mark_vm_stack_values), so
559 * compaction does not need to update them. */
560}
561
562static const rb_data_type_t ractor_data_type = {
563 "ractor",
564 {
565 ractor_mark,
566 ractor_free,
567 ractor_memsize,
568 ractor_update_references,
569 },
570 0, 0, RUBY_TYPED_FREE_IMMEDIATELY /* | RUBY_TYPED_WB_PROTECTED */
571};
572
573bool
574rb_ractor_p(VALUE gv)
575{
576 if (rb_typeddata_is_kind_of(gv, &ractor_data_type)) {
577 return true;
578 }
579 else {
580 return false;
581 }
582}
583
584static inline rb_ractor_t *
585RACTOR_PTR(VALUE self)
586{
587 VM_ASSERT(rb_ractor_p(self));
588 rb_ractor_t *r = DATA_PTR(self);
589 return r;
590}
591
592#define MAIN_RACTOR_ID 1
593static rb_serial_t ractor_last_id = MAIN_RACTOR_ID;
594
595#include "ractor_sync.c"
596
597// creation/termination
598
599/* Ids are never reused, so they must not wrap either: 64 bits, which rules out an
600 * atomic (there is no portable 64-bit one). Serialized by the VM lock, or by the
601 * GVL before there is a second Ractor to take it against -- the same condition
602 * vm_insert_ractor0 asserts. */
603static rb_serial_t
604ractor_next_id(void)
605{
606 VM_ASSERT(RB_VM_LOCKED_P() || !rb_multi_ractor_p());
607 return ++ractor_last_id;
608}
609
610static void
611vm_insert_ractor0(rb_vm_t *vm, rb_ractor_t *r, bool single_ractor_mode)
612{
613 RUBY_DEBUG_LOG("r:%"PRI_SERIALT_PREFIX"u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
614 VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
615
616 /* End main's cycle before a second Ractor becomes visible: the collection was
617 * planned for a single-objspace world (a local GC is a whole-world GC there and may
618 * free shareable objects), so it must not straddle the transition. */
619 if (vm->ractor.cnt == 1) {
620 rb_gc_rest();
621 }
622
623 ccan_list_add_tail(&vm->ractor.set, &r->vmlr_node);
624 vm->ractor.cnt++;
625
626 if (r->newobj_cache) {
627 VM_ASSERT(r == ruby_single_main_ractor);
628 }
629 else {
630 r->newobj_cache = rb_gc_ractor_cache_alloc(r);
631 }
632}
633
634static void
635cancel_single_ractor_mode(void)
636{
637 // enable multi-ractor mode
638 RUBY_DEBUG_LOG("enable multi-ractor mode");
639
640 ruby_single_main_ractor = NULL;
641 rb_yjit_invalidate_single_ractor();
642 rb_zjit_invalidate_single_ractor();
643
644 ASSERT_vm_unlocking();
645 rb_funcall(rb_cRactor, rb_intern("_activated"), 0);
646}
647
648static void
649vm_insert_ractor(rb_vm_t *vm, rb_ractor_t *r)
650{
651 VM_ASSERT(ractor_status_p(r, ractor_created));
652
653 if (rb_multi_ractor_p()) {
654 RB_VM_LOCK();
655 {
656 vm_insert_ractor0(vm, r, false);
657 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
658 /* The child is in the set and enumerated on its own now, so drop the cover
659 * through its creator and avoid enumerating it twice. Cleared under the
660 * same VM lock that added it, so no whole-VM walk sees both. */
661 rb_ractor_t *cur = rb_current_ractor_raw(false);
662 if (cur && cur->creating_child_objspace == r->objspace) {
663 cur->creating_child_objspace = NULL;
664 }
665 }
666 RB_VM_UNLOCK();
667 }
668 else {
669 if (vm->ractor.cnt == 0) {
670 // main ractor
671 vm_insert_ractor0(vm, r, true);
672 ractor_status_set(r, ractor_blocking);
673 ractor_status_set(r, ractor_running);
674 }
675 else {
676 cancel_single_ractor_mode();
677 vm_insert_ractor0(vm, r, true);
678 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
679 /* As in the multi-Ractor branch: the child joined the set, so drop the
680 * creator's cover, or a global GC enumerates the child's objspace twice and
681 * sweeps its live main Thread and root Fiber. */
682 rb_ractor_t *cur = rb_current_ractor_raw(false);
683 if (cur && cur->creating_child_objspace == r->objspace) {
684 cur->creating_child_objspace = NULL;
685 }
686 }
687 }
688}
689
690static void
691vm_remove_ractor(rb_vm_t *vm, rb_ractor_t *cr)
692{
693 VM_ASSERT(ractor_status_p(cr, ractor_running));
694 VM_ASSERT(vm->ractor.cnt > 1);
695 VM_ASSERT(cr->threads.cnt == 1);
696
697 RB_VM_LOCK();
698 {
699 RUBY_DEBUG_LOG("ractor.cnt:%u-- terminate_waiting:%d",
700 vm->ractor.cnt, vm->ractor.sync.terminate_waiting);
701
702 VM_ASSERT(vm->ractor.cnt > 0);
703 ccan_list_del(&cr->vmlr_node);
704
705 /* A single-objspace impl has no zombie_objspaces, so nothing roots the
706 * registered_marks of a Ractor that left the set; track it in a separate list
707 * until ractor_free. */
708 if (!rb_gc_multi_objspace_p()) {
709 rb_native_mutex_lock(&vm->gc.registered_addrs.lock);
710 ccan_list_add(&vm->ractor.terminated_set, &cr->vmlr_node);
711 cr->in_terminated_set = true;
712 rb_native_mutex_unlock(&vm->gc.registered_addrs.lock);
713 }
714
715 if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
716 rb_native_cond_signal(&vm->ractor.sync.terminate_cond);
717 }
718
719 rb_gc_ractor_cache_free(cr->newobj_cache);
720 cr->newobj_cache = NULL;
721
722 /* The objspace loses its owning thread: keep it enumerable until inheritance
723 * merges it. Register in zombie_objspaces BEFORE decrementing cnt: other
724 * Ractors read rb_gc_single_objspace_p lock-free, and the other order opens a
725 * cnt==1-no-zombie window where a GC skips shareable pinning and collects
726 * objects (a cc, say) reachable only through this objspace. */
727 if (cr->objspace) {
728 /* The final self collection already ran (ractor_postmortem_collect),
729 * so the entry measures exactly the pages the joiner will inherit. */
730 rb_gc_objspace_retire(&cr->objspace);
731 }
732 vm->ractor.cnt--;
733
734 ractor_status_set(cr, ractor_terminated);
735 }
736 RB_VM_UNLOCK();
737}
738
739/* The dying thread's final collection of its own objspace, and the capture of what it
740 * must free itself. Called with the GVL still held: a concurrent global GC waits for
741 * this thread's safepoint, so the collection is lock-free like any local GC. */
742static void
743ractor_postmortem_collect(rb_thread_t *th, struct rb_ractor_postmortem_frees *pf)
744{
745 rb_ractor_t *const cr = th->ractor;
746 VM_ASSERT(cr != GET_VM()->ractor.main_ractor);
747 VM_ASSERT(th->ec != NULL);
748
749 struct rb_fiber_struct *const fiber = th->ec->fiber_ptr;
750 const bool fiber_wrapped = fiber && rb_fiberptr_self(fiber) != 0;
751
752 /* With a thread-event hook registered the callbacks may retain any Ractor's Thread
753 * object (rb_internal_thread_event_hook_t), and such references are invisible to
754 * the reduced roots: keep the ordinary retire roots, wrappers survive to absorb. */
755 cr->postmortem = rb_gc_multi_objspace_p() && !rb_thread_event_hooks_registered_p();
756 rb_gc_objspace_postmortem_self();
757
758 /* Only what this collection provably swept (wrapper gone, struct deferred with an
759 * in-band mark) may be touched after vm_remove_ractor unlinks the Ractor. */
760 pf->th = (th->self == 0) ? th : NULL;
761 pf->fiber = (fiber_wrapped && rb_fiberptr_self(fiber) == 0) ? fiber : NULL;
762}
763
764void
765rb_ractor_postmortem_free(const struct rb_ractor_postmortem_frees *pf)
766{
767 if (pf->fiber == NULL && pf->th == NULL) return;
768
769 /* The frees below resolve their objspace through the TLS ec, which sits inside
770 * pf->fiber and leads to the retired Ractor: cut the resolution off so they fall
771 * back to the main objspace. (The MN epilogue has already done this.) */
772#ifdef RB_THREAD_LOCAL_SPECIFIER
773 rb_current_ec_set(NULL);
774#else
775 native_tls_set(ruby_current_ec_key, NULL);
776#endif
777
778 /* the fiber struct embeds the thread's final ec, so free it first */
779 if (pf->fiber) rb_fiber_free_body(pf->fiber);
780 if (pf->th) rb_thread_free_body(pf->th);
781}
782
783static VALUE
784ractor_alloc(VALUE klass)
785{
786 rb_ractor_t *r;
787 VALUE rv = TypedData_Make_Struct(klass, rb_ractor_t, &ractor_data_type, r);
789 r->pub.self = rv;
790 r->next_ec_serial = 1;
791 VM_ASSERT(ractor_status_p(r, ractor_created));
792 return rv;
793}
794
795static rb_ractor_t _main_ractor = {
796 .loc = Qnil,
797 .name = Qnil,
798 .pub.id = MAIN_RACTOR_ID,
799 .pub.self = Qnil,
800 .next_ec_serial = 1,
801 .main_ractor = true,
802};
803
805rb_ractor_main_alloc(void)
806{
807 rb_ractor_t *r = &_main_ractor;
808 /* The main Ractor is allocated before its objspace exists, so its newobj cache is
809 * created later in Init_BareVM, once rb_gc_init_objspaces has set r->objspace. */
810 ruby_single_main_ractor = r;
811
812 return r;
813}
814
815#if defined(HAVE_WORKING_FORK)
816// Set up the main Ractor for the VM after fork.
817// Puts us in "single Ractor mode"
818void
819rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th)
820{
821 // initialize as a main ractor
822 vm->ractor.cnt = 0;
823 vm->ractor.blocking_cnt = 0;
824 /* Only main survives a fork: the holds of dead Ractors and of critical sections are
825 * gone, leaving main's own disable. */
826 rb_gc_disable_holders_atfork();
827 /* Only the main Ractor survives a fork, so drop the creation cover. The set was
828 * just emptied by rb_vm_living_threads_init, and zombie_objspaces still holds the
829 * non-main objspaces that terminate_atfork parked there for the orphan merge. */
830 th->ractor->creating_child_objspace = NULL;
831 ruby_single_main_ractor = th->ractor;
832 th->ractor->status_ = ractor_created;
833
834 rb_ractor_living_threads_init(th->ractor);
835 rb_ractor_living_threads_insert(th->ractor, th);
836
837 VM_ASSERT(vm->ractor.blocking_cnt == 0);
838 VM_ASSERT(vm->ractor.cnt == 1);
839}
840
841void
842rb_ractor_terminate_atfork(rb_vm_t *vm, rb_ractor_t *r)
843{
844 rb_gc_ractor_cache_free(r->newobj_cache);
845 r->newobj_cache = NULL;
846 r->status_ = ractor_terminated;
847 // a termination epilogue in the parent did not survive the fork
848 r->threads.dying_th = NULL;
849 if (!rb_gc_multi_objspace_p()) {
850 ccan_list_del(&r->vmlr_node);
851 ccan_list_add(&vm->ractor.terminated_set, &r->vmlr_node);
852 r->in_terminated_set = true;
853 }
854
855 /* In a forked child every other Ractor is terminated-unjoined, so keep its objspace
856 * enumerable until a join or a global GC merges it. */
857 if (r->objspace) {
858 rb_gc_objspace_retire(&r->objspace);
859 }
860 ractor_sync_terminate_atfork(vm, r);
861}
862#endif
863
864void rb_thread_sched_init(struct rb_thread_sched *, bool atfork);
865
866void
867rb_ractor_living_threads_init(rb_ractor_t *r)
868{
869 ccan_list_head_init(&r->threads.set);
870 r->threads.cnt = 0;
871 r->threads.blocking_cnt = 0;
872 r->threads.terminating = false;
873 // atfork: a sibling's termination epilogue did not survive the fork
874 r->threads.dying_th = NULL;
875}
876
877static void
878ractor_init(rb_ractor_t *r, VALUE name, VALUE loc)
879{
880 ractor_sync_init(r);
881 st_init_existing_numtable_with_size(&r->pub.targeted_hooks, 0);
882 r->pub.hooks.type = hook_list_type_ractor_local;
883
884 // thread management
885 rb_thread_sched_init(&r->threads.sched, false);
886 rb_ractor_living_threads_init(r);
887
888 // naming
889 if (!NIL_P(name)) {
890 rb_encoding *enc;
891 StringValueCStr(name);
892 enc = rb_enc_get(name);
893 if (!rb_enc_asciicompat(enc)) {
894 rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
895 rb_enc_name(enc));
896 }
898 }
899
901 r->loc = loc;
902 r->name = name;
903}
904
905void
906rb_ractor_main_setup(rb_vm_t *vm, rb_ractor_t *r, rb_thread_t *th)
907{
908 VALUE rv = r->pub.self = TypedData_Wrap_Struct(rb_cRactor, &ractor_data_type, r);
909 RB_OBJ_SET_SHAREABLE(r->pub.self);
910 ractor_init(r, Qnil, Qnil);
911 r->threads.main = th;
912 rb_ractor_living_threads_insert(r, th);
913 rb_ractor_setup_default_port(r);
914
915 RB_GC_GUARD(rv);
916}
917
918static VALUE
919ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VALUE args, VALUE block)
920{
921 VALUE rv = ractor_alloc(self);
922 rb_ractor_t *r = RACTOR_PTR(rv);
923 ractor_init(r, name, loc);
924
925 RB_VM_LOCKING() {
926 r->pub.id = ractor_next_id();
927 }
928 RUBY_DEBUG_LOG("r:%"PRI_SERIALT_PREFIX"u", r->pub.id);
929
930 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
931 r->verbose = cr->verbose;
932 r->debug = cr->debug;
933
934 /* Every Ractor has an objspace, and it must exist before its thread runs: the
935 * first allocation goes there through rb_gc_get_objspace. */
936 r->objspace = rb_gc_objspace_alloc();
937
938 rb_thread_create_ractor(r, args, block);
939
940 RB_GC_GUARD(rv);
941 return rv;
942}
943
944#if 0
945static VALUE
946ractor_create_func(VALUE klass, VALUE loc, VALUE name, VALUE args, rb_block_call_func_t func)
947{
948 VALUE block = rb_proc_new(func, Qnil);
949 return ractor_create(rb_current_ec_noinline(), klass, loc, name, args, block);
950}
951#endif
952
953static void
954ractor_atexit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE result, bool exc)
955{
956 ractor_notify_exit(ec, cr, result, exc);
957}
958
959/* The dying thread's last work inside its Ractor. The order is the point: a joiner woken
960 * before the collection spins in ractor_value for the whole of it. */
961void
962rb_ractor_postmortem(rb_thread_t *th, struct rb_ractor_postmortem_frees *pf)
963{
964 ractor_postmortem_collect(th, pf);
965 ractor_send_exit_tokens(th->ec, th->ractor);
966}
967
968void
969rb_ractor_atexit(rb_execution_context_t *ec, VALUE result)
970{
971 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
972 ractor_atexit(ec, cr, result, false);
973}
974
975void
976rb_ractor_atexit_exception(rb_execution_context_t *ec)
977{
978 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
979 ractor_atexit(ec, cr, ec->errinfo, true);
980}
981
982void
983rb_ractor_teardown(rb_execution_context_t *ec)
984{
985 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
986
987 // sync with rb_ractor_terminate_interrupt_main_thread()
988 RB_VM_LOCKING() {
989 VM_ASSERT(cr->threads.main != NULL);
990 cr->threads.main = NULL;
991 }
992}
993
994void
995rb_ractor_receive_parameters(rb_execution_context_t *ec, rb_ractor_t *r, int len, VALUE *ptr)
996{
997 for (int i=0; i<len; i++) {
998 ptr[i] = ractor_receive(ec, ractor_default_port(r), NULL);
999 }
1000}
1001
1002void
1003rb_ractor_send_parameters(rb_execution_context_t *ec, rb_ractor_t *r, VALUE args)
1004{
1005 int len = RARRAY_LENINT(args);
1006 for (int i=0; i<len; i++) {
1007 ractor_send(ec, ractor_default_port(r), RARRAY_AREF(args, i), false);
1008 }
1009}
1010
1011bool
1012rb_ractor_main_p_(void)
1013{
1014 VM_ASSERT(rb_multi_ractor_p());
1015 rb_execution_context_t *ec = GET_EC();
1016 return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
1017}
1018
1019int
1020rb_ractor_living_thread_num(const rb_ractor_t *r)
1021{
1022 return r->threads.cnt;
1023}
1024
1025// only for current ractor
1026VALUE
1027rb_ractor_thread_list(void)
1028{
1029 rb_ractor_t *r = GET_RACTOR();
1030 rb_thread_t *th = 0;
1031 VALUE ary = rb_ary_new();
1032
1033 ccan_list_for_each(&r->threads.set, th, lt_node) {
1034 switch (th->status) {
1035 case THREAD_RUNNABLE:
1036 case THREAD_STOPPED:
1037 case THREAD_STOPPED_FOREVER:
1038 rb_ary_push(ary, th->self);
1039 default:
1040 break;
1041 }
1042 }
1043
1044 return ary;
1045}
1046
1047void
1048rb_ractor_living_threads_insert(rb_ractor_t *r, rb_thread_t *th)
1049{
1050 VM_ASSERT(th != NULL);
1051
1052 RACTOR_LOCK(r);
1053 {
1054 RUBY_DEBUG_LOG("r(%"PRI_SERIALT_PREFIX"u)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
1055 ccan_list_add_tail(&r->threads.set, &th->lt_node);
1056 r->threads.cnt++;
1057 }
1058 RACTOR_UNLOCK(r);
1059
1060 // first thread for a ractor
1061 if (r->threads.cnt == 1) {
1062 VM_ASSERT(ractor_status_p(r, ractor_created));
1063 vm_insert_ractor(th->vm, r);
1064 }
1065}
1066
1067static void
1068vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line)
1069{
1070 ractor_status_set(r, ractor_blocking);
1071
1072 RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d++", vm->ractor.blocking_cnt);
1073 vm->ractor.blocking_cnt++;
1074 VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
1075}
1076
1077void
1078rb_vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
1079{
1080 ASSERT_vm_locking();
1081 VM_ASSERT(GET_RACTOR() == cr);
1082 vm_ractor_blocking_cnt_inc(vm, cr, file, line);
1083}
1084
1085void
1086rb_vm_ractor_blocking_cnt_dec(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
1087{
1088 ASSERT_vm_locking();
1089 VM_ASSERT(GET_RACTOR() == cr);
1090
1091 RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d--", vm->ractor.blocking_cnt);
1092 VM_ASSERT(vm->ractor.blocking_cnt > 0);
1093 vm->ractor.blocking_cnt--;
1094
1095 ractor_status_set(cr, ractor_running);
1096}
1097
1098/* Remove a child that never started (send_parameters failed during creation). The
1099 * creator calls this (rb_ractor_living_threads_remove assumes the current Ractor);
1100 * leaving the set and disowning the objspace share one VM-lock section, no window. */
1101void
1102rb_ractor_cancel_creation(rb_ractor_t *r, rb_thread_t *th)
1103{
1104 RACTOR_LOCK(r);
1105 {
1106 ccan_list_del(&th->lt_node);
1107 r->threads.cnt--;
1108 }
1109 RACTOR_UNLOCK(r);
1110
1111 RB_VM_LOCK();
1112 {
1113 rb_vm_t *vm = th->vm;
1114 VM_ASSERT(vm->ractor.cnt > 1);
1115 ccan_list_del(&r->vmlr_node);
1116 vm->ractor.cnt--;
1117 /* Give back the blocking count vm_insert_ractor took at insert time. A child
1118 * that never ran has no chance to decrement it, and without this the
1119 * blocking_cnt <= cnt invariant breaks on the next insert. */
1120 VM_ASSERT(r->status_ == ractor_blocking);
1121 VM_ASSERT(vm->ractor.blocking_cnt > 0);
1122 vm->ractor.blocking_cnt--;
1123
1124 rb_gc_ractor_cache_free(r->newobj_cache);
1125 r->newobj_cache = NULL;
1126
1127 if (r->objspace) {
1128 rb_gc_objspace_disown(r->objspace);
1129 r->objspace = NULL;
1130 }
1131 r->status_ = ractor_terminated;
1132 }
1133 RB_VM_UNLOCK();
1134}
1135
1136void
1137rb_ractor_living_threads_remove(rb_ractor_t *cr, rb_thread_t *th)
1138{
1139 VM_ASSERT(cr == GET_RACTOR());
1140 RUBY_DEBUG_LOG("r->threads.cnt:%d--", cr->threads.cnt);
1141
1142 if (cr->threads.cnt == 1) {
1143 vm_remove_ractor(th->vm, cr);
1144 }
1145 else {
1146 RACTOR_LOCK(cr);
1147 {
1148 ccan_list_del(&th->lt_node);
1149 cr->threads.cnt--;
1150 }
1151 RACTOR_UNLOCK(cr);
1152 }
1153}
1154
1155void
1156rb_ractor_blocking_threads_inc(rb_ractor_t *cr, const char *file, int line)
1157{
1158 RUBY_DEBUG_LOG2(file, line, "cr->threads.blocking_cnt:%d++", cr->threads.blocking_cnt);
1159
1160 VM_ASSERT(cr->threads.cnt > 0);
1161 VM_ASSERT(cr == GET_RACTOR());
1162
1163 cr->threads.blocking_cnt++;
1164}
1165
1166void
1167rb_ractor_blocking_threads_dec(rb_ractor_t *cr, const char *file, int line)
1168{
1169 RUBY_DEBUG_LOG2(file, line,
1170 "r->threads.blocking_cnt:%d--, r->threads.cnt:%u",
1171 cr->threads.blocking_cnt, cr->threads.cnt);
1172
1173 VM_ASSERT(cr == GET_RACTOR());
1174
1175 cr->threads.blocking_cnt--;
1176}
1177
1178void
1179rb_ractor_vm_barrier_interrupt_running_thread(rb_ractor_t *r)
1180{
1181 VM_ASSERT(r != GET_RACTOR());
1182 ASSERT_ractor_unlocking(r);
1183 ASSERT_vm_locking();
1184
1185 RACTOR_LOCK(r);
1186 {
1187 if (ractor_status_p(r, ractor_running)) {
1188 rb_execution_context_t *ec = r->threads.running_ec;
1189 if (ec) {
1190 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec);
1191 }
1192 }
1193 }
1194 RACTOR_UNLOCK(r);
1195}
1196
1197void
1198rb_ractor_terminate_interrupt_main_thread(rb_ractor_t *r)
1199{
1200 VM_ASSERT(r != GET_RACTOR());
1201 ASSERT_ractor_unlocking(r);
1202 ASSERT_vm_locking();
1203
1204 rb_thread_t *main_th = r->threads.main;
1205 if (main_th) {
1206 if (main_th->status != THREAD_KILLED) {
1207 RUBY_VM_SET_TERMINATE_INTERRUPT(main_th->ec);
1208 rb_threadptr_interrupt(main_th);
1209 }
1210 else {
1211 RUBY_DEBUG_LOG("killed (%p)", (void *)main_th);
1212 }
1213 }
1214}
1215
1216void rb_thread_terminate_all(rb_thread_t *th); // thread.c
1217
1218static void
1219ractor_terminal_interrupt_all(rb_vm_t *vm)
1220{
1221 if (vm->ractor.cnt > 1) {
1222 // send terminate notification to all ractors
1223 rb_ractor_t *r = 0;
1224 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
1225 if (r != vm->ractor.main_ractor) {
1226 RUBY_DEBUG_LOG("r:%"PRI_SERIALT_PREFIX"u", rb_ractor_id(r));
1227 rb_ractor_terminate_interrupt_main_thread(r);
1228 }
1229 }
1230 }
1231}
1232
1233void rb_add_running_thread(rb_thread_t *th);
1234void rb_del_running_thread(rb_thread_t *th);
1235
1236void
1237rb_ractor_terminate_all(void)
1238{
1239 rb_vm_t *vm = GET_VM();
1240 rb_ractor_t *cr = vm->ractor.main_ractor;
1241
1242 RUBY_DEBUG_LOG("ractor.cnt:%d", (int)vm->ractor.cnt);
1243
1244 VM_ASSERT(cr == GET_RACTOR()); // only main-ractor's main-thread should kick it.
1245
1246 RB_VM_LOCK();
1247 {
1248 ractor_terminal_interrupt_all(vm); // kill all ractors
1249 }
1250 RB_VM_UNLOCK();
1251 rb_thread_terminate_all(GET_THREAD()); // kill other threads in main-ractor and wait
1252
1253 RB_VM_LOCK();
1254 {
1255 while (vm->ractor.cnt > 1) {
1256 RUBY_DEBUG_LOG("terminate_waiting:%d", vm->ractor.sync.terminate_waiting);
1257 vm->ractor.sync.terminate_waiting = true;
1258
1259 // wait for 1sec
1260 rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
1261 rb_del_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
1262 rb_ractor_sched_wait_terminate(vm, &vm->ractor.sync.terminate_cond, 1000 /* ms */);
1263 while (vm->ractor.sched.barrier_is_waiting) {
1264 // A barrier is waiting. Threads relinquish the VM lock before joining the barrier and
1265 // since we just acquired the VM lock back, we're blocking other threads from joining it.
1266 // We loop until the barrier is over. We can't join this barrier because our thread isn't added to
1267 // running_threads until the call below to `rb_add_running_thread`.
1268 RB_VM_UNLOCK();
1269 unsigned int lev;
1270 RB_VM_LOCK_ENTER_LEV_NB(&lev);
1271 }
1272 rb_add_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
1273 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
1274
1275 ractor_terminal_interrupt_all(vm);
1276 }
1277 }
1278 RB_VM_UNLOCK();
1279
1280 /* Every other Ractor is dead. main inherits all uninherited objspaces, so the
1281 * remaining at-exit work (finalizers, IO flush, free-at-exit) sees every object. */
1282 rb_gc_objspace_absorb_all_zombies();
1283}
1284
1286rb_vm_main_ractor_ec(rb_vm_t *vm)
1287{
1288 /* This code needs to carefully work around two bugs:
1289 * - Bug #20016: When M:N threading is enabled, running_ec is NULL if no thread is
1290 * actually currently running (as opposed to without M:N threading, when
1291 * running_ec will still point to the _last_ thread which ran)
1292 * - Bug #20197: If the main thread is sleeping, setting its postponed job
1293 * interrupt flag is pointless; it won't look at the flag until it stops sleeping
1294 * for some reason. It would be better to set the flag on the running ec, which
1295 * will presumably look at it soon.
1296 *
1297 * Solution: use running_ec if it's set, otherwise fall back to the main thread ec.
1298 * This is still susceptible to some rare race conditions (what if the last thread
1299 * to run just entered a long-running sleep?), but seems like the best balance of
1300 * robustness and complexity.
1301 */
1302 rb_execution_context_t *running_ec = vm->ractor.main_ractor->threads.running_ec;
1303 if (running_ec) { return running_ec; }
1304 return vm->ractor.main_thread->ec;
1305}
1306
1307static VALUE
1308ractor_moved_missing(int argc, VALUE *argv, VALUE self)
1309{
1310 rb_raise(rb_eRactorMovedError, "can not send any methods to a moved object");
1311}
1312
1313/*
1314 * Document-class: Ractor::Error
1315 *
1316 * The parent class of Ractor-related error classes.
1317 */
1318
1319/*
1320 * Document-class: Ractor::ClosedError
1321 *
1322 * Raised when an attempt is made to send a message to a closed port,
1323 * or to retrieve a message from a closed and empty port.
1324 * Ports may be closed explicitly with Ractor::Port#close
1325 * and are closed implicitly when a Ractor terminates.
1326 *
1327 * port = Ractor::Port.new
1328 * port.close
1329 * port << "test" # Ractor::ClosedError
1330 * port.receive # Ractor::ClosedError
1331 *
1332 * ClosedError is a descendant of StopIteration, so the closing of a port will break
1333 * out of loops without propagating the error.
1334 */
1335
1336/*
1337 * Document-class: Ractor::IsolationError
1338 *
1339 * Raised on attempt to make a Ractor-unshareable object
1340 * Ractor-shareable.
1341 */
1342
1343/*
1344 * Document-class: Ractor::RemoteError
1345 *
1346 * Raised on Ractor#join or Ractor#value if there was an uncaught exception in the Ractor.
1347 * Its +cause+ will contain the original exception, and +ractor+ is the original ractor
1348 * it was raised in.
1349 *
1350 * r = Ractor.new { raise "Something weird happened" }
1351 *
1352 * begin
1353 * r.value
1354 * rescue => e
1355 * p e # => #<Ractor::RemoteError: thrown by remote Ractor.>
1356 * p e.ractor == r # => true
1357 * p e.cause # => #<RuntimeError: Something weird happened>
1358 * end
1359 *
1360 */
1361
1362/*
1363 * Document-class: Ractor::MovedError
1364 *
1365 * Raised on an attempt to access an object which was moved in Ractor#send or Ractor::Port#send.
1366 *
1367 * r = Ractor.new { sleep }
1368 *
1369 * ary = [1, 2, 3]
1370 * r.send(ary, move: true)
1371 * ary.inspect
1372 * # Ractor::MovedError (can not send any methods to a moved object)
1373 *
1374 */
1375
1376/*
1377 * Document-class: Ractor::MovedObject
1378 *
1379 * A special object which replaces any value that was moved to another ractor in Ractor#send
1380 * or Ractor::Port#send. Any attempt to access the object results in Ractor::MovedError.
1381 *
1382 * r = Ractor.new { receive }
1383 *
1384 * ary = [1, 2, 3]
1385 * r.send(ary, move: true)
1386 * p Ractor::MovedObject === ary
1387 * # => true
1388 * ary.inspect
1389 * # Ractor::MovedError (can not send any methods to a moved object)
1390 */
1391
1392/*
1393 * Document-class: Ractor::UnsafeError
1394 *
1395 * Raised when Ractor-unsafe C-methods is invoked by a non-main Ractor.
1396 */
1397
1398// Main docs are in ractor.rb, but without this clause there are weird artifacts
1399// in their rendering.
1400/*
1401 * Document-class: Ractor
1402 *
1403 */
1404
1405void
1406Init_Ractor(void)
1407{
1408 rb_cRactor = rb_define_class("Ractor", rb_cObject);
1410
1411 rb_eRactorError = rb_define_class_under(rb_cRactor, "Error", rb_eRuntimeError);
1412 rb_eRactorIsolationError = rb_define_class_under(rb_cRactor, "IsolationError", rb_eRactorError);
1413 rb_eRactorRemoteError = rb_define_class_under(rb_cRactor, "RemoteError", rb_eRactorError);
1414 rb_eRactorMovedError = rb_define_class_under(rb_cRactor, "MovedError", rb_eRactorError);
1415 rb_eRactorClosedError = rb_define_class_under(rb_cRactor, "ClosedError", rb_eStopIteration);
1416 rb_eRactorUnsafeError = rb_define_class_under(rb_cRactor, "UnsafeError", rb_eRactorError);
1417
1418 rb_cRactorMovedObject = rb_define_class_under(rb_cRactor, "MovedObject", rb_cBasicObject);
1419 rb_undef_alloc_func(rb_cRactorMovedObject);
1420 rb_define_method(rb_cRactorMovedObject, "method_missing", ractor_moved_missing, -1);
1421
1422 // override methods defined in BasicObject
1423 rb_define_method(rb_cRactorMovedObject, "__send__", ractor_moved_missing, -1);
1424 rb_define_method(rb_cRactorMovedObject, "!", ractor_moved_missing, -1);
1425 rb_define_method(rb_cRactorMovedObject, "==", ractor_moved_missing, -1);
1426 rb_define_method(rb_cRactorMovedObject, "!=", ractor_moved_missing, -1);
1427 rb_define_method(rb_cRactorMovedObject, "__id__", ractor_moved_missing, -1);
1428 rb_define_method(rb_cRactorMovedObject, "equal?", ractor_moved_missing, -1);
1429 rb_define_method(rb_cRactorMovedObject, "instance_eval", ractor_moved_missing, -1);
1430 rb_define_method(rb_cRactorMovedObject, "instance_exec", ractor_moved_missing, -1);
1431
1432 id_marshal_dump = rb_intern_const("marshal_dump");
1433 id_marshal_load = rb_intern_const("marshal_load");
1434 id_dump = rb_intern_const("_dump");
1435 id_load = rb_intern_const("_load");
1436 id_dump_data = rb_intern_const("_dump_data");
1437 id_load_data = rb_intern_const("_load_data");
1438
1439 Init_RactorPort();
1440}
1441
1442void
1443rb_ractor_dump(void)
1444{
1445 rb_vm_t *vm = GET_VM();
1446 rb_ractor_t *r = 0;
1447
1448 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
1449 if (r != vm->ractor.main_ractor) {
1450 fprintf(stderr, "r:%"PRI_SERIALT_PREFIX"u (%s)\n", r->pub.id, ractor_status_str(r->status_));
1451 }
1452 }
1453}
1454
1455VALUE
1457{
1458 if (rb_ractor_main_p()) {
1459 return rb_stdin;
1460 }
1461 else {
1462 rb_ractor_t *cr = GET_RACTOR();
1463 if (UNLIKELY(cr->r_stdin == 0)) {
1464 cr->r_stdin = rb_io_prep_stdin();
1465 }
1466 return cr->r_stdin;
1467 }
1468}
1469
1470VALUE
1471rb_ractor_stdout(void)
1472{
1473 if (rb_ractor_main_p()) {
1474 return rb_stdout;
1475 }
1476 else {
1477 rb_ractor_t *cr = GET_RACTOR();
1478 if (UNLIKELY(cr->r_stdout == 0)) {
1479 cr->r_stdout = rb_io_prep_stdout();
1480 }
1481 return cr->r_stdout;
1482 }
1483}
1484
1485VALUE
1486rb_ractor_stderr(void)
1487{
1488 if (rb_ractor_main_p()) {
1489 return rb_stderr;
1490 }
1491 else {
1492 rb_ractor_t *cr = GET_RACTOR();
1493 if (UNLIKELY(cr->r_stderr == 0)) {
1494 cr->r_stderr = rb_io_prep_stderr();
1495 }
1496 return cr->r_stderr;
1497 }
1498}
1499
1500void
1502{
1503 if (rb_ractor_main_p()) {
1504 rb_stdin = in;
1505 }
1506 else {
1507 rb_ractor_t *cr = GET_RACTOR();
1508 RB_OBJ_WRITE(cr->pub.self, &cr->r_stdin, in);
1509 }
1510}
1511
1512void
1514{
1515 if (rb_ractor_main_p()) {
1516 rb_stdout = out;
1517 }
1518 else {
1519 rb_ractor_t *cr = GET_RACTOR();
1520 RB_OBJ_WRITE(cr->pub.self, &cr->r_stdout, out);
1521 }
1522}
1523
1524void
1526{
1527 if (rb_ractor_main_p()) {
1528 rb_stderr = err;
1529 }
1530 else {
1531 rb_ractor_t *cr = GET_RACTOR();
1532 RB_OBJ_WRITE(cr->pub.self, &cr->r_stderr, err);
1533 }
1534}
1535
1536
1537st_table *
1538rb_ractor_targeted_hooks(rb_ractor_t *cr)
1539{
1540 return &cr->pub.targeted_hooks;
1541}
1542
1543static void
1544rb_obj_set_shareable_no_assert(VALUE obj)
1545{
1546 /* make_shareable_check_shareable refuses an IO, because the traversal cannot reach
1547 * the VALUE members inside its fptr. */
1548 VM_ASSERT(!RB_TYPE_P(obj, T_FILE));
1549
1551 rb_gc_obj_became_shareable(obj);
1552
1553 /* Ivars on a shareable object would be mutable shared state, so freeze them
1554 * (not obj itself). A T_IMEMO has no shape id to transition. */
1555 bool froze_ivars = false;
1556 if (!RB_OBJ_FROZEN_RAW(obj) && !RB_TYPE_P(obj, T_IMEMO) &&
1557 !RB_TYPE_P(obj, T_CLASS) && !RB_TYPE_P(obj, T_MODULE) && !RB_TYPE_P(obj, T_ICLASS)) {
1558
1559 RBASIC_SET_SHAPE_ID(obj, rb_shape_transition_frozen(RBASIC_SHAPE_ID(obj)));
1560 froze_ivars = true;
1561 }
1562
1563 /* A T_OBJECT can have a fields imemo too (too_complex and friends), and an imemo
1564 * born while its owner was unshareable stays unshareable
1565 * (imemo_fields_complex_from_obj), so align it here. */
1566 if (rb_obj_gen_fields_p(obj) || BUILTIN_TYPE(obj) == T_OBJECT) {
1567 /* obj is shareable already, so rb_obj_fields_no_ractor_check finds the right
1568 * table. Make the fields imemo itself shareable and record shrefs for the
1569 * hidden field values the traversal never reaches. */
1570 VALUE fields = rb_obj_fields_no_ractor_check(obj);
1571 if (imemo_type_p(fields, imemo_fields)) {
1572 // no recursive mark
1573 FL_SET_RAW(fields, FL_SHAREABLE);
1574 rb_gc_obj_became_shareable(fields);
1575 // the imemo carries its owner's shape id, frozen bit included
1576 if (froze_ivars) RBASIC_SET_SHAPE_ID(fields, RBASIC_SHAPE_ID(obj));
1577 // Field values the traversal never reaches (hidden internal ivars, say)
1578 // can stay unshareable, so record their shrefs to keep the shareable
1579 // fields imemo's edges correct.
1580 rb_imemo_fields_record_shrefs(fields);
1581 }
1582 }
1583}
1584
1585#ifndef STRICT_VERIFY_SHAREABLE
1586#define STRICT_VERIFY_SHAREABLE 0
1587#endif
1588
1589bool
1590rb_ractor_verify_shareable(VALUE obj)
1591{
1592#if STRICT_VERIFY_SHAREABLE
1593 rb_gc_verify_shareable(obj);
1594#endif
1595 return true;
1596}
1597
1598VALUE
1600{
1602
1603 rb_obj_set_shareable_no_assert(obj);
1604 RUBY_ASSERT(rb_ractor_verify_shareable(obj));
1605
1606 return obj;
1607}
1608
1610
1611// 2: stop search
1612// 1: skip child
1613// 0: continue
1614
1615enum obj_traverse_iterator_result {
1616 traverse_cont,
1617 traverse_skip,
1618 traverse_stop,
1619};
1620
1621typedef enum obj_traverse_iterator_result (*rb_obj_traverse_enter_func)(VALUE obj);
1622typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(VALUE obj);
1623typedef enum obj_traverse_iterator_result (*rb_obj_traverse_final_func)(VALUE obj);
1624
1625static enum obj_traverse_iterator_result null_leave(VALUE obj);
1626
1628 rb_obj_traverse_enter_func enter_func;
1629 rb_obj_traverse_leave_func leave_func;
1630
1631 st_table *rec;
1632 VALUE rec_hash;
1633};
1634
1635
1637 bool stop;
1638 struct obj_traverse_data *data;
1639};
1640
1641static int obj_traverse_i(VALUE obj, struct obj_traverse_data *data);
1642
1643static int
1644obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
1645{
1647
1648 if (obj_traverse_i(key, d->data)) {
1649 d->stop = true;
1650 return ST_STOP;
1651 }
1652
1653 if (obj_traverse_i(val, d->data)) {
1654 d->stop = true;
1655 return ST_STOP;
1656 }
1657
1658 return ST_CONTINUE;
1659}
1660
1661static void
1662obj_traverse_reachable_i(VALUE obj, void *ptr)
1663{
1665
1666 // We need to assume foreign objects are properly shareable. If we check
1667 // their shareability, this could result in a crash because the page could
1668 // be unmapped. The object could also be freed concurrently.
1669 if (!rb_objspace_live_object_p(obj)) {
1670 return;
1671 }
1672 // If the object is "garbage" (unmarked during lazy sweeping), it's okay
1673 // to traverse it.
1674
1675 if (obj_traverse_i(obj, d->data)) {
1676 d->stop = true;
1677 }
1678}
1679
1680// Traverse obj's children via its GC mark function. Returns 1 to stop.
1681static int
1682obj_traverse_reachable(VALUE obj, struct obj_traverse_data *data)
1683{
1684 struct obj_traverse_callback_data d = {
1685 .stop = false,
1686 .data = data,
1687 };
1688 rb_objspace_reachable_objects_from_local(obj, obj_traverse_reachable_i, &d);
1689 return d.stop;
1690}
1691
1692static struct st_table *
1693obj_traverse_rec(struct obj_traverse_data *data)
1694{
1695 if (UNLIKELY(!data->rec)) {
1696 data->rec_hash = rb_ident_hash_new();
1697 rb_obj_hide(data->rec_hash);
1698 data->rec = RHASH_ST_TABLE(data->rec_hash);
1699 }
1700 return data->rec;
1701}
1702
1703static int
1704obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr)
1705{
1707
1708 if (obj_traverse_i(val, d->data)) {
1709 d->stop = true;
1710 return ST_STOP;
1711 }
1712
1713 return ST_CONTINUE;
1714}
1715
1716static int
1717obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
1718{
1719 if (RB_SPECIAL_CONST_P(obj)) return 0;
1720
1721 switch (data->enter_func(obj)) {
1722 case traverse_cont: break;
1723 case traverse_skip: return 0; // skip children
1724 case traverse_stop: return 1; // stop search
1725 }
1726
1727 if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
1728 // already traversed
1729 return 0;
1730 }
1731 RB_OBJ_WRITTEN(data->rec_hash, Qundef, obj);
1732
1733 if (rb_obj_shape_has_ivars(obj)) {
1734 struct obj_traverse_callback_data d = {
1735 .stop = false,
1736 .data = data,
1737 };
1738 rb_ivar_foreach(obj, obj_traverse_ivar_foreach_i, (st_data_t)&d);
1739 if (d.stop) return 1;
1740 }
1741
1742 switch (BUILTIN_TYPE(obj)) {
1743 // no child node
1744 case T_STRING:
1745 case T_FLOAT:
1746 case T_BIGNUM:
1747 case T_REGEXP:
1748 case T_SYMBOL:
1749 break;
1750
1751 case T_OBJECT:
1752 /* Instance variables already traversed. */
1753 break;
1754
1755 case T_ARRAY:
1756 {
1757 for (rb_len_t i = 0; i < RARRAY_LEN(obj); i++) {
1758 VALUE e = RARRAY_AREF(obj, i);
1759 if (obj_traverse_i(e, data)) return 1;
1760 }
1761 }
1762 break;
1763
1764 case T_HASH:
1765 {
1766 if (obj_traverse_i(RHASH_IFNONE(obj), data)) return 1;
1767
1768 struct obj_traverse_callback_data d = {
1769 .stop = false,
1770 .data = data,
1771 };
1772 rb_hash_foreach(obj, obj_hash_traverse_i, (VALUE)&d);
1773 if (d.stop) return 1;
1774 }
1775 break;
1776
1777 case T_STRUCT:
1778 {
1779 rb_len_t len = RSTRUCT_LEN_RAW(obj);
1780 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
1781
1782 for (rb_len_t i=0; i<len; i++) {
1783 if (obj_traverse_i(ptr[i], data)) return 1;
1784 }
1785 }
1786 break;
1787
1788 case T_MATCH:
1789 if (obj_traverse_i(RMATCH(obj)->str, data)) return 1;
1790 break;
1791
1792 case T_RATIONAL:
1793 if (obj_traverse_i(RRATIONAL(obj)->num, data)) return 1;
1794 if (obj_traverse_i(RRATIONAL(obj)->den, data)) return 1;
1795 break;
1796 case T_COMPLEX:
1797 if (obj_traverse_i(RCOMPLEX(obj)->real, data)) return 1;
1798 if (obj_traverse_i(RCOMPLEX(obj)->imag, data)) return 1;
1799 break;
1800
1801 case T_DATA:
1802 {
1803 void *const ptr = RTYPEDDATA_GET_DATA(obj);
1804 const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
1805
1806 if (!ptr || !type->function.dmark) {
1807 // no references (the class and ivars are handled elsewhere)
1808 }
1809 else if (type->flags & RUBY_TYPED_DECL_MARKING) {
1810 const size_t *offsets = (const size_t *)(uintptr_t)type->function.dmark;
1811 for (; *offsets != RUBY_REF_END; offsets++) {
1812 VALUE ref = *(VALUE *)((char *)ptr + *offsets);
1813 if (obj_traverse_i(ref, data)) return 1;
1814 }
1815 }
1816 else {
1817 if (obj_traverse_reachable(obj, data)) return 1;
1818 }
1819 }
1820 break;
1821
1822 case T_IMEMO:
1823 // TODO: Not sure this can actually happen; traverse rather than crash.
1824 if (obj_traverse_reachable(obj, data)) return 1;
1825 break;
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 if (data->leave_func(obj) == traverse_stop) {
1837 return 1;
1838 }
1839 else {
1840 return 0;
1841 }
1842}
1843
1845 rb_obj_traverse_final_func final_func;
1846 int stopped;
1847};
1848
1849static int
1850obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
1851{
1852 struct rb_obj_traverse_final_data *data = (void *)arg;
1853 if (data->final_func(key)) {
1854 data->stopped = 1;
1855 return ST_STOP;
1856 }
1857 return ST_CONTINUE;
1858}
1859
1860// 0: traverse all
1861// 1: stopped
1862static int
1863rb_obj_traverse(VALUE obj,
1864 rb_obj_traverse_enter_func enter_func,
1865 rb_obj_traverse_leave_func leave_func,
1866 rb_obj_traverse_final_func final_func)
1867{
1868 struct obj_traverse_data data = {
1869 .enter_func = enter_func,
1870 .leave_func = leave_func,
1871 .rec = NULL,
1872 };
1873
1874 if (obj_traverse_i(obj, &data)) return 1;
1875 if (final_func && data.rec) {
1876 struct rb_obj_traverse_final_data f = {final_func, 0};
1877 st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
1878 return f.stopped;
1879 }
1880 return 0;
1881}
1882
1883static int
1884allow_frozen_shareable_p(VALUE obj)
1885{
1886 if (RB_TYPE_P(obj, T_FILE)) {
1887 return false;
1888 }
1889 else if (!RB_TYPE_P(obj, T_DATA)) {
1890 return true;
1891 }
1892 else {
1893 const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
1894 if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
1895 return true;
1896 }
1897 }
1898
1899 return false;
1900}
1901
1902static void
1903make_shareable_freeze(VALUE obj)
1904{
1905 VALUE klass = RBASIC_CLASS(obj);
1906 if (klass == rb_cString && BASIC_OP_UNREDEFINED_P(BOP_FREEZE, STRING_REDEFINED_OP_FLAG)) {
1907 rb_str_freeze(obj);
1908 }
1909 else if (klass == rb_cArray && BASIC_OP_UNREDEFINED_P(BOP_FREEZE, ARRAY_REDEFINED_OP_FLAG)) {
1910 rb_ary_freeze(obj);
1911 }
1912 else if (klass == rb_cHash && BASIC_OP_UNREDEFINED_P(BOP_FREEZE, HASH_REDEFINED_OP_FLAG)) {
1913 rb_hash_freeze(obj);
1914 }
1915 else {
1916 rb_funcall(obj, idFreeze, 0);
1917 }
1918}
1919
1920static enum obj_traverse_iterator_result
1921make_shareable_check_shareable_freeze(VALUE obj, enum obj_traverse_iterator_result result)
1922{
1923 if (!RB_OBJ_FROZEN_RAW(obj)) {
1924 make_shareable_freeze(obj);
1925
1926 if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
1927 rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
1928 }
1929
1930 if (RB_OBJ_SHAREABLE_P(obj)) {
1931 return traverse_skip;
1932 }
1933 }
1934
1935 return result;
1936}
1937
1938static int obj_refer_only_shareables_p(VALUE obj);
1939
1940static enum obj_traverse_iterator_result
1941make_shareable_check_shareable(VALUE obj)
1942{
1943 VM_ASSERT(!SPECIAL_CONST_P(obj));
1944
1945 if (rb_ractor_shareable_p(obj)) {
1946 return traverse_skip;
1947 }
1948 else if (!allow_frozen_shareable_p(obj)) {
1949 if (!RB_TYPE_P(obj, T_DATA)) {
1950 rb_raise(rb_eRactorError,
1951 "can not make shareable object for %+"PRIsVALUE, obj);
1952 }
1953 else if (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC) {
1954 if (obj_refer_only_shareables_p(obj)) {
1955 make_shareable_check_shareable_freeze(obj, traverse_skip);
1957 return traverse_skip;
1958 }
1959 else {
1960 rb_raise(rb_eRactorError,
1961 "can not make shareable object for %+"PRIsVALUE" because it refers unshareable objects", obj);
1962 }
1963 }
1964 else if (rb_obj_is_proc(obj)) {
1965 rb_proc_ractor_make_shareable(obj, Qundef);
1966 return traverse_cont;
1967 }
1968 else {
1969 rb_raise(rb_eRactorError, "can not make shareable object for %+"PRIsVALUE, obj);
1970 }
1971 }
1972
1973 switch (TYPE(obj)) {
1974 case T_IMEMO:
1975 return traverse_skip;
1976 case T_OBJECT:
1977 {
1978 // If a T_OBJECT is shared and has no free capacity, we can't safely store the object_id inline,
1979 // as it would require to move the object content into an external buffer.
1980 // This is only a problem for T_OBJECT, given other types have external fields and can do RCU.
1981 // To avoid this issue, we proactively create the object_id.
1982 shape_id_t shape_id = RBASIC_SHAPE_ID(obj);
1983 attr_index_t capacity = RSHAPE_CAPACITY(shape_id);
1984 attr_index_t free_capacity = capacity - RSHAPE_LEN(shape_id);
1985 if (!rb_shape_has_object_id(shape_id) && capacity && !free_capacity) {
1986 rb_obj_id(obj);
1987 }
1988 }
1989 break;
1990 default:
1991 break;
1992 }
1993
1994 return make_shareable_check_shareable_freeze(obj, traverse_cont);
1995}
1996
1997static enum obj_traverse_iterator_result
1998mark_shareable(VALUE obj)
1999{
2000 if (RB_BUILTIN_TYPE(obj) == T_STRING) {
2001 rb_str_make_independent(obj);
2002 }
2003 else if (RB_BUILTIN_TYPE(obj) == T_ARRAY) {
2004 rb_ary_cancel_sharing(obj);
2005 }
2006
2007 rb_obj_set_shareable_no_assert(obj);
2008 return traverse_cont;
2009}
2010
2011VALUE
2013{
2014 rb_obj_traverse(obj,
2015 make_shareable_check_shareable,
2016 null_leave, mark_shareable);
2017 return obj;
2018}
2019
2020static VALUE ractor_copy(VALUE obj); // defined below
2021
2022VALUE
2024{
2025 VALUE copy = ractor_copy(obj);
2026 return rb_ractor_make_shareable(copy);
2027}
2028
2029VALUE
2030rb_ractor_ensure_shareable(VALUE obj, VALUE name)
2031{
2032 if (!rb_ractor_shareable_p(obj)) {
2033 VALUE message = rb_sprintf("cannot assign unshareable object to %"PRIsVALUE,
2034 name);
2035 rb_exc_raise(rb_exc_new_str(rb_eRactorIsolationError, message));
2036 }
2037 return obj;
2038}
2039
2040void
2041rb_ractor_ensure_main_ractor(const char *msg)
2042{
2043 if (!rb_ractor_main_p()) {
2044 rb_raise(rb_eRactorIsolationError, "%s", msg);
2045 }
2046}
2047
2048static enum obj_traverse_iterator_result
2049shareable_p_enter(VALUE obj)
2050{
2051 if (RB_OBJ_SHAREABLE_P(obj)) {
2052 return traverse_skip;
2053 }
2054 else if (RB_TYPE_P(obj, T_CLASS) ||
2055 RB_TYPE_P(obj, T_MODULE) ||
2056 RB_TYPE_P(obj, T_ICLASS)) {
2057 // TODO: remove it
2058 mark_shareable(obj);
2059 return traverse_skip;
2060 }
2061 else if (RB_OBJ_FROZEN_RAW(obj) &&
2062 allow_frozen_shareable_p(obj)) {
2063 return traverse_cont;
2064 }
2065 else if (RB_OBJ_FROZEN_RAW(obj) &&
2066 RB_TYPE_P(obj, T_DATA) &&
2067 (RTYPEDDATA_TYPE(obj)->flags & RUBY_TYPED_FROZEN_SHAREABLE_NO_REC)) {
2068 // Similar to RUBY_TYPED_FROZEN_SHAREABLE, but the object is only
2069 // shareable if all reachable objects are already shareable (they
2070 // are not made shareable recursively).
2071 if (obj_refer_only_shareables_p(obj)) {
2072 mark_shareable(obj);
2073 return traverse_skip;
2074 }
2075 }
2076
2077 return traverse_stop; // fail
2078}
2079
2080bool
2081rb_ractor_shareable_p_continue(VALUE obj)
2082{
2083 if (rb_obj_traverse(obj,
2084 shareable_p_enter, null_leave,
2085 mark_shareable)) {
2086 return false;
2087 }
2088 else {
2089 return true;
2090 }
2091}
2092
2093static enum obj_traverse_iterator_result
2094null_leave(VALUE obj)
2095{
2096 return traverse_cont;
2097}
2098
2099
2101
2102// 2: stop search
2103// 1: skip child
2104// 0: continue
2105
2107static int obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data);
2108typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_enter_func)(VALUE obj, struct obj_traverse_replace_data *data);
2109typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_leave_func)(VALUE obj, struct obj_traverse_replace_data *data);
2110
2112 rb_obj_traverse_replace_enter_func enter_func;
2113 rb_obj_traverse_replace_leave_func leave_func;
2114
2115 /* old -> new map, a plain st_table: an OLD key may live in another Ractor's
2116 * objspace and must not become a GC edge here (marking a freed foreign key is a
2117 * UAF). Keys compare by address; replacements stay alive via rec_keepalive. */
2118 st_table *rec;
2119 VALUE rec_keepalive;
2120
2121 VALUE replacement;
2122 bool move;
2123};
2124
2126 bool stop;
2127 VALUE src;
2128 struct obj_traverse_replace_data *data;
2129};
2130
2131static int
2132obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp, int error)
2133{
2134 return ST_REPLACE;
2135}
2136
2137static int
2138obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr, int exists)
2139{
2141 struct obj_traverse_replace_data *data = d->data;
2142
2143 if (obj_traverse_replace_i(*key, data)) {
2144 d->stop = true;
2145 return ST_STOP;
2146 }
2147 else if (*key != data->replacement) {
2148 VALUE v = *key = data->replacement;
2149 RB_OBJ_WRITTEN(d->src, Qundef, v);
2150 }
2151
2152 if (obj_traverse_replace_i(*val, data)) {
2153 d->stop = true;
2154 return ST_STOP;
2155 }
2156 else if (*val != data->replacement) {
2157 VALUE v = *val = data->replacement;
2158 RB_OBJ_WRITTEN(d->src, Qundef, v);
2159 }
2160
2161 return ST_CONTINUE;
2162}
2163
2164static int
2165obj_iv_hash_traverse_replace_foreach_i(st_data_t _key, st_data_t _val, st_data_t _data, int _x)
2166{
2167 return ST_REPLACE;
2168}
2169
2170static int
2171obj_iv_hash_traverse_replace_i(st_data_t * _key, st_data_t * val, st_data_t ptr, int exists)
2172{
2174 struct obj_traverse_replace_data *data = d->data;
2175
2176 if (obj_traverse_replace_i(*(VALUE *)val, data)) {
2177 d->stop = true;
2178 return ST_STOP;
2179 }
2180 else if (*(VALUE *)val != data->replacement) {
2181 VALUE v = *(VALUE *)val = data->replacement;
2182 RB_OBJ_WRITTEN(d->src, Qundef, v);
2183 }
2184
2185 return ST_CONTINUE;
2186}
2187
2188static struct st_table *
2189obj_traverse_replace_rec(struct obj_traverse_replace_data *data)
2190{
2191 if (UNLIKELY(!data->rec)) {
2192 data->rec = st_init_numtable();
2193 data->rec_keepalive = rb_ary_hidden_new(0);
2194 }
2195 return data->rec;
2196}
2197
2198static void
2199obj_refer_only_shareables_p_i(VALUE obj, void *ptr)
2200{
2201 int *pcnt = (int *)ptr;
2202
2203 if (!rb_objspace_live_object_p(obj)) {
2204 return;
2205 }
2206
2207 if (!rb_ractor_shareable_p(obj)) {
2208 ++*pcnt;
2209 }
2210}
2211
2212static int
2213obj_refer_only_shareables_p(VALUE obj)
2214{
2215 int cnt = 0;
2216 rb_objspace_reachable_objects_from_local(obj, obj_refer_only_shareables_p_i, &cnt);
2217 return cnt == 0;
2218}
2219
2220static int
2221obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
2222{
2223 st_data_t replacement;
2224
2225 if (RB_SPECIAL_CONST_P(obj)) {
2226 data->replacement = obj;
2227 return 0;
2228 }
2229
2230 /* Dedup before enter_func, so a revisited shared/cyclic node reuses its recorded
2231 * replacement; otherwise the copy path would build a wasteful temporary holding a
2232 * containment-breaking cross-objspace edge. */
2233 if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, &replacement))) {
2234 data->replacement = (VALUE)replacement;
2235 return 0;
2236 }
2237
2238 switch (data->enter_func(obj, data)) {
2239 case traverse_cont: break;
2240 case traverse_skip: return 0; // skip children
2241 case traverse_stop: return 1; // stop search
2242 }
2243
2244 replacement = (st_data_t)data->replacement;
2245 st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, replacement);
2246 if (!RB_SPECIAL_CONST_P((VALUE)replacement)) {
2247 rb_ary_push(data->rec_keepalive, (VALUE)replacement);
2248 }
2249
2250 if (!data->move) {
2251 obj = replacement;
2252 }
2253
2254#define CHECK_AND_REPLACE(parent_obj, v) do { \
2255 VALUE _val = (v); \
2256 if (obj_traverse_replace_i(_val, data)) { return 1; } \
2257 else if (data->replacement != _val) { RB_OBJ_WRITE(parent_obj, &v, data->replacement); } \
2258} while (0)
2259
2260 if (UNLIKELY(rb_obj_gen_fields_p(obj))) {
2261 VALUE fields_obj = rb_obj_fields_no_ractor_check(obj);
2262
2263 if (UNLIKELY(rb_obj_shape_complex_p(obj))) {
2265 .stop = false,
2266 .data = data,
2267 .src = fields_obj,
2268 };
2269 rb_st_foreach_with_replace(
2270 rb_imemo_fields_complex_tbl(fields_obj),
2271 obj_iv_hash_traverse_replace_foreach_i,
2272 obj_iv_hash_traverse_replace_i,
2273 (st_data_t)&d
2274 );
2275 if (d.stop) return 1;
2276 }
2277 else {
2278 uint32_t fields_count = RSHAPE_LEN(RBASIC_SHAPE_ID(obj));
2279 VALUE *fields = rb_imemo_fields_ptr(fields_obj);
2280 for (uint32_t i = 0; i < fields_count; i++) {
2281 CHECK_AND_REPLACE(fields_obj, fields[i]);
2282 }
2283 }
2284 }
2285
2286 switch (BUILTIN_TYPE(obj)) {
2287 // no child node
2288 case T_FLOAT:
2289 case T_BIGNUM:
2290 case T_REGEXP:
2291 case T_FILE:
2292 case T_SYMBOL:
2293 break;
2294 case T_STRING:
2295 rb_str_make_independent(obj);
2296 break;
2297
2298 case T_OBJECT:
2299 {
2300 VALUE fields_obj = ROBJECT_FIELDS_OBJ(obj);
2301 shape_id_t shape_id = RBASIC_SHAPE_ID(fields_obj);
2302 if (rb_shape_complex_p(shape_id)) {
2304 .stop = false,
2305 .data = data,
2306 .src = obj,
2307 };
2308 rb_st_foreach_with_replace(
2309 rb_imemo_fields_complex_tbl(fields_obj),
2310 obj_iv_hash_traverse_replace_foreach_i,
2311 obj_iv_hash_traverse_replace_i,
2312 (st_data_t)&d
2313 );
2314 if (d.stop) return 1;
2315 }
2316 else {
2317 attr_index_t len = RSHAPE_LEN(shape_id);
2318 VALUE *ptr = rb_imemo_fields_ptr(fields_obj);
2319
2320 for (attr_index_t i = 0; i < len; i++) {
2321 CHECK_AND_REPLACE(obj, ptr[i]);
2322 }
2323 }
2324 }
2325 break;
2326
2327 case T_ARRAY:
2328 {
2329 rb_ary_cancel_sharing(obj);
2330
2331 for (rb_len_t i = 0; i < RARRAY_LEN(obj); i++) {
2332 VALUE e = RARRAY_AREF(obj, i);
2333
2334 if (obj_traverse_replace_i(e, data)) {
2335 return 1;
2336 }
2337 else if (e != data->replacement) {
2338 RARRAY_ASET(obj, i, data->replacement);
2339 }
2340 }
2341 RB_GC_GUARD(obj);
2342 }
2343 break;
2344 case T_HASH:
2345 {
2347 .stop = false,
2348 .data = data,
2349 .src = obj,
2350 };
2351 rb_hash_stlike_foreach_with_replace(obj,
2352 obj_hash_traverse_replace_foreach_i,
2353 obj_hash_traverse_replace_i,
2354 (VALUE)&d);
2355 if (d.stop) return 1;
2356 // TODO: rehash here?
2357
2358 VALUE ifnone = RHASH_IFNONE(obj);
2359 if (obj_traverse_replace_i(ifnone, data)) {
2360 return 1;
2361 }
2362 else if (ifnone != data->replacement) {
2363 RHASH_SET_IFNONE(obj, data->replacement);
2364 }
2365 }
2366 break;
2367
2368 case T_STRUCT:
2369 {
2370 rb_len_t len = RSTRUCT_LEN_RAW(obj);
2371 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2372
2373 for (rb_len_t i=0; i<len; i++) {
2374 CHECK_AND_REPLACE(obj, ptr[i]);
2375 }
2376 }
2377 break;
2378
2379 case T_MATCH:
2380 CHECK_AND_REPLACE(obj, RMATCH(obj)->str);
2381 break;
2382
2383 case T_RATIONAL:
2384 CHECK_AND_REPLACE(obj, RRATIONAL(obj)->num);
2385 CHECK_AND_REPLACE(obj, RRATIONAL(obj)->den);
2386 break;
2387 case T_COMPLEX:
2388 CHECK_AND_REPLACE(obj, RCOMPLEX(obj)->real);
2389 CHECK_AND_REPLACE(obj, RCOMPLEX(obj)->imag);
2390 break;
2391
2392 case T_DATA:
2393 if (!data->move && obj_refer_only_shareables_p(obj)) {
2394 break;
2395 }
2396 else {
2397 rb_raise(rb_eRactorError, "can not %s %"PRIsVALUE" object.",
2398 data->move ? "move" : "copy", rb_class_of(obj));
2399 }
2400
2401 case T_IMEMO:
2402 // not supported yet
2403 return 1;
2404
2405 // unreachable
2406 case T_CLASS:
2407 case T_MODULE:
2408 case T_ICLASS:
2409 default:
2410 rp(obj);
2411 rb_bug("unreachable");
2412 }
2413
2414 data->replacement = (VALUE)replacement;
2415
2416 if (data->leave_func(obj, data) == traverse_stop) {
2417 return 1;
2418 }
2419 else {
2420 return 0;
2421 }
2422}
2423
2424// 0: traverse all
2425// 1: stopped
2426static VALUE
2427rb_obj_traverse_replace(VALUE obj,
2428 rb_obj_traverse_replace_enter_func enter_func,
2429 rb_obj_traverse_replace_leave_func leave_func,
2430 bool move)
2431{
2432 struct obj_traverse_replace_data data = {
2433 .enter_func = enter_func,
2434 .leave_func = leave_func,
2435 .rec = NULL,
2436 .rec_keepalive = Qfalse,
2437 .replacement = Qundef,
2438 .move = move,
2439 };
2440
2441 int stopped = obj_traverse_replace_i(obj, &data);
2442
2443 /* The enter and leave functions report failure with traverse_stop rather than by
2444 * raising, so this is the only place the table is freed. */
2445 if (data.rec) st_free_table(data.rec);
2446 RB_GC_GUARD(data.rec_keepalive);
2447
2448 if (stopped) {
2449 return Qundef;
2450 }
2451 else {
2452 return data.replacement;
2453 }
2454}
2455
2456/* Courier: serializes a Ractor message payload -- copied or moved -- into an xmalloc'd
2457 * structure that belongs to no objspace, so no sender GC can mark, sweep, compact or
2458 * race with it. A node array with id references handles sharing and cycles, and the
2459 * receiver rebuilds it in its own objspace in two passes. Copy and move differ only in
2460 * whether the source is read or taken apart: see courier_build.copy. */
2461
2462enum courier_node_kind {
2463 COURIER_KIND_REF, /* an immediate or a shareable object: carried by value */
2464 COURIER_KIND_BACKTRACE, /* an exception's backtrace: frames copied into an off-heap blob */
2465 COURIER_KIND_STRING,
2466 COURIER_KIND_ARRAY,
2467 COURIER_KIND_HASH,
2468 COURIER_KIND_OBJECT,
2469 COURIER_KIND_STRUCT,
2470 COURIER_KIND_MATCH,
2471 COURIER_KIND_IO,
2472 COURIER_KIND_REGEXP, /* recompiled from its source and options (copy only) */
2473 COURIER_KIND_HOOKED, /* rebuilt from its dump hook's payload by klass._load, marshal_load or _load_data */
2474};
2475
2476/* Marshal's protocols, but nothing is serialized: the hook's return value travels as an
2477 * ordinary child node, so sharing, cycles and shareable references all survive. */
2478enum courier_hook {
2479 COURIER_HOOK_NONE,
2480 COURIER_HOOK_MARSHAL_DUMP, /* marshal_dump -> alloc + marshal_load */
2481 COURIER_HOOK_DUMP, /* _dump -> klass._load */
2482 COURIER_HOOK_COMPAT, /* rb_marshal_define_compat dumper -> alloc + loader (Set, Process::Status) */
2483 COURIER_HOOK_DUMP_DATA, /* _dump_data -> alloc + _load_data */
2484};
2485
2486/* Which one obj's class implements, in Marshal's order of preference. */
2487static enum courier_hook
2488courier_hook_of(VALUE obj)
2489{
2490 if (rb_obj_respond_to(obj, id_marshal_dump, TRUE)) return COURIER_HOOK_MARSHAL_DUMP;
2491 if (rb_obj_respond_to(obj, id_dump, TRUE)) return COURIER_HOOK_DUMP;
2492 if (rb_marshal_compat_lookup(CLASS_OF(obj), NULL, NULL)) return COURIER_HOOK_COMPAT;
2493 if (BUILTIN_TYPE(obj) == T_DATA && rb_obj_respond_to(obj, id_dump_data, TRUE)) return COURIER_HOOK_DUMP_DATA;
2494 return COURIER_HOOK_NONE;
2495}
2496
2498 enum courier_node_kind kind;
2499 bool frozen;
2500 /* The instance and generic ivars every non-REF node can have (a String or Array
2501 * can hold generic ivars too) */
2502 uint32_t niv;
2503 ID *iv_ids; /* owned by the courier */
2504 uint32_t *iv_vals; /* owned by the courier; node ids */
2505 union {
2506 VALUE ref;
2507 struct { char *ptr; rb_len_t len, capa; int encidx; VALUE klass; } str; /* the courier owns ptr */
2508 struct { rb_len_t len; uint32_t *elems; VALUE klass; } ary; /* the courier owns elems */
2509 struct { long size; uint32_t *kv; uint32_t ifnone_id; bool compare_by_id; bool proc_default; VALUE klass; } hash; /* owns kv (2*size) */
2510 struct { VALUE klass; } obj;
2511 struct { rb_len_t len; uint32_t *elems; VALUE klass; } strct; /* owns elems */
2512 struct { uint32_t regexp_id, str_id; int num_regs; void *regs; VALUE klass; } match; /* owns regs */
2513 struct { void *blob; int size; } bt; /* the courier owns blob */
2514 struct { VALUE src; int options; VALUE klass; } re; /* src is an fstring: shareable */
2515 struct { VALUE klass; uint32_t payload_id; enum courier_hook hook; } hooked;
2516 struct {
2517 struct rb_io *fptr; /* carried by pointer (it owns the fd) */
2518 VALUE klass;
2519 /* The sender-side VALUE members of fptr travel as ordinary child nodes:
2520 * capture detaches them from fptr (see the T_FILE case) and rebuild writes
2521 * them back into the receiving shell with RB_OBJ_WRITE. */
2522 uint32_t pathv_id, ecopts_id, wc_pre_ecopts_id, wc_asciicompat_id, timeout_id;
2523 } io;
2524 } u;
2525};
2526
2527/* A child slot holds a node id, or -- with this bit set -- an index into c->refs.
2528 * The courier is in-process, so a shareable payload can travel as the VALUE itself
2529 * instead of costing a whole courier_node; the basket holding the courier marks c->refs. */
2530#define COURIER_ID_REF_BIT 0x80000000u
2531
2533 struct courier_node *nodes;
2534 uint32_t *order; /* node ids in capture's post-order: children before parents */
2535 uint32_t count;
2536 uint32_t capa;
2537 VALUE *refs; /* shareable payloads, embedded by value */
2538 uint32_t refs_count;
2539 uint32_t refs_capa;
2540 uint32_t root;
2541 /* src VALUE -> (node id + 1), only while building. Marked so that a key cannot
2542 * die (a dump hook's payload has no other owner) or move (compaction). */
2543 st_table *seen;
2544};
2545
2547 struct rb_ractor_courier *c;
2548 /* Copy mode: read the sources instead of taking them apart. No husk, no buffer
2549 * hand-over, no freeing of the source's internals. */
2550 bool copy;
2551 uint32_t ordered; /* nodes appended to c->order so far */
2552};
2553
2554static uint32_t courier_capture(struct courier_build *b, VALUE obj);
2555
2556/* Off the hot path: the preflight sizes both arrays, so this only runs if its count
2557 * came out short. Swap a fresh array in rather than realloc -- the courier is a GC
2558 * root while it is being built, and a realloc leaves the old pointer live over a
2559 * window where it may already have been freed. */
2560NOINLINE(static void courier_grow_nodes(struct rb_ractor_courier *c));
2561NOINLINE(static void courier_grow_refs(struct rb_ractor_courier *c));
2562
2563static void
2564courier_grow_nodes(struct rb_ractor_courier *c)
2565{
2566 uint32_t capa = c->capa ? c->capa * 2 : 8;
2567 struct courier_node *nodes = ALLOC_N(struct courier_node, capa);
2568 if (c->count > 0) MEMCPY(nodes, c->nodes, struct courier_node, c->count);
2569 struct courier_node *old_nodes = c->nodes;
2570 c->nodes = nodes;
2571 c->capa = capa;
2572 ruby_xfree(old_nodes);
2573 REALLOC_N(c->order, uint32_t, capa);
2574}
2575
2576static void
2577courier_grow_refs(struct rb_ractor_courier *c)
2578{
2579 uint32_t capa = c->refs_capa ? c->refs_capa * 2 : 8;
2580 VALUE *refs = ALLOC_N(VALUE, capa);
2581 if (c->refs_count > 0) MEMCPY(refs, c->refs, VALUE, c->refs_count);
2582 VALUE *old_refs = c->refs;
2583 c->refs = refs;
2584 c->refs_capa = capa;
2585 ruby_xfree(old_refs);
2586}
2587
2588static uint32_t
2589courier_alloc_node(struct rb_ractor_courier *c)
2590{
2591 if (RB_UNLIKELY(c->count == c->capa)) courier_grow_nodes(c);
2592 /* Fill the slot with a harmless REF/Qnil and bump the count only after, the way
2593 * courier_alloc_ref does: the courier is a GC root while it is being built, and
2594 * the mark walks nodes[0, count). A captured node overwrites this later. */
2595 struct courier_node *n = &c->nodes[c->count];
2596 n->kind = COURIER_KIND_REF;
2597 n->frozen = false;
2598 n->niv = 0;
2599 n->iv_ids = NULL;
2600 n->iv_vals = NULL;
2601 n->u.ref = Qnil;
2602 return c->count++;
2603}
2604
2605/* Size the arrays from the preflight's count, so capture never grows them. A count
2606 * that turns out short is not a problem: the growth path below still works. */
2607static void
2608courier_reserve(struct rb_ractor_courier *c, uint32_t nodes, uint32_t refs)
2609{
2610 if (nodes > 0) {
2611 c->nodes = ALLOC_N(struct courier_node, nodes);
2612 c->order = ALLOC_N(uint32_t, nodes);
2613 c->capa = nodes;
2614 }
2615 if (refs > 0) {
2616 c->refs = ALLOC_N(VALUE, refs);
2617 c->refs_capa = refs;
2618 }
2619}
2620
2621/* Embed a shareable payload by value and return its tagged child id. No dedup: a REF
2622 * is the same word however often it appears, and an array of immediates would otherwise
2623 * pay a lookup and an insert per element. */
2624static uint32_t
2625courier_alloc_ref(struct rb_ractor_courier *c, VALUE v)
2626{
2627 /* The count is bumped only after the slot holds a real VALUE: the courier is a GC
2628 * root while it is being built and must never be walkable half-written. */
2629 if (RB_UNLIKELY(c->refs_count == c->refs_capa)) courier_grow_refs(c);
2630 c->refs[c->refs_count] = v;
2631 return COURIER_ID_REF_BIT | c->refs_count++;
2632}
2633
2634/* Resolve a child slot to the object it names. */
2635static VALUE
2636courier_child(const struct rb_ractor_courier *c, VALUE shells, uint32_t id)
2637{
2638 if (id & COURIER_ID_REF_BIT) return c->refs[id & ~COURIER_ID_REF_BIT];
2639 return RARRAY_AREF(shells, id);
2640}
2641
2642/* Turn a moved source into a valid RactorMovedObject without passing through flags==0,
2643 * so a concurrent foreign marker always sees either the original object or the shell. */
2644static void
2645move_neutralize_source(VALUE obj)
2646{
2647 /* The shell stays in the original slot: keep the capacity bits, give it a frozen
2648 * field-less ROBJECT shape (read before the flags are overwritten). The old body is
2649 * then never read as ivars and compaction's slot-size check still holds. */
2650 shape_id_t shape_id = (RBASIC_SHAPE_ID(obj) & SHAPE_ID_CAPACITY_MASK) |
2651 ROOT_SHAPE_ID | SHAPE_ID_LAYOUT_ROBJECT | SHAPE_ID_FL_FROZEN;
2652
2653 /* A non-T_OBJECT host (a String with ivars, say) must drop its generic_fields
2654 * entry: obj stops being a host below and its fields_obj is collected, so a stale
2655 * entry would let the global GC walk a freed value. */
2657
2658 /* A copy-on-write sharer reads its payload straight out of an embedded root's slot
2659 * (String#dup of a frozen string, Array#[] of a frozen array), and it outlives the
2660 * move, so that body has to survive as it is. */
2661 bool wipe_body = true;
2662 switch (BUILTIN_TYPE(obj)) {
2663 case T_STRING:
2664 if (!STR_EMBED_P(obj) && !rb_str_reembeddable_p(obj)) {
2665 /* A heap (non-embedded), shared root string keeps its buffer because
2666 * other strings reference this shared root. It needs to keep T_STRING
2667 * because otherwise the GC will not free the buffer when this object
2668 * dies which will leak memory. */
2669 RBASIC_SET_CLASS_RAW(obj, rb_cRactorMovedObject);
2670 RBASIC(obj)->flags |= FL_FREEZE;
2671 RBASIC_SET_FULL_SHAPE_ID(obj, (shape_id & ~SHAPE_ID_LAYOUT_MASK) | SHAPE_ID_LAYOUT_OTHER);
2672 RSTRING(obj)->len = 0;
2673 return;
2674 }
2675 wipe_body = !rb_str_embedded_shared_root_p(obj);
2676 break;
2677 case T_ARRAY:
2678 if (!ARY_EMBED_P(obj) && !ARY_SHARED_P(obj) && (ARY_SHARED_ROOT_P(obj) || OBJ_FROZEN(obj))) {
2679 /* A heap (non-embedded), shared root array keeps its buffer because
2680 * other arrays reference this shared root. It needs to keep T_ARRAY
2681 * because otherwise the GC will not free the buffer when this object
2682 * dies which will leak memory. */
2683 RBASIC_SET_CLASS_RAW(obj, rb_cRactorMovedObject);
2684 RBASIC(obj)->flags |= FL_FREEZE;
2685 RBASIC_SET_FULL_SHAPE_ID(obj, (shape_id & ~SHAPE_ID_LAYOUT_MASK) | SHAPE_ID_LAYOUT_OTHER);
2686 if (!ARY_SHARED_ROOT_P(obj)) {
2687 /* Present as empty to stale readers. Not for a shared root: its
2688 * len doubles as the buffer capacity that ARY_HEAP_SIZE frees by. */
2689 RARRAY(obj)->as.heap.len = 0;
2690 }
2691 return;
2692 }
2693 wipe_body = !rb_ary_embedded_shared_root_p(obj);
2694 break;
2695 default:
2696 break;
2697 }
2698
2699 /* Keep FL_FINALIZE: the finalizer table entry stays keyed on this slot, and a
2700 * shell without the flag makes the two disagree (rb_gc_impl_shutdown_call_finalizer_i
2701 * asserts on it). The finalizer runs when the shell dies, in the Ractor that
2702 * defined it; the rebuilt object gets fresh flags and does not inherit it. */
2703 VALUE flags = T_OBJECT | FL_FREEZE | (RBASIC(obj)->flags & (FL_PROMOTED | FL_FINALIZE));
2704 /* Read the slot size before the header is rewritten. */
2705 size_t slot_size = rb_gc_obj_slot_size(obj);
2706 RBASIC_SET_CLASS_RAW(obj, rb_cRactorMovedObject);
2707 RBASIC(obj)->flags = flags;
2708 RBASIC_SET_FULL_SHAPE_ID(obj, shape_id);
2709
2710 /* Wipe the old body. The shell has no fields, so nothing reads it as ivars, but
2711 * C code holding the object from before the move still reads it with its old type
2712 * (a running Array iteration, the RMatch capa of a $~ entry): a zeroed body makes
2713 * those reads see an empty object instead of stale internals. */
2714 if (wipe_body) {
2715 MEMZERO((char *)obj + sizeof(struct RBasic), char, slot_size - sizeof(struct RBasic));
2716 }
2717}
2718
2720 struct courier_build *b;
2721 uint32_t *kv;
2722 long i;
2723};
2724
2725static int
2726courier_capture_hash_i(st_data_t key, st_data_t val, st_data_t arg)
2727{
2728 struct courier_hash_ctx *hc = (struct courier_hash_ctx *)arg;
2729 uint32_t kid = courier_capture(hc->b, (VALUE)key);
2730 uint32_t vid = courier_capture(hc->b, (VALUE)val);
2731 hc->kv[hc->i++] = kid;
2732 hc->kv[hc->i++] = vid;
2733 return ST_CONTINUE;
2734}
2735
2737 struct courier_build *b;
2738 ID *ids;
2739 uint32_t *vals;
2740 long n;
2741 long capa;
2742};
2743
2744static int
2745courier_capture_ivar_i(ID name, VALUE val, st_data_t arg)
2746{
2747 struct courier_obj_ctx *oc = (struct courier_obj_ctx *)arg;
2748 if (oc->n == oc->capa) {
2749 oc->capa = oc->capa ? oc->capa * 2 : 4;
2750 REALLOC_N(oc->ids, ID, oc->capa);
2751 REALLOC_N(oc->vals, uint32_t, oc->capa);
2752 }
2753 uint32_t vid = courier_capture(oc->b, val);
2754 oc->ids[oc->n] = name;
2755 oc->vals[oc->n] = vid;
2756 oc->n++;
2757 return ST_CONTINUE;
2758}
2759
2760/* Capture obj's instance and generic ivars as node ids, recursing into the values.
2761 * Handles both a T_OBJECT's inline ivars and the generic ivars of a String, Array and
2762 * so on. */
2763static void
2764courier_capture_ivars(struct courier_build *b, VALUE obj, uint32_t id)
2765{
2766 struct courier_obj_ctx oc = { b, NULL, NULL, 0, 0 };
2767 rb_ivar_foreach_buffered(obj, courier_capture_ivar_i, (st_data_t)&oc);
2768 b->c->nodes[id].niv = (uint32_t)oc.n;
2769 b->c->nodes[id].iv_ids = oc.ids;
2770 b->c->nodes[id].iv_vals = oc.vals;
2771}
2772
2773/* Run obj's dump hook and capture what it returns as an ordinary child node. That
2774 * includes _dump's String: Marshal writes its ivars next to its bytes (Time keeps the
2775 * sub-microsecond part and the zone there), and a String node carries them the same
2776 * way. */
2777static void
2778courier_capture_hooked(struct courier_build *b, VALUE obj, uint32_t id, enum courier_hook hook)
2779{
2780 VALUE klass = rb_obj_class(obj);
2781 VALUE payload;
2782
2783 switch (hook) {
2784 case COURIER_HOOK_DUMP: {
2785 /* _dump takes the depth limit Marshal would have applied; a copy has none. */
2786 VALUE limit = INT2FIX(-1);
2787 payload = rb_funcallv(obj, id_dump, 1, &limit);
2788 if (!RB_TYPE_P(payload, T_STRING)) {
2789 rb_raise(rb_eTypeError, "_dump() must return string");
2790 }
2791 break;
2792 }
2793 case COURIER_HOOK_MARSHAL_DUMP:
2794 payload = rb_funcallv(obj, id_marshal_dump, 0, 0);
2795 break;
2796 case COURIER_HOOK_DUMP_DATA:
2797 payload = rb_funcallv(obj, id_dump_data, 0, 0);
2798 break;
2799 case COURIER_HOOK_COMPAT: {
2800 VALUE (*dumper)(VALUE);
2801 rb_marshal_compat_lookup(klass, &dumper, NULL);
2802 payload = dumper(obj);
2803 break;
2804 }
2805 default:
2806 rb_bug("courier_capture_hooked: no dump protocol");
2807 }
2808
2809 uint32_t payload_id = courier_capture(b, payload);
2810
2811 b->c->nodes[id].kind = COURIER_KIND_HOOKED;
2812 b->c->nodes[id].u.hooked.klass = klass;
2813 b->c->nodes[id].u.hooked.hook = hook;
2814 b->c->nodes[id].u.hooked.payload_id = payload_id;
2815}
2816
2817/* A move carries the singleton class with its object; a copy drops it, like #dup. */
2818static inline VALUE
2819courier_klass(struct courier_build *b, VALUE obj)
2820{
2821 return b->copy ? rb_obj_class(obj) : RBASIC_CLASS(obj);
2822}
2823
2824/* Capture obj into the courier, recurse into its children, return its node id. The id
2825 * is registered before recursing (a cycle back resolves to the same node); node fields
2826 * are written after (recursion can realloc c->nodes); a move neutralizes the source
2827 * exactly once after the switch. */
2828static uint32_t
2829courier_capture(struct courier_build *b, VALUE obj)
2830{
2831 /* An immediate is never in seen (only captured objects are inserted), so it can
2832 * skip the lookup entirely: that is the whole cost of an array of numbers. */
2833 if (RB_SPECIAL_CONST_P(obj)) {
2834 return courier_alloc_ref(b->c, obj);
2835 }
2836
2837 /* Seen first, and only then shareable: move husks each source as it goes, and a
2838 * husk is a frozen field-less object, which rb_ractor_shareable_p answers true for.
2839 * Testing shareable first would embed the husk instead of resolving the second
2840 * occurrence to the node the first one built. */
2841 st_data_t existing;
2842 if (st_lookup(b->c->seen, (st_data_t)obj, &existing)) {
2843 return (uint32_t)existing - 1;
2844 }
2845
2846 if (rb_ractor_shareable_p(obj)) {
2847 return courier_alloc_ref(b->c, obj);
2848 }
2849
2850 uint32_t id = courier_alloc_node(b->c);
2851 st_insert(b->c->seen, (st_data_t)obj, (st_data_t)(uintptr_t)(id + 1));
2852
2853 /* Reject an unmovable object before anything is mutated. */
2854 if (BUILTIN_TYPE(obj) == T_FILE && RFILE(obj)->fptr == NULL) {
2855 rb_raise(rb_eRactorError, "can not move an uninitialized IO");
2856 }
2857
2858 bool frozen = OBJ_FROZEN(obj);
2859 b->c->nodes[id].frozen = frozen;
2860 courier_capture_ivars(b, obj, id); /* shared: instance and generic ivars */
2861
2862 switch (BUILTIN_TYPE(obj)) {
2863 case T_STRING: {
2864 /* Give the source its own buffer (drop sharing, copy a static STR_NOFREE one).
2865 * Safe even when frozen: it changes ownership, not content. Afterwards a string
2866 * is embedded, owns a private heap buffer, or is a shared ROOT (a no-op). */
2867 if (!b->copy) rb_str_make_independent(obj);
2868 rb_len_t len = RSTRING_LEN(obj);
2869 int encidx = ENCODING_GET(obj);
2870 /* The receiver adopts this buffer as a String body, which is freed by size:
2871 * capa has to describe the allocation exactly (capa + terminator bytes). */
2872 const int termlen = rb_enc_mbminlen(rb_enc_from_index(encidx));
2873 char *ptr;
2874 rb_len_t capa;
2875 if (!b->copy && !STR_EMBED_P(obj) && rb_str_reembeddable_p(obj)) {
2876 /* Owns a private heap buffer: carry the pointer over (zero-copy) and leave
2877 * the source as a shell that does not free it. */
2878 ptr = RSTRING(obj)->as.heap.ptr;
2879 capa = RSTRING(obj)->as.heap.aux.capa;
2880 }
2881 else {
2882 /* Embedded or a shared root: copy the bytes into a courier-owned buffer.
2883 * Taking a root's buffer would dangle its copy-on-write children, so leave
2884 * it (the same reason T_ARRAY excludes ARY_SHARED_ROOT_P below). */
2885 ptr = ALLOC_N(char, len + termlen);
2886 if (len) memcpy(ptr, RSTRING_PTR(obj), len);
2887 memset(ptr + len, 0, termlen);
2888 capa = len;
2889 }
2890 b->c->nodes[id].kind = COURIER_KIND_STRING;
2891 b->c->nodes[id].u.str.klass = courier_klass(b, obj);
2892 b->c->nodes[id].u.str.ptr = ptr;
2893 b->c->nodes[id].u.str.len = len;
2894 b->c->nodes[id].u.str.capa = capa;
2895 b->c->nodes[id].u.str.encidx = encidx;
2896 break;
2897 }
2898
2899 case T_ARRAY: {
2900 rb_len_t len = RARRAY_LEN(obj);
2901 uint32_t *elems = len ? ALLOC_N(uint32_t, len) : NULL;
2902 for (rb_len_t i = 0; i < len; i++) {
2903 elems[i] = courier_capture(b, RARRAY_AREF(obj, i));
2904 }
2905 b->c->nodes[id].kind = COURIER_KIND_ARRAY;
2906 b->c->nodes[id].u.ary.klass = courier_klass(b, obj);
2907 b->c->nodes[id].u.ary.len = len;
2908 b->c->nodes[id].u.ary.elems = elems;
2909 /* Free the source's heap buffer now that the children were read, but only when it
2910 * is private: a sharer's belongs to its root, a root's to its sharers -- and a
2911 * frozen array is a root without carrying the flag. */
2912 if (!b->copy && !ARY_EMBED_P(obj) && !ARY_SHARED_P(obj) && !ARY_SHARED_ROOT_P(obj) && !OBJ_FROZEN(obj)) {
2913 ruby_xfree((void *)RARRAY_CONST_PTR(obj));
2914 }
2915 break;
2916 }
2917
2918 case T_HASH: {
2919 uint32_t ifnone_id = courier_capture(b, RHASH_IFNONE(obj));
2920 /* An entry count, not a String or Array length, so u.hash.size stays long */
2921 long size = RHASH_SIZE(obj);
2922 uint32_t *kv = size ? ALLOC_N(uint32_t, size * 2) : NULL;
2923 struct courier_hash_ctx hc = { b, kv, 0 };
2924 rb_hash_stlike_foreach(obj, courier_capture_hash_i, (st_data_t)&hc);
2925 b->c->nodes[id].kind = COURIER_KIND_HASH;
2926 b->c->nodes[id].u.hash.klass = courier_klass(b, obj);
2927 b->c->nodes[id].u.hash.size = size;
2928 b->c->nodes[id].u.hash.kv = kv;
2929 b->c->nodes[id].u.hash.ifnone_id = ifnone_id;
2930 b->c->nodes[id].u.hash.compare_by_id = RTEST(rb_hash_compare_by_id_p(obj));
2931 b->c->nodes[id].u.hash.proc_default = FL_TEST_RAW(obj, RHASH_PROC_DEFAULT) != 0;
2932 /* Free the source's st-table internals (an ar table lives in the slot) */
2933 if (!b->copy) rb_hash_free(obj);
2934 break;
2935 }
2936
2937 case T_OBJECT:
2938 b->c->nodes[id].kind = COURIER_KIND_OBJECT;
2939 b->c->nodes[id].u.obj.klass = courier_klass(b, obj);
2940 break;
2941
2942 case T_STRUCT: {
2943 rb_len_t len = RSTRUCT_LEN(obj);
2944 uint32_t *elems = len ? ALLOC_N(uint32_t, len) : NULL;
2945 for (rb_len_t i = 0; i < len; i++) {
2946 elems[i] = courier_capture(b, RSTRUCT_GET(obj, (int)i));
2947 }
2948 b->c->nodes[id].kind = COURIER_KIND_STRUCT;
2949 b->c->nodes[id].u.strct.len = len;
2950 b->c->nodes[id].u.strct.elems = elems;
2951 b->c->nodes[id].u.strct.klass = courier_klass(b, obj);
2952 /* Free the source's private heap buffer (an embedded struct has none) */
2953 if (!b->copy && RSTRUCT_EMBED_LEN(obj) == 0) {
2954 ruby_xfree((void *)RSTRUCT_CONST_PTR(obj));
2955 }
2956 break;
2957 }
2958
2959 case T_MATCH: {
2960 /* The regexp and the matched string travel as ordinary children; re.c dumps the
2961 * registers (freeing the source's onig and char_offset). */
2962 VALUE re, st;
2963 int nregs;
2964 void *regs = rb_match_blob_dump(obj, &re, &st, &nregs, !b->copy);
2965 uint32_t rid = courier_capture(b, re);
2966 uint32_t sid = courier_capture(b, st);
2967 b->c->nodes[id].kind = COURIER_KIND_MATCH;
2968 b->c->nodes[id].u.match.regexp_id = rid;
2969 b->c->nodes[id].u.match.str_id = sid;
2970 b->c->nodes[id].u.match.num_regs = nregs;
2971 b->c->nodes[id].u.match.regs = regs;
2972 b->c->nodes[id].u.match.klass = courier_klass(b, obj);
2973 break;
2974 }
2975
2976 case T_FILE:
2977 {
2978 VM_ASSERT(!b->copy); /* copy_courier_supported_p rejects it */
2979 /* Carry the whole fptr (fd included) by pointer; the source shell does not
2980 * close it. fptr's VALUE members lose their root once the source is T_MOVED,
2981 * so capture them as ordinary child nodes, detached; rebuild writes them back. */
2982 struct rb_io *fptr = RFILE(obj)->fptr;
2983 VM_ASSERT(!RTEST(fptr->tied_io_for_writing) && !RTEST(fptr->wakeup_mutex));
2984 uint32_t pathv_id = courier_capture(b, fptr->pathv);
2985 uint32_t ecopts_id = courier_capture(b, fptr->encs.ecopts);
2986 uint32_t wc_pre_id = courier_capture(b, fptr->writeconv_pre_ecopts);
2987 uint32_t wc_ac_id = courier_capture(b, fptr->writeconv_asciicompat);
2988 uint32_t timeout_id = courier_capture(b, fptr->timeout);
2989 fptr->self = Qnil; /* it points at the moved-from T_MOVED; attach rebuilds it */
2990 fptr->pathv = Qnil;
2991 fptr->encs.ecopts = Qnil;
2992 fptr->writeconv_pre_ecopts = Qnil;
2994 fptr->timeout = Qnil;
2995 fptr->write_lock = Qnil;
2996 fptr->wakeup_mutex = Qnil;
2997 fptr->tied_io_for_writing = 0; /* io.c tests it as a C boolean, so 0 rather than Qnil */
2998 b->c->nodes[id].kind = COURIER_KIND_IO;
2999 b->c->nodes[id].u.io.fptr = fptr;
3000 b->c->nodes[id].u.io.klass = courier_klass(b, obj);
3001 b->c->nodes[id].u.io.pathv_id = pathv_id;
3002 b->c->nodes[id].u.io.ecopts_id = ecopts_id;
3003 b->c->nodes[id].u.io.wc_pre_ecopts_id = wc_pre_id;
3004 b->c->nodes[id].u.io.wc_asciicompat_id = wc_ac_id;
3005 b->c->nodes[id].u.io.timeout_id = timeout_id;
3006 break;
3007 }
3008
3009 case T_REGEXP:
3010 /* Copy only: the receiver compiles the source again, as Marshal does. Move
3011 * would have to take the onig pattern apart. */
3012 if (b->copy) {
3013 /* The source is an fstring (reg_set_source), so it can be carried as is. */
3014 VALUE src = RREGEXP_SRC(obj);
3015 VM_ASSERT(rb_ractor_shareable_p(src));
3016 b->c->nodes[id].kind = COURIER_KIND_REGEXP;
3017 b->c->nodes[id].u.re.klass = courier_klass(b, obj);
3018 b->c->nodes[id].u.re.src = src;
3019 b->c->nodes[id].u.re.options = rb_reg_options(obj);
3020 break;
3021 }
3022 /* fall through */
3023 case T_DATA:
3024 /* Only an exception's backtrace, and only for a copy: move still refuses every
3025 * T_DATA (its source would have to be taken apart). */
3026 if (b->copy && rb_backtrace_p(obj)) {
3027 int size;
3028 void *blob = rb_backtrace_blob_dump(obj, &size);
3029 b->c->nodes[id].kind = COURIER_KIND_BACKTRACE;
3030 b->c->nodes[id].u.bt.blob = blob;
3031 b->c->nodes[id].u.bt.size = size;
3032 break;
3033 }
3034 /* fall through */
3035 default: {
3036 /* Copy has one more option: the object's own dump hook, which the preflight
3037 * already found. Move has not, since it would have to take the source apart. */
3038 enum courier_hook hook = b->copy ? courier_hook_of(obj) : COURIER_HOOK_NONE;
3039 if (hook == COURIER_HOOK_NONE) {
3040 rb_raise(rb_eRactorError, "can not %s a %"PRIsVALUE" object",
3041 b->copy ? "copy" : "move", rb_class_name(rb_obj_class(obj)));
3042 }
3043 courier_capture_hooked(b, obj, id, hook);
3044 break;
3045 }
3046 }
3047
3048 if (!b->copy) move_neutralize_source(obj);
3049 /* Every child has returned: the post-order materialize fills in. */
3050 b->c->order[b->ordered++] = id;
3051 return id;
3052}
3053
3054/* Like the copy walk, this also sizes the courier: see copy_support_ctx. */
3056 st_table *seen;
3057 uint32_t nodes, refs;
3058};
3059
3060static void move_preflight(VALUE obj, struct move_preflight_ctx *ctx);
3061
3062static int
3063move_preflight_ivar_i(ID name, VALUE val, st_data_t arg)
3064{
3065 move_preflight(val, (struct move_preflight_ctx *)arg);
3066 return ST_CONTINUE;
3067}
3068
3069static int
3070move_preflight_hash_i(st_data_t key, st_data_t val, st_data_t arg)
3071{
3072 move_preflight((VALUE)key, (struct move_preflight_ctx *)arg);
3073 move_preflight((VALUE)val, (struct move_preflight_ctx *)arg);
3074 return ST_CONTINUE;
3075}
3076
3077/* A read-only pre-walk of courier_capture's decision tree. Capture turns sources into
3078 * T_MOVED as it goes, so an unmovable object midway would leave the graph broken beyond
3079 * repair; every "can not move" error is raised here, before anything is mutated. */
3080static void
3081move_preflight(VALUE obj, struct move_preflight_ctx *ctx)
3082{
3083 st_table *const seen = ctx->seen;
3084
3085 if (RB_SPECIAL_CONST_P(obj) || rb_ractor_shareable_p(obj)) {
3086 ctx->refs++;
3087 return;
3088 }
3089 if (st_lookup(seen, (st_data_t)obj, NULL)) return; /* cycle */
3090 st_insert(seen, (st_data_t)obj, 0);
3091 ctx->nodes++;
3092
3093 /* The receiver takes over a materialized singleton class, so its contents have to
3094 * be movable too. */
3095 VALUE klass = RBASIC_CLASS(obj);
3096 if (RB_UNLIKELY(klass && FL_TEST_RAW(klass, FL_SINGLETON))) {
3097 rb_class_check_singleton_movable(klass);
3098 }
3099
3100 switch (BUILTIN_TYPE(obj)) {
3101 case T_STRING:
3102 case T_OBJECT:
3103 break; /* children are ivars only (below) */
3104 case T_MATCH: {
3105 struct RMatch *rm = RMATCH(obj);
3106 move_preflight(rm->regexp, ctx);
3107 move_preflight(rm->str, ctx);
3108 break;
3109 }
3110 case T_ARRAY:
3111 for (rb_len_t i = 0; i < RARRAY_LEN(obj); i++) {
3112 move_preflight(RARRAY_AREF(obj, i), ctx);
3113 }
3114 break;
3115 case T_HASH:
3116 rb_hash_stlike_foreach(obj, move_preflight_hash_i, (st_data_t)ctx);
3117 move_preflight(RHASH_IFNONE(obj), ctx);
3118 break;
3119 case T_STRUCT:
3120 for (rb_len_t i = 0; i < RSTRUCT_LEN(obj); i++) {
3121 move_preflight(RSTRUCT_GET(obj, (int)i), ctx);
3122 }
3123 break;
3124 case T_FILE: {
3125 struct rb_io *fptr = RFILE(obj)->fptr;
3126 if (fptr == NULL) {
3127 rb_raise(rb_eRactorError, "can not move an uninitialized IO");
3128 }
3129 if (RTEST(fptr->tied_io_for_writing)) {
3130 /* A popen("r+") pair: moving one side would dangle the tied writer on the
3131 * sender. */
3132 rb_raise(rb_eRactorError, "can not move an IO tied to a writer IO");
3133 }
3134 if (RTEST(fptr->wakeup_mutex)) {
3135 /* A close is in progress: a thread is blocked on this IO. */
3136 rb_raise(rb_eRactorError, "can not move an IO that is being closed");
3137 }
3138 move_preflight(fptr->pathv, ctx);
3139 move_preflight(fptr->encs.ecopts, ctx);
3140 move_preflight(fptr->writeconv_pre_ecopts, ctx);
3141 move_preflight(fptr->writeconv_asciicompat, ctx);
3142 move_preflight(fptr->timeout, ctx);
3143 break;
3144 }
3145 default:
3146 rb_raise(rb_eRactorError, "can not move a %"PRIsVALUE" object",
3148 }
3149
3150 rb_ivar_foreach(obj, move_preflight_ivar_i, (st_data_t)ctx);
3151}
3152
3153/* The walk also sizes the courier: one node per distinct unshareable object, one ref
3154 * per occurrence of a shareable one -- exactly what courier_capture allocates, so the
3155 * arrays never have to grow while the graph is being captured. */
3157 st_table *seen;
3158 uint32_t nodes, refs;
3159 bool ok;
3160};
3161
3162static bool copy_courier_supported_p(VALUE obj, struct copy_support_ctx *ctx);
3163
3164static int
3165copy_support_val_i(st_data_t val, st_data_t arg)
3166{
3167 struct copy_support_ctx *ctx = (struct copy_support_ctx *)arg;
3168 if (!copy_courier_supported_p((VALUE)val, ctx)) {
3169 ctx->ok = false;
3170 return ST_STOP;
3171 }
3172 return ST_CONTINUE;
3173}
3174
3175static int
3176copy_support_ivar_i(ID name, VALUE val, st_data_t arg)
3177{
3178 return copy_support_val_i((st_data_t)val, arg);
3179}
3180
3181static int
3182copy_support_hash_i(st_data_t key, st_data_t val, st_data_t arg)
3183{
3184 if (copy_support_val_i(key, arg) == ST_STOP) return ST_STOP;
3185 return copy_support_val_i(val, arg);
3186}
3187
3188/* Read-only walk: can the copy courier carry obj's whole graph? A no is a send error. */
3189static bool
3190copy_courier_supported_p(VALUE obj, struct copy_support_ctx *ctx)
3191{
3192 st_table *const seen = ctx->seen;
3193
3194 if (RB_SPECIAL_CONST_P(obj) || rb_ractor_shareable_p(obj)) {
3195 ctx->refs++;
3196 return true;
3197 }
3198 if (st_lookup(seen, (st_data_t)obj, NULL)) return true; /* cycle */
3199 st_insert(seen, (st_data_t)obj, 0);
3200 ctx->nodes++;
3201
3202 if (RBASIC_CLASS(obj) == 0) return false;
3203
3204 switch (BUILTIN_TYPE(obj)) {
3205 case T_STRING:
3206 case T_OBJECT:
3207 case T_REGEXP:
3208 break; /* children are ivars only (below) */
3209 case T_MATCH: {
3210 struct RMatch *rm = RMATCH(obj);
3211 if (!copy_courier_supported_p(rm->regexp, ctx)) return false;
3212 if (!copy_courier_supported_p(rm->str, ctx)) return false;
3213 break;
3214 }
3215 case T_DATA:
3216 /* An exception's backtrace is the one T_DATA the courier carries natively. */
3217 if (!rb_backtrace_p(obj) && courier_hook_of(obj) == COURIER_HOOK_NONE) return false;
3218 break;
3219 case T_ARRAY:
3220 for (rb_len_t i = 0; i < RARRAY_LEN(obj); i++) {
3221 if (!copy_courier_supported_p(RARRAY_AREF(obj, i), ctx)) return false;
3222 }
3223 break;
3224 case T_HASH:
3225 rb_hash_stlike_foreach(obj, copy_support_hash_i, (st_data_t)ctx);
3226 if (!ctx->ok) return false;
3227 if (!copy_courier_supported_p(RHASH_IFNONE(obj), ctx)) return false;
3228 break;
3229 case T_STRUCT:
3230 for (rb_len_t i = 0; i < RSTRUCT_LEN(obj); i++) {
3231 if (!copy_courier_supported_p(RSTRUCT_GET(obj, (int)i), ctx)) return false;
3232 }
3233 break;
3234 default:
3235 /* Anything else has to dump itself. What the hook returns is not walked here:
3236 * running it twice is not an option, so capture allocates its nodes through the
3237 * growth path instead of the reservation. */
3238 if (courier_hook_of(obj) == COURIER_HOOK_NONE) return false;
3239 break;
3240 }
3241
3242 rb_ivar_foreach(obj, copy_support_ivar_i, (st_data_t)ctx);
3243 return ctx->ok;
3244}
3245
3246/* Build a courier holding a copy of obj's graph, leaving the sources untouched.
3247 * Returns NULL when the graph has a type it cannot carry. */
3248struct rb_ractor_courier *
3249rb_ractor_courier_build_copy(VALUE obj, struct rb_ractor_courier **slot)
3250{
3251 struct copy_support_ctx scan = { st_init_numtable(), 0, 0, true };
3252 {
3253 bool ok = copy_courier_supported_p(obj, &scan);
3254 st_free_table(scan.seen);
3255 if (!ok) return NULL;
3256 }
3257
3258 struct rb_ractor_courier *c = ZALLOC(struct rb_ractor_courier);
3259 courier_reserve(c, scan.nodes, scan.refs);
3260 c->seen = st_init_numtable();
3261 struct courier_build b = { c, true };
3262
3263 /* Publish it into the caller's basket before capturing anything: from here the
3264 * shareable payloads it collects are rooted by the basket's holder. */
3265 *slot = c;
3266
3267 enum ruby_tag_type state;
3268 rb_execution_context_t *ec = GET_EC();
3269 EC_PUSH_TAG(ec);
3270 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
3271 c->root = courier_capture(&b, obj);
3272 }
3273 EC_POP_TAG();
3274 st_free_table(c->seen);
3275 c->seen = NULL;
3276 /* Published above, so the basket owns it even half-built: it frees it. */
3277 if (state != TAG_NONE) EC_JUMP_TAG(ec, state);
3278 return c;
3279}
3280
3281/* Build a courier from obj and turn every captured source into a RactorMovedObject
3282 * (move semantics). Returns the xmalloc'd courier. */
3283struct rb_ractor_courier *
3284rb_ractor_courier_build_move(VALUE obj, struct rb_ractor_courier **slot)
3285{
3286 /* Two phases, preflight then commit, so an unmovable object is raised from the
3287 * read-only walk while the graph is still intact. */
3288 struct move_preflight_ctx scan = { st_init_numtable(), 0, 0 };
3289 {
3290 enum ruby_tag_type state;
3291 rb_execution_context_t *ec = GET_EC();
3292 EC_PUSH_TAG(ec);
3293 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
3294 move_preflight(obj, &scan);
3295 }
3296 EC_POP_TAG();
3297 st_free_table(scan.seen);
3298 if (state != TAG_NONE) EC_JUMP_TAG(ec, state);
3299 }
3300
3301 struct rb_ractor_courier *c = ZALLOC(struct rb_ractor_courier);
3302 courier_reserve(c, scan.nodes, scan.refs);
3303 c->seen = st_init_numtable();
3304 struct courier_build b = { c, false };
3305
3306 /* Publish it into the caller's basket before the sources become T_MOVED: from here
3307 * the basket's holder roots what the courier carries, and partial nodes are
3308 * initialized mark-safe. */
3309 *slot = c;
3310
3311 enum ruby_tag_type state;
3312 rb_execution_context_t *ec = GET_EC();
3313 EC_PUSH_TAG(ec);
3314 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
3315 c->root = courier_capture(&b, obj);
3316 }
3317 EC_POP_TAG();
3318 st_free_table(c->seen);
3319 c->seen = NULL;
3320 if (state != TAG_NONE) {
3321 /* courier_capture raised (an unmovable type, an interrupt). The courier belongs
3322 * to the basket from the publish above, so leave it there and re-raise: the
3323 * basket frees it, once, on the way out. */
3324 EC_JUMP_TAG(ec, state);
3325 }
3326 return c;
3327}
3328
3329/* Shells are created with the base/real class, so re-attach the original subclass or
3330 * singleton class (classes are shareable; the reference is safe). A singleton's
3331 * attached object still points at the sender's source: re-attach it to the shell. */
3332static void
3333courier_apply_klass(VALUE shell, VALUE klass)
3334{
3335 if (klass != RBASIC_CLASS(shell)) {
3336 RBASIC_SET_CLASS(shell, klass);
3337 }
3338 if (RB_UNLIKELY(FL_TEST_RAW(klass, FL_SINGLETON))) {
3339 rb_singleton_class_attached(klass, shell);
3340 /* the singleton class follows its object, which is now this Ractor's */
3341 rb_class_take_ownership(klass);
3342 }
3343}
3344
3345/* Rebuild the courier's graph in the current Ractor's objspace and return its root.
3346 * Two passes (allocate shells, then fill) break reference cycles. */
3347VALUE
3348rb_ractor_courier_materialize(struct rb_ractor_courier *c)
3349{
3350 /* A hidden Array roots every shell, keeping them alive while the allocations that
3351 * build the rest of the graph (which can start this Ractor's GC) run. */
3352 VALUE shells = rb_ary_hidden_new(c->count);
3353
3354 for (uint32_t i = 0; i < c->count; i++) {
3355 struct courier_node *n = &c->nodes[i];
3356 VALUE shell;
3357 switch (n->kind) {
3358 case COURIER_KIND_REF:
3359 shell = n->u.ref;
3360 break;
3361 case COURIER_KIND_STRING:
3362 /* Hand the courier's buffer to the String instead of copying it again: the
3363 * bytes were already copied (or taken from the source) when the node was
3364 * built. */
3365 shell = rb_str_new_owned(n->u.str.ptr, n->u.str.len, n->u.str.capa, n->u.str.encidx);
3366 n->u.str.ptr = NULL; /* consumed: the new String owns it now */
3367 courier_apply_klass(shell, n->u.str.klass);
3368 break;
3369 case COURIER_KIND_ARRAY:
3370 shell = rb_ary_new_capa(n->u.ary.len);
3371 courier_apply_klass(shell, n->u.ary.klass);
3372 break;
3373 case COURIER_KIND_HASH:
3374 shell = n->u.hash.compare_by_id ? rb_ident_hash_new() : rb_hash_new();
3375 courier_apply_klass(shell, n->u.hash.klass);
3376 break;
3377 case COURIER_KIND_OBJECT:
3378 /* A singleton class cannot allocate, so make an instance of the real class
3379 * and re-attach it afterwards */
3380 shell = rb_obj_alloc(rb_class_real(n->u.obj.klass));
3381 courier_apply_klass(shell, n->u.obj.klass);
3382 break;
3383 case COURIER_KIND_STRUCT:
3384 shell = rb_obj_alloc(rb_class_real(n->u.strct.klass));
3385 courier_apply_klass(shell, n->u.strct.klass);
3386 break;
3387 case COURIER_KIND_MATCH:
3388 shell = rb_match_blob_alloc(rb_class_real(n->u.match.klass), n->u.match.num_regs);
3389 courier_apply_klass(shell, n->u.match.klass);
3390 break;
3391 case COURIER_KIND_BACKTRACE:
3392 shell = rb_backtrace_blob_load(n->u.bt.blob, n->u.bt.size);
3393 break;
3394 case COURIER_KIND_REGEXP:
3395 /* Allocated as its real class up front, as Marshal does: initializing a
3396 * plain Regexp freezes it, and the freeze pass below decides that here. */
3397 shell = rb_reg_init_str(rb_reg_s_alloc(rb_class_real(n->u.re.klass)), n->u.re.src, n->u.re.options);
3398 courier_apply_klass(shell, n->u.re.klass);
3399 break;
3400 case COURIER_KIND_HOOKED:
3401 if (n->u.hooked.hook == COURIER_HOOK_DUMP) {
3402 shell = Qnil; /* klass._load makes it below, once its String exists */
3403 break;
3404 }
3405 /* Allocated now and filled by its load hook below, which is what lets a
3406 * cycle back through the payload resolve to the object itself. */
3407 shell = rb_obj_alloc(rb_class_real(n->u.hooked.klass));
3408 courier_apply_klass(shell, n->u.hooked.klass);
3409 break;
3410 case COURIER_KIND_IO:
3411 shell = rb_obj_alloc(rb_class_real(n->u.io.klass));
3412 courier_apply_klass(shell, n->u.io.klass);
3413 RFILE(shell)->fptr = n->u.io.fptr;
3414 n->u.io.fptr->self = shell;
3415 n->u.io.fptr = NULL; /* consumed: the new IO owns it now */
3416 break;
3417 default:
3418 rb_bug("rb_ractor_courier_materialize: bad node kind");
3419 }
3420 rb_ary_push(shells, shell);
3421 }
3422
3423 /* Fill in capture's post-order, so each node is settled after everything below it,
3424 * shared children included: a Hash sees complete keys (a content-based #hash would
3425 * collide on every key while the graph is still empty), a load hook sees a complete
3426 * payload, and a parent sees the object klass._load returned. Only a cycle reaches
3427 * a node still being filled (a #hash or a payload cycling through itself is out of
3428 * scope). */
3429 for (uint32_t k = 0; k < c->count; k++) {
3430 uint32_t i = c->order[k];
3431 struct courier_node *n = &c->nodes[i];
3432 VALUE shell = RARRAY_AREF(shells, i);
3433 switch (n->kind) {
3434 case COURIER_KIND_ARRAY: {
3435 /* The length is known, so set it once and write the slots, rather than
3436 * pushing each element through the capacity check. */
3437 const rb_len_t len = n->u.ary.len;
3438 if (len > 0) {
3439 rb_ary_resize(shell, len);
3440 for (rb_len_t j = 0; j < len; j++) {
3441 RARRAY_ASET(shell, j, courier_child(c, shells, n->u.ary.elems[j]));
3442 }
3443 }
3444 break;
3445 }
3446 case COURIER_KIND_HASH:
3447 for (long j = 0; j < n->u.hash.size; j++) {
3448 rb_hash_aset(shell, courier_child(c, shells, n->u.hash.kv[2 * j]),
3449 courier_child(c, shells, n->u.hash.kv[2 * j + 1]));
3450 }
3451 /* Restore the default value and default proc (before freezing) */
3452 VALUE ifnone = courier_child(c, shells, n->u.hash.ifnone_id);
3453 if (n->u.hash.proc_default) {
3454 rb_hash_set_default_proc(shell, ifnone);
3455 }
3456 else if (ifnone != Qnil) {
3457 rb_hash_set_default(shell, ifnone);
3458 }
3459 break;
3460 case COURIER_KIND_HOOKED: {
3461 VALUE payload = courier_child(c, shells, n->u.hooked.payload_id);
3462 VALUE klass = n->u.hooked.klass;
3463 ID mid;
3464 switch (n->u.hooked.hook) {
3465 case COURIER_HOOK_DUMP:
3466 if (!rb_obj_respond_to(klass, id_load, TRUE)) {
3467 rb_raise(rb_eTypeError, "class %"PRIsVALUE" needs to have method '_load'", klass);
3468 }
3469 /* _load returns the object: it takes the place of the Qnil placeholder
3470 * so everything filled after this receives it, and the ivars restored
3471 * below land on it. */
3472 shell = rb_funcallv(klass, id_load, 1, &payload);
3473 RARRAY_ASET(shells, i, shell);
3474 break;
3475 case COURIER_HOOK_MARSHAL_DUMP:
3476 case COURIER_HOOK_DUMP_DATA:
3477 mid = n->u.hooked.hook == COURIER_HOOK_MARSHAL_DUMP ? id_marshal_load : id_load_data;
3478 if (!rb_obj_respond_to(shell, mid, TRUE)) {
3479 rb_raise(rb_eTypeError, "instance of %"PRIsVALUE" needs to have method '%"PRIsVALUE"'",
3480 klass, rb_id2str(mid));
3481 }
3482 rb_funcallv(shell, mid, 1, &payload);
3483 break;
3484 case COURIER_HOOK_COMPAT: {
3485 VALUE (*loader)(VALUE, VALUE);
3486 rb_marshal_compat_lookup(klass, NULL, &loader);
3487 loader(shell, payload);
3488 break;
3489 }
3490 default:
3491 rb_bug("rb_ractor_courier_materialize: no dump protocol");
3492 }
3493 break;
3494 }
3495 case COURIER_KIND_STRUCT:
3496 for (rb_len_t j = 0; j < n->u.strct.len; j++) {
3497 RSTRUCT_SET(shell, (int)j, courier_child(c, shells, n->u.strct.elems[j]));
3498 }
3499 break;
3500 case COURIER_KIND_MATCH:
3501 rb_match_blob_load(shell, courier_child(c, shells, n->u.match.regexp_id),
3502 courier_child(c, shells, n->u.match.str_id),
3503 n->u.match.num_regs, n->u.match.regs);
3504 break;
3505 case COURIER_KIND_IO: {
3506 /* Write the rebuilt VALUE members back into fptr (capture detached them).
3507 * write_lock and wakeup_mutex stay nil; io.c recreates them lazily. */
3508 struct rb_io *fptr = RFILE(shell)->fptr;
3509 RB_OBJ_WRITE(shell, &fptr->pathv, courier_child(c, shells, n->u.io.pathv_id));
3510 RB_OBJ_WRITE(shell, &fptr->encs.ecopts, courier_child(c, shells, n->u.io.ecopts_id));
3511 RB_OBJ_WRITE(shell, &fptr->writeconv_pre_ecopts, courier_child(c, shells, n->u.io.wc_pre_ecopts_id));
3512 RB_OBJ_WRITE(shell, &fptr->writeconv_asciicompat, courier_child(c, shells, n->u.io.wc_asciicompat_id));
3513 RB_OBJ_WRITE(shell, &fptr->timeout, courier_child(c, shells, n->u.io.timeout_id));
3514 break;
3515 }
3516 default:
3517 break;
3518 }
3519 /* Restore instance and generic ivars (any non-REF node can have them) */
3520 for (uint32_t j = 0; j < n->niv; j++) {
3521 rb_ivar_set(shell, n->iv_ids[j], courier_child(c, shells, n->iv_vals[j]));
3522 }
3523 }
3524
3525 /* Freeze after filling, so frozen containers and strings can be built too. */
3526 for (uint32_t i = 0; i < c->count; i++) {
3527 VALUE shell = RARRAY_AREF(shells, i);
3528 if (c->nodes[i].frozen && !RB_SPECIAL_CONST_P(shell)) {
3529 rb_obj_freeze(shell);
3530 }
3531 }
3532
3533 VALUE root = (c->count || c->refs_count) ? courier_child(c, shells, c->root) : Qnil;
3534 RB_GC_GUARD(shells);
3535 return root;
3536}
3537
3538void
3539rb_ractor_courier_free(struct rb_ractor_courier *c)
3540{
3541 for (uint32_t i = 0; i < c->count; i++) {
3542 struct courier_node *n = &c->nodes[i];
3543 ruby_xfree(n->iv_ids);
3544 ruby_xfree(n->iv_vals);
3545 switch (n->kind) {
3546 case COURIER_KIND_STRING:
3547 ruby_xfree(n->u.str.ptr);
3548 break;
3549 case COURIER_KIND_ARRAY:
3550 ruby_xfree(n->u.ary.elems);
3551 break;
3552 case COURIER_KIND_HASH:
3553 ruby_xfree(n->u.hash.kv);
3554 break;
3555 case COURIER_KIND_STRUCT:
3556 ruby_xfree(n->u.strct.elems);
3557 break;
3558 case COURIER_KIND_MATCH:
3559 rb_match_blob_free(n->u.match.regs);
3560 break;
3561 case COURIER_KIND_BACKTRACE:
3562 ruby_xfree(n->u.bt.blob);
3563 break;
3564 case COURIER_KIND_IO:
3565 /* A delivered IO left fptr == NULL (the rebuilt IO owns it). An
3566 * undelivered one still owns the fd and its source is already a
3567 * RactorMovedObject nobody can close: close it here, not leak it. */
3568 if (n->u.io.fptr) {
3569 rb_io_fptr_finalize(n->u.io.fptr);
3570 n->u.io.fptr = NULL;
3571 }
3572 break;
3573 default:
3574 break;
3575 }
3576 }
3577 ruby_xfree(c->nodes);
3578 ruby_xfree(c->order);
3579 ruby_xfree(c->refs);
3580 ruby_xfree(c);
3581}
3582
3583/* Mark the only VALUEs a courier holds: shareable objects and immediates (REF) and the
3584 * classes of its objects. All of them are shareable, so marking cannot race, and the
3585 * global GC keeps them reachable through the courier. While it is being built it also
3586 * holds the sender's sources in seen; the basket is on the sender's own list then. */
3587void
3588rb_ractor_courier_mark(const struct rb_gc_mark_ctx *ctx, struct rb_ractor_courier *c)
3589{
3590 if (!c) return;
3591 if (c->seen) rb_mark_set(c->seen);
3592 for (uint32_t i = 0; i < c->refs_count; i++) {
3593 rb_gc_mark_ctx(ctx, c->refs[i]);
3594 }
3595 for (uint32_t i = 0; i < c->count; i++) {
3596 struct courier_node *n = &c->nodes[i];
3597 if (n->kind == COURIER_KIND_REF) {
3598 rb_gc_mark_ctx(ctx, n->u.ref);
3599 }
3600 else if (n->kind == COURIER_KIND_OBJECT) {
3601 rb_gc_mark_ctx(ctx, n->u.obj.klass);
3602 }
3603 else if (n->kind == COURIER_KIND_STRUCT) {
3604 rb_gc_mark_ctx(ctx, n->u.strct.klass);
3605 }
3606 else if (n->kind == COURIER_KIND_MATCH) {
3607 rb_gc_mark_ctx(ctx, n->u.match.klass);
3608 }
3609 else if (n->kind == COURIER_KIND_IO) {
3610 rb_gc_mark_ctx(ctx, n->u.io.klass);
3611 }
3612 else if (n->kind == COURIER_KIND_STRING) {
3613 rb_gc_mark_ctx(ctx, n->u.str.klass);
3614 }
3615 else if (n->kind == COURIER_KIND_BACKTRACE) {
3616 rb_backtrace_blob_mark_ctx(ctx, n->u.bt.blob, n->u.bt.size);
3617 }
3618 else if (n->kind == COURIER_KIND_ARRAY) {
3619 rb_gc_mark_ctx(ctx, n->u.ary.klass);
3620 }
3621 else if (n->kind == COURIER_KIND_HASH) {
3622 rb_gc_mark_ctx(ctx, n->u.hash.klass);
3623 }
3624 else if (n->kind == COURIER_KIND_REGEXP) {
3625 rb_gc_mark_ctx(ctx, n->u.re.src);
3626 rb_gc_mark_ctx(ctx, n->u.re.klass);
3627 }
3628 else if (n->kind == COURIER_KIND_HOOKED) {
3629 rb_gc_mark_ctx(ctx, n->u.hooked.klass);
3630 }
3631 }
3632}
3633
3634/* The message copy traversal never calls #clone or #initialize_clone. Core container
3635 * types get a native shallow copy here (the traversal then rewrites the children inside
3636 * the copy); any other unshareable type falls back to a full Marshal round trip. */
3637static VALUE
3638ractor_native_shallow_copy(VALUE obj)
3639{
3640 VALUE copy;
3641
3642 /* An object with a singleton class cannot be copied natively; fall back to Marshal
3643 * so it reports a proper error. */
3644 VALUE klass = RBASIC_CLASS(obj);
3645 if (klass == 0 || FL_TEST_RAW(klass, FL_SINGLETON)) {
3646 return Qundef;
3647 }
3648
3649 switch (BUILTIN_TYPE(obj)) {
3650 case T_OBJECT:
3651 copy = rb_obj_alloc(rb_obj_class(obj));
3652 rb_obj_copy_ivar(copy, obj);
3653 break;
3654 case T_STRING:
3655 copy = rb_enc_str_new(RSTRING_PTR(obj), RSTRING_LEN(obj), rb_enc_get(obj));
3656 break;
3657 case T_ARRAY:
3659 break;
3660 case T_HASH:
3661 copy = rb_hash_dup(obj);
3662 break;
3663 case T_STRUCT:
3664 copy = rb_obj_alloc(rb_obj_class(obj));
3665 rb_struct_init_copy(copy, obj);
3666 break;
3667 case T_MATCH:
3668 copy = rb_obj_alloc(rb_obj_class(obj));
3669 rb_match_init_copy(copy, obj);
3670 break;
3671 case T_DATA:
3672 /* Keep a copied exception from carrying a raw pointer to the sender's backtrace
3673 * across objspaces */
3674 if (rb_backtrace_p(obj)) {
3675 copy = rb_backtrace_dup(obj);
3676 break;
3677 }
3678 return Qundef;
3679 default:
3680 return Qundef;
3681 }
3682
3683 /* A non-T_OBJECT host keeps its ivars in the generic fields table: copy them.
3684 * T_HASH is excluded: rb_hash_dup already ran rb_copy_generic_ivar, and a second
3685 * call asserts in rb_shape_rebuild (the first gave the copy an ivar shape). */
3686 if (BUILTIN_TYPE(obj) != T_OBJECT && BUILTIN_TYPE(obj) != T_HASH &&
3687 UNLIKELY(rb_obj_gen_fields_p(obj))) {
3688 rb_copy_generic_ivar(copy, obj);
3689 }
3690
3691 /* The traversal rewrites the children inside the copy with raw stores, so the frozen
3692 * bit can be set now: by the time leave runs the original is out of sight. The shape
3693 * has to be transitioned along with the flag, because field writes are refused based
3694 * on the shape (see rb_check_ivar_modifiable). */
3695 if (OBJ_FROZEN(obj)) {
3697 RBASIC_SET_SHAPE_ID(copy, rb_obj_shape_transition_frozen(copy));
3698 }
3699 return copy;
3700}
3701
3702static enum obj_traverse_iterator_result
3703copy_enter(VALUE obj, struct obj_traverse_replace_data *data)
3704{
3705 if (rb_ractor_shareable_p(obj)) {
3706 data->replacement = obj;
3707 return traverse_skip;
3708 }
3709 else {
3710 VALUE copy = ractor_native_shallow_copy(obj);
3711 if (UNDEF_P(copy)) return traverse_stop; /* no native copy for this type */
3712 data->replacement = copy;
3713 return traverse_cont;
3714 }
3715}
3716
3717static enum obj_traverse_iterator_result
3718copy_leave(VALUE obj, struct obj_traverse_replace_data *data)
3719{
3720 return traverse_cont;
3721}
3722
3723/* Native deep copy of obj's graph. Returns Qundef when it contains a type the native
3724 * copier does not support, and the caller falls back to Marshal. */
3725static VALUE
3726ractor_copy_native_try(VALUE obj)
3727{
3728 return rb_obj_traverse_replace(obj, copy_enter, copy_leave, false);
3729}
3730
3731/* Deep copy within one objspace (Ractor.make_shareable(obj, copy: true)): native first,
3732 * then a whole-graph Marshal round trip. */
3733static VALUE
3734ractor_copy(VALUE obj)
3735{
3736 VALUE copy = ractor_copy_native_try(obj);
3737 if (UNDEF_P(copy)) {
3738 copy = rb_marshal_load(rb_rescue2(ractor_marshal_dump_body, obj,
3739 ractor_marshal_dump_rescue, obj,
3740 rb_eTypeError, (VALUE)0));
3741 }
3742 return copy;
3743}
3744
3745// Ractor local storage
3746
3748 const struct rb_ractor_local_storage_type *type;
3749 void *main_cache;
3750};
3751
3753 int cnt;
3754 int capa;
3756} freed_ractor_local_keys;
3757
3758/* Purge deleted ractor-local keys from the storage tables and run their free hooks. */
3759static void
3760ractor_local_keys_purge(st_table *local_storage)
3761{
3762 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
3763 rb_ractor_local_key_t key = freed_ractor_local_keys.keys[i];
3764 st_data_t val, k = (st_data_t)key;
3765 if (st_delete(local_storage, &k, &val) &&
3766 (key = (rb_ractor_local_key_t)k)->type->free) {
3767 (*key->type->free)((void *)val);
3768 }
3769 }
3770}
3771
3772
3773static int
3774ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
3775{
3777 if (k->type->mark) (*k->type->mark)((void *)val);
3778 return ST_CONTINUE;
3779}
3780
3781static enum rb_id_table_iterator_result
3782idkey_local_storage_mark_i(VALUE val, void *dmy)
3783{
3784 rb_gc_mark(val);
3785 return ID_TABLE_CONTINUE;
3786}
3787
3788static void
3789ractor_local_storage_mark(rb_ractor_t *r)
3790{
3791 if (r->local_storage) {
3792 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
3793
3794 /* A deleted key is purged from every Ractor's storage in one collection, which
3795 * then frees its struct. Only a collection that visits every Ractor with no
3796 * other marker running can do that: a global GC, or a single objspace. */
3797 if (rb_gc_single_objspace_p() || rb_gc_during_global_gc_p()) {
3798 ractor_local_keys_purge(r->local_storage);
3799 }
3800 }
3801
3802 if (r->idkey_local_storage) {
3803 rb_id_table_foreach_values(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
3804 }
3805
3806 rb_gc_mark(r->local_storage_store_lock);
3807}
3808
3809static int
3810ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
3811{
3813 if (k->type->free) (*k->type->free)((void *)val);
3814 return ST_CONTINUE;
3815}
3816
3817static void
3818ractor_local_storage_free(rb_ractor_t *r)
3819{
3820 if (r->local_storage) {
3821 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
3822 st_free_table(r->local_storage);
3823 }
3824
3825 if (r->idkey_local_storage) {
3826 rb_id_table_free(r->idkey_local_storage);
3827 }
3828}
3829
3830static void
3831rb_ractor_local_storage_value_mark(void *ptr)
3832{
3833 rb_gc_mark((VALUE)ptr);
3834}
3835
3836static const struct rb_ractor_local_storage_type ractor_local_storage_type_null = {
3837 NULL,
3838 NULL,
3839};
3840
3842 NULL,
3843 ruby_xfree,
3844};
3845
3846static const struct rb_ractor_local_storage_type ractor_local_storage_type_value = {
3847 rb_ractor_local_storage_value_mark,
3848 NULL,
3849};
3850
3853{
3855 key->type = type ? type : &ractor_local_storage_type_null;
3856 key->main_cache = (void *)Qundef;
3857 return key;
3858}
3859
3862{
3863 return rb_ractor_local_storage_ptr_newkey(&ractor_local_storage_type_value);
3864}
3865
3866void
3867rb_ractor_local_storage_delkey(rb_ractor_local_key_t key)
3868{
3869 RB_VM_LOCKING() {
3870 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
3871 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
3872 SIZED_REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, freed_ractor_local_keys.capa, freed_ractor_local_keys.cnt);
3873 }
3874 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
3875 }
3876}
3877
3878static bool
3879ractor_local_ref(rb_ractor_local_key_t key, void **pret)
3880{
3881 if (rb_ractor_main_p()) {
3882 if (!UNDEF_P((VALUE)key->main_cache)) {
3883 *pret = key->main_cache;
3884 return true;
3885 }
3886 else {
3887 return false;
3888 }
3889 }
3890 else {
3891 rb_ractor_t *cr = GET_RACTOR();
3892
3893 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
3894 return true;
3895 }
3896 else {
3897 return false;
3898 }
3899 }
3900}
3901
3902static void
3903ractor_local_set(rb_ractor_local_key_t key, void *ptr)
3904{
3905 rb_ractor_t *cr = GET_RACTOR();
3906
3907 if (cr->local_storage == NULL) {
3908 cr->local_storage = st_init_numtable();
3909 }
3910
3911 st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
3912
3913 if (rb_ractor_main_p()) {
3914 key->main_cache = ptr;
3915 }
3916}
3917
3918VALUE
3920{
3921 void *val;
3922 if (ractor_local_ref(key, &val)) {
3923 return (VALUE)val;
3924 }
3925 else {
3926 return Qnil;
3927 }
3928}
3929
3930bool
3932{
3933 if (ractor_local_ref(key, (void **)val)) {
3934 return true;
3935 }
3936 else {
3937 return false;
3938 }
3939}
3940
3941void
3943{
3944 ractor_local_set(key, (void *)val);
3945}
3946
3947void *
3949{
3950 void *ret;
3951 if (ractor_local_ref(key, &ret)) {
3952 return ret;
3953 }
3954 else {
3955 return NULL;
3956 }
3957}
3958
3959void
3961{
3962 ractor_local_set(key, ptr);
3963}
3964
3965#define DEFAULT_KEYS_CAPA 0x10
3966
3967void
3968rb_ractor_finish_marking(bool full_mark)
3969{
3970 /* A freed key's struct may only be released by a collection that purged every
3971 * Ractor's storage with no other marker running: a global GC, or a single objspace.
3972 * A local GC also reaches here (gc_marks_finish) and must do nothing. */
3973 if (!(rb_gc_single_objspace_p() || rb_gc_during_global_gc_p())) {
3974 return;
3975 }
3976
3977 /* The root scan's purge never reaches a zombie's storage (not in the set;
3978 * zombie_objspaces only marks the join slot): purge here, under the barrier, before
3979 * the struct is freed, or a later ractor_free reads a freed key. */
3980 rb_vm_t *vm = GET_VM();
3981 rb_ractor_t *r;
3982
3983 for (size_t zi = 0; zi < vm->gc.zombie_objspaces_count; zi++) {
3984 rb_ractor_t *owner = vm->gc.zombie_objspaces[zi].owner;
3985 if (owner == NULL || owner->local_storage == NULL) continue;
3986 ractor_local_keys_purge(owner->local_storage);
3987 }
3988
3989 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
3990 SIZED_FREE(freed_ractor_local_keys.keys[i]);
3991 }
3992 freed_ractor_local_keys.cnt = 0;
3993 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
3994 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
3995 SIZED_REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, DEFAULT_KEYS_CAPA, freed_ractor_local_keys.capa);
3996 }
3997
3998 /* Under a minor mark an unmarked port is not a dead one. */
3999 if (full_mark) {
4000 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
4001 rb_ractor_reap_dead_ports(r);
4002 }
4003 if (vm->ractor.cnt == 0 && vm->ractor.main_ractor) {
4004 rb_ractor_reap_dead_ports(vm->ractor.main_ractor);
4005 }
4006 }
4007}
4008
4009static VALUE
4010ractor_local_value(rb_execution_context_t *ec, VALUE self, VALUE sym)
4011{
4012 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
4013 ID id = rb_check_id(&sym);
4014 struct rb_id_table *tbl = cr->idkey_local_storage;
4015 VALUE val;
4016
4017 if (id && tbl && rb_id_table_lookup(tbl, id, &val)) {
4018 return val;
4019 }
4020 else {
4021 return Qnil;
4022 }
4023}
4024
4025static VALUE
4026ractor_local_value_set(rb_execution_context_t *ec, VALUE self, VALUE sym, VALUE val)
4027{
4028 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
4029 ID id = SYM2ID(rb_to_symbol(sym));
4030 struct rb_id_table *tbl = cr->idkey_local_storage;
4031
4032 if (tbl == NULL) {
4033 tbl = cr->idkey_local_storage = rb_id_table_create(2);
4034 }
4035 rb_id_table_insert(tbl, id, val);
4036 return val;
4037}
4038
4041 struct rb_id_table *tbl;
4042 ID id;
4043 VALUE sym;
4044};
4045
4046static VALUE
4047ractor_local_value_store_i(VALUE ptr)
4048{
4049 VALUE val;
4051
4052 if (rb_id_table_lookup(data->tbl, data->id, &val)) {
4053 // after synchronization, we found already registered entry
4054 }
4055 else {
4056 val = rb_yield(Qnil);
4057 ractor_local_value_set(data->ec, Qnil, data->sym, val);
4058 }
4059 return val;
4060}
4061
4062static VALUE
4063ractor_local_value_store_if_absent(rb_execution_context_t *ec, VALUE self, VALUE sym)
4064{
4065 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
4066 struct ractor_local_storage_store_data data = {
4067 .ec = ec,
4068 .sym = sym,
4069 .id = SYM2ID(rb_to_symbol(sym)),
4070 .tbl = cr->idkey_local_storage,
4071 };
4072 VALUE val;
4073
4074 if (data.tbl == NULL) {
4075 data.tbl = cr->idkey_local_storage = rb_id_table_create(2);
4076 }
4077 else if (rb_id_table_lookup(data.tbl, data.id, &val)) {
4078 // already set
4079 return val;
4080 }
4081
4082 if (!cr->local_storage_store_lock) {
4083 cr->local_storage_store_lock = rb_mutex_new();
4084 }
4085
4086 return rb_mutex_synchronize(cr->local_storage_store_lock, ractor_local_value_store_i, (VALUE)&data);
4087}
4088
4089// shareable_proc
4090
4091static VALUE
4092ractor_shareable_proc(rb_execution_context_t *ec, VALUE replace_self, bool is_lambda)
4093{
4094 if (!rb_ractor_shareable_p(replace_self)) {
4095 rb_raise(rb_eRactorIsolationError, "self should be shareable: %" PRIsVALUE, replace_self);
4096 }
4097 else {
4098 VALUE proc = is_lambda ? rb_block_lambda() : rb_block_proc();
4099 return rb_proc_ractor_make_shareable(rb_proc_dup(proc), replace_self);
4100 }
4101}
4102
4103// Ractor#require
4104
4106 VALUE port;
4107 bool raised;
4108
4109 union {
4110 struct {
4111 VALUE feature;
4112 } require;
4113
4114 struct {
4115 VALUE module;
4116 ID name;
4117 } autoload;
4118 } as;
4119
4120 bool silent;
4121};
4122
4123RUBY_REFERENCES(cross_ractor_require_refs) = {
4124 RUBY_REF_EDGE(struct cross_ractor_require, port),
4125 RUBY_REF_EDGE(struct cross_ractor_require, as.require.feature),
4126 RUBY_REF_END
4127};
4128
4129static const rb_data_type_t cross_ractor_require_data_type = {
4130 "ractor/cross_ractor_require",
4131 {
4132 RUBY_REFS_LIST_PTR(cross_ractor_require_refs),
4134 NULL, // memsize
4135 NULL, // compact
4136 },
4137 0, 0, RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_DECL_MARKING | RUBY_TYPED_EMBEDDABLE
4138};
4139
4140static VALUE
4141require_body(VALUE crr_obj)
4142{
4143 struct cross_ractor_require *crr;
4144 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
4145 VALUE feature = crr->as.require.feature;
4146
4147 ID require;
4148 CONST_ID(require, "require");
4149
4150 if (crr->silent) {
4151 int rb_require_internal_silent(VALUE fname);
4152 return INT2NUM(rb_require_internal_silent(feature));
4153 }
4154 else {
4155 return rb_funcallv(Qnil, require, 1, &feature);
4156 }
4157}
4158
4159static VALUE
4160require_rescue(VALUE crr_obj, VALUE errinfo)
4161{
4162 struct cross_ractor_require *crr;
4163 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
4164 crr->raised = true;
4165 return errinfo;
4166}
4167
4168static VALUE
4169require_result_send_body(VALUE ary)
4170{
4171 VALUE port = RARRAY_AREF(ary, 0);
4172 VALUE results = RARRAY_AREF(ary, 1);
4173
4174 rb_execution_context_t *ec = GET_EC();
4175
4176 ractor_port_send(ec, port, results, Qfalse);
4177 return Qnil;
4178}
4179
4180static VALUE
4181require_result_send_resuce(VALUE port, VALUE errinfo)
4182{
4183 // TODO: need rescue?
4184 ractor_port_send(GET_EC(), port, errinfo, Qfalse);
4185 return Qnil;
4186}
4187
4188static VALUE
4189ractor_require_protect(VALUE crr_obj, VALUE (*func)(VALUE))
4190{
4191 struct cross_ractor_require *crr;
4192 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
4193
4194 const bool silent = crr->silent;
4195
4196 VALUE debug, errinfo;
4197 if (silent) {
4198 debug = ruby_debug;
4199 errinfo = rb_errinfo();
4200 }
4201
4202 // get normal result or raised exception (with crr->raised == true)
4203 VALUE result = rb_rescue2(func, crr_obj, require_rescue, crr_obj, rb_eException, 0);
4204
4205 if (silent) {
4206 ruby_debug = debug;
4207 rb_set_errinfo(errinfo);
4208 }
4209
4210 rb_rescue2(require_result_send_body,
4211 // [port, [result, raised]]
4212 rb_ary_new_from_args(2, crr->port, rb_ary_new_from_args(2, result, crr->raised ? Qtrue : Qfalse)),
4213 require_result_send_resuce, rb_eException, crr->port);
4214
4215 RB_GC_GUARD(crr_obj);
4216 return Qnil;
4217}
4218
4219static VALUE
4220ractor_require_func(void *crr_obj)
4221{
4222 return ractor_require_protect((VALUE)crr_obj, require_body);
4223}
4224
4225VALUE
4226rb_ractor_require(VALUE feature, bool silent)
4227{
4228 // We're about to block on the main ractor, so if we're holding the global lock we'll deadlock.
4229 ASSERT_vm_unlocking();
4230
4231 struct cross_ractor_require *crr;
4232 VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
4233 RB_OBJ_SET_SHAREABLE(crr_obj); // TODO: internal data?
4234
4235 // Convert feature to proper file path and make it shareable as fstring
4236 RB_OBJ_WRITE(crr_obj, &crr->as.require.feature, rb_fstring(FilePathValue(feature)));
4237 RB_OBJ_WRITE(crr_obj, &crr->port, rb_ractor_make_shareable(ractor_port_new(GET_RACTOR())));
4238 crr->raised = false;
4239 crr->silent = silent;
4240
4241 rb_execution_context_t *ec = GET_EC();
4242 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
4243 rb_ractor_interrupt_exec(main_r, ractor_require_func, (void *)crr_obj, rb_interrupt_exec_flag_value_data);
4244
4245 // wait for require done
4246 VALUE results = ractor_port_receive(ec, crr->port, Qnil);
4247 ractor_port_close(ec, crr->port);
4248
4249 VALUE exc = rb_ary_pop(results);
4250 VALUE result = rb_ary_pop(results);
4251 RB_GC_GUARD(crr_obj);
4252
4253 if (RTEST(exc)) {
4254 rb_exc_raise(result);
4255 }
4256 else {
4257 return result;
4258 }
4259}
4260
4261static VALUE
4262ractor_require(rb_execution_context_t *ec, VALUE self, VALUE feature)
4263{
4264 return rb_ractor_require(feature, false);
4265}
4266
4267static VALUE
4268autoload_load_body(VALUE crr_obj)
4269{
4270 struct cross_ractor_require *crr;
4271 TypedData_Get_Struct(crr_obj, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
4272 return rb_autoload_load(crr->as.autoload.module, crr->as.autoload.name);
4273}
4274
4275static VALUE
4276ractor_autoload_load_func(void *crr_obj)
4277{
4278 return ractor_require_protect((VALUE)crr_obj, autoload_load_body);
4279}
4280
4281VALUE
4282rb_ractor_autoload_load(VALUE module, ID name)
4283{
4284 struct cross_ractor_require *crr;
4285 VALUE crr_obj = TypedData_Make_Struct(0, struct cross_ractor_require, &cross_ractor_require_data_type, crr);
4286 RB_OBJ_SET_SHAREABLE(crr_obj); // TODO: internal data?
4287
4288 RB_OBJ_WRITE(crr_obj, &crr->as.autoload.module, module);
4289 RB_OBJ_WRITE(crr_obj, &crr->as.autoload.name, name);
4290 RB_OBJ_WRITE(crr_obj, &crr->port, rb_ractor_make_shareable(ractor_port_new(GET_RACTOR())));
4291
4292 rb_execution_context_t *ec = GET_EC();
4293 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
4294 rb_ractor_interrupt_exec(main_r, ractor_autoload_load_func, (void *)crr_obj, rb_interrupt_exec_flag_value_data);
4295
4296 // wait for require done
4297 VALUE results = ractor_port_receive(ec, crr->port, Qnil);
4298 ractor_port_close(ec, crr->port);
4299
4300 VALUE exc = rb_ary_pop(results);
4301 VALUE result = rb_ary_pop(results);
4302 RB_GC_GUARD(crr_obj);
4303
4304 if (RTEST(exc)) {
4305 rb_exc_raise(result);
4306 }
4307 else {
4308 return result;
4309 }
4310}
4311
4312VALUE
4313rb_builtin_shareable_proc(rb_execution_context_t *ec, VALUE self, VALUE arg_self)
4314{
4315 return ractor_shareable_proc(ec, arg_self, false);
4316}
4317
4318VALUE
4319rb_builtin_shareable_lambda(rb_execution_context_t *ec, VALUE self, VALUE arg_self)
4320{
4321 return ractor_shareable_proc(ec, arg_self, true);
4322}
4323
4324#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
#define RUBY_ATOMIC_PTR_LOAD(var)
Identical to RUBY_ATOMIC_LOAD, except it expects its arguments are void*.
Definition atomic.h:348
#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:699
static void RB_FL_SET_RAW(VALUE obj, VALUE flags)
This is an implementation detail of RB_FL_SET().
Definition fl_type.h:544
@ RUBY_FL_FREEZE
This flag has something to do with data immutability.
Definition fl_type.h:278
void rb_singleton_class_attached(VALUE klass, VALUE obj)
Attaches a singleton class to its corresponding object.
Definition class.c:1243
#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 FL_SINGLETON
Old name of RUBY_FL_SINGLETON.
Definition fl_type.h:58
#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 INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
Definition fl_type.h:133
#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 ZALLOC
Old name of RB_ZALLOC.
Definition memory.h:402
#define FL_SHAREABLE
Old name of RUBY_FL_SHAREABLE.
Definition fl_type.h:62
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:205
#define ENCODING_GET(obj)
Old name of RB_ENCODING_GET.
Definition encoding.h:109
#define FL_FINALIZE
Old name of RUBY_FL_FINALIZE.
Definition fl_type.h:61
#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 ALLOC_N
Old name of RB_ALLOC_N.
Definition memory.h:399
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
Definition fl_type.h:128
#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 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:65
#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:126
#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:487
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:678
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1457
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1455
VALUE rb_eStopIteration
StopIteration exception.
Definition enumerator.c:196
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:1508
VALUE rb_eException
Mother of all exceptions.
Definition error.c:1449
VALUE rb_cArray
Array class.
VALUE rb_cObject
Object class.
Definition object.c:60
VALUE rb_obj_alloc(VALUE klass)
Allocates an instance of the given class.
Definition object.c:2274
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:94
VALUE rb_cRactor
Ractor class.
Definition ractor.c:38
VALUE rb_stdin
STDIN constant.
Definition io.c:194
VALUE rb_cHash
Hash class.
Definition hash.c:123
VALUE rb_stderr
STDERR constant.
Definition io.c:194
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
Definition globals.h:174
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:240
VALUE rb_cBasicObject
BasicObject class.
Definition object.c:58
VALUE rb_class_real(VALUE klass)
Finds a "real" class.
Definition object.c:231
VALUE rb_obj_freeze(VALUE obj)
Same as RB_OBJ_FREEZE(), but returns the given object.
Definition object.c:1315
VALUE rb_stdout
STDOUT constant.
Definition io.c:194
VALUE rb_cString
String class.
Definition string.c:85
#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:504
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:492
Encoding relates APIs.
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1148
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.
Definition vm_eval.c:1106
VALUE rb_ary_new_capa(rb_len_t capa)
Identical to rb_ary_new(), except it additionally specifies how many rooms of objects it should alloc...
VALUE rb_ary_resize(VALUE ary, rb_len_t len)
Expands or shrinks the passed array to the passed length.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_pop(VALUE ary)
Destructively deletes an element from the end of the passed array and returns what was deleted.
VALUE rb_ary_hidden_new(rb_len_t 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_ary_new_from_values(rb_len_t n, const VALUE *elts)
Identical to rb_ary_new_from_args(), except how objects are passed.
VALUE rb_marshal_load(VALUE port)
Deserialises a previous output of rb_marshal_dump() into a network of objects.
Definition marshal.c:2735
VALUE rb_block_proc(void)
Constructs a Proc object from implicitly passed components.
Definition proc.c:1580
VALUE rb_block_lambda(void)
Identical to rb_proc_new(), except it returns a lambda.
Definition proc.c:1599
VALUE rb_obj_is_proc(VALUE recv)
Queries if the given object is a proc.
Definition proc.c:386
int rb_reg_options(VALUE re)
Queries the options of the passed regular expression.
Definition re.c:4624
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition string.c:1569
VALUE rb_str_freeze(VALUE str)
This is the implementation of String#freeze.
Definition string.c:3416
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.
Definition variable.c:2150
VALUE rb_autoload_load(VALUE space, ID name)
Kicks the autoload procedure as if it was "touched".
Definition variable.c:3303
VALUE rb_class_name(VALUE obj)
Queries the name of the given object's class.
Definition variable.c:516
void rb_free_generic_ivar(VALUE obj)
Frees the list of instance variables.
Definition variable.c:1448
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1865
int rb_obj_respond_to(VALUE obj, ID mid, int private_p)
Identical to rb_respond_to(), except it additionally takes the visibility parameter.
Definition vm_method.c:3699
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:285
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition symbol.c:1289
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
Definition string.c:14235
int capa
Designed capacity of the buffer.
Definition io.h:11
int rb_io_fptr_finalize(rb_io_t *fptr)
Destroys the given IO.
Definition io.c:5896
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:3841
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:2023
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:3948
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:3960
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:3852
#define RB_OBJ_SET_SHAREABLE(obj)
Wrapper of rb_obj_set_shareable().
Definition ractor.h:290
VALUE rb_ractor_stdin(void)
Queries the standard input of the current Ractor that is calling this function.
Definition ractor.c:1456
static bool rb_ractor_shareable_p(VALUE obj)
Queries if multiple Ractors can share the passed object or not.
Definition ractor.h:269
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:1525
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:3942
bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
Queries the key.
Definition ractor.c:3931
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
Definition ractor.h:255
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
Definition ractor.c:2012
VALUE rb_obj_set_shareable(VALUE obj)
Marks the passed object as shareable, without any check.
Definition ractor.c:1599
rb_ractor_local_key_t rb_ractor_local_storage_value_newkey(void)
Issues a new key.
Definition ractor.c:3861
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:1513
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:1501
VALUE rb_ractor_local_storage_value(rb_ractor_local_key_t key)
Queries the key.
Definition ractor.c:3919
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1403
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 MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:372
#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.
void rb_copy_generic_ivar(VALUE clone, VALUE obj)
Copies the list of instance variables.
Definition variable.c:2344
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:50
#define RARRAY(obj)
Convenient casting macro.
Definition rarray.h:44
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:280
#define RARRAY_AREF(a, i)
References an object in an array.
Definition rarray.h:421
static void RARRAY_ASET(VALUE ary, rb_len_t i, VALUE v)
Assigns an object in an array.
Definition rarray.h:385
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
Definition rarray.h:51
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
Definition rbasic.h:166
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
#define RUBY_DEFAULT_FREE
This is a value you can set to RData::dfree.
Definition rdata.h:56
#define RFILE(obj)
Convenient casting macro.
Definition rfile.h:50
#define RHASH_SET_IFNONE(h, ifnone)
Destructively updates the default value of the hash.
Definition rhash.h:80
#define RHASH_IFNONE(h)
Definition rhash.h:47
#define RHASH_SIZE(h)
Queries the size of the hash.
Definition rhash.h:57
#define RMATCH(obj)
Convenient casting macro.
Definition rmatch.h:37
static VALUE RREGEXP_SRC(VALUE rexp)
Convenient getter function.
Definition rregexp.h:102
#define RSTRING(obj)
Convenient casting macro.
Definition rstring.h:41
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
static VALUE RSTRUCT_SET(VALUE st, int k, VALUE v)
Resembles Struct#[]=.
Definition rstruct.h:92
static VALUE RSTRUCT_GET(VALUE st, int k)
Resembles Struct#[].
Definition rstruct.h:102
static rb_len_t RSTRUCT_LEN(VALUE st)
Returns the number of struct members.
Definition rstruct.h:82
#define RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
Definition rtypeddata.h:122
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:773
#define DATA_PTR(obj)
Convenient casting macro for backward compatibility.
Definition rtypeddata.h:439
static const rb_data_type_t * RTYPEDDATA_TYPE(VALUE obj)
Queries for the type of given object.
Definition rtypeddata.h:692
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:557
#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:604
#define FilePathValue(v)
Ensures that the parameter object is a path.
Definition ruby.h:90
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
#define RTEST
This is an old name of RB_TEST.
Ruby object's base components.
Definition rbasic.h:69
Regular expression execution context.
Definition rmatch.h:79
VALUE regexp
The expression of this match.
Definition rmatch.h:92
VALUE str
The target string that the match was made against.
Definition rmatch.h:87
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:242
VALUE ecopts
Flags as Ruby hash.
Definition io.h:152
Ruby's IO, metadata and buffers.
Definition io.h:295
struct rb_io_encoding encs
Decomposed encoding flags.
Definition io.h:348
VALUE self
The IO's Ruby level counterpart.
Definition io.h:298
VALUE write_lock
This is a Ruby level mutex.
Definition io.h:400
VALUE timeout
The timeout associated with this IO when performing blocking operations.
Definition io.h:406
VALUE writeconv_pre_ecopts
Value of ::rb_io_t::rb_io_enc_t::ecopts stored right before initialising rb_io_t::writeconv.
Definition io.h:390
VALUE tied_io_for_writing
Duplex IO object, if set.
Definition io.h:345
VALUE writeconv_asciicompat
This is, when set, an instance of rb_cString which holds the "common" encoding.
Definition io.h:372
VALUE pathv
pathname for file
Definition io.h:322
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_signal(rb_nativethread_cond_t *cond)
Signals a condition variable.
long rb_len_t
A signed integer type for lengths and indices of objects such as String and Array.
Definition value.h:142
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