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