Ruby: ractor.c Source File

Ruby 3.4.5p51 (2025-07-16 revision 20cda200d3ce092571d0b5d342dadca69636cb0f)

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