Ruby: ractor.c Source File

Ruby 3.4.9p82 (2026-03-11 revision 76cca827ab52ab1d346a728f068d5b8da3e2952b)

ractor.c
1// Ractor implementation
2
3#include "ruby/ruby.h"
4#include "ruby/thread.h"
5#include "ruby/ractor.h"
6#include "ruby/thread_native.h"
7#include "vm_core.h"
8#include "eval_intern.h"
9#include "vm_sync.h"
10#include "ractor_core.h"
11#include "internal/complex.h"
12#include "internal/error.h"
13#include "internal/gc.h"
14#include "internal/hash.h"
15#include "internal/ractor.h"
16#include "internal/rational.h"
17#include "internal/struct.h"
18#include "internal/thread.h"
19#include "variable.h"
20#include "yjit.h"
21#include "rjit.h"
22
23 VALUE rb_cRactor;
24static VALUE rb_cRactorSelector;
25
26 VALUE rb_eRactorUnsafeError;
27 VALUE rb_eRactorIsolationError;
28static VALUE rb_eRactorError;
29static VALUE rb_eRactorRemoteError;
30static VALUE rb_eRactorMovedError;
31static VALUE rb_eRactorClosedError;
32static VALUE rb_cRactorMovedObject;
33
34static void vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line);
35
36// Ractor locking
37
38static void
39ASSERT_ractor_unlocking(rb_ractor_t *r)
40{
41#if RACTOR_CHECK_MODE > 0
42 // GET_EC is NULL in an RJIT worker
43 if (rb_current_execution_context(false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) {
44 rb_bug("recursive ractor locking");
45 }
46#endif
47}
48
49static void
50ASSERT_ractor_locking(rb_ractor_t *r)
51{
52#if RACTOR_CHECK_MODE > 0
53 // GET_EC is NULL in an RJIT worker
54 if (rb_current_execution_context(false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) {
55 rp(r->sync.locked_by);
56 rb_bug("ractor lock is not acquired.");
57 }
58#endif
59}
60
61static void
62ractor_lock(rb_ractor_t *r, const char *file, int line)
63{
64 RUBY_DEBUG_LOG2(file, line, "locking r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
65
66 ASSERT_ractor_unlocking(r);
67 rb_native_mutex_lock(&r->sync.lock);
68
69#if RACTOR_CHECK_MODE > 0
70 if (rb_current_execution_context(false) != NULL) { // GET_EC is NULL in an RJIT worker
71 rb_ractor_t *cr = rb_current_ractor_raw(false);
72 r->sync.locked_by = cr ? rb_ractor_self(cr) : Qundef;
73 }
74#endif
75
76 RUBY_DEBUG_LOG2(file, line, "locked r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
77}
78
79static void
80ractor_lock_self(rb_ractor_t *cr, const char *file, int line)
81{
82 VM_ASSERT(cr == GET_RACTOR());
83#if RACTOR_CHECK_MODE > 0
84 VM_ASSERT(cr->sync.locked_by != cr->pub.self);
85#endif
86 ractor_lock(cr, file, line);
87}
88
89static void
90ractor_unlock(rb_ractor_t *r, const char *file, int line)
91{
92 ASSERT_ractor_locking(r);
93#if RACTOR_CHECK_MODE > 0
94 r->sync.locked_by = Qnil;
95#endif
96 rb_native_mutex_unlock(&r->sync.lock);
97
98 RUBY_DEBUG_LOG2(file, line, "r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
99}
100
101static void
102ractor_unlock_self(rb_ractor_t *cr, const char *file, int line)
103{
104 VM_ASSERT(cr == GET_RACTOR());
105#if RACTOR_CHECK_MODE > 0
106 VM_ASSERT(cr->sync.locked_by == cr->pub.self);
107#endif
108 ractor_unlock(cr, file, line);
109}
110
111#define RACTOR_LOCK(r) ractor_lock(r, __FILE__, __LINE__)
112#define RACTOR_UNLOCK(r) ractor_unlock(r, __FILE__, __LINE__)
113#define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
114#define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
115
116void
117rb_ractor_lock_self(rb_ractor_t *r)
118{
119 RACTOR_LOCK_SELF(r);
120}
121
122void
123rb_ractor_unlock_self(rb_ractor_t *r)
124{
125 RACTOR_UNLOCK_SELF(r);
126}
127
128// Ractor status
129
130static const char *
131ractor_status_str(enum ractor_status status)
132{
133 switch (status) {
134 case ractor_created: return "created";
135 case ractor_running: return "running";
136 case ractor_blocking: return "blocking";
137 case ractor_terminated: return "terminated";
138 }
139 rb_bug("unreachable");
140}
141
142static void
143ractor_status_set(rb_ractor_t *r, enum ractor_status status)
144{
145 RUBY_DEBUG_LOG("r:%u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
146
147 // check 1
148 if (r->status_ != ractor_created) {
149 VM_ASSERT(r == GET_RACTOR()); // only self-modification is allowed.
150 ASSERT_vm_locking();
151 }
152
153 // check2: transition check. assume it will be vanished on non-debug build.
154 switch (r->status_) {
155 case ractor_created:
156 VM_ASSERT(status == ractor_blocking);
157 break;
158 case ractor_running:
159 VM_ASSERT(status == ractor_blocking||
160 status == ractor_terminated);
161 break;
162 case ractor_blocking:
163 VM_ASSERT(status == ractor_running);
164 break;
165 case ractor_terminated:
166 rb_bug("unreachable");
167 break;
168 }
169
170 r->status_ = status;
171}
172
173static bool
174ractor_status_p(rb_ractor_t *r, enum ractor_status status)
175{
176 return rb_ractor_status_p(r, status);
177}
178
179// Ractor data/mark/free
180
181static struct rb_ractor_basket *ractor_queue_at(rb_ractor_t *r, struct rb_ractor_queue *rq, int i);
182static void ractor_local_storage_mark(rb_ractor_t *r);
183static void ractor_local_storage_free(rb_ractor_t *r);
184
185static void
186ractor_queue_mark(struct rb_ractor_queue *rq)
187{
188 for (int i=0; i<rq->cnt; i++) {
189 struct rb_ractor_basket *b = ractor_queue_at(NULL, rq, i);
190 rb_gc_mark(b->sender);
191
192 switch (b->type.e) {
193 case basket_type_yielding:
194 case basket_type_take_basket:
195 case basket_type_deleted:
196 case basket_type_reserved:
197 // ignore
198 break;
199 default:
200 rb_gc_mark(b->p.send.v);
201 }
202 }
203}
204
205static void
206ractor_mark(void *ptr)
207{
208 rb_ractor_t *r = (rb_ractor_t *)ptr;
209
210 ractor_queue_mark(&r->sync.recv_queue);
211 ractor_queue_mark(&r->sync.takers_queue);
212
213 rb_gc_mark(r->receiving_mutex);
214
215 rb_gc_mark(r->loc);
216 rb_gc_mark(r->name);
217 rb_gc_mark(r->r_stdin);
218 rb_gc_mark(r->r_stdout);
219 rb_gc_mark(r->r_stderr);
220 rb_gc_mark(r->verbose);
221 rb_gc_mark(r->debug);
222 rb_hook_list_mark(&r->pub.hooks);
223
224 if (r->threads.cnt > 0) {
225 rb_thread_t *th = 0;
226 ccan_list_for_each(&r->threads.set, th, lt_node) {
227 VM_ASSERT(th != NULL);
228 rb_gc_mark(th->self);
229 }
230 }
231
232 ractor_local_storage_mark(r);
233}
234
235static void
236ractor_queue_free(struct rb_ractor_queue *rq)
237{
238 free(rq->baskets);
239}
240
241static void
242ractor_free(void *ptr)
243{
244 rb_ractor_t *r = (rb_ractor_t *)ptr;
245 RUBY_DEBUG_LOG("free r:%d", rb_ractor_id(r));
246 rb_native_mutex_destroy(&r->sync.lock);
247#ifdef RUBY_THREAD_WIN32_H
248 rb_native_cond_destroy(&r->sync.cond);
249#endif
250 ractor_queue_free(&r->sync.recv_queue);
251 ractor_queue_free(&r->sync.takers_queue);
252 ractor_local_storage_free(r);
253 rb_hook_list_free(&r->pub.hooks);
254
255 if (r->newobj_cache) {
256 RUBY_ASSERT(r == ruby_single_main_ractor);
257
258 rb_gc_ractor_cache_free(r->newobj_cache);
259 r->newobj_cache = NULL;
260 }
261
262 ruby_xfree(r);
263}
264
265static size_t
266ractor_queue_memsize(const struct rb_ractor_queue *rq)
267{
268 return sizeof(struct rb_ractor_basket) * rq->size;
269}
270
271static size_t
272ractor_memsize(const void *ptr)
273{
274 rb_ractor_t *r = (rb_ractor_t *)ptr;
275
276 // TODO: more correct?
277 return sizeof(rb_ractor_t) +
278 ractor_queue_memsize(&r->sync.recv_queue) +
279 ractor_queue_memsize(&r->sync.takers_queue);
280}
281
282static const rb_data_type_t ractor_data_type = {
283 "ractor",
284 {
285 ractor_mark,
286 ractor_free,
287 ractor_memsize,
288 NULL, // update
289 },
290 0, 0, RUBY_TYPED_FREE_IMMEDIATELY /* | RUBY_TYPED_WB_PROTECTED */
291};
292
293bool
294rb_ractor_p(VALUE gv)
295{
296 if (rb_typeddata_is_kind_of(gv, &ractor_data_type)) {
297 return true;
298 }
299 else {
300 return false;
301 }
302}
303
304static inline rb_ractor_t *
305RACTOR_PTR(VALUE self)
306{
307 VM_ASSERT(rb_ractor_p(self));
308 rb_ractor_t *r = DATA_PTR(self);
309 return r;
310}
311
312static rb_atomic_t ractor_last_id;
313
314#if RACTOR_CHECK_MODE > 0
315uint32_t
316rb_ractor_current_id(void)
317{
318 if (GET_THREAD()->ractor == NULL) {
319 return 1; // main ractor
320 }
321 else {
322 return rb_ractor_id(GET_RACTOR());
323 }
324}
325#endif
326
327// Ractor queue
328
329static void
330ractor_queue_setup(struct rb_ractor_queue *rq)
331{
332 rq->size = 2;
333 rq->cnt = 0;
334 rq->start = 0;
335 rq->baskets = malloc(sizeof(struct rb_ractor_basket) * rq->size);
336}
337
338static struct rb_ractor_basket *
339ractor_queue_head(rb_ractor_t *r, struct rb_ractor_queue *rq)
340{
341 if (r != NULL) ASSERT_ractor_locking(r);
342 return &rq->baskets[rq->start];
343}
344
345static struct rb_ractor_basket *
346ractor_queue_at(rb_ractor_t *r, struct rb_ractor_queue *rq, int i)
347{
348 if (r != NULL) ASSERT_ractor_locking(r);
349 return &rq->baskets[(rq->start + i) % rq->size];
350}
351
352static void
353ractor_queue_advance(rb_ractor_t *r, struct rb_ractor_queue *rq)
354{
355 ASSERT_ractor_locking(r);
356
357 if (rq->reserved_cnt == 0) {
358 rq->cnt--;
359 rq->start = (rq->start + 1) % rq->size;
360 rq->serial++;
361 }
362 else {
363 ractor_queue_at(r, rq, 0)->type.e = basket_type_deleted;
364 }
365}
366
367static bool
368ractor_queue_skip_p(rb_ractor_t *r, struct rb_ractor_queue *rq, int i)
369{
370 struct rb_ractor_basket *b = ractor_queue_at(r, rq, i);
371 return basket_type_p(b, basket_type_deleted) ||
372 basket_type_p(b, basket_type_reserved);
373}
374
375static void
376ractor_queue_compact(rb_ractor_t *r, struct rb_ractor_queue *rq)
377{
378 ASSERT_ractor_locking(r);
379
380 while (rq->cnt > 0 && basket_type_p(ractor_queue_at(r, rq, 0), basket_type_deleted)) {
381 ractor_queue_advance(r, rq);
382 }
383}
384
385static bool
386ractor_queue_empty_p(rb_ractor_t *r, struct rb_ractor_queue *rq)
387{
388 ASSERT_ractor_locking(r);
389
390 if (rq->cnt == 0) {
391 return true;
392 }
393
394 ractor_queue_compact(r, rq);
395
396 for (int i=0; i<rq->cnt; i++) {
397 if (!ractor_queue_skip_p(r, rq, i)) {
398 return false;
399 }
400 }
401
402 return true;
403}
404
405static bool
406ractor_queue_deq(rb_ractor_t *r, struct rb_ractor_queue *rq, struct rb_ractor_basket *basket)
407{
408 ASSERT_ractor_locking(r);
409
410 for (int i=0; i<rq->cnt; i++) {
411 if (!ractor_queue_skip_p(r, rq, i)) {
412 struct rb_ractor_basket *b = ractor_queue_at(r, rq, i);
413 *basket = *b;
414
415 // remove from queue
416 b->type.e = basket_type_deleted;
417 ractor_queue_compact(r, rq);
418 return true;
419 }
420 }
421
422 return false;
423}
424
425static void
426ractor_queue_enq(rb_ractor_t *r, struct rb_ractor_queue *rq, struct rb_ractor_basket *basket)
427{
428 ASSERT_ractor_locking(r);
429
430 if (rq->size <= rq->cnt) {
431 rq->baskets = realloc(rq->baskets, sizeof(struct rb_ractor_basket) * rq->size * 2);
432 for (int i=rq->size - rq->start; i<rq->cnt; i++) {
433 rq->baskets[i + rq->start] = rq->baskets[i + rq->start - rq->size];
434 }
435 rq->size *= 2;
436 }
437 rq->baskets[(rq->start + rq->cnt++) % rq->size] = *basket;
438 // fprintf(stderr, "%s %p->cnt:%d\n", RUBY_FUNCTION_NAME_STRING, (void *)rq, rq->cnt);
439}
440
441static void
442ractor_queue_delete(rb_ractor_t *r, struct rb_ractor_queue *rq, struct rb_ractor_basket *basket)
443{
444 basket->type.e = basket_type_deleted;
445}
446
447// Ractor basket
448
449static VALUE ractor_reset_belonging(VALUE obj); // in this file
450
451static VALUE
452ractor_basket_value(struct rb_ractor_basket *b)
453{
454 switch (b->type.e) {
455 case basket_type_ref:
456 break;
457 case basket_type_copy:
458 case basket_type_move:
459 case basket_type_will:
460 b->type.e = basket_type_ref;
461 b->p.send.v = ractor_reset_belonging(b->p.send.v);
462 break;
463 default:
464 rb_bug("unreachable");
465 }
466
467 return b->p.send.v;
468}
469
470static VALUE
471ractor_basket_accept(struct rb_ractor_basket *b)
472{
473 VALUE v = ractor_basket_value(b);
474
475 if (b->p.send.exception) {
476 VALUE cause = v;
477 VALUE err = rb_exc_new_cstr(rb_eRactorRemoteError, "thrown by remote Ractor.");
478 rb_ivar_set(err, rb_intern("@ractor"), b->sender);
479 rb_ec_setup_exception(NULL, err, cause);
480 rb_exc_raise(err);
481 }
482
483 return v;
484}
485
486// Ractor synchronizations
487
488#if USE_RUBY_DEBUG_LOG
489static const char *
490wait_status_str(enum rb_ractor_wait_status wait_status)
491{
492 switch ((int)wait_status) {
493 case wait_none: return "none";
494 case wait_receiving: return "receiving";
495 case wait_taking: return "taking";
496 case wait_yielding: return "yielding";
497 case wait_receiving|wait_taking: return "receiving|taking";
498 case wait_receiving|wait_yielding: return "receiving|yielding";
499 case wait_taking|wait_yielding: return "taking|yielding";
500 case wait_receiving|wait_taking|wait_yielding: return "receiving|taking|yielding";
501 }
502 rb_bug("unreachable");
503}
504
505static const char *
506wakeup_status_str(enum rb_ractor_wakeup_status wakeup_status)
507{
508 switch (wakeup_status) {
509 case wakeup_none: return "none";
510 case wakeup_by_send: return "by_send";
511 case wakeup_by_yield: return "by_yield";
512 case wakeup_by_take: return "by_take";
513 case wakeup_by_close: return "by_close";
514 case wakeup_by_interrupt: return "by_interrupt";
515 case wakeup_by_retry: return "by_retry";
516 }
517 rb_bug("unreachable");
518}
519
520static const char *
521basket_type_name(enum rb_ractor_basket_type type)
522{
523 switch (type) {
524 case basket_type_none: return "none";
525 case basket_type_ref: return "ref";
526 case basket_type_copy: return "copy";
527 case basket_type_move: return "move";
528 case basket_type_will: return "will";
529 case basket_type_deleted: return "deleted";
530 case basket_type_reserved: return "reserved";
531 case basket_type_take_basket: return "take_basket";
532 case basket_type_yielding: return "yielding";
533 }
534 VM_ASSERT(0);
535 return NULL;
536}
537#endif // USE_RUBY_DEBUG_LOG
538
539static bool
540ractor_sleeping_by(const rb_ractor_t *r, enum rb_ractor_wait_status wait_status)
541{
542 return (r->sync.wait.status & wait_status) && r->sync.wait.wakeup_status == wakeup_none;
543}
544
545#ifdef RUBY_THREAD_PTHREAD_H
546// thread_*.c
547void rb_ractor_sched_wakeup(rb_ractor_t *r);
548#else
549
550static void
551rb_ractor_sched_wakeup(rb_ractor_t *r)
552{
553 rb_native_cond_broadcast(&r->sync.cond);
554}
555#endif
556
557
558static bool
559ractor_wakeup(rb_ractor_t *r, enum rb_ractor_wait_status wait_status, enum rb_ractor_wakeup_status wakeup_status)
560{
561 ASSERT_ractor_locking(r);
562
563 RUBY_DEBUG_LOG("r:%u wait_by:%s -> wait:%s wakeup:%s",
564 rb_ractor_id(r),
565 wait_status_str(r->sync.wait.status),
566 wait_status_str(wait_status),
567 wakeup_status_str(wakeup_status));
568
569 if (ractor_sleeping_by(r, wait_status)) {
570 r->sync.wait.wakeup_status = wakeup_status;
571 rb_ractor_sched_wakeup(r);
572 return true;
573 }
574 else {
575 return false;
576 }
577}
578
579static void
580ractor_sleep_interrupt(void *ptr)
581{
582 rb_ractor_t *r = ptr;
583
584 RACTOR_LOCK(r);
585 {
586 ractor_wakeup(r, wait_receiving | wait_taking | wait_yielding, wakeup_by_interrupt);
587 }
588 RACTOR_UNLOCK(r);
589}
590
591typedef void (*ractor_sleep_cleanup_function)(rb_ractor_t *cr, void *p);
592
593static void
594ractor_check_ints(rb_execution_context_t *ec, rb_ractor_t *cr, ractor_sleep_cleanup_function cf_func, void *cf_data)
595{
596 if (cr->sync.wait.status != wait_none) {
597 enum rb_ractor_wait_status prev_wait_status = cr->sync.wait.status;
598 cr->sync.wait.status = wait_none;
599 cr->sync.wait.wakeup_status = wakeup_by_interrupt;
600
601 RACTOR_UNLOCK(cr);
602 {
603 if (cf_func) {
604 enum ruby_tag_type state;
605 EC_PUSH_TAG(ec);
606 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
607 rb_ec_check_ints(ec);
608 }
609 EC_POP_TAG();
610
611 if (state) {
612 (*cf_func)(cr, cf_data);
613 EC_JUMP_TAG(ec, state);
614 }
615 }
616 else {
617 rb_ec_check_ints(ec);
618 }
619 }
620
621 // reachable?
622 RACTOR_LOCK(cr);
623 cr->sync.wait.status = prev_wait_status;
624 }
625}
626
627#ifdef RUBY_THREAD_PTHREAD_H
628void rb_ractor_sched_sleep(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_function_t *ubf);
629#else
630
631// win32
632static void
633ractor_cond_wait(rb_ractor_t *r)
634{
635#if RACTOR_CHECK_MODE > 0
636 VALUE locked_by = r->sync.locked_by;
637 r->sync.locked_by = Qnil;
638#endif
639 rb_native_cond_wait(&r->sync.cond, &r->sync.lock);
640
641#if RACTOR_CHECK_MODE > 0
642 r->sync.locked_by = locked_by;
643#endif
644}
645
646static void *
647ractor_sleep_wo_gvl(void *ptr)
648{
649 rb_ractor_t *cr = ptr;
650 RACTOR_LOCK_SELF(cr);
651 {
652 VM_ASSERT(cr->sync.wait.status != wait_none);
653 if (cr->sync.wait.wakeup_status == wakeup_none) {
654 ractor_cond_wait(cr);
655 }
656 cr->sync.wait.status = wait_none;
657 }
658 RACTOR_UNLOCK_SELF(cr);
659 return NULL;
660}
661
662static void
663rb_ractor_sched_sleep(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_function_t *ubf)
664{
665 RACTOR_UNLOCK(cr);
666 {
667 rb_nogvl(ractor_sleep_wo_gvl, cr,
668 ubf, cr,
669 RB_NOGVL_UBF_ASYNC_SAFE | RB_NOGVL_INTR_FAIL);
670 }
671 RACTOR_LOCK(cr);
672}
673#endif
674
675static enum rb_ractor_wakeup_status
676ractor_sleep_with_cleanup(rb_execution_context_t *ec, rb_ractor_t *cr, enum rb_ractor_wait_status wait_status,
677 ractor_sleep_cleanup_function cf_func, void *cf_data)
678{
679 enum rb_ractor_wakeup_status wakeup_status;
680 VM_ASSERT(GET_RACTOR() == cr);
681
682 // TODO: multi-threads
683 VM_ASSERT(cr->sync.wait.status == wait_none);
684 VM_ASSERT(wait_status != wait_none);
685 cr->sync.wait.status = wait_status;
686 cr->sync.wait.wakeup_status = wakeup_none;
687
688 // fprintf(stderr, "%s r:%p status:%s, wakeup_status:%s\n", RUBY_FUNCTION_NAME_STRING, (void *)cr,
689 // wait_status_str(cr->sync.wait.status), wakeup_status_str(cr->sync.wait.wakeup_status));
690
691 RUBY_DEBUG_LOG("sleep by %s", wait_status_str(wait_status));
692
693 while (cr->sync.wait.wakeup_status == wakeup_none) {
694 rb_ractor_sched_sleep(ec, cr, ractor_sleep_interrupt);
695 ractor_check_ints(ec, cr, cf_func, cf_data);
696 }
697
698 cr->sync.wait.status = wait_none;
699
700 // TODO: multi-thread
701 wakeup_status = cr->sync.wait.wakeup_status;
702 cr->sync.wait.wakeup_status = wakeup_none;
703
704 RUBY_DEBUG_LOG("wakeup %s", wakeup_status_str(wakeup_status));
705
706 return wakeup_status;
707}
708
709static enum rb_ractor_wakeup_status
710ractor_sleep(rb_execution_context_t *ec, rb_ractor_t *cr, enum rb_ractor_wait_status wait_status)
711{
712 return ractor_sleep_with_cleanup(ec, cr, wait_status, 0, NULL);
713}
714
715// Ractor.receive
716
717static void
718ractor_recursive_receive_if(rb_ractor_t *r)
719{
720 if (r->receiving_mutex && rb_mutex_owned_p(r->receiving_mutex)) {
721 rb_raise(rb_eRactorError, "can not call receive/receive_if recursively");
722 }
723}
724
725static VALUE
726ractor_try_receive(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_queue *rq)
727{
728 struct rb_ractor_basket basket;
729 ractor_recursive_receive_if(cr);
730 bool received = false;
731
732 RACTOR_LOCK_SELF(cr);
733 {
734 RUBY_DEBUG_LOG("rq->cnt:%d", rq->cnt);
735 received = ractor_queue_deq(cr, rq, &basket);
736 }
737 RACTOR_UNLOCK_SELF(cr);
738
739 if (!received) {
740 if (cr->sync.incoming_port_closed) {
741 rb_raise(rb_eRactorClosedError, "The incoming port is already closed");
742 }
743 return Qundef;
744 }
745 else {
746 return ractor_basket_accept(&basket);
747 }
748}
749
750static void
751ractor_wait_receive(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_queue *rq)
752{
753 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
754 ractor_recursive_receive_if(cr);
755
756 RACTOR_LOCK(cr);
757 {
758 while (ractor_queue_empty_p(cr, rq) && !cr->sync.incoming_port_closed) {
759 ractor_sleep(ec, cr, wait_receiving);
760 }
761 }
762 RACTOR_UNLOCK(cr);
763}
764
765static VALUE
766ractor_receive(rb_execution_context_t *ec, rb_ractor_t *cr)
767{
768 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
769 VALUE v;
770 struct rb_ractor_queue *rq = &cr->sync.recv_queue;
771
772 while (UNDEF_P(v = ractor_try_receive(ec, cr, rq))) {
773 ractor_wait_receive(ec, cr, rq);
774 }
775
776 return v;
777}
778
779#if 0
780static void
781rq_dump(struct rb_ractor_queue *rq)
782{
783 bool bug = false;
784 for (int i=0; i<rq->cnt; i++) {
785 struct rb_ractor_basket *b = ractor_queue_at(NULL, rq, i);
786 fprintf(stderr, "%d (start:%d) type:%s %p %s\n", i, rq->start, basket_type_name(b->type),
787 (void *)b, RSTRING_PTR(RARRAY_AREF(b->v, 1)));
788 if (basket_type_p(b, basket_type_reserved) bug = true;
789 }
790 if (bug) rb_bug("!!");
791}
792#endif
793
794 struct receive_block_data {
795 rb_ractor_t *cr;
796 struct rb_ractor_queue *rq;
797 VALUE v;
798 int index;
799 bool success;
800};
801
802static void
803ractor_receive_if_lock(rb_ractor_t *cr)
804{
805 VALUE m = cr->receiving_mutex;
806 if (m == Qfalse) {
807 m = cr->receiving_mutex = rb_mutex_new();
808 }
809 rb_mutex_lock(m);
810}
811
812static VALUE
813receive_if_body(VALUE ptr)
814{
815 struct receive_block_data *data = (struct receive_block_data *)ptr;
816
817 ractor_receive_if_lock(data->cr);
818 VALUE block_result = rb_yield(data->v);
819 rb_ractor_t *cr = data->cr;
820
821 RACTOR_LOCK_SELF(cr);
822 {
823 struct rb_ractor_basket *b = ractor_queue_at(cr, data->rq, data->index);
824 VM_ASSERT(basket_type_p(b, basket_type_reserved));
825 data->rq->reserved_cnt--;
826
827 if (RTEST(block_result)) {
828 ractor_queue_delete(cr, data->rq, b);
829 ractor_queue_compact(cr, data->rq);
830 }
831 else {
832 b->type.e = basket_type_ref;
833 }
834 }
835 RACTOR_UNLOCK_SELF(cr);
836
837 data->success = true;
838
839 if (RTEST(block_result)) {
840 return data->v;
841 }
842 else {
843 return Qundef;
844 }
845}
846
847static VALUE
848receive_if_ensure(VALUE v)
849{
850 struct receive_block_data *data = (struct receive_block_data *)v;
851 rb_ractor_t *cr = data->cr;
852
853 if (!data->success) {
854 RACTOR_LOCK_SELF(cr);
855 {
856 struct rb_ractor_basket *b = ractor_queue_at(cr, data->rq, data->index);
857 VM_ASSERT(basket_type_p(b, basket_type_reserved));
858 b->type.e = basket_type_deleted;
859 data->rq->reserved_cnt--;
860 }
861 RACTOR_UNLOCK_SELF(cr);
862 }
863
864 rb_mutex_unlock(cr->receiving_mutex);
865 return Qnil;
866}
867
868static VALUE
869ractor_receive_if(rb_execution_context_t *ec, VALUE crv, VALUE b)
870{
871 if (!RTEST(b)) rb_raise(rb_eArgError, "no block given");
872
873 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
874 unsigned int serial = (unsigned int)-1;
875 int index = 0;
876 struct rb_ractor_queue *rq = &cr->sync.recv_queue;
877
878 while (1) {
879 VALUE v = Qundef;
880
881 ractor_wait_receive(ec, cr, rq);
882
883 RACTOR_LOCK_SELF(cr);
884 {
885 if (serial != rq->serial) {
886 serial = rq->serial;
887 index = 0;
888 }
889
890 // check newer version
891 for (int i=index; i<rq->cnt; i++) {
892 if (!ractor_queue_skip_p(cr, rq, i)) {
893 struct rb_ractor_basket *b = ractor_queue_at(cr, rq, i);
894 v = ractor_basket_value(b);
895 b->type.e = basket_type_reserved;
896 rq->reserved_cnt++;
897 index = i;
898 break;
899 }
900 }
901 }
902 RACTOR_UNLOCK_SELF(cr);
903
904 if (!UNDEF_P(v)) {
905 struct receive_block_data data = {
906 .cr = cr,
907 .rq = rq,
908 .v = v,
909 .index = index,
910 .success = false,
911 };
912
913 VALUE result = rb_ensure(receive_if_body, (VALUE)&data,
914 receive_if_ensure, (VALUE)&data);
915
916 if (!UNDEF_P(result)) return result;
917 index++;
918 }
919
920 RUBY_VM_CHECK_INTS(ec);
921 }
922}
923
924static void
925ractor_send_basket(rb_execution_context_t *ec, rb_ractor_t *r, struct rb_ractor_basket *b)
926{
927 bool closed = false;
928
929 RACTOR_LOCK(r);
930 {
931 if (r->sync.incoming_port_closed) {
932 closed = true;
933 }
934 else {
935 ractor_queue_enq(r, &r->sync.recv_queue, b);
936 ractor_wakeup(r, wait_receiving, wakeup_by_send);
937 }
938 }
939 RACTOR_UNLOCK(r);
940
941 if (closed) {
942 rb_raise(rb_eRactorClosedError, "The incoming-port is already closed");
943 }
944}
945
946// Ractor#send
947
948static VALUE ractor_move(VALUE obj); // in this file
949static VALUE ractor_copy(VALUE obj); // in this file
950
951static void
952ractor_basket_prepare_contents(VALUE obj, VALUE move, volatile VALUE *pobj, enum rb_ractor_basket_type *ptype)
953{
954 VALUE v;
955 enum rb_ractor_basket_type type;
956
957 if (rb_ractor_shareable_p(obj)) {
958 type = basket_type_ref;
959 v = obj;
960 }
961 else if (!RTEST(move)) {
962 v = ractor_copy(obj);
963 type = basket_type_copy;
964 }
965 else {
966 type = basket_type_move;
967 v = ractor_move(obj);
968 }
969
970 *pobj = v;
971 *ptype = type;
972}
973
974static void
975ractor_basket_fill_(rb_ractor_t *cr, struct rb_ractor_basket *basket, VALUE obj, bool exc)
976{
977 VM_ASSERT(cr == GET_RACTOR());
978
979 basket->sender = cr->pub.self;
980 basket->p.send.exception = exc;
981 basket->p.send.v = obj;
982}
983
984static void
985ractor_basket_fill(rb_ractor_t *cr, struct rb_ractor_basket *basket, VALUE obj, VALUE move, bool exc)
986{
987 VALUE v;
988 enum rb_ractor_basket_type type;
989 ractor_basket_prepare_contents(obj, move, &v, &type);
990 ractor_basket_fill_(cr, basket, v, exc);
991 basket->type.e = type;
992}
993
994static void
995ractor_basket_fill_will(rb_ractor_t *cr, struct rb_ractor_basket *basket, VALUE obj, bool exc)
996{
997 ractor_basket_fill_(cr, basket, obj, exc);
998 basket->type.e = basket_type_will;
999}
1000
1001static VALUE
1002ractor_send(rb_execution_context_t *ec, rb_ractor_t *r, VALUE obj, VALUE move)
1003{
1004 struct rb_ractor_basket basket;
1005 // TODO: Ractor local GC
1006 ractor_basket_fill(rb_ec_ractor_ptr(ec), &basket, obj, move, false);
1007 ractor_send_basket(ec, r, &basket);
1008 return r->pub.self;
1009}
1010
1011// Ractor#take
1012
1013static bool
1014ractor_take_has_will(rb_ractor_t *r)
1015{
1016 ASSERT_ractor_locking(r);
1017
1018 return basket_type_p(&r->sync.will_basket, basket_type_will);
1019}
1020
1021static bool
1022ractor_take_will(rb_ractor_t *r, struct rb_ractor_basket *b)
1023{
1024 ASSERT_ractor_locking(r);
1025
1026 if (ractor_take_has_will(r)) {
1027 *b = r->sync.will_basket;
1028 r->sync.will_basket.type.e = basket_type_none;
1029 return true;
1030 }
1031 else {
1032 VM_ASSERT(basket_type_p(&r->sync.will_basket, basket_type_none));
1033 return false;
1034 }
1035}
1036
1037static bool
1038ractor_take_will_lock(rb_ractor_t *r, struct rb_ractor_basket *b)
1039{
1040 ASSERT_ractor_unlocking(r);
1041 bool taken;
1042
1043 RACTOR_LOCK(r);
1044 {
1045 taken = ractor_take_will(r, b);
1046 }
1047 RACTOR_UNLOCK(r);
1048
1049 return taken;
1050}
1051
1052static bool
1053ractor_register_take(rb_ractor_t *cr, rb_ractor_t *r, struct rb_ractor_basket *take_basket,
1054 bool is_take, struct rb_ractor_selector_take_config *config, bool ignore_error)
1055{
1056 struct rb_ractor_basket b = {
1057 .type.e = basket_type_take_basket,
1058 .sender = cr->pub.self,
1059 .p = {
1060 .take = {
1061 .basket = take_basket,
1062 .config = config,
1063 },
1064 },
1065 };
1066 bool closed = false;
1067
1068 RACTOR_LOCK(r);
1069 {
1070 if (is_take && ractor_take_will(r, take_basket)) {
1071 RUBY_DEBUG_LOG("take over a will of r:%d", rb_ractor_id(r));
1072 }
1073 else if (!is_take && ractor_take_has_will(r)) {
1074 RUBY_DEBUG_LOG("has_will");
1075 VM_ASSERT(config != NULL);
1076 config->closed = true;
1077 }
1078 else if (r->sync.outgoing_port_closed) {
1079 closed = true;
1080 }
1081 else {
1082 RUBY_DEBUG_LOG("register in r:%d", rb_ractor_id(r));
1083 ractor_queue_enq(r, &r->sync.takers_queue, &b);
1084
1085 if (basket_none_p(take_basket)) {
1086 ractor_wakeup(r, wait_yielding, wakeup_by_take);
1087 }
1088 }
1089 }
1090 RACTOR_UNLOCK(r);
1091
1092 if (closed) {
1093 if (!ignore_error) rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
1094 return false;
1095 }
1096 else {
1097 return true;
1098 }
1099}
1100
1101static bool
1102ractor_deregister_take(rb_ractor_t *r, struct rb_ractor_basket *take_basket)
1103{
1104 struct rb_ractor_queue *ts = &r->sync.takers_queue;
1105 bool deleted = false;
1106
1107 RACTOR_LOCK(r);
1108 {
1109 if (r->sync.outgoing_port_closed) {
1110 // ok
1111 }
1112 else {
1113 for (int i=0; i<ts->cnt; i++) {
1114 struct rb_ractor_basket *b = ractor_queue_at(r, ts, i);
1115 if (basket_type_p(b, basket_type_take_basket) && b->p.take.basket == take_basket) {
1116 ractor_queue_delete(r, ts, b);
1117 deleted = true;
1118 }
1119 }
1120 if (deleted) {
1121 ractor_queue_compact(r, ts);
1122 }
1123 }
1124 }
1125 RACTOR_UNLOCK(r);
1126
1127 return deleted;
1128}
1129
1130static VALUE
1131ractor_try_take(rb_ractor_t *cr, rb_ractor_t *r, struct rb_ractor_basket *take_basket)
1132{
1133 bool taken;
1134
1135 RACTOR_LOCK_SELF(cr);
1136 {
1137 if (basket_none_p(take_basket) || basket_type_p(take_basket, basket_type_yielding)) {
1138 taken = false;
1139 }
1140 else {
1141 taken = true;
1142 }
1143 }
1144 RACTOR_UNLOCK_SELF(cr);
1145
1146 if (taken) {
1147 RUBY_DEBUG_LOG("taken");
1148 if (basket_type_p(take_basket, basket_type_deleted)) {
1149 VM_ASSERT(r->sync.outgoing_port_closed);
1150 rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
1151 }
1152 return ractor_basket_accept(take_basket);
1153 }
1154 else {
1155 RUBY_DEBUG_LOG("not taken");
1156 return Qundef;
1157 }
1158}
1159
1160
1161#if VM_CHECK_MODE > 0
1162static bool
1163ractor_check_specific_take_basket_lock(rb_ractor_t *r, struct rb_ractor_basket *tb)
1164{
1165 bool ret = false;
1166 struct rb_ractor_queue *ts = &r->sync.takers_queue;
1167
1168 RACTOR_LOCK(r);
1169 {
1170 for (int i=0; i<ts->cnt; i++) {
1171 struct rb_ractor_basket *b = ractor_queue_at(r, ts, i);
1172 if (basket_type_p(b, basket_type_take_basket) && b->p.take.basket == tb) {
1173 ret = true;
1174 break;
1175 }
1176 }
1177 }
1178 RACTOR_UNLOCK(r);
1179
1180 return ret;
1181}
1182#endif
1183
1184static void
1185ractor_take_cleanup(rb_ractor_t *cr, rb_ractor_t *r, struct rb_ractor_basket *tb)
1186{
1187 retry:
1188 if (basket_none_p(tb)) { // not yielded yet
1189 if (!ractor_deregister_take(r, tb)) {
1190 // not in r's takers queue
1191 rb_thread_sleep(0);
1192 goto retry;
1193 }
1194 }
1195 else {
1196 VM_ASSERT(!ractor_check_specific_take_basket_lock(r, tb));
1197 }
1198}
1199
1200 struct take_wait_take_cleanup_data {
1201 rb_ractor_t *r;
1202 struct rb_ractor_basket *tb;
1203};
1204
1205static void
1206ractor_wait_take_cleanup(rb_ractor_t *cr, void *ptr)
1207{
1208 struct take_wait_take_cleanup_data *data = (struct take_wait_take_cleanup_data *)ptr;
1209 ractor_take_cleanup(cr, data->r, data->tb);
1210}
1211
1212static void
1213ractor_wait_take(rb_execution_context_t *ec, rb_ractor_t *cr, rb_ractor_t *r, struct rb_ractor_basket *take_basket)
1214{
1215 struct take_wait_take_cleanup_data data = {
1216 .r = r,
1217 .tb = take_basket,
1218 };
1219
1220 RACTOR_LOCK_SELF(cr);
1221 {
1222 if (basket_none_p(take_basket) || basket_type_p(take_basket, basket_type_yielding)) {
1223 ractor_sleep_with_cleanup(ec, cr, wait_taking, ractor_wait_take_cleanup, &data);
1224 }
1225 }
1226 RACTOR_UNLOCK_SELF(cr);
1227}
1228
1229static VALUE
1230ractor_take(rb_execution_context_t *ec, rb_ractor_t *r)
1231{
1232 RUBY_DEBUG_LOG("from r:%u", rb_ractor_id(r));
1233 VALUE v;
1234 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1235
1236 struct rb_ractor_basket take_basket = {
1237 .type.e = basket_type_none,
1238 .sender = 0,
1239 };
1240
1241 ractor_register_take(cr, r, &take_basket, true, NULL, false);
1242
1243 while (UNDEF_P(v = ractor_try_take(cr, r, &take_basket))) {
1244 ractor_wait_take(ec, cr, r, &take_basket);
1245 }
1246
1247 VM_ASSERT(!basket_none_p(&take_basket));
1248 VM_ASSERT(!ractor_check_specific_take_basket_lock(r, &take_basket));
1249
1250 return v;
1251}
1252
1253// Ractor.yield
1254
1255static bool
1256ractor_check_take_basket(rb_ractor_t *cr, struct rb_ractor_queue *rs)
1257{
1258 ASSERT_ractor_locking(cr);
1259
1260 for (int i=0; i<rs->cnt; i++) {
1261 struct rb_ractor_basket *b = ractor_queue_at(cr, rs, i);
1262 if (basket_type_p(b, basket_type_take_basket) &&
1263 basket_none_p(b->p.take.basket)) {
1264 return true;
1265 }
1266 }
1267
1268 return false;
1269}
1270
1271static bool
1272ractor_deq_take_basket(rb_ractor_t *cr, struct rb_ractor_queue *rs, struct rb_ractor_basket *b)
1273{
1274 ASSERT_ractor_unlocking(cr);
1275 struct rb_ractor_basket *first_tb = NULL;
1276 bool found = false;
1277
1278 RACTOR_LOCK_SELF(cr);
1279 {
1280 while (ractor_queue_deq(cr, rs, b)) {
1281 if (basket_type_p(b, basket_type_take_basket)) {
1282 struct rb_ractor_basket *tb = b->p.take.basket;
1283
1284 if (RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_yielding) == basket_type_none) {
1285 found = true;
1286 break;
1287 }
1288 else {
1289 ractor_queue_enq(cr, rs, b);
1290 if (first_tb == NULL) first_tb = tb;
1291 struct rb_ractor_basket *head = ractor_queue_head(cr, rs);
1292 VM_ASSERT(head != NULL);
1293 if (basket_type_p(head, basket_type_take_basket) && head->p.take.basket == first_tb) {
1294 break; // loop detected
1295 }
1296 }
1297 }
1298 else {
1299 VM_ASSERT(basket_none_p(b));
1300 }
1301 }
1302
1303 if (found && b->p.take.config && !b->p.take.config->oneshot) {
1304 ractor_queue_enq(cr, rs, b);
1305 }
1306 }
1307 RACTOR_UNLOCK_SELF(cr);
1308
1309 return found;
1310}
1311
1312static bool
1313ractor_try_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_queue *ts, volatile VALUE obj, VALUE move, bool exc, bool is_will)
1314{
1315 ASSERT_ractor_unlocking(cr);
1316
1317 struct rb_ractor_basket b;
1318
1319 if (ractor_deq_take_basket(cr, ts, &b)) {
1320 VM_ASSERT(basket_type_p(&b, basket_type_take_basket));
1321 VM_ASSERT(basket_type_p(b.p.take.basket, basket_type_yielding));
1322
1323 rb_ractor_t *tr = RACTOR_PTR(b.sender);
1324 struct rb_ractor_basket *tb = b.p.take.basket;
1325 enum rb_ractor_basket_type type;
1326
1327 RUBY_DEBUG_LOG("basket from r:%u", rb_ractor_id(tr));
1328
1329 if (is_will) {
1330 type = basket_type_will;
1331 }
1332 else {
1333 enum ruby_tag_type state;
1334
1335 // begin
1336 EC_PUSH_TAG(ec);
1337 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1338 // TODO: Ractor local GC
1339 ractor_basket_prepare_contents(obj, move, &obj, &type);
1340 }
1341 EC_POP_TAG();
1342 // rescue
1343 if (state) {
1344 RACTOR_LOCK_SELF(cr);
1345 {
1346 b.p.take.basket->type.e = basket_type_none;
1347 ractor_queue_enq(cr, ts, &b);
1348 }
1349 RACTOR_UNLOCK_SELF(cr);
1350 EC_JUMP_TAG(ec, state);
1351 }
1352 }
1353
1354 RACTOR_LOCK(tr);
1355 {
1356 VM_ASSERT(basket_type_p(tb, basket_type_yielding));
1357 // fill atomic
1358 RUBY_DEBUG_LOG("fill %sbasket from r:%u", is_will ? "will " : "", rb_ractor_id(tr));
1359 ractor_basket_fill_(cr, tb, obj, exc);
1360 if (RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_yielding, type) != basket_type_yielding) {
1361 rb_bug("unreachable");
1362 }
1363 ractor_wakeup(tr, wait_taking, wakeup_by_yield);
1364 }
1365 RACTOR_UNLOCK(tr);
1366
1367 return true;
1368 }
1369 else if (cr->sync.outgoing_port_closed) {
1370 rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
1371 }
1372 else {
1373 RUBY_DEBUG_LOG("no take basket");
1374 return false;
1375 }
1376}
1377
1378static void
1379ractor_wait_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_queue *ts)
1380{
1381 RACTOR_LOCK_SELF(cr);
1382 {
1383 while (!ractor_check_take_basket(cr, ts) && !cr->sync.outgoing_port_closed) {
1384 ractor_sleep(ec, cr, wait_yielding);
1385 }
1386 }
1387 RACTOR_UNLOCK_SELF(cr);
1388}
1389
1390static VALUE
1391ractor_yield(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE obj, VALUE move)
1392{
1393 struct rb_ractor_queue *ts = &cr->sync.takers_queue;
1394
1395 while (!ractor_try_yield(ec, cr, ts, obj, move, false, false)) {
1396 ractor_wait_yield(ec, cr, ts);
1397 }
1398
1399 return Qnil;
1400}
1401
1402// Ractor::Selector
1403
1404 struct rb_ractor_selector {
1405 rb_ractor_t *r;
1406 struct rb_ractor_basket take_basket;
1407 st_table *take_ractors; // rb_ractor_t * => (struct rb_ractor_selector_take_config *)
1408};
1409
1410static int
1411ractor_selector_mark_ractors_i(st_data_t key, st_data_t value, st_data_t data)
1412{
1413 const rb_ractor_t *r = (rb_ractor_t *)key;
1414 rb_gc_mark(r->pub.self);
1415 return ST_CONTINUE;
1416}
1417
1418static void
1419ractor_selector_mark(void *ptr)
1420{
1421 struct rb_ractor_selector *s = ptr;
1422
1423 if (s->take_ractors) {
1424 st_foreach(s->take_ractors, ractor_selector_mark_ractors_i, 0);
1425 }
1426
1427 switch (s->take_basket.type.e) {
1428 case basket_type_ref:
1429 case basket_type_copy:
1430 case basket_type_move:
1431 case basket_type_will:
1432 rb_gc_mark(s->take_basket.sender);
1433 rb_gc_mark(s->take_basket.p.send.v);
1434 break;
1435 default:
1436 break;
1437 }
1438}
1439
1440static int
1441ractor_selector_release_i(st_data_t key, st_data_t val, st_data_t data)
1442{
1443 struct rb_ractor_selector *s = (struct rb_ractor_selector *)data;
1444 struct rb_ractor_selector_take_config *config = (struct rb_ractor_selector_take_config *)val;
1445
1446 if (!config->closed) {
1447 ractor_deregister_take((rb_ractor_t *)key, &s->take_basket);
1448 }
1449 free(config);
1450 return ST_CONTINUE;
1451}
1452
1453static void
1454ractor_selector_free(void *ptr)
1455{
1456 struct rb_ractor_selector *s = ptr;
1457 st_foreach(s->take_ractors, ractor_selector_release_i, (st_data_t)s);
1458 st_free_table(s->take_ractors);
1459 ruby_xfree(ptr);
1460}
1461
1462static size_t
1463ractor_selector_memsize(const void *ptr)
1464{
1465 const struct rb_ractor_selector *s = ptr;
1466 return sizeof(struct rb_ractor_selector) +
1467 st_memsize(s->take_ractors) +
1468 s->take_ractors->num_entries * sizeof(struct rb_ractor_selector_take_config);
1469}
1470
1471static const rb_data_type_t ractor_selector_data_type = {
1472 "ractor/selector",
1473 {
1474 ractor_selector_mark,
1475 ractor_selector_free,
1476 ractor_selector_memsize,
1477 NULL, // update
1478 },
1479 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
1480};
1481
1482static struct rb_ractor_selector *
1483RACTOR_SELECTOR_PTR(VALUE selv)
1484{
1485 VM_ASSERT(rb_typeddata_is_kind_of(selv, &ractor_selector_data_type));
1486
1487 return (struct rb_ractor_selector *)DATA_PTR(selv);
1488}
1489
1490// Ractor::Selector.new
1491
1492static VALUE
1493ractor_selector_create(VALUE klass)
1494{
1495 struct rb_ractor_selector *s;
1496 VALUE selv = TypedData_Make_Struct(klass, struct rb_ractor_selector, &ractor_selector_data_type, s);
1497 s->take_basket.type.e = basket_type_reserved;
1498 s->take_ractors = st_init_numtable(); // ractor (ptr) -> take_config
1499 return selv;
1500}
1501
1502// Ractor::Selector#add(r)
1503
1504/*
1505 * call-seq:
1506 * add(ractor) -> ractor
1507 *
1508 * Adds _ractor_ to +self+. Raises an exception if _ractor_ is already added.
1509 * Returns _ractor_.
1510 */
1511static VALUE
1512ractor_selector_add(VALUE selv, VALUE rv)
1513{
1514 if (!rb_ractor_p(rv)) {
1515 rb_raise(rb_eArgError, "Not a ractor object");
1516 }
1517
1518 rb_ractor_t *r = RACTOR_PTR(rv);
1519 struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1520
1521 if (st_lookup(s->take_ractors, (st_data_t)r, NULL)) {
1522 rb_raise(rb_eArgError, "already added");
1523 }
1524
1525 struct rb_ractor_selector_take_config *config = malloc(sizeof(struct rb_ractor_selector_take_config));
1526 VM_ASSERT(config != NULL);
1527 config->closed = false;
1528 config->oneshot = false;
1529
1530 if (ractor_register_take(GET_RACTOR(), r, &s->take_basket, false, config, true)) {
1531 st_insert(s->take_ractors, (st_data_t)r, (st_data_t)config);
1532 }
1533
1534 return rv;
1535}
1536
1537// Ractor::Selector#remove(r)
1538
1539/* call-seq:
1540 * remove(ractor) -> ractor
1541 *
1542 * Removes _ractor_ from +self+. Raises an exception if _ractor_ is not added.
1543 * Returns the removed _ractor_.
1544 */
1545static VALUE
1546ractor_selector_remove(VALUE selv, VALUE rv)
1547{
1548 if (!rb_ractor_p(rv)) {
1549 rb_raise(rb_eArgError, "Not a ractor object");
1550 }
1551
1552 rb_ractor_t *r = RACTOR_PTR(rv);
1553 struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1554
1555 RUBY_DEBUG_LOG("r:%u", rb_ractor_id(r));
1556
1557 if (!st_lookup(s->take_ractors, (st_data_t)r, NULL)) {
1558 rb_raise(rb_eArgError, "not added yet");
1559 }
1560
1561 ractor_deregister_take(r, &s->take_basket);
1562 struct rb_ractor_selector_take_config *config;
1563 st_delete(s->take_ractors, (st_data_t *)&r, (st_data_t *)&config);
1564 free(config);
1565
1566 return rv;
1567}
1568
1569// Ractor::Selector#clear
1570
1571 struct ractor_selector_clear_data {
1572 VALUE selv;
1573 rb_execution_context_t *ec;
1574};
1575
1576static int
1577ractor_selector_clear_i(st_data_t key, st_data_t val, st_data_t data)
1578{
1579 VALUE selv = (VALUE)data;
1580 rb_ractor_t *r = (rb_ractor_t *)key;
1581 ractor_selector_remove(selv, r->pub.self);
1582 return ST_CONTINUE;
1583}
1584
1585/*
1586 * call-seq:
1587 * clear -> self
1588 *
1589 * Removes all ractors from +self+. Raises +self+.
1590 */
1591static VALUE
1592ractor_selector_clear(VALUE selv)
1593{
1594 struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1595
1596 st_foreach(s->take_ractors, ractor_selector_clear_i, (st_data_t)selv);
1597 st_clear(s->take_ractors);
1598 return selv;
1599}
1600
1601/*
1602 * call-seq:
1603 * empty? -> true or false
1604 *
1605 * Returns +true+ if no ractor is added.
1606 */
1607static VALUE
1608ractor_selector_empty_p(VALUE selv)
1609{
1610 struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1611 return s->take_ractors->num_entries == 0 ? Qtrue : Qfalse;
1612}
1613
1614static int
1615ractor_selector_wait_i(st_data_t key, st_data_t val, st_data_t dat)
1616{
1617 rb_ractor_t *r = (rb_ractor_t *)key;
1618 struct rb_ractor_basket *tb = (struct rb_ractor_basket *)dat;
1619 int ret;
1620
1621 if (!basket_none_p(tb)) {
1622 RUBY_DEBUG_LOG("already taken:%s", basket_type_name(tb->type.e));
1623 return ST_STOP;
1624 }
1625
1626 RACTOR_LOCK(r);
1627 {
1628 if (basket_type_p(&r->sync.will_basket, basket_type_will)) {
1629 RUBY_DEBUG_LOG("r:%u has will", rb_ractor_id(r));
1630
1631 if (RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_will) == basket_type_none) {
1632 ractor_take_will(r, tb);
1633 ret = ST_STOP;
1634 }
1635 else {
1636 RUBY_DEBUG_LOG("has will, but already taken (%s)", basket_type_name(tb->type.e));
1637 ret = ST_CONTINUE;
1638 }
1639 }
1640 else if (r->sync.outgoing_port_closed) {
1641 RUBY_DEBUG_LOG("r:%u is closed", rb_ractor_id(r));
1642
1643 if (RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_deleted) == basket_type_none) {
1644 tb->sender = r->pub.self;
1645 ret = ST_STOP;
1646 }
1647 else {
1648 RUBY_DEBUG_LOG("closed, but already taken (%s)", basket_type_name(tb->type.e));
1649 ret = ST_CONTINUE;
1650 }
1651 }
1652 else {
1653 RUBY_DEBUG_LOG("wakeup r:%u", rb_ractor_id(r));
1654 ractor_wakeup(r, wait_yielding, wakeup_by_take);
1655 ret = ST_CONTINUE;
1656 }
1657 }
1658 RACTOR_UNLOCK(r);
1659
1660 return ret;
1661}
1662
1663// Ractor::Selector#wait
1664
1665static void
1666ractor_selector_wait_cleaup(rb_ractor_t *cr, void *ptr)
1667{
1668 struct rb_ractor_basket *tb = (struct rb_ractor_basket *)ptr;
1669
1670 RACTOR_LOCK_SELF(cr);
1671 {
1672 while (basket_type_p(tb, basket_type_yielding)) rb_thread_sleep(0);
1673 // if tb->type is not none, taking is succeeded, but interruption ignore it unfortunately.
1674 tb->type.e = basket_type_reserved;
1675 }
1676 RACTOR_UNLOCK_SELF(cr);
1677}
1678
1679/* :nodoc: */
1680static VALUE
1681ractor_selector__wait(VALUE selv, VALUE do_receivev, VALUE do_yieldv, VALUE yield_value, VALUE move)
1682{
1683 rb_execution_context_t *ec = GET_EC();
1684 struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1685 struct rb_ractor_basket *tb = &s->take_basket;
1686 struct rb_ractor_basket taken_basket;
1687 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1688 bool do_receive = !!RTEST(do_receivev);
1689 bool do_yield = !!RTEST(do_yieldv);
1690 VALUE ret_v, ret_r;
1691 enum rb_ractor_wait_status wait_status;
1692 struct rb_ractor_queue *rq = &cr->sync.recv_queue;
1693 struct rb_ractor_queue *ts = &cr->sync.takers_queue;
1694
1695 RUBY_DEBUG_LOG("start");
1696
1697 retry:
1698 RUBY_DEBUG_LOG("takers:%ld", s->take_ractors->num_entries);
1699
1700 // setup wait_status
1701 wait_status = wait_none;
1702 if (s->take_ractors->num_entries > 0) wait_status |= wait_taking;
1703 if (do_receive) wait_status |= wait_receiving;
1704 if (do_yield) wait_status |= wait_yielding;
1705
1706 RUBY_DEBUG_LOG("wait:%s", wait_status_str(wait_status));
1707
1708 if (wait_status == wait_none) {
1709 rb_raise(rb_eRactorError, "no taking ractors");
1710 }
1711
1712 // check recv_queue
1713 if (do_receive && !UNDEF_P(ret_v = ractor_try_receive(ec, cr, rq))) {
1714 ret_r = ID2SYM(rb_intern("receive"));
1715 goto success;
1716 }
1717
1718 // check takers
1719 if (do_yield && ractor_try_yield(ec, cr, ts, yield_value, move, false, false)) {
1720 ret_v = Qnil;
1721 ret_r = ID2SYM(rb_intern("yield"));
1722 goto success;
1723 }
1724
1725 // check take_basket
1726 VM_ASSERT(basket_type_p(&s->take_basket, basket_type_reserved));
1727 s->take_basket.type.e = basket_type_none;
1728 // kick all take target ractors
1729 st_foreach(s->take_ractors, ractor_selector_wait_i, (st_data_t)tb);
1730
1731 RACTOR_LOCK_SELF(cr);
1732 {
1733 retry_waiting:
1734 while (1) {
1735 if (!basket_none_p(tb)) {
1736 RUBY_DEBUG_LOG("taken:%s from r:%u", basket_type_name(tb->type.e),
1737 tb->sender ? rb_ractor_id(RACTOR_PTR(tb->sender)) : 0);
1738 break;
1739 }
1740 if (do_receive && !ractor_queue_empty_p(cr, rq)) {
1741 RUBY_DEBUG_LOG("can receive (%d)", rq->cnt);
1742 break;
1743 }
1744 if (do_yield && ractor_check_take_basket(cr, ts)) {
1745 RUBY_DEBUG_LOG("can yield");
1746 break;
1747 }
1748
1749 ractor_sleep_with_cleanup(ec, cr, wait_status, ractor_selector_wait_cleaup, tb);
1750 }
1751
1752 taken_basket = *tb;
1753
1754 // ensure
1755 // tb->type.e = basket_type_reserved # do it atomic in the following code
1756 if (taken_basket.type.e == basket_type_yielding ||
1757 RUBY_ATOMIC_CAS(tb->type.atomic, taken_basket.type.e, basket_type_reserved) != taken_basket.type.e) {
1758
1759 if (basket_type_p(tb, basket_type_yielding)) {
1760 RACTOR_UNLOCK_SELF(cr);
1761 {
1762 rb_thread_sleep(0);
1763 }
1764 RACTOR_LOCK_SELF(cr);
1765 }
1766 goto retry_waiting;
1767 }
1768 }
1769 RACTOR_UNLOCK_SELF(cr);
1770
1771 // check the taken result
1772 switch (taken_basket.type.e) {
1773 case basket_type_none:
1774 VM_ASSERT(do_receive || do_yield);
1775 goto retry;
1776 case basket_type_yielding:
1777 rb_bug("unreachable");
1778 case basket_type_deleted: {
1779 ractor_selector_remove(selv, taken_basket.sender);
1780
1781 rb_ractor_t *r = RACTOR_PTR(taken_basket.sender);
1782 if (ractor_take_will_lock(r, &taken_basket)) {
1783 RUBY_DEBUG_LOG("has_will");
1784 }
1785 else {
1786 RUBY_DEBUG_LOG("no will");
1787 // rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
1788 // remove and retry wait
1789 goto retry;
1790 }
1791 break;
1792 }
1793 case basket_type_will:
1794 // no more messages
1795 ractor_selector_remove(selv, taken_basket.sender);
1796 break;
1797 default:
1798 break;
1799 }
1800
1801 RUBY_DEBUG_LOG("taken_basket:%s", basket_type_name(taken_basket.type.e));
1802
1803 ret_v = ractor_basket_accept(&taken_basket);
1804 ret_r = taken_basket.sender;
1805 success:
1806 return rb_ary_new_from_args(2, ret_r, ret_v);
1807}
1808
1809/*
1810 * call-seq:
1811 * wait(receive: false, yield_value: undef, move: false) -> [ractor, value]
1812 *
1813 * Waits until any ractor in _selector_ can be active.
1814 */
1815static VALUE
1816ractor_selector_wait(int argc, VALUE *argv, VALUE selector)
1817{
1818 VALUE options;
1819 ID keywords[3];
1820 VALUE values[3];
1821
1822 keywords[0] = rb_intern("receive");
1823 keywords[1] = rb_intern("yield_value");
1824 keywords[2] = rb_intern("move");
1825
1826 rb_scan_args(argc, argv, "0:", &options);
1827 rb_get_kwargs(options, keywords, 0, numberof(values), values);
1828 return ractor_selector__wait(selector,
1829 values[0] == Qundef ? Qfalse : RTEST(values[0]),
1830 values[1] != Qundef, values[1], values[2]);
1831}
1832
1833static VALUE
1834ractor_selector_new(int argc, VALUE *ractors, VALUE klass)
1835{
1836 VALUE selector = ractor_selector_create(klass);
1837
1838 for (int i=0; i<argc; i++) {
1839 ractor_selector_add(selector, ractors[i]);
1840 }
1841
1842 return selector;
1843}
1844
1845static VALUE
1846ractor_select_internal(rb_execution_context_t *ec, VALUE self, VALUE ractors, VALUE do_receive, VALUE do_yield, VALUE yield_value, VALUE move)
1847{
1848 VALUE selector = ractor_selector_new(RARRAY_LENINT(ractors), (VALUE *)RARRAY_CONST_PTR(ractors), rb_cRactorSelector);
1849 VALUE result;
1850 int state;
1851
1852 EC_PUSH_TAG(ec);
1853 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1854 result = ractor_selector__wait(selector, do_receive, do_yield, yield_value, move);
1855 }
1856 EC_POP_TAG();
1857 if (state != TAG_NONE) {
1858 // ensure
1859 ractor_selector_clear(selector);
1860
1861 // jump
1862 EC_JUMP_TAG(ec, state);
1863 }
1864
1865 RB_GC_GUARD(ractors);
1866 return result;
1867}
1868
1869// Ractor#close_incoming
1870
1871static VALUE
1872ractor_close_incoming(rb_execution_context_t *ec, rb_ractor_t *r)
1873{
1874 VALUE prev;
1875
1876 RACTOR_LOCK(r);
1877 {
1878 if (!r->sync.incoming_port_closed) {
1879 prev = Qfalse;
1880 r->sync.incoming_port_closed = true;
1881 if (ractor_wakeup(r, wait_receiving, wakeup_by_close)) {
1882 VM_ASSERT(ractor_queue_empty_p(r, &r->sync.recv_queue));
1883 RUBY_DEBUG_LOG("cancel receiving");
1884 }
1885 }
1886 else {
1887 prev = Qtrue;
1888 }
1889 }
1890 RACTOR_UNLOCK(r);
1891 return prev;
1892}
1893
1894// Ractor#close_outgoing
1895
1896static VALUE
1897ractor_close_outgoing(rb_execution_context_t *ec, rb_ractor_t *r)
1898{
1899 VALUE prev;
1900
1901 RACTOR_LOCK(r);
1902 {
1903 struct rb_ractor_queue *ts = &r->sync.takers_queue;
1904 rb_ractor_t *tr;
1905 struct rb_ractor_basket b;
1906
1907 if (!r->sync.outgoing_port_closed) {
1908 prev = Qfalse;
1909 r->sync.outgoing_port_closed = true;
1910 }
1911 else {
1912 VM_ASSERT(ractor_queue_empty_p(r, ts));
1913 prev = Qtrue;
1914 }
1915
1916 // wakeup all taking ractors
1917 while (ractor_queue_deq(r, ts, &b)) {
1918 if (basket_type_p(&b, basket_type_take_basket)) {
1919 tr = RACTOR_PTR(b.sender);
1920 struct rb_ractor_basket *tb = b.p.take.basket;
1921
1922 if (RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_yielding) == basket_type_none) {
1923 b.p.take.basket->sender = r->pub.self;
1924 if (RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_yielding, basket_type_deleted) != basket_type_yielding) {
1925 rb_bug("unreachable");
1926 }
1927 RUBY_DEBUG_LOG("set delete for r:%u", rb_ractor_id(RACTOR_PTR(b.sender)));
1928 }
1929
1930 if (b.p.take.config) {
1931 b.p.take.config->closed = true;
1932 }
1933
1934 // TODO: deadlock-able?
1935 RACTOR_LOCK(tr);
1936 {
1937 ractor_wakeup(tr, wait_taking, wakeup_by_close);
1938 }
1939 RACTOR_UNLOCK(tr);
1940 }
1941 }
1942
1943 // raising yielding Ractor
1944 ractor_wakeup(r, wait_yielding, wakeup_by_close);
1945
1946 VM_ASSERT(ractor_queue_empty_p(r, ts));
1947 }
1948 RACTOR_UNLOCK(r);
1949 return prev;
1950}
1951
1952// creation/termination
1953
1954static uint32_t
1955ractor_next_id(void)
1956{
1957 uint32_t id;
1958
1959 id = (uint32_t)(RUBY_ATOMIC_FETCH_ADD(ractor_last_id, 1) + 1);
1960
1961 return id;
1962}
1963
1964static void
1965vm_insert_ractor0(rb_vm_t *vm, rb_ractor_t *r, bool single_ractor_mode)
1966{
1967 RUBY_DEBUG_LOG("r:%u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
1968 VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
1969
1970 ccan_list_add_tail(&vm->ractor.set, &r->vmlr_node);
1971 vm->ractor.cnt++;
1972
1973 if (r->newobj_cache) {
1974 VM_ASSERT(r == ruby_single_main_ractor);
1975 }
1976 else {
1977 r->newobj_cache = rb_gc_ractor_cache_alloc(r);
1978 }
1979}
1980
1981static void
1982cancel_single_ractor_mode(void)
1983{
1984 // enable multi-ractor mode
1985 RUBY_DEBUG_LOG("enable multi-ractor mode");
1986
1987 VALUE was_disabled = rb_gc_enable();
1988
1989 rb_gc_start();
1990
1991 if (was_disabled) {
1992 rb_gc_disable();
1993 }
1994
1995 ruby_single_main_ractor = NULL;
1996 rb_funcall(rb_cRactor, rb_intern("_activated"), 0);
1997}
1998
1999static void
2000vm_insert_ractor(rb_vm_t *vm, rb_ractor_t *r)
2001{
2002 VM_ASSERT(ractor_status_p(r, ractor_created));
2003
2004 if (rb_multi_ractor_p()) {
2005 RB_VM_LOCK();
2006 {
2007 vm_insert_ractor0(vm, r, false);
2008 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
2009 }
2010 RB_VM_UNLOCK();
2011 }
2012 else {
2013 if (vm->ractor.cnt == 0) {
2014 // main ractor
2015 vm_insert_ractor0(vm, r, true);
2016 ractor_status_set(r, ractor_blocking);
2017 ractor_status_set(r, ractor_running);
2018 }
2019 else {
2020 cancel_single_ractor_mode();
2021 vm_insert_ractor0(vm, r, true);
2022 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
2023 }
2024 }
2025}
2026
2027static void
2028vm_remove_ractor(rb_vm_t *vm, rb_ractor_t *cr)
2029{
2030 VM_ASSERT(ractor_status_p(cr, ractor_running));
2031 VM_ASSERT(vm->ractor.cnt > 1);
2032 VM_ASSERT(cr->threads.cnt == 1);
2033
2034 RB_VM_LOCK();
2035 {
2036 RUBY_DEBUG_LOG("ractor.cnt:%u-- terminate_waiting:%d",
2037 vm->ractor.cnt, vm->ractor.sync.terminate_waiting);
2038
2039 VM_ASSERT(vm->ractor.cnt > 0);
2040 ccan_list_del(&cr->vmlr_node);
2041
2042 if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
2043 rb_native_cond_signal(&vm->ractor.sync.terminate_cond);
2044 }
2045 vm->ractor.cnt--;
2046
2047 rb_gc_ractor_cache_free(cr->newobj_cache);
2048 cr->newobj_cache = NULL;
2049
2050 ractor_status_set(cr, ractor_terminated);
2051 }
2052 RB_VM_UNLOCK();
2053}
2054
2055static VALUE
2056ractor_alloc(VALUE klass)
2057{
2058 rb_ractor_t *r;
2059 VALUE rv = TypedData_Make_Struct(klass, rb_ractor_t, &ractor_data_type, r);
2060 FL_SET_RAW(rv, RUBY_FL_SHAREABLE);
2061 r->pub.self = rv;
2062 VM_ASSERT(ractor_status_p(r, ractor_created));
2063 return rv;
2064}
2065
2066rb_ractor_t *
2067rb_ractor_main_alloc(void)
2068{
2069 rb_ractor_t *r = ruby_mimcalloc(1, sizeof(rb_ractor_t));
2070 if (r == NULL) {
2071 fprintf(stderr, "[FATAL] failed to allocate memory for main ractor\n");
2072 exit(EXIT_FAILURE);
2073 }
2074 r->pub.id = ++ractor_last_id;
2075 r->loc = Qnil;
2076 r->name = Qnil;
2077 r->pub.self = Qnil;
2078 r->newobj_cache = rb_gc_ractor_cache_alloc(r);
2079 ruby_single_main_ractor = r;
2080
2081 return r;
2082}
2083
2084#if defined(HAVE_WORKING_FORK)
2085void
2086rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th)
2087{
2088 // initialize as a main ractor
2089 vm->ractor.cnt = 0;
2090 vm->ractor.blocking_cnt = 0;
2091 ruby_single_main_ractor = th->ractor;
2092 th->ractor->status_ = ractor_created;
2093
2094 rb_ractor_living_threads_init(th->ractor);
2095 rb_ractor_living_threads_insert(th->ractor, th);
2096
2097 VM_ASSERT(vm->ractor.blocking_cnt == 0);
2098 VM_ASSERT(vm->ractor.cnt == 1);
2099}
2100#endif
2101
2102void rb_thread_sched_init(struct rb_thread_sched *, bool atfork);
2103
2104void
2105rb_ractor_living_threads_init(rb_ractor_t *r)
2106{
2107 ccan_list_head_init(&r->threads.set);
2108 r->threads.cnt = 0;
2109 r->threads.blocking_cnt = 0;
2110}
2111
2112static void
2113ractor_init(rb_ractor_t *r, VALUE name, VALUE loc)
2114{
2115 ractor_queue_setup(&r->sync.recv_queue);
2116 ractor_queue_setup(&r->sync.takers_queue);
2117 rb_native_mutex_initialize(&r->sync.lock);
2118 rb_native_cond_initialize(&r->barrier_wait_cond);
2119
2120#ifdef RUBY_THREAD_WIN32_H
2121 rb_native_cond_initialize(&r->sync.cond);
2122 rb_native_cond_initialize(&r->barrier_wait_cond);
2123#endif
2124
2125 // thread management
2126 rb_thread_sched_init(&r->threads.sched, false);
2127 rb_ractor_living_threads_init(r);
2128
2129 // naming
2130 if (!NIL_P(name)) {
2131 rb_encoding *enc;
2132 StringValueCStr(name);
2133 enc = rb_enc_get(name);
2134 if (!rb_enc_asciicompat(enc)) {
2135 rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
2136 rb_enc_name(enc));
2137 }
2138 name = rb_str_new_frozen(name);
2139 }
2140 r->name = name;
2141 r->loc = loc;
2142}
2143
2144void
2145rb_ractor_main_setup(rb_vm_t *vm, rb_ractor_t *r, rb_thread_t *th)
2146{
2147 r->pub.self = TypedData_Wrap_Struct(rb_cRactor, &ractor_data_type, r);
2148 FL_SET_RAW(r->pub.self, RUBY_FL_SHAREABLE);
2149 ractor_init(r, Qnil, Qnil);
2150 r->threads.main = th;
2151 rb_ractor_living_threads_insert(r, th);
2152}
2153
2154static VALUE
2155ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VALUE args, VALUE block)
2156{
2157 VALUE rv = ractor_alloc(self);
2158 rb_ractor_t *r = RACTOR_PTR(rv);
2159 ractor_init(r, name, loc);
2160
2161 // can block here
2162 r->pub.id = ractor_next_id();
2163 RUBY_DEBUG_LOG("r:%u", r->pub.id);
2164
2165 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2166 r->verbose = cr->verbose;
2167 r->debug = cr->debug;
2168
2169 rb_yjit_before_ractor_spawn();
2170 rb_rjit_before_ractor_spawn();
2171 rb_thread_create_ractor(r, args, block);
2172
2173 RB_GC_GUARD(rv);
2174 return rv;
2175}
2176
2177static VALUE
2178ractor_create_func(VALUE klass, VALUE loc, VALUE name, VALUE args, rb_block_call_func_t func)
2179{
2180 VALUE block = rb_proc_new(func, Qnil);
2181 return ractor_create(rb_current_ec_noinline(), klass, loc, name, args, block);
2182}
2183
2184static void
2185ractor_yield_atexit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE v, bool exc)
2186{
2187 if (cr->sync.outgoing_port_closed) {
2188 return;
2189 }
2190
2191 ASSERT_ractor_unlocking(cr);
2192
2193 struct rb_ractor_queue *ts = &cr->sync.takers_queue;
2194
2195 retry:
2196 if (ractor_try_yield(ec, cr, ts, v, Qfalse, exc, true)) {
2197 // OK.
2198 }
2199 else {
2200 bool retry = false;
2201 RACTOR_LOCK(cr);
2202 {
2203 if (!ractor_check_take_basket(cr, ts)) {
2204 VM_ASSERT(cr->sync.wait.status == wait_none);
2205 RUBY_DEBUG_LOG("leave a will");
2206 ractor_basket_fill_will(cr, &cr->sync.will_basket, v, exc);
2207 }
2208 else {
2209 RUBY_DEBUG_LOG("rare timing!");
2210 retry = true; // another ractor is waiting for the yield.
2211 }
2212 }
2213 RACTOR_UNLOCK(cr);
2214
2215 if (retry) goto retry;
2216 }
2217}
2218
2219void
2220rb_ractor_atexit(rb_execution_context_t *ec, VALUE result)
2221{
2222 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2223 ractor_yield_atexit(ec, cr, result, false);
2224}
2225
2226void
2227rb_ractor_atexit_exception(rb_execution_context_t *ec)
2228{
2229 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2230 ractor_yield_atexit(ec, cr, ec->errinfo, true);
2231}
2232
2233void
2234rb_ractor_teardown(rb_execution_context_t *ec)
2235{
2236 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2237 ractor_close_incoming(ec, cr);
2238 ractor_close_outgoing(ec, cr);
2239
2240 // sync with rb_ractor_terminate_interrupt_main_thread()
2241 RB_VM_LOCK_ENTER();
2242 {
2243 VM_ASSERT(cr->threads.main != NULL);
2244 cr->threads.main = NULL;
2245 }
2246 RB_VM_LOCK_LEAVE();
2247}
2248
2249void
2250rb_ractor_receive_parameters(rb_execution_context_t *ec, rb_ractor_t *r, int len, VALUE *ptr)
2251{
2252 for (int i=0; i<len; i++) {
2253 ptr[i] = ractor_receive(ec, r);
2254 }
2255}
2256
2257void
2258rb_ractor_send_parameters(rb_execution_context_t *ec, rb_ractor_t *r, VALUE args)
2259{
2260 int len = RARRAY_LENINT(args);
2261 for (int i=0; i<len; i++) {
2262 ractor_send(ec, r, RARRAY_AREF(args, i), false);
2263 }
2264}
2265
2266bool
2267rb_ractor_main_p_(void)
2268{
2269 VM_ASSERT(rb_multi_ractor_p());
2270 rb_execution_context_t *ec = GET_EC();
2271 return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
2272}
2273
2274bool
2275rb_obj_is_main_ractor(VALUE gv)
2276{
2277 if (!rb_ractor_p(gv)) return false;
2278 rb_ractor_t *r = DATA_PTR(gv);
2279 return r == GET_VM()->ractor.main_ractor;
2280}
2281
2282int
2283rb_ractor_living_thread_num(const rb_ractor_t *r)
2284{
2285 return r->threads.cnt;
2286}
2287
2288// only for current ractor
2289 VALUE
2290rb_ractor_thread_list(void)
2291{
2292 rb_ractor_t *r = GET_RACTOR();
2293 rb_thread_t *th = 0;
2294 VALUE ary = rb_ary_new();
2295
2296 ccan_list_for_each(&r->threads.set, th, lt_node) {
2297 switch (th->status) {
2298 case THREAD_RUNNABLE:
2299 case THREAD_STOPPED:
2300 case THREAD_STOPPED_FOREVER:
2301 rb_ary_push(ary, th->self);
2302 default:
2303 break;
2304 }
2305 }
2306
2307 return ary;
2308}
2309
2310void
2311rb_ractor_living_threads_insert(rb_ractor_t *r, rb_thread_t *th)
2312{
2313 VM_ASSERT(th != NULL);
2314
2315 RACTOR_LOCK(r);
2316 {
2317 RUBY_DEBUG_LOG("r(%d)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
2318 ccan_list_add_tail(&r->threads.set, &th->lt_node);
2319 r->threads.cnt++;
2320 }
2321 RACTOR_UNLOCK(r);
2322
2323 // first thread for a ractor
2324 if (r->threads.cnt == 1) {
2325 VM_ASSERT(ractor_status_p(r, ractor_created));
2326 vm_insert_ractor(th->vm, r);
2327 }
2328}
2329
2330static void
2331vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line)
2332{
2333 ractor_status_set(r, ractor_blocking);
2334
2335 RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d++", vm->ractor.blocking_cnt);
2336 vm->ractor.blocking_cnt++;
2337 VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
2338}
2339
2340void
2341rb_vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
2342{
2343 ASSERT_vm_locking();
2344 VM_ASSERT(GET_RACTOR() == cr);
2345 vm_ractor_blocking_cnt_inc(vm, cr, file, line);
2346}
2347
2348void
2349rb_vm_ractor_blocking_cnt_dec(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
2350{
2351 ASSERT_vm_locking();
2352 VM_ASSERT(GET_RACTOR() == cr);
2353
2354 RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d--", vm->ractor.blocking_cnt);
2355 VM_ASSERT(vm->ractor.blocking_cnt > 0);
2356 vm->ractor.blocking_cnt--;
2357
2358 ractor_status_set(cr, ractor_running);
2359}
2360
2361static void
2362ractor_check_blocking(rb_ractor_t *cr, unsigned int remained_thread_cnt, const char *file, int line)
2363{
2364 VM_ASSERT(cr == GET_RACTOR());
2365
2366 RUBY_DEBUG_LOG2(file, line,
2367 "cr->threads.cnt:%u cr->threads.blocking_cnt:%u vm->ractor.cnt:%u vm->ractor.blocking_cnt:%u",
2368 cr->threads.cnt, cr->threads.blocking_cnt,
2369 GET_VM()->ractor.cnt, GET_VM()->ractor.blocking_cnt);
2370
2371 VM_ASSERT(cr->threads.cnt >= cr->threads.blocking_cnt + 1);
2372
2373 if (remained_thread_cnt > 0 &&
2374 // will be block
2375 cr->threads.cnt == cr->threads.blocking_cnt + 1) {
2376 // change ractor status: running -> blocking
2377 rb_vm_t *vm = GET_VM();
2378
2379 RB_VM_LOCK_ENTER();
2380 {
2381 rb_vm_ractor_blocking_cnt_inc(vm, cr, file, line);
2382 }
2383 RB_VM_LOCK_LEAVE();
2384 }
2385}
2386
2387void rb_threadptr_remove(rb_thread_t *th);
2388
2389void
2390rb_ractor_living_threads_remove(rb_ractor_t *cr, rb_thread_t *th)
2391{
2392 VM_ASSERT(cr == GET_RACTOR());
2393 RUBY_DEBUG_LOG("r->threads.cnt:%d--", cr->threads.cnt);
2394 ractor_check_blocking(cr, cr->threads.cnt - 1, __FILE__, __LINE__);
2395
2396 rb_threadptr_remove(th);
2397
2398 if (cr->threads.cnt == 1) {
2399 vm_remove_ractor(th->vm, cr);
2400 }
2401 else {
2402 RACTOR_LOCK(cr);
2403 {
2404 ccan_list_del(&th->lt_node);
2405 cr->threads.cnt--;
2406 }
2407 RACTOR_UNLOCK(cr);
2408 }
2409}
2410
2411void
2412rb_ractor_blocking_threads_inc(rb_ractor_t *cr, const char *file, int line)
2413{
2414 RUBY_DEBUG_LOG2(file, line, "cr->threads.blocking_cnt:%d++", cr->threads.blocking_cnt);
2415
2416 VM_ASSERT(cr->threads.cnt > 0);
2417 VM_ASSERT(cr == GET_RACTOR());
2418
2419 ractor_check_blocking(cr, cr->threads.cnt, __FILE__, __LINE__);
2420 cr->threads.blocking_cnt++;
2421}
2422
2423void
2424rb_ractor_blocking_threads_dec(rb_ractor_t *cr, const char *file, int line)
2425{
2426 RUBY_DEBUG_LOG2(file, line,
2427 "r->threads.blocking_cnt:%d--, r->threads.cnt:%u",
2428 cr->threads.blocking_cnt, cr->threads.cnt);
2429
2430 VM_ASSERT(cr == GET_RACTOR());
2431
2432 if (cr->threads.cnt == cr->threads.blocking_cnt) {
2433 rb_vm_t *vm = GET_VM();
2434
2435 RB_VM_LOCK_ENTER();
2436 {
2437 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
2438 }
2439 RB_VM_LOCK_LEAVE();
2440 }
2441
2442 cr->threads.blocking_cnt--;
2443}
2444
2445void
2446rb_ractor_vm_barrier_interrupt_running_thread(rb_ractor_t *r)
2447{
2448 VM_ASSERT(r != GET_RACTOR());
2449 ASSERT_ractor_unlocking(r);
2450 ASSERT_vm_locking();
2451
2452 RACTOR_LOCK(r);
2453 {
2454 if (ractor_status_p(r, ractor_running)) {
2455 rb_execution_context_t *ec = r->threads.running_ec;
2456 if (ec) {
2457 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec);
2458 }
2459 }
2460 }
2461 RACTOR_UNLOCK(r);
2462}
2463
2464void
2465rb_ractor_terminate_interrupt_main_thread(rb_ractor_t *r)
2466{
2467 VM_ASSERT(r != GET_RACTOR());
2468 ASSERT_ractor_unlocking(r);
2469 ASSERT_vm_locking();
2470
2471 rb_thread_t *main_th = r->threads.main;
2472 if (main_th) {
2473 if (main_th->status != THREAD_KILLED) {
2474 RUBY_VM_SET_TERMINATE_INTERRUPT(main_th->ec);
2475 rb_threadptr_interrupt(main_th);
2476 }
2477 else {
2478 RUBY_DEBUG_LOG("killed (%p)", (void *)main_th);
2479 }
2480 }
2481}
2482
2483void rb_thread_terminate_all(rb_thread_t *th); // thread.c
2484
2485static void
2486ractor_terminal_interrupt_all(rb_vm_t *vm)
2487{
2488 if (vm->ractor.cnt > 1) {
2489 // send terminate notification to all ractors
2490 rb_ractor_t *r = 0;
2491 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
2492 if (r != vm->ractor.main_ractor) {
2493 RUBY_DEBUG_LOG("r:%d", rb_ractor_id(r));
2494 rb_ractor_terminate_interrupt_main_thread(r);
2495 }
2496 }
2497 }
2498}
2499
2500void rb_add_running_thread(rb_thread_t *th);
2501void rb_del_running_thread(rb_thread_t *th);
2502
2503void
2504rb_ractor_terminate_all(void)
2505{
2506 rb_vm_t *vm = GET_VM();
2507 rb_ractor_t *cr = vm->ractor.main_ractor;
2508
2509 RUBY_DEBUG_LOG("ractor.cnt:%d", (int)vm->ractor.cnt);
2510
2511 VM_ASSERT(cr == GET_RACTOR()); // only main-ractor's main-thread should kick it.
2512
2513 if (vm->ractor.cnt > 1) {
2514 RB_VM_LOCK();
2515 {
2516 ractor_terminal_interrupt_all(vm); // kill all ractors
2517 }
2518 RB_VM_UNLOCK();
2519 }
2520 rb_thread_terminate_all(GET_THREAD()); // kill other threads in main-ractor and wait
2521
2522 RB_VM_LOCK();
2523 {
2524 while (vm->ractor.cnt > 1) {
2525 RUBY_DEBUG_LOG("terminate_waiting:%d", vm->ractor.sync.terminate_waiting);
2526 vm->ractor.sync.terminate_waiting = true;
2527
2528 // wait for 1sec
2529 rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
2530 rb_del_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
2531 rb_vm_cond_timedwait(vm, &vm->ractor.sync.terminate_cond, 1000 /* ms */);
2532 rb_add_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
2533 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
2534
2535 ractor_terminal_interrupt_all(vm);
2536 }
2537 }
2538 RB_VM_UNLOCK();
2539}
2540
2541rb_execution_context_t *
2542rb_vm_main_ractor_ec(rb_vm_t *vm)
2543{
2544 /* This code needs to carefully work around two bugs:
2545 * - Bug #20016: When M:N threading is enabled, running_ec is NULL if no thread is
2546 * actually currently running (as opposed to without M:N threading, when
2547 * running_ec will still point to the _last_ thread which ran)
2548 * - Bug #20197: If the main thread is sleeping, setting its postponed job
2549 * interrupt flag is pointless; it won't look at the flag until it stops sleeping
2550 * for some reason. It would be better to set the flag on the running ec, which
2551 * will presumably look at it soon.
2552 *
2553 * Solution: use running_ec if it's set, otherwise fall back to the main thread ec.
2554 * This is still susceptible to some rare race conditions (what if the last thread
2555 * to run just entered a long-running sleep?), but seems like the best balance of
2556 * robustness and complexity.
2557 */
2558 rb_execution_context_t *running_ec = vm->ractor.main_ractor->threads.running_ec;
2559 if (running_ec) { return running_ec; }
2560 return vm->ractor.main_thread->ec;
2561}
2562
2563static VALUE
2564ractor_moved_missing(int argc, VALUE *argv, VALUE self)
2565{
2566 rb_raise(rb_eRactorMovedError, "can not send any methods to a moved object");
2567}
2568
2569#ifndef USE_RACTOR_SELECTOR
2570#define USE_RACTOR_SELECTOR 0
2571#endif
2572
2573RUBY_SYMBOL_EXPORT_BEGIN
2574void rb_init_ractor_selector(void);
2575RUBY_SYMBOL_EXPORT_END
2576
2577/*
2578 * Document-class: Ractor::Selector
2579 * :nodoc: currently
2580 *
2581 * Selects multiple Ractors to be activated.
2582 */
2583void
2584rb_init_ractor_selector(void)
2585{
2586 rb_cRactorSelector = rb_define_class_under(rb_cRactor, "Selector", rb_cObject);
2587 rb_undef_alloc_func(rb_cRactorSelector);
2588
2589 rb_define_singleton_method(rb_cRactorSelector, "new", ractor_selector_new , -1);
2590 rb_define_method(rb_cRactorSelector, "add", ractor_selector_add, 1);
2591 rb_define_method(rb_cRactorSelector, "remove", ractor_selector_remove, 1);
2592 rb_define_method(rb_cRactorSelector, "clear", ractor_selector_clear, 0);
2593 rb_define_method(rb_cRactorSelector, "empty?", ractor_selector_empty_p, 0);
2594 rb_define_method(rb_cRactorSelector, "wait", ractor_selector_wait, -1);
2595 rb_define_method(rb_cRactorSelector, "_wait", ractor_selector__wait, 4);
2596}
2597
2598/*
2599 * Document-class: Ractor::ClosedError
2600 *
2601 * Raised when an attempt is made to send a message to a closed port,
2602 * or to retrieve a message from a closed and empty port.
2603 * Ports may be closed explicitly with Ractor#close_outgoing/close_incoming
2604 * and are closed implicitly when a Ractor terminates.
2605 *
2606 * r = Ractor.new { sleep(500) }
2607 * r.close_outgoing
2608 * r.take # Ractor::ClosedError
2609 *
2610 * ClosedError is a descendant of StopIteration, so the closing of the ractor will break
2611 * the loops without propagating the error:
2612 *
2613 * r = Ractor.new do
2614 * loop do
2615 * msg = receive # raises ClosedError and loop traps it
2616 * puts "Received: #{msg}"
2617 * end
2618 * puts "loop exited"
2619 * end
2620 *
2621 * 3.times{|i| r << i}
2622 * r.close_incoming
2623 * r.take
2624 * puts "Continue successfully"
2625 *
2626 * This will print:
2627 *
2628 * Received: 0
2629 * Received: 1
2630 * Received: 2
2631 * loop exited
2632 * Continue successfully
2633 */
2634
2635/*
2636 * Document-class: Ractor::RemoteError
2637 *
2638 * Raised on attempt to Ractor#take if there was an uncaught exception in the Ractor.
2639 * Its +cause+ will contain the original exception, and +ractor+ is the original ractor
2640 * it was raised in.
2641 *
2642 * r = Ractor.new { raise "Something weird happened" }
2643 *
2644 * begin
2645 * r.take
2646 * rescue => e
2647 * p e # => #<Ractor::RemoteError: thrown by remote Ractor.>
2648 * p e.ractor == r # => true
2649 * p e.cause # => #<RuntimeError: Something weird happened>
2650 * end
2651 *
2652 */
2653
2654/*
2655 * Document-class: Ractor::MovedError
2656 *
2657 * Raised on an attempt to access an object which was moved in Ractor#send or Ractor.yield.
2658 *
2659 * r = Ractor.new { sleep }
2660 *
2661 * ary = [1, 2, 3]
2662 * r.send(ary, move: true)
2663 * ary.inspect
2664 * # Ractor::MovedError (can not send any methods to a moved object)
2665 *
2666 */
2667
2668/*
2669 * Document-class: Ractor::MovedObject
2670 *
2671 * A special object which replaces any value that was moved to another ractor in Ractor#send
2672 * or Ractor.yield. Any attempt to access the object results in Ractor::MovedError.
2673 *
2674 * r = Ractor.new { receive }
2675 *
2676 * ary = [1, 2, 3]
2677 * r.send(ary, move: true)
2678 * p Ractor::MovedObject === ary
2679 * # => true
2680 * ary.inspect
2681 * # Ractor::MovedError (can not send any methods to a moved object)
2682 */
2683
2684// Main docs are in ractor.rb, but without this clause there are weird artifacts
2685// in their rendering.
2686/*
2687 * Document-class: Ractor
2688 *
2689 */
2690
2691void
2692Init_Ractor(void)
2693{
2694 rb_cRactor = rb_define_class("Ractor", rb_cObject);
2695 rb_undef_alloc_func(rb_cRactor);
2696
2697 rb_eRactorError = rb_define_class_under(rb_cRactor, "Error", rb_eRuntimeError);
2698 rb_eRactorIsolationError = rb_define_class_under(rb_cRactor, "IsolationError", rb_eRactorError);
2699 rb_eRactorRemoteError = rb_define_class_under(rb_cRactor, "RemoteError", rb_eRactorError);
2700 rb_eRactorMovedError = rb_define_class_under(rb_cRactor, "MovedError", rb_eRactorError);
2701 rb_eRactorClosedError = rb_define_class_under(rb_cRactor, "ClosedError", rb_eStopIteration);
2702 rb_eRactorUnsafeError = rb_define_class_under(rb_cRactor, "UnsafeError", rb_eRactorError);
2703
2704 rb_cRactorMovedObject = rb_define_class_under(rb_cRactor, "MovedObject", rb_cBasicObject);
2705 rb_undef_alloc_func(rb_cRactorMovedObject);
2706 rb_define_method(rb_cRactorMovedObject, "method_missing", ractor_moved_missing, -1);
2707
2708 // override methods defined in BasicObject
2709 rb_define_method(rb_cRactorMovedObject, "__send__", ractor_moved_missing, -1);
2710 rb_define_method(rb_cRactorMovedObject, "!", ractor_moved_missing, -1);
2711 rb_define_method(rb_cRactorMovedObject, "==", ractor_moved_missing, -1);
2712 rb_define_method(rb_cRactorMovedObject, "!=", ractor_moved_missing, -1);
2713 rb_define_method(rb_cRactorMovedObject, "__id__", ractor_moved_missing, -1);
2714 rb_define_method(rb_cRactorMovedObject, "equal?", ractor_moved_missing, -1);
2715 rb_define_method(rb_cRactorMovedObject, "instance_eval", ractor_moved_missing, -1);
2716 rb_define_method(rb_cRactorMovedObject, "instance_exec", ractor_moved_missing, -1);
2717
2718 // internal
2719
2720#if USE_RACTOR_SELECTOR
2721 rb_init_ractor_selector();
2722#endif
2723}
2724
2725void
2726rb_ractor_dump(void)
2727{
2728 rb_vm_t *vm = GET_VM();
2729 rb_ractor_t *r = 0;
2730
2731 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
2732 if (r != vm->ractor.main_ractor) {
2733 fprintf(stderr, "r:%u (%s)\n", r->pub.id, ractor_status_str(r->status_));
2734 }
2735 }
2736}
2737
2738 VALUE
2739rb_ractor_stdin(void)
2740{
2741 if (rb_ractor_main_p()) {
2742 return rb_stdin;
2743 }
2744 else {
2745 rb_ractor_t *cr = GET_RACTOR();
2746 return cr->r_stdin;
2747 }
2748}
2749
2750 VALUE
2751rb_ractor_stdout(void)
2752{
2753 if (rb_ractor_main_p()) {
2754 return rb_stdout;
2755 }
2756 else {
2757 rb_ractor_t *cr = GET_RACTOR();
2758 return cr->r_stdout;
2759 }
2760}
2761
2762 VALUE
2763rb_ractor_stderr(void)
2764{
2765 if (rb_ractor_main_p()) {
2766 return rb_stderr;
2767 }
2768 else {
2769 rb_ractor_t *cr = GET_RACTOR();
2770 return cr->r_stderr;
2771 }
2772}
2773
2774void
2775rb_ractor_stdin_set(VALUE in)
2776{
2777 if (rb_ractor_main_p()) {
2778 rb_stdin = in;
2779 }
2780 else {
2781 rb_ractor_t *cr = GET_RACTOR();
2782 RB_OBJ_WRITE(cr->pub.self, &cr->r_stdin, in);
2783 }
2784}
2785
2786void
2787rb_ractor_stdout_set(VALUE out)
2788{
2789 if (rb_ractor_main_p()) {
2790 rb_stdout = out;
2791 }
2792 else {
2793 rb_ractor_t *cr = GET_RACTOR();
2794 RB_OBJ_WRITE(cr->pub.self, &cr->r_stdout, out);
2795 }
2796}
2797
2798void
2799rb_ractor_stderr_set(VALUE err)
2800{
2801 if (rb_ractor_main_p()) {
2802 rb_stderr = err;
2803 }
2804 else {
2805 rb_ractor_t *cr = GET_RACTOR();
2806 RB_OBJ_WRITE(cr->pub.self, &cr->r_stderr, err);
2807 }
2808}
2809
2810rb_hook_list_t *
2811rb_ractor_hooks(rb_ractor_t *cr)
2812{
2813 return &cr->pub.hooks;
2814}
2815
2817
2818// 2: stop search
2819// 1: skip child
2820// 0: continue
2821
2822enum obj_traverse_iterator_result {
2823 traverse_cont,
2824 traverse_skip,
2825 traverse_stop,
2826};
2827
2828typedef enum obj_traverse_iterator_result (*rb_obj_traverse_enter_func)(VALUE obj);
2829typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(VALUE obj);
2830typedef enum obj_traverse_iterator_result (*rb_obj_traverse_final_func)(VALUE obj);
2831
2832static enum obj_traverse_iterator_result null_leave(VALUE obj);
2833
2834 struct obj_traverse_data {
2835 rb_obj_traverse_enter_func enter_func;
2836 rb_obj_traverse_leave_func leave_func;
2837
2838 st_table *rec;
2839 VALUE rec_hash;
2840};
2841
2842
2843 struct obj_traverse_callback_data {
2844 bool stop;
2845 struct obj_traverse_data *data;
2846};
2847
2848static int obj_traverse_i(VALUE obj, struct obj_traverse_data *data);
2849
2850static int
2851obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
2852{
2853 struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
2854
2855 if (obj_traverse_i(key, d->data)) {
2856 d->stop = true;
2857 return ST_STOP;
2858 }
2859
2860 if (obj_traverse_i(val, d->data)) {
2861 d->stop = true;
2862 return ST_STOP;
2863 }
2864
2865 return ST_CONTINUE;
2866}
2867
2868static void
2869obj_traverse_reachable_i(VALUE obj, void *ptr)
2870{
2871 struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
2872
2873 if (obj_traverse_i(obj, d->data)) {
2874 d->stop = true;
2875 }
2876}
2877
2878static struct st_table *
2879obj_traverse_rec(struct obj_traverse_data *data)
2880{
2881 if (UNLIKELY(!data->rec)) {
2882 data->rec_hash = rb_ident_hash_new();
2883 data->rec = RHASH_ST_TABLE(data->rec_hash);
2884 }
2885 return data->rec;
2886}
2887
2888static int
2889obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr)
2890{
2891 struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
2892
2893 if (obj_traverse_i(val, d->data)) {
2894 d->stop = true;
2895 return ST_STOP;
2896 }
2897
2898 return ST_CONTINUE;
2899}
2900
2901static int
2902obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
2903{
2904 if (RB_SPECIAL_CONST_P(obj)) return 0;
2905
2906 switch (data->enter_func(obj)) {
2907 case traverse_cont: break;
2908 case traverse_skip: return 0; // skip children
2909 case traverse_stop: return 1; // stop search
2910 }
2911
2912 if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
2913 // already traversed
2914 return 0;
2915 }
2916 RB_OBJ_WRITTEN(data->rec_hash, Qundef, obj);
2917
2918 struct obj_traverse_callback_data d = {
2919 .stop = false,
2920 .data = data,
2921 };
2922 rb_ivar_foreach(obj, obj_traverse_ivar_foreach_i, (st_data_t)&d);
2923 if (d.stop) return 1;
2924
2925 switch (BUILTIN_TYPE(obj)) {
2926 // no child node
2927 case T_STRING:
2928 case T_FLOAT:
2929 case T_BIGNUM:
2930 case T_REGEXP:
2931 case T_FILE:
2932 case T_SYMBOL:
2933 case T_MATCH:
2934 break;
2935
2936 case T_OBJECT:
2937 /* Instance variables already traversed. */
2938 break;
2939
2940 case T_ARRAY:
2941 {
2942 for (int i = 0; i < RARRAY_LENINT(obj); i++) {
2943 VALUE e = rb_ary_entry(obj, i);
2944 if (obj_traverse_i(e, data)) return 1;
2945 }
2946 }
2947 break;
2948
2949 case T_HASH:
2950 {
2951 if (obj_traverse_i(RHASH_IFNONE(obj), data)) return 1;
2952
2953 struct obj_traverse_callback_data d = {
2954 .stop = false,
2955 .data = data,
2956 };
2957 rb_hash_foreach(obj, obj_hash_traverse_i, (VALUE)&d);
2958 if (d.stop) return 1;
2959 }
2960 break;
2961
2962 case T_STRUCT:
2963 {
2964 long len = RSTRUCT_LEN(obj);
2965 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2966
2967 for (long i=0; i<len; i++) {
2968 if (obj_traverse_i(ptr[i], data)) return 1;
2969 }
2970 }
2971 break;
2972
2973 case T_RATIONAL:
2974 if (obj_traverse_i(RRATIONAL(obj)->num, data)) return 1;
2975 if (obj_traverse_i(RRATIONAL(obj)->den, data)) return 1;
2976 break;
2977 case T_COMPLEX:
2978 if (obj_traverse_i(RCOMPLEX(obj)->real, data)) return 1;
2979 if (obj_traverse_i(RCOMPLEX(obj)->imag, data)) return 1;
2980 break;
2981
2982 case T_DATA:
2983 case T_IMEMO:
2984 {
2985 struct obj_traverse_callback_data d = {
2986 .stop = false,
2987 .data = data,
2988 };
2989 RB_VM_LOCK_ENTER_NO_BARRIER();
2990 {
2991 rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
2992 }
2993 RB_VM_LOCK_LEAVE_NO_BARRIER();
2994 if (d.stop) return 1;
2995 }
2996 break;
2997
2998 // unreachable
2999 case T_CLASS:
3000 case T_MODULE:
3001 case T_ICLASS:
3002 default:
3003 rp(obj);
3004 rb_bug("unreachable");
3005 }
3006
3007 if (data->leave_func(obj) == traverse_stop) {
3008 return 1;
3009 }
3010 else {
3011 return 0;
3012 }
3013}
3014
3015 struct rb_obj_traverse_final_data {
3016 rb_obj_traverse_final_func final_func;
3017 int stopped;
3018};
3019
3020static int
3021obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
3022{
3023 struct rb_obj_traverse_final_data *data = (void *)arg;
3024 if (data->final_func(key)) {
3025 data->stopped = 1;
3026 return ST_STOP;
3027 }
3028 return ST_CONTINUE;
3029}
3030
3031// 0: traverse all
3032// 1: stopped
3033static int
3034rb_obj_traverse(VALUE obj,
3035 rb_obj_traverse_enter_func enter_func,
3036 rb_obj_traverse_leave_func leave_func,
3037 rb_obj_traverse_final_func final_func)
3038{
3039 struct obj_traverse_data data = {
3040 .enter_func = enter_func,
3041 .leave_func = leave_func,
3042 .rec = NULL,
3043 };
3044
3045 if (obj_traverse_i(obj, &data)) return 1;
3046 if (final_func && data.rec) {
3047 struct rb_obj_traverse_final_data f = {final_func, 0};
3048 st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
3049 return f.stopped;
3050 }
3051 return 0;
3052}
3053
3054static int
3055frozen_shareable_p(VALUE obj, bool *made_shareable)
3056{
3057 if (!RB_TYPE_P(obj, T_DATA)) {
3058 return true;
3059 }
3060 else if (RTYPEDDATA_P(obj)) {
3061 const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
3062 if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
3063 return true;
3064 }
3065 else if (made_shareable && rb_obj_is_proc(obj)) {
3066 // special path to make shareable Proc.
3067 rb_proc_ractor_make_shareable(obj);
3068 *made_shareable = true;
3069 VM_ASSERT(RB_OBJ_SHAREABLE_P(obj));
3070 return false;
3071 }
3072 }
3073
3074 return false;
3075}
3076
3077static enum obj_traverse_iterator_result
3078make_shareable_check_shareable(VALUE obj)
3079{
3080 VM_ASSERT(!SPECIAL_CONST_P(obj));
3081 bool made_shareable = false;
3082
3083 if (rb_ractor_shareable_p(obj)) {
3084 return traverse_skip;
3085 }
3086 if (!frozen_shareable_p(obj, &made_shareable)) {
3087 if (made_shareable) {
3088 return traverse_skip;
3089 }
3090 else {
3091 rb_raise(rb_eRactorError, "can not make shareable object for %"PRIsVALUE, obj);
3092 }
3093 }
3094
3095 if (!RB_OBJ_FROZEN_RAW(obj)) {
3096 rb_funcall(obj, idFreeze, 0);
3097
3098 if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
3099 rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
3100 }
3101
3102 if (RB_OBJ_SHAREABLE_P(obj)) {
3103 return traverse_skip;
3104 }
3105 }
3106
3107 return traverse_cont;
3108}
3109
3110static enum obj_traverse_iterator_result
3111mark_shareable(VALUE obj)
3112{
3113 FL_SET_RAW(obj, RUBY_FL_SHAREABLE);
3114 return traverse_cont;
3115}
3116
3117 VALUE
3118rb_ractor_make_shareable(VALUE obj)
3119{
3120 rb_obj_traverse(obj,
3121 make_shareable_check_shareable,
3122 null_leave, mark_shareable);
3123 return obj;
3124}
3125
3126 VALUE
3127rb_ractor_make_shareable_copy(VALUE obj)
3128{
3129 VALUE copy = ractor_copy(obj);
3130 return rb_ractor_make_shareable(copy);
3131}
3132
3133 VALUE
3134rb_ractor_ensure_shareable(VALUE obj, VALUE name)
3135{
3136 if (!rb_ractor_shareable_p(obj)) {
3137 VALUE message = rb_sprintf("cannot assign unshareable object to %"PRIsVALUE,
3138 name);
3139 rb_exc_raise(rb_exc_new_str(rb_eRactorIsolationError, message));
3140 }
3141 return obj;
3142}
3143
3144void
3145rb_ractor_ensure_main_ractor(const char *msg)
3146{
3147 if (!rb_ractor_main_p()) {
3148 rb_raise(rb_eRactorIsolationError, "%s", msg);
3149 }
3150}
3151
3152static enum obj_traverse_iterator_result
3153shareable_p_enter(VALUE obj)
3154{
3155 if (RB_OBJ_SHAREABLE_P(obj)) {
3156 return traverse_skip;
3157 }
3158 else if (RB_TYPE_P(obj, T_CLASS) ||
3159 RB_TYPE_P(obj, T_MODULE) ||
3160 RB_TYPE_P(obj, T_ICLASS)) {
3161 // TODO: remove it
3162 mark_shareable(obj);
3163 return traverse_skip;
3164 }
3165 else if (RB_OBJ_FROZEN_RAW(obj) &&
3166 frozen_shareable_p(obj, NULL)) {
3167 return traverse_cont;
3168 }
3169
3170 return traverse_stop; // fail
3171}
3172
3173bool
3174rb_ractor_shareable_p_continue(VALUE obj)
3175{
3176 if (rb_obj_traverse(obj,
3177 shareable_p_enter, null_leave,
3178 mark_shareable)) {
3179 return false;
3180 }
3181 else {
3182 return true;
3183 }
3184}
3185
3186#if RACTOR_CHECK_MODE > 0
3187void
3188rb_ractor_setup_belonging(VALUE obj)
3189{
3190 rb_ractor_setup_belonging_to(obj, rb_ractor_current_id());
3191}
3192
3193static enum obj_traverse_iterator_result
3194reset_belonging_enter(VALUE obj)
3195{
3196 if (rb_ractor_shareable_p(obj)) {
3197 return traverse_skip;
3198 }
3199 else {
3200 rb_ractor_setup_belonging(obj);
3201 return traverse_cont;
3202 }
3203}
3204#endif
3205
3206static enum obj_traverse_iterator_result
3207null_leave(VALUE obj)
3208{
3209 return traverse_cont;
3210}
3211
3212static VALUE
3213ractor_reset_belonging(VALUE obj)
3214{
3215#if RACTOR_CHECK_MODE > 0
3216 rb_obj_traverse(obj, reset_belonging_enter, null_leave, NULL);
3217#endif
3218 return obj;
3219}
3220
3221
3223
3224// 2: stop search
3225// 1: skip child
3226// 0: continue
3227
3228struct obj_traverse_replace_data;
3229static int obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data);
3230typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_enter_func)(VALUE obj, struct obj_traverse_replace_data *data);
3231typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_leave_func)(VALUE obj, struct obj_traverse_replace_data *data);
3232
3233 struct obj_traverse_replace_data {
3234 rb_obj_traverse_replace_enter_func enter_func;
3235 rb_obj_traverse_replace_leave_func leave_func;
3236
3237 st_table *rec;
3238 VALUE rec_hash;
3239
3240 VALUE replacement;
3241 bool move;
3242};
3243
3244 struct obj_traverse_replace_callback_data {
3245 bool stop;
3246 VALUE src;
3247 struct obj_traverse_replace_data *data;
3248};
3249
3250static int
3251obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp, int error)
3252{
3253 return ST_REPLACE;
3254}
3255
3256static int
3257obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr, int exists)
3258{
3259 struct obj_traverse_replace_callback_data *d = (struct obj_traverse_replace_callback_data *)ptr;
3260 struct obj_traverse_replace_data *data = d->data;
3261
3262 if (obj_traverse_replace_i(*key, data)) {
3263 d->stop = true;
3264 return ST_STOP;
3265 }
3266 else if (*key != data->replacement) {
3267 VALUE v = *key = data->replacement;
3268 RB_OBJ_WRITTEN(d->src, Qundef, v);
3269 }
3270
3271 if (obj_traverse_replace_i(*val, data)) {
3272 d->stop = true;
3273 return ST_STOP;
3274 }
3275 else if (*val != data->replacement) {
3276 VALUE v = *val = data->replacement;
3277 RB_OBJ_WRITTEN(d->src, Qundef, v);
3278 }
3279
3280 return ST_CONTINUE;
3281}
3282
3283static int
3284obj_iv_hash_traverse_replace_foreach_i(st_data_t _key, st_data_t _val, st_data_t _data, int _x)
3285{
3286 return ST_REPLACE;
3287}
3288
3289static int
3290obj_iv_hash_traverse_replace_i(st_data_t * _key, st_data_t * val, st_data_t ptr, int exists)
3291{
3292 struct obj_traverse_replace_callback_data *d = (struct obj_traverse_replace_callback_data *)ptr;
3293 struct obj_traverse_replace_data *data = d->data;
3294
3295 if (obj_traverse_replace_i(*(VALUE *)val, data)) {
3296 d->stop = true;
3297 return ST_STOP;
3298 }
3299 else if (*(VALUE *)val != data->replacement) {
3300 VALUE v = *(VALUE *)val = data->replacement;
3301 RB_OBJ_WRITTEN(d->src, Qundef, v);
3302 }
3303
3304 return ST_CONTINUE;
3305}
3306
3307static struct st_table *
3308obj_traverse_replace_rec(struct obj_traverse_replace_data *data)
3309{
3310 if (UNLIKELY(!data->rec)) {
3311 data->rec_hash = rb_ident_hash_new();
3312 data->rec = RHASH_ST_TABLE(data->rec_hash);
3313 }
3314 return data->rec;
3315}
3316
3317static void
3318obj_refer_only_shareables_p_i(VALUE obj, void *ptr)
3319{
3320 int *pcnt = (int *)ptr;
3321
3322 if (!rb_ractor_shareable_p(obj)) {
3323 ++*pcnt;
3324 }
3325}
3326
3327static int
3328obj_refer_only_shareables_p(VALUE obj)
3329{
3330 int cnt = 0;
3331 RB_VM_LOCK_ENTER_NO_BARRIER();
3332 {
3333 rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
3334 }
3335 RB_VM_LOCK_LEAVE_NO_BARRIER();
3336 return cnt == 0;
3337}
3338
3339static int
3340obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
3341{
3342 st_data_t replacement;
3343
3344 if (RB_SPECIAL_CONST_P(obj)) {
3345 data->replacement = obj;
3346 return 0;
3347 }
3348
3349 switch (data->enter_func(obj, data)) {
3350 case traverse_cont: break;
3351 case traverse_skip: return 0; // skip children
3352 case traverse_stop: return 1; // stop search
3353 }
3354
3355 replacement = (st_data_t)data->replacement;
3356
3357 if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, &replacement))) {
3358 data->replacement = (VALUE)replacement;
3359 return 0;
3360 }
3361 else {
3362 st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, replacement);
3363 RB_OBJ_WRITTEN(data->rec_hash, Qundef, obj);
3364 RB_OBJ_WRITTEN(data->rec_hash, Qundef, replacement);
3365 }
3366
3367 if (!data->move) {
3368 obj = replacement;
3369 }
3370
3371#define CHECK_AND_REPLACE(v) do { \
3372 VALUE _val = (v); \
3373 if (obj_traverse_replace_i(_val, data)) { return 1; } \
3374 else if (data->replacement != _val) { RB_OBJ_WRITE(obj, &v, data->replacement); } \
3375} while (0)
3376
3377 if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
3378 struct gen_ivtbl *ivtbl;
3379 rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
3380
3381 if (UNLIKELY(rb_shape_obj_too_complex(obj))) {
3382 struct obj_traverse_replace_callback_data d = {
3383 .stop = false,
3384 .data = data,
3385 .src = obj,
3386 };
3387 rb_st_foreach_with_replace(
3388 ivtbl->as.complex.table,
3389 obj_iv_hash_traverse_replace_foreach_i,
3390 obj_iv_hash_traverse_replace_i,
3391 (st_data_t)&d
3392 );
3393 if (d.stop) return 1;
3394 }
3395 else {
3396 for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
3397 if (!UNDEF_P(ivtbl->as.shape.ivptr[i])) {
3398 CHECK_AND_REPLACE(ivtbl->as.shape.ivptr[i]);
3399 }
3400 }
3401 }
3402 }
3403
3404 switch (BUILTIN_TYPE(obj)) {
3405 // no child node
3406 case T_FLOAT:
3407 case T_BIGNUM:
3408 case T_REGEXP:
3409 case T_FILE:
3410 case T_SYMBOL:
3411 case T_MATCH:
3412 break;
3413 case T_STRING:
3414 rb_str_make_independent(obj);
3415 break;
3416
3417 case T_OBJECT:
3418 {
3419 if (rb_shape_obj_too_complex(obj)) {
3420 struct obj_traverse_replace_callback_data d = {
3421 .stop = false,
3422 .data = data,
3423 .src = obj,
3424 };
3425 rb_st_foreach_with_replace(
3426 ROBJECT_IV_HASH(obj),
3427 obj_iv_hash_traverse_replace_foreach_i,
3428 obj_iv_hash_traverse_replace_i,
3429 (st_data_t)&d
3430 );
3431 if (d.stop) return 1;
3432 }
3433 else {
3434 uint32_t len = ROBJECT_IV_COUNT(obj);
3435 VALUE *ptr = ROBJECT_IVPTR(obj);
3436
3437 for (uint32_t i = 0; i < len; i++) {
3438 CHECK_AND_REPLACE(ptr[i]);
3439 }
3440 }
3441 }
3442 break;
3443
3444 case T_ARRAY:
3445 {
3446 rb_ary_cancel_sharing(obj);
3447
3448 for (int i = 0; i < RARRAY_LENINT(obj); i++) {
3449 VALUE e = rb_ary_entry(obj, i);
3450
3451 if (obj_traverse_replace_i(e, data)) {
3452 return 1;
3453 }
3454 else if (e != data->replacement) {
3455 RARRAY_ASET(obj, i, data->replacement);
3456 }
3457 }
3458 RB_GC_GUARD(obj);
3459 }
3460 break;
3461 case T_HASH:
3462 {
3463 struct obj_traverse_replace_callback_data d = {
3464 .stop = false,
3465 .data = data,
3466 .src = obj,
3467 };
3468 rb_hash_stlike_foreach_with_replace(obj,
3469 obj_hash_traverse_replace_foreach_i,
3470 obj_hash_traverse_replace_i,
3471 (VALUE)&d);
3472 if (d.stop) return 1;
3473 // TODO: rehash here?
3474
3475 VALUE ifnone = RHASH_IFNONE(obj);
3476 if (obj_traverse_replace_i(ifnone, data)) {
3477 return 1;
3478 }
3479 else if (ifnone != data->replacement) {
3480 RHASH_SET_IFNONE(obj, data->replacement);
3481 }
3482 }
3483 break;
3484
3485 case T_STRUCT:
3486 {
3487 long len = RSTRUCT_LEN(obj);
3488 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
3489
3490 for (long i=0; i<len; i++) {
3491 CHECK_AND_REPLACE(ptr[i]);
3492 }
3493 }
3494 break;
3495
3496 case T_RATIONAL:
3497 CHECK_AND_REPLACE(RRATIONAL(obj)->num);
3498 CHECK_AND_REPLACE(RRATIONAL(obj)->den);
3499 break;
3500 case T_COMPLEX:
3501 CHECK_AND_REPLACE(RCOMPLEX(obj)->real);
3502 CHECK_AND_REPLACE(RCOMPLEX(obj)->imag);
3503 break;
3504
3505 case T_DATA:
3506 if (!data->move && obj_refer_only_shareables_p(obj)) {
3507 break;
3508 }
3509 else {
3510 rb_raise(rb_eRactorError, "can not %s %"PRIsVALUE" object.",
3511 data->move ? "move" : "copy", rb_class_of(obj));
3512 }
3513
3514 case T_IMEMO:
3515 // not supported yet
3516 return 1;
3517
3518 // unreachable
3519 case T_CLASS:
3520 case T_MODULE:
3521 case T_ICLASS:
3522 default:
3523 rp(obj);
3524 rb_bug("unreachable");
3525 }
3526
3527 data->replacement = (VALUE)replacement;
3528
3529 if (data->leave_func(obj, data) == traverse_stop) {
3530 return 1;
3531 }
3532 else {
3533 return 0;
3534 }
3535}
3536
3537// 0: traverse all
3538// 1: stopped
3539static VALUE
3540rb_obj_traverse_replace(VALUE obj,
3541 rb_obj_traverse_replace_enter_func enter_func,
3542 rb_obj_traverse_replace_leave_func leave_func,
3543 bool move)
3544{
3545 struct obj_traverse_replace_data data = {
3546 .enter_func = enter_func,
3547 .leave_func = leave_func,
3548 .rec = NULL,
3549 .replacement = Qundef,
3550 .move = move,
3551 };
3552
3553 if (obj_traverse_replace_i(obj, &data)) {
3554 return Qundef;
3555 }
3556 else {
3557 return data.replacement;
3558 }
3559}
3560
3561 struct RVALUE {
3562 VALUE flags;
3563 VALUE klass;
3564 VALUE v1;
3565 VALUE v2;
3566 VALUE v3;
3567};
3568
3569static const VALUE fl_users = FL_USER1 | FL_USER2 | FL_USER3 |
3570 FL_USER4 | FL_USER5 | FL_USER6 | FL_USER7 |
3571 FL_USER8 | FL_USER9 | FL_USER10 | FL_USER11 |
3572 FL_USER12 | FL_USER13 | FL_USER14 | FL_USER15 |
3573 FL_USER16 | FL_USER17 | FL_USER18 | FL_USER19;
3574
3575static void
3576ractor_moved_bang(VALUE obj)
3577{
3578 // invalidate src object
3579 struct RVALUE *rv = (void *)obj;
3580
3581 rv->klass = rb_cRactorMovedObject;
3582 rv->v1 = 0;
3583 rv->v2 = 0;
3584 rv->v3 = 0;
3585 rv->flags = rv->flags & ~fl_users;
3586
3587 if (BUILTIN_TYPE(obj) == T_OBJECT) ROBJECT_SET_SHAPE_ID(obj, ROOT_SHAPE_ID);
3588
3589 // TODO: record moved location
3590}
3591
3592static enum obj_traverse_iterator_result
3593move_enter(VALUE obj, struct obj_traverse_replace_data *data)
3594{
3595 if (rb_ractor_shareable_p(obj)) {
3596 data->replacement = obj;
3597 return traverse_skip;
3598 }
3599 else {
3600 VALUE moved = rb_obj_alloc(RBASIC_CLASS(obj));
3601 rb_shape_set_shape(moved, rb_shape_get_shape(obj));
3602 data->replacement = moved;
3603 return traverse_cont;
3604 }
3605}
3606
3607void rb_replace_generic_ivar(VALUE clone, VALUE obj); // variable.c
3608
3609static enum obj_traverse_iterator_result
3610move_leave(VALUE obj, struct obj_traverse_replace_data *data)
3611{
3612 VALUE v = data->replacement;
3613 struct RVALUE *dst = (struct RVALUE *)v;
3614 struct RVALUE *src = (struct RVALUE *)obj;
3615
3616 dst->flags = (dst->flags & ~fl_users) | (src->flags & fl_users);
3617
3618 dst->v1 = src->v1;
3619 dst->v2 = src->v2;
3620 dst->v3 = src->v3;
3621
3622 if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
3623 rb_replace_generic_ivar(v, obj);
3624 }
3625
3626 if (OBJ_FROZEN(obj)) {
3627 OBJ_FREEZE(v);
3628 }
3629
3630 // TODO: generic_ivar
3631
3632 ractor_moved_bang(obj);
3633 return traverse_cont;
3634}
3635
3636static VALUE
3637ractor_move(VALUE obj)
3638{
3639 VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave, true);
3640 if (!UNDEF_P(val)) {
3641 return val;
3642 }
3643 else {
3644 rb_raise(rb_eRactorError, "can not move the object");
3645 }
3646}
3647
3648static enum obj_traverse_iterator_result
3649copy_enter(VALUE obj, struct obj_traverse_replace_data *data)
3650{
3651 if (rb_ractor_shareable_p(obj)) {
3652 data->replacement = obj;
3653 return traverse_skip;
3654 }
3655 else {
3656 data->replacement = rb_obj_clone(obj);
3657 return traverse_cont;
3658 }
3659}
3660
3661static enum obj_traverse_iterator_result
3662copy_leave(VALUE obj, struct obj_traverse_replace_data *data)
3663{
3664 return traverse_cont;
3665}
3666
3667static VALUE
3668ractor_copy(VALUE obj)
3669{
3670 VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave, false);
3671 if (!UNDEF_P(val)) {
3672 return val;
3673 }
3674 else {
3675 rb_raise(rb_eRactorError, "can not copy the object");
3676 }
3677}
3678
3679// Ractor local storage
3680
3681 struct rb_ractor_local_key_struct {
3682 const struct rb_ractor_local_storage_type *type;
3683 void *main_cache;
3684};
3685
3686 static struct freed_ractor_local_keys_struct {
3687 int cnt;
3688 int capa;
3689 rb_ractor_local_key_t *keys;
3690} freed_ractor_local_keys;
3691
3692static int
3693ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
3694{
3695 struct rb_ractor_local_key_struct *k = (struct rb_ractor_local_key_struct *)key;
3696 if (k->type->mark) (*k->type->mark)((void *)val);
3697 return ST_CONTINUE;
3698}
3699
3700static enum rb_id_table_iterator_result
3701idkey_local_storage_mark_i(VALUE val, void *dmy)
3702{
3703 rb_gc_mark(val);
3704 return ID_TABLE_CONTINUE;
3705}
3706
3707static void
3708ractor_local_storage_mark(rb_ractor_t *r)
3709{
3710 if (r->local_storage) {
3711 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
3712
3713 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
3714 rb_ractor_local_key_t key = freed_ractor_local_keys.keys[i];
3715 st_data_t val, k = (st_data_t)key;
3716 if (st_delete(r->local_storage, &k, &val) &&
3717 (key = (rb_ractor_local_key_t)k)->type->free) {
3718 (*key->type->free)((void *)val);
3719 }
3720 }
3721 }
3722
3723 if (r->idkey_local_storage) {
3724 rb_id_table_foreach_values(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
3725 }
3726
3727 rb_gc_mark(r->local_storage_store_lock);
3728}
3729
3730static int
3731ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
3732{
3733 struct rb_ractor_local_key_struct *k = (struct rb_ractor_local_key_struct *)key;
3734 if (k->type->free) (*k->type->free)((void *)val);
3735 return ST_CONTINUE;
3736}
3737
3738static void
3739ractor_local_storage_free(rb_ractor_t *r)
3740{
3741 if (r->local_storage) {
3742 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
3743 st_free_table(r->local_storage);
3744 }
3745
3746 if (r->idkey_local_storage) {
3747 rb_id_table_free(r->idkey_local_storage);
3748 }
3749}
3750
3751static void
3752rb_ractor_local_storage_value_mark(void *ptr)
3753{
3754 rb_gc_mark((VALUE)ptr);
3755}
3756
3757static const struct rb_ractor_local_storage_type ractor_local_storage_type_null = {
3758 NULL,
3759 NULL,
3760};
3761
3762 const struct rb_ractor_local_storage_type rb_ractor_local_storage_type_free = {
3763 NULL,
3764 ruby_xfree,
3765};
3766
3767static const struct rb_ractor_local_storage_type ractor_local_storage_type_value = {
3768 rb_ractor_local_storage_value_mark,
3769 NULL,
3770};
3771
3772 rb_ractor_local_key_t
3773rb_ractor_local_storage_ptr_newkey(const struct rb_ractor_local_storage_type *type)
3774{
3775 rb_ractor_local_key_t key = ALLOC(struct rb_ractor_local_key_struct);
3776 key->type = type ? type : &ractor_local_storage_type_null;
3777 key->main_cache = (void *)Qundef;
3778 return key;
3779}
3780
3781 rb_ractor_local_key_t
3782rb_ractor_local_storage_value_newkey(void)
3783{
3784 return rb_ractor_local_storage_ptr_newkey(&ractor_local_storage_type_value);
3785}
3786
3787void
3788rb_ractor_local_storage_delkey(rb_ractor_local_key_t key)
3789{
3790 RB_VM_LOCK_ENTER();
3791 {
3792 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
3793 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
3794 REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, freed_ractor_local_keys.capa);
3795 }
3796 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
3797 }
3798 RB_VM_LOCK_LEAVE();
3799}
3800
3801static bool
3802ractor_local_ref(rb_ractor_local_key_t key, void **pret)
3803{
3804 if (rb_ractor_main_p()) {
3805 if (!UNDEF_P((VALUE)key->main_cache)) {
3806 *pret = key->main_cache;
3807 return true;
3808 }
3809 else {
3810 return false;
3811 }
3812 }
3813 else {
3814 rb_ractor_t *cr = GET_RACTOR();
3815
3816 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
3817 return true;
3818 }
3819 else {
3820 return false;
3821 }
3822 }
3823}
3824
3825static void
3826ractor_local_set(rb_ractor_local_key_t key, void *ptr)
3827{
3828 rb_ractor_t *cr = GET_RACTOR();
3829
3830 if (cr->local_storage == NULL) {
3831 cr->local_storage = st_init_numtable();
3832 }
3833
3834 st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
3835
3836 if (rb_ractor_main_p()) {
3837 key->main_cache = ptr;
3838 }
3839}
3840
3841 VALUE
3842rb_ractor_local_storage_value(rb_ractor_local_key_t key)
3843{
3844 void *val;
3845 if (ractor_local_ref(key, &val)) {
3846 return (VALUE)val;
3847 }
3848 else {
3849 return Qnil;
3850 }
3851}
3852
3853bool
3854rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
3855{
3856 if (ractor_local_ref(key, (void **)val)) {
3857 return true;
3858 }
3859 else {
3860 return false;
3861 }
3862}
3863
3864void
3865rb_ractor_local_storage_value_set(rb_ractor_local_key_t key, VALUE val)
3866{
3867 ractor_local_set(key, (void *)val);
3868}
3869
3870void *
3871rb_ractor_local_storage_ptr(rb_ractor_local_key_t key)
3872{
3873 void *ret;
3874 if (ractor_local_ref(key, &ret)) {
3875 return ret;
3876 }
3877 else {
3878 return NULL;
3879 }
3880}
3881
3882void
3883rb_ractor_local_storage_ptr_set(rb_ractor_local_key_t key, void *ptr)
3884{
3885 ractor_local_set(key, ptr);
3886}
3887
3888#define DEFAULT_KEYS_CAPA 0x10
3889
3890void
3891rb_ractor_finish_marking(void)
3892{
3893 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
3894 ruby_xfree(freed_ractor_local_keys.keys[i]);
3895 }
3896 freed_ractor_local_keys.cnt = 0;
3897 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
3898 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
3899 REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, DEFAULT_KEYS_CAPA);
3900 }
3901}
3902
3903static VALUE
3904ractor_local_value(rb_execution_context_t *ec, VALUE self, VALUE sym)
3905{
3906 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3907 ID id = rb_check_id(&sym);
3908 struct rb_id_table *tbl = cr->idkey_local_storage;
3909 VALUE val;
3910
3911 if (id && tbl && rb_id_table_lookup(tbl, id, &val)) {
3912 return val;
3913 }
3914 else {
3915 return Qnil;
3916 }
3917}
3918
3919static VALUE
3920ractor_local_value_set(rb_execution_context_t *ec, VALUE self, VALUE sym, VALUE val)
3921{
3922 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3923 ID id = SYM2ID(rb_to_symbol(sym));
3924 struct rb_id_table *tbl = cr->idkey_local_storage;
3925
3926 if (tbl == NULL) {
3927 tbl = cr->idkey_local_storage = rb_id_table_create(2);
3928 }
3929 rb_id_table_insert(tbl, id, val);
3930 return val;
3931}
3932
3933 struct ractor_local_storage_store_data {
3934 rb_execution_context_t *ec;
3935 struct rb_id_table *tbl;
3936 ID id;
3937 VALUE sym;
3938};
3939
3940static VALUE
3941ractor_local_value_store_i(VALUE ptr)
3942{
3943 VALUE val;
3944 struct ractor_local_storage_store_data *data = (struct ractor_local_storage_store_data *)ptr;
3945
3946 if (rb_id_table_lookup(data->tbl, data->id, &val)) {
3947 // after synchronization, we found already registered entry
3948 }
3949 else {
3950 val = rb_yield(Qnil);
3951 ractor_local_value_set(data->ec, Qnil, data->sym, val);
3952 }
3953 return val;
3954}
3955
3956static VALUE
3957ractor_local_value_store_if_absent(rb_execution_context_t *ec, VALUE self, VALUE sym)
3958{
3959 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3960 struct ractor_local_storage_store_data data = {
3961 .ec = ec,
3962 .sym = sym,
3963 .id = SYM2ID(rb_to_symbol(sym)),
3964 .tbl = cr->idkey_local_storage,
3965 };
3966 VALUE val;
3967
3968 if (data.tbl == NULL) {
3969 data.tbl = cr->idkey_local_storage = rb_id_table_create(2);
3970 }
3971 else if (rb_id_table_lookup(data.tbl, data.id, &val)) {
3972 // already set
3973 return val;
3974 }
3975
3976 if (!cr->local_storage_store_lock) {
3977 cr->local_storage_store_lock = rb_mutex_new();
3978 }
3979
3980 return rb_mutex_synchronize(cr->local_storage_store_lock, ractor_local_value_store_i, (VALUE)&data);
3981}
3982
3983// Ractor::Channel (emulate with Ractor)
3984
3985typedef rb_ractor_t rb_ractor_channel_t;
3986
3987static VALUE
3988ractor_channel_func(RB_BLOCK_CALL_FUNC_ARGLIST(y, c))
3989{
3990 rb_execution_context_t *ec = GET_EC();
3991 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3992
3993 while (1) {
3994 int state;
3995
3996 EC_PUSH_TAG(ec);
3997 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
3998 VALUE obj = ractor_receive(ec, cr);
3999 ractor_yield(ec, cr, obj, Qfalse);
4000 }
4001 EC_POP_TAG();
4002
4003 if (state) {
4004 // ignore the error
4005 break;
4006 }
4007 }
4008
4009 return Qnil;
4010}
4011
4012static VALUE
4013rb_ractor_channel_new(void)
4014{
4015#if 0
4016 return rb_funcall(rb_const_get(rb_cRactor, rb_intern("Channel")), rb_intern("new"), 0);
4017#else
4018 // class Channel
4019 // def self.new
4020 // Ractor.new do # func body
4021 // while true
4022 // obj = Ractor.receive
4023 // Ractor.yield obj
4024 // end
4025 // rescue Ractor::ClosedError
4026 // nil
4027 // end
4028 // end
4029 // end
4030
4031 return ractor_create_func(rb_cRactor, Qnil, rb_str_new2("Ractor/channel"), rb_ary_new(), ractor_channel_func);
4032#endif
4033}
4034
4035static VALUE
4036rb_ractor_channel_yield(rb_execution_context_t *ec, VALUE vch, VALUE obj)
4037{
4038 VM_ASSERT(ec == rb_current_ec_noinline());
4039 rb_ractor_channel_t *ch = RACTOR_PTR(vch);
4040
4041 ractor_send(ec, (rb_ractor_t *)ch, obj, Qfalse);
4042 return Qnil;
4043}
4044
4045static VALUE
4046rb_ractor_channel_take(rb_execution_context_t *ec, VALUE vch)
4047{
4048 VM_ASSERT(ec == rb_current_ec_noinline());
4049 rb_ractor_channel_t *ch = RACTOR_PTR(vch);
4050
4051 return ractor_take(ec, (rb_ractor_t *)ch);
4052}
4053
4054static VALUE
4055rb_ractor_channel_close(rb_execution_context_t *ec, VALUE vch)
4056{
4057 VM_ASSERT(ec == rb_current_ec_noinline());
4058 rb_ractor_channel_t *ch = RACTOR_PTR(vch);
4059
4060 ractor_close_incoming(ec, (rb_ractor_t *)ch);
4061 return ractor_close_outgoing(ec, (rb_ractor_t *)ch);
4062}
4063
4064// Ractor#require
4065
4066 struct cross_ractor_require {
4067 VALUE ch;
4068 VALUE result;
4069 VALUE exception;
4070
4071 // require
4072 VALUE feature;
4073
4074 // autoload
4075 VALUE module;
4076 ID name;
4077};
4078
4079static VALUE
4080require_body(VALUE data)
4081{
4082 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4083
4084 ID require;
4085 CONST_ID(require, "require");
4086 crr->result = rb_funcallv(Qnil, require, 1, &crr->feature);
4087
4088 return Qnil;
4089}
4090
4091static VALUE
4092require_rescue(VALUE data, VALUE errinfo)
4093{
4094 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4095 crr->exception = errinfo;
4096 return Qundef;
4097}
4098
4099static VALUE
4100require_result_copy_body(VALUE data)
4101{
4102 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4103
4104 if (crr->exception != Qundef) {
4105 VM_ASSERT(crr->result == Qundef);
4106 crr->exception = ractor_copy(crr->exception);
4107 }
4108 else{
4109 VM_ASSERT(crr->result != Qundef);
4110 crr->result = ractor_copy(crr->result);
4111 }
4112
4113 return Qnil;
4114}
4115
4116static VALUE
4117require_result_copy_resuce(VALUE data, VALUE errinfo)
4118{
4119 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4120 crr->exception = errinfo; // ractor_move(crr->exception);
4121 return Qnil;
4122}
4123
4124static VALUE
4125ractor_require_protect(struct cross_ractor_require *crr, VALUE (*func)(VALUE))
4126{
4127 // catch any error
4128 rb_rescue2(func, (VALUE)crr,
4129 require_rescue, (VALUE)crr, rb_eException, 0);
4130
4131 rb_rescue2(require_result_copy_body, (VALUE)crr,
4132 require_result_copy_resuce, (VALUE)crr, rb_eException, 0);
4133
4134 rb_ractor_channel_yield(GET_EC(), crr->ch, Qtrue);
4135 return Qnil;
4136
4137}
4138
4139static VALUE
4140ractore_require_func(void *data)
4141{
4142 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4143 return ractor_require_protect(crr, require_body);
4144}
4145
4146 VALUE
4147rb_ractor_require(VALUE feature)
4148{
4149 // TODO: make feature shareable
4150 struct cross_ractor_require crr = {
4151 .feature = feature, // TODO: ractor
4152 .ch = rb_ractor_channel_new(),
4153 .result = Qundef,
4154 .exception = Qundef,
4155 };
4156
4157 rb_execution_context_t *ec = GET_EC();
4158 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
4159 rb_ractor_interrupt_exec(main_r, ractore_require_func, &crr, 0);
4160
4161 // wait for require done
4162 rb_ractor_channel_take(ec, crr.ch);
4163 rb_ractor_channel_close(ec, crr.ch);
4164
4165 if (crr.exception != Qundef) {
4166 rb_exc_raise(crr.exception);
4167 }
4168 else {
4169 return crr.result;
4170 }
4171}
4172
4173static VALUE
4174ractor_require(rb_execution_context_t *ec, VALUE self, VALUE feature)
4175{
4176 return rb_ractor_require(feature);
4177}
4178
4179static VALUE
4180autoload_load_body(VALUE data)
4181{
4182 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4183 crr->result = rb_autoload_load(crr->module, crr->name);
4184 return Qnil;
4185}
4186
4187static VALUE
4188ractor_autoload_load_func(void *data)
4189{
4190 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4191 return ractor_require_protect(crr, autoload_load_body);
4192}
4193
4194 VALUE
4195rb_ractor_autoload_load(VALUE module, ID name)
4196{
4197 struct cross_ractor_require crr = {
4198 .module = module,
4199 .name = name,
4200 .ch = rb_ractor_channel_new(),
4201 .result = Qundef,
4202 .exception = Qundef,
4203 };
4204
4205 rb_execution_context_t *ec = GET_EC();
4206 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
4207 rb_ractor_interrupt_exec(main_r, ractor_autoload_load_func, &crr, 0);
4208
4209 // wait for require done
4210 rb_ractor_channel_take(ec, crr.ch);
4211 rb_ractor_channel_close(ec, crr.ch);
4212
4213 if (crr.exception != Qundef) {
4214 rb_exc_raise(crr.exception);
4215 }
4216 else {
4217 return crr.result;
4218 }
4219}
4220
4221#include "ractor.rbinc"
RUBY_ASSERT
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
RUBY_ATOMIC_CAS
#define RUBY_ATOMIC_CAS(var, oldval, newval)
Atomic compare-and-swap.
Definition atomic.h:140
rb_atomic_t
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
Definition atomic.h:69
RUBY_ATOMIC_FETCH_ADD
#define RUBY_ATOMIC_FETCH_ADD(var, val)
Atomically replaces the value pointed by var with the result of addition of val to the old value of v...
Definition atomic.h:93
rb_define_method
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition cxxanyargs.hpp:670
rb_define_singleton_method
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
Definition cxxanyargs.hpp:685
RB_OBJ_FROZEN_RAW
static VALUE RB_OBJ_FROZEN_RAW(VALUE obj)
This is an implementation detail of RB_OBJ_FROZEN().
Definition fl_type.h:883
RUBY_FL_SHAREABLE
@ RUBY_FL_SHAREABLE
This flag has something to do with Ractor.
Definition fl_type.h:266
rb_define_class
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition class.c:980
rb_define_class_under
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1012
rb_scan_args
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
Definition class.c:2640
rb_get_kwargs
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)
Keyword argument deconstructor.
Definition class.c:2429
rb_str_new2
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1675
T_COMPLEX
#define T_COMPLEX
Old name of RUBY_T_COMPLEX.
Definition value_type.h:59
T_FILE
#define T_FILE
Old name of RUBY_T_FILE.
Definition value_type.h:62
FL_EXIVAR
#define FL_EXIVAR
Old name of RUBY_FL_EXIVAR.
Definition fl_type.h:66
FL_USER3
#define FL_USER3
Old name of RUBY_FL_USER3.
Definition fl_type.h:74
REALLOC_N
#define REALLOC_N
Old name of RB_REALLOC_N.
Definition memory.h:403
ALLOC
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
T_STRING
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
FL_USER7
#define FL_USER7
Old name of RUBY_FL_USER7.
Definition fl_type.h:78
FL_USER10
#define FL_USER10
Old name of RUBY_FL_USER10.
Definition fl_type.h:81
Qundef
#define Qundef
Old name of RUBY_Qundef.
Definition special_consts.h:62
OBJ_FROZEN
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
Definition fl_type.h:137
FL_USER6
#define FL_USER6
Old name of RUBY_FL_USER6.
Definition fl_type.h:77
T_FLOAT
#define T_FLOAT
Old name of RUBY_T_FLOAT.
Definition value_type.h:64
T_IMEMO
#define T_IMEMO
Old name of RUBY_T_IMEMO.
Definition value_type.h:67
FL_USER1
#define FL_USER1
Old name of RUBY_FL_USER1.
Definition fl_type.h:72
ID2SYM
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
T_BIGNUM
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
Definition value_type.h:57
SPECIAL_CONST_P
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
Definition special_consts.h:56
T_STRUCT
#define T_STRUCT
Old name of RUBY_T_STRUCT.
Definition value_type.h:79
OBJ_FREEZE
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:135
FL_USER19
#define FL_USER19
Old name of RUBY_FL_USER19.
Definition fl_type.h:90
SYM2ID
#define SYM2ID
Old name of RB_SYM2ID.
Definition symbol.h:45
FL_USER12
#define FL_USER12
Old name of RUBY_FL_USER12.
Definition fl_type.h:83
T_DATA
#define T_DATA
Old name of RUBY_T_DATA.
Definition value_type.h:60
FL_USER11
#define FL_USER11
Old name of RUBY_FL_USER11.
Definition fl_type.h:82
FL_USER15
#define FL_USER15
Old name of RUBY_FL_USER15.
Definition fl_type.h:86
T_MODULE
#define T_MODULE
Old name of RUBY_T_MODULE.
Definition value_type.h:70
FL_USER8
#define FL_USER8
Old name of RUBY_FL_USER8.
Definition fl_type.h:79
FL_USER14
#define FL_USER14
Old name of RUBY_FL_USER14.
Definition fl_type.h:85
FL_USER17
#define FL_USER17
Old name of RUBY_FL_USER17.
Definition fl_type.h:88
T_RATIONAL
#define T_RATIONAL
Old name of RUBY_T_RATIONAL.
Definition value_type.h:76
T_ICLASS
#define T_ICLASS
Old name of RUBY_T_ICLASS.
Definition value_type.h:66
T_HASH
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
FL_TEST_RAW
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
Definition fl_type.h:132
FL_USER18
#define FL_USER18
Old name of RUBY_FL_USER18.
Definition fl_type.h:89
FL_USER2
#define FL_USER2
Old name of RUBY_FL_USER2.
Definition fl_type.h:73
FL_USER9
#define FL_USER9
Old name of RUBY_FL_USER9.
Definition fl_type.h:80
Qtrue
#define Qtrue
Old name of RUBY_Qtrue.
Definition special_consts.h:61
FL_USER13
#define FL_USER13
Old name of RUBY_FL_USER13.
Definition fl_type.h:84
Qnil
#define Qnil
Old name of RUBY_Qnil.
Definition special_consts.h:60
Qfalse
#define Qfalse
Old name of RUBY_Qfalse.
Definition special_consts.h:59
T_ARRAY
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
T_OBJECT
#define T_OBJECT
Old name of RUBY_T_OBJECT.
Definition value_type.h:75
NIL_P
#define NIL_P
Old name of RB_NIL_P.
Definition special_consts.h:55
T_SYMBOL
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
Definition value_type.h:80
T_MATCH
#define T_MATCH
Old name of RUBY_T_MATCH.
Definition value_type.h:69
FL_USER16
#define FL_USER16
Old name of RUBY_FL_USER16.
Definition fl_type.h:87
T_CLASS
#define T_CLASS
Old name of RUBY_T_CLASS.
Definition value_type.h:58
BUILTIN_TYPE
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition value_type.h:85
FL_USER5
#define FL_USER5
Old name of RUBY_FL_USER5.
Definition fl_type.h:76
CONST_ID
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
FL_USER4
#define FL_USER4
Old name of RUBY_FL_USER4.
Definition fl_type.h:75
FL_SET_RAW
#define FL_SET_RAW
Old name of RB_FL_SET_RAW.
Definition fl_type.h:130
T_REGEXP
#define T_REGEXP
Old name of RUBY_T_REGEXP.
Definition value_type.h:77
rb_typeddata_is_kind_of
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Checks if the given object is of given kind.
Definition error.c:1380
rb_eRuntimeError
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1428
rb_eStopIteration
VALUE rb_eStopIteration
StopIteration exception.
Definition enumerator.c:182
rb_exc_new_str
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of C's.
Definition error.c:1481
rb_eException
VALUE rb_eException
Mother of all exceptions.
Definition error.c:1422
rb_obj_alloc
VALUE rb_obj_alloc(VALUE klass)
Allocates an instance of the given class.
Definition object.c:2097
rb_cRactor
VALUE rb_cRactor
Ractor class.
Definition ractor.c:23
rb_stdin
VALUE rb_stdin
STDIN constant.
Definition io.c:201
rb_stderr
VALUE rb_stderr
STDERR constant.
Definition io.c:201
rb_class_of
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
Definition globals.h:172
rb_cBasicObject
VALUE rb_cBasicObject
BasicObject class.
Definition object.c:64
rb_obj_clone
VALUE rb_obj_clone(VALUE obj)
Produces a shallow copy of the given object.
Definition object.c:521
rb_stdout
VALUE rb_stdout
STDOUT constant.
Definition io.c:201
RB_OBJ_WRITTEN
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:615
RB_OBJ_WRITE
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:603
rb_funcall
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1099
rb_gc_disable
VALUE rb_gc_disable(void)
Disables GC.
Definition gc.c:3891
rb_gc_start
VALUE rb_gc_start(void)
Identical to rb_gc(), except the return value.
Definition gc.c:3685
rb_gc_enable
VALUE rb_gc_enable(void)
(Re-) enables GC.
Definition gc.c:3857
rb_ary_new
VALUE rb_ary_new(void)
Allocates a new, empty array.
Definition array.c:741
rb_obj_is_proc
VALUE rb_obj_is_proc(VALUE recv)
Queries if the given object is a proc.
Definition proc.c:120
rb_exc_new_cstr
#define rb_exc_new_cstr(exc, str)
Identical to rb_exc_new(), except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1670
rb_str_new_frozen
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition string.c:1468
rb_mutex_new
VALUE rb_mutex_new(void)
Creates a mutex.
Definition thread_sync.c:216
rb_mutex_synchronize
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.
Definition thread_sync.c:683
rb_unblock_function_t
void rb_unblock_function_t(void *)
This is the type of UBFs.
Definition thread.h:336
rb_mutex_unlock
VALUE rb_mutex_unlock(VALUE mutex)
Releases the mutex.
Definition thread_sync.c:548
rb_mutex_lock
VALUE rb_mutex_lock(VALUE mutex)
Attempts to lock the mutex.
Definition thread_sync.c:475
rb_thread_sleep
void rb_thread_sleep(int sec)
Blocks for the given period of time.
Definition thread.c:1457
rb_const_get
VALUE rb_const_get(VALUE space, ID name)
Identical to rb_const_defined(), except it returns the actual defined value.
Definition variable.c:3231
rb_ivar_set
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:1939
rb_autoload_load
VALUE rb_autoload_load(VALUE space, ID name)
Kicks the autoload procedure as if it was "touched".
Definition variable.c:3066
rb_undef_alloc_func
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1297
rb_check_id
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition symbol.c:1118
rb_to_symbol
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
Definition string.c:12486
len
int len
Length of the buffer.
Definition io.h:8
ractor.h
rb_ractor_local_storage_type_free
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:3762
rb_ractor_make_shareable_copy
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:3127
rb_ractor_local_key_t
struct rb_ractor_local_key_struct * rb_ractor_local_key_t
(Opaque) struct that holds a ractor-local storage key.
Definition ractor.h:42
rb_ractor_local_storage_ptr
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:3871
rb_ractor_local_storage_ptr_set
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:3883
rb_ractor_local_storage_ptr_newkey
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:3773
rb_ractor_stderr
VALUE rb_ractor_stderr(void)
Queries the standard error of the current Ractor that is calling this function.
Definition ractor.c:2763
rb_ractor_stdin
VALUE rb_ractor_stdin(void)
Queries the standard input of the current Ractor that is calling this function.
Definition ractor.c:2739
rb_ractor_shareable_p
static bool rb_ractor_shareable_p(VALUE obj)
Queries if multiple Ractors can share the passed object or not.
Definition ractor.h:249
rb_ractor_stderr_set
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:2799
rb_ractor_local_storage_value_set
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:3865
rb_ractor_local_storage_value_lookup
bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
Queries the key.
Definition ractor.c:3854
rb_ractor_stdout
VALUE rb_ractor_stdout(void)
Queries the standard output of the current Ractor that is calling this function.
Definition ractor.c:2751
RB_OBJ_SHAREABLE_P
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
Definition ractor.h:235
rb_ractor_make_shareable
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
Definition ractor.c:3118
rb_ractor_local_storage_value_newkey
rb_ractor_local_key_t rb_ractor_local_storage_value_newkey(void)
Issues a new key.
Definition ractor.c:3782
rb_ractor_stdout_set
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:2787
rb_ractor_stdin_set
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:2775
rb_ractor_local_storage_value
VALUE rb_ractor_local_storage_value(rb_ractor_local_key_t key)
Queries the key.
Definition ractor.c:3842
thread.h
RB_NOGVL_UBF_ASYNC_SAFE
#define RB_NOGVL_UBF_ASYNC_SAFE
Passing this flag to rb_nogvl() indicates that the passed UBF is async-signal-safe.
Definition thread.h:60
RB_NOGVL_INTR_FAIL
#define RB_NOGVL_INTR_FAIL
Passing this flag to rb_nogvl() prevents it from checking interrupts.
Definition thread.h:48
rb_nogvl
void * rb_nogvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2, int flags)
Identical to rb_thread_call_without_gvl(), except it additionally takes "flags" that change the behav...
Definition thread.c:1539
RB_BLOCK_CALL_FUNC_ARGLIST
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
Shim for block function parameters.
Definition iterator.h:58
rb_yield
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1354
rb_block_call_func_t
rb_block_call_func * rb_block_call_func_t
Shorthand type that represents an iterator-written-in-C function pointer.
Definition iterator.h:88
RB_GC_GUARD
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
ruby::backward::cxxanyargs::rb_proc_new
VALUE rb_proc_new(type *q, VALUE w)
Creates a rb_cProc instance.
Definition cxxanyargs.hpp:394
ruby::backward::cxxanyargs::type
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition cxxanyargs.hpp:56
ruby::backward::cxxanyargs::st_foreach
int st_foreach(st_table *q, int_type *w, st_data_t e)
Iteration over the given table.
Definition cxxanyargs.hpp:432
ruby::backward::cxxanyargs::rb_hash_foreach
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
Definition cxxanyargs.hpp:482
ruby::backward::cxxanyargs::rb_ivar_foreach
void rb_ivar_foreach(VALUE q, int_type *w, VALUE e)
Iteration over each instance variable of the object.
Definition cxxanyargs.hpp:498
ruby::backward::cxxanyargs::rb_rescue2
VALUE rb_rescue2(type *q, VALUE w, type *e, VALUE r,...)
An equivalent of rescue clause.
Definition cxxanyargs.hpp:284
ruby::backward::cxxanyargs::rb_ensure
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
Definition cxxanyargs.hpp:310
RARRAY_LENINT
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:281
RARRAY_ASET
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:386
RARRAY_AREF
#define RARRAY_AREF(a, i)
Definition rarray.h:403
RARRAY_CONST_PTR
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
Definition rarray.h:52
RBASIC_CLASS
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
Definition rbasic.h:153
DATA_PTR
#define DATA_PTR(obj)
Convenient getter macro.
Definition rdata.h:67
RHASH_SET_IFNONE
#define RHASH_SET_IFNONE(h, ifnone)
Destructively updates the default value of the hash.
Definition rhash.h:92
RHASH_IFNONE
#define RHASH_IFNONE(h)
Definition rhash.h:59
ROBJECT_IVPTR
static VALUE * ROBJECT_IVPTR(VALUE obj)
Queries the instance variables.
Definition robject.h:136
StringValueCStr
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
RTYPEDDATA_P
static bool RTYPEDDATA_P(VALUE obj)
Checks whether the passed object is RTypedData or RData.
Definition rtypeddata.h:579
TypedData_Wrap_Struct
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:449
rb_data_type_t
struct rb_data_type_struct rb_data_type_t
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:197
TypedData_Make_Struct
#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:497
RTYPEDDATA_TYPE
static const struct rb_data_type_struct * RTYPEDDATA_TYPE(VALUE obj)
Queries for the type of given object.
Definition rtypeddata.h:602
ruby.h
RB_SPECIAL_CONST_P
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
Definition special_consts.h:327
RTEST
#define RTEST
This is an old name of RB_TEST.
Definition special_consts.h:51
RVALUE
Definition ractor.c:3561
cross_ractor_require
Definition ractor.c:4066
freed_ractor_local_keys_struct
Definition ractor.c:3686
gen_ivtbl
Definition variable.h:15
obj_traverse_callback_data
Definition ractor.c:2843
obj_traverse_data
Definition ractor.c:2834
obj_traverse_replace_callback_data
Definition ractor.c:3244
obj_traverse_replace_data
Definition ractor.c:3233
ractor_local_storage_store_data
Definition ractor.c:3933
ractor_selector_clear_data
Definition ractor.c:1571
rb_id_table
Definition id_table.c:41
rb_obj_traverse_final_data
Definition ractor.c:3015
rb_ractor_basket
Definition ractor_core.h:41
rb_ractor_local_key_struct
Definition ractor.c:3681
rb_ractor_local_storage_type
Type that defines a ractor-local storage.
Definition ractor.h:21
rb_ractor_local_storage_type::free
void(* free)(void *ptr)
A function to destruct a ractor-local storage.
Definition ractor.h:37
rb_ractor_local_storage_type::mark
void(* mark)(void *ptr)
A function to mark a ractor-local storage.
Definition ractor.h:29
rb_ractor_queue
Definition ractor_core.h:73
rb_ractor_selector_take_config
Definition ractor_core.h:36
rb_ractor_selector
Definition ractor.c:1404
rb_thread_sched
Definition thread_none.h:16
receive_block_data
Definition ractor.c:794
st_table
Definition st.h:79
take_wait_take_cleanup_data
Definition ractor.c:1200
tr
Definition string.c:8278
thread_native.h
rb_native_mutex_lock
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_lock.
rb_native_cond_initialize
void rb_native_cond_initialize(rb_nativethread_cond_t *cond)
Fills the passed condition variable with an initial value.
rb_native_cond_broadcast
void rb_native_cond_broadcast(rb_nativethread_cond_t *cond)
Signals a condition variable.
rb_native_mutex_initialize
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_initialize.
rb_native_mutex_unlock
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_unlock.
rb_native_mutex_destroy
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_destroy.
rb_native_cond_destroy
void rb_native_cond_destroy(rb_nativethread_cond_t *cond)
Destroys the passed condition variable.
rb_native_cond_signal
void rb_native_cond_signal(rb_nativethread_cond_t *cond)
Signals a condition variable.
rb_native_cond_wait
void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
Waits for the passed condition variable to be signalled.
ID
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
VALUE
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
RB_TYPE_P
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