Ruby 3.1.3p185 (2022-11-24 revision 1a6b16756e0ba6b95ab71a441357ed5484e33498)
ractor.c
1// Ractor implementation
2
3#include "ruby/ruby.h"
4#include "ruby/thread.h"
5#include "ruby/ractor.h"
7#include "vm_core.h"
8#include "vm_sync.h"
9#include "ractor_core.h"
10#include "internal/complex.h"
11#include "internal/error.h"
12#include "internal/hash.h"
13#include "internal/rational.h"
14#include "internal/struct.h"
15#include "internal/thread.h"
16#include "variable.h"
17#include "gc.h"
18#include "transient_heap.h"
19#include "yjit.h"
20
22
23VALUE rb_eRactorUnsafeError;
24VALUE rb_eRactorIsolationError;
25static VALUE rb_eRactorError;
26static VALUE rb_eRactorRemoteError;
27static VALUE rb_eRactorMovedError;
28static VALUE rb_eRactorClosedError;
29static VALUE rb_cRactorMovedObject;
30
31static void vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line);
32
33static void
34ASSERT_ractor_unlocking(rb_ractor_t *r)
35{
36#if RACTOR_CHECK_MODE > 0
37 // GET_EC is NULL in an MJIT worker
38 if (rb_current_execution_context(false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) {
39 rb_bug("recursive ractor locking");
40 }
41#endif
42}
43
44static void
45ASSERT_ractor_locking(rb_ractor_t *r)
46{
47#if RACTOR_CHECK_MODE > 0
48 // GET_EC is NULL in an MJIT worker
49 if (rb_current_execution_context(false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) {
50 rp(r->sync.locked_by);
51 rb_bug("ractor lock is not acquired.");
52 }
53#endif
54}
55
56static void
57ractor_lock(rb_ractor_t *r, const char *file, int line)
58{
59 RUBY_DEBUG_LOG2(file, line, "locking r:%u%s", r->pub.id, GET_RACTOR() == r ? " (self)" : "");
60
61 ASSERT_ractor_unlocking(r);
62 rb_native_mutex_lock(&r->sync.lock);
63
64#if RACTOR_CHECK_MODE > 0
65 if (rb_current_execution_context(false) != NULL) { // GET_EC is NULL in an MJIT worker
66 r->sync.locked_by = rb_ractor_self(GET_RACTOR());
67 }
68#endif
69
70 RUBY_DEBUG_LOG2(file, line, "locked r:%u%s", r->pub.id, GET_RACTOR() == r ? " (self)" : "");
71}
72
73static void
74ractor_lock_self(rb_ractor_t *cr, const char *file, int line)
75{
76 VM_ASSERT(cr == GET_RACTOR());
77 VM_ASSERT(cr->sync.locked_by != cr->pub.self);
78 ractor_lock(cr, file, line);
79}
80
81static void
82ractor_unlock(rb_ractor_t *r, const char *file, int line)
83{
84 ASSERT_ractor_locking(r);
85#if RACTOR_CHECK_MODE > 0
86 r->sync.locked_by = Qnil;
87#endif
88 rb_native_mutex_unlock(&r->sync.lock);
89
90 RUBY_DEBUG_LOG2(file, line, "r:%u%s", r->pub.id, GET_RACTOR() == r ? " (self)" : "");
91}
92
93static void
94ractor_unlock_self(rb_ractor_t *cr, const char *file, int line)
95{
96 VM_ASSERT(cr == GET_RACTOR());
97 VM_ASSERT(cr->sync.locked_by == cr->pub.self);
98 ractor_unlock(cr, file, line);
99}
100
101#define RACTOR_LOCK(r) ractor_lock(r, __FILE__, __LINE__)
102#define RACTOR_UNLOCK(r) ractor_unlock(r, __FILE__, __LINE__)
103#define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
104#define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
105
106static void
107ractor_cond_wait(rb_ractor_t *r)
108{
109#if RACTOR_CHECK_MODE > 0
110 VALUE locked_by = r->sync.locked_by;
111 r->sync.locked_by = Qnil;
112#endif
113 rb_native_cond_wait(&r->sync.cond, &r->sync.lock);
114
115#if RACTOR_CHECK_MODE > 0
116 r->sync.locked_by = locked_by;
117#endif
118}
119
120static const char *
121ractor_status_str(enum ractor_status status)
122{
123 switch (status) {
124 case ractor_created: return "created";
125 case ractor_running: return "running";
126 case ractor_blocking: return "blocking";
127 case ractor_terminated: return "terminated";
128 }
129 rb_bug("unreachable");
130}
131
132static void
133ractor_status_set(rb_ractor_t *r, enum ractor_status status)
134{
135 RUBY_DEBUG_LOG("r:%u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
136
137 // check 1
138 if (r->status_ != ractor_created) {
139 VM_ASSERT(r == GET_RACTOR()); // only self-modification is allowed.
140 ASSERT_vm_locking();
141 }
142
143 // check2: transition check. assume it will be vanished on non-debug build.
144 switch (r->status_) {
145 case ractor_created:
146 VM_ASSERT(status == ractor_blocking);
147 break;
148 case ractor_running:
149 VM_ASSERT(status == ractor_blocking||
150 status == ractor_terminated);
151 break;
152 case ractor_blocking:
153 VM_ASSERT(status == ractor_running);
154 break;
155 case ractor_terminated:
156 VM_ASSERT(0); // unreachable
157 break;
158 }
159
160 r->status_ = status;
161}
162
163static bool
164ractor_status_p(rb_ractor_t *r, enum ractor_status status)
165{
166 return rb_ractor_status_p(r, status);
167}
168
169static struct rb_ractor_basket *ractor_queue_at(struct rb_ractor_queue *rq, int i);
170
171static void
172ractor_queue_mark(struct rb_ractor_queue *rq)
173{
174 for (int i=0; i<rq->cnt; i++) {
175 struct rb_ractor_basket *b = ractor_queue_at(rq, i);
176 rb_gc_mark(b->v);
177 rb_gc_mark(b->sender);
178 }
179}
180
181static void ractor_local_storage_mark(rb_ractor_t *r);
182static void ractor_local_storage_free(rb_ractor_t *r);
183
184static void
185ractor_mark(void *ptr)
186{
187 rb_ractor_t *r = (rb_ractor_t *)ptr;
188
189 ractor_queue_mark(&r->sync.incoming_queue);
190 rb_gc_mark(r->sync.wait.taken_basket.v);
191 rb_gc_mark(r->sync.wait.taken_basket.sender);
192 rb_gc_mark(r->sync.wait.yielded_basket.v);
193 rb_gc_mark(r->sync.wait.yielded_basket.sender);
194 rb_gc_mark(r->receiving_mutex);
195
196 rb_gc_mark(r->loc);
197 rb_gc_mark(r->name);
198 rb_gc_mark(r->r_stdin);
199 rb_gc_mark(r->r_stdout);
200 rb_gc_mark(r->r_stderr);
201 rb_hook_list_mark(&r->pub.hooks);
202
203 if (r->threads.cnt > 0) {
204 rb_thread_t *th = 0;
205 list_for_each(&r->threads.set, th, lt_node) {
206 VM_ASSERT(th != NULL);
207 rb_gc_mark(th->self);
208 }
209 }
210
211 ractor_local_storage_mark(r);
212}
213
214static void
215ractor_queue_free(struct rb_ractor_queue *rq)
216{
217 free(rq->baskets);
218}
219
220static void
221ractor_waiting_list_free(struct rb_ractor_waiting_list *wl)
222{
223 free(wl->ractors);
224}
225
226static void
227ractor_free(void *ptr)
228{
229 rb_ractor_t *r = (rb_ractor_t *)ptr;
230 rb_native_mutex_destroy(&r->sync.lock);
231 rb_native_cond_destroy(&r->sync.cond);
232 ractor_queue_free(&r->sync.incoming_queue);
233 ractor_waiting_list_free(&r->sync.taking_ractors);
234 ractor_local_storage_free(r);
235 rb_hook_list_free(&r->pub.hooks);
236 ruby_xfree(r);
237}
238
239static size_t
240ractor_queue_memsize(const struct rb_ractor_queue *rq)
241{
242 return sizeof(struct rb_ractor_basket) * rq->size;
243}
244
245static size_t
246ractor_waiting_list_memsize(const struct rb_ractor_waiting_list *wl)
247{
248 return sizeof(rb_ractor_t *) * wl->size;
249}
250
251static size_t
252ractor_memsize(const void *ptr)
253{
254 rb_ractor_t *r = (rb_ractor_t *)ptr;
255
256 // TODO
257 return sizeof(rb_ractor_t) +
258 ractor_queue_memsize(&r->sync.incoming_queue) +
259 ractor_waiting_list_memsize(&r->sync.taking_ractors);
260}
261
262static const rb_data_type_t ractor_data_type = {
263 "ractor",
264 {
265 ractor_mark,
266 ractor_free,
267 ractor_memsize,
268 NULL, // update
269 },
270 0, 0, RUBY_TYPED_FREE_IMMEDIATELY /* | RUBY_TYPED_WB_PROTECTED */
271};
272
273bool
274rb_ractor_p(VALUE gv)
275{
276 if (rb_typeddata_is_kind_of(gv, &ractor_data_type)) {
277 return true;
278 }
279 else {
280 return false;
281 }
282}
283
284static inline rb_ractor_t *
285RACTOR_PTR(VALUE self)
286{
287 VM_ASSERT(rb_ractor_p(self));
288
289 rb_ractor_t *r = DATA_PTR(self);
290 // TODO: check
291 return r;
292}
293
294static rb_atomic_t ractor_last_id;
295
296#if RACTOR_CHECK_MODE > 0
297MJIT_FUNC_EXPORTED uint32_t
298rb_ractor_current_id(void)
299{
300 if (GET_THREAD()->ractor == NULL) {
301 return 1; // main ractor
302 }
303 else {
304 return rb_ractor_id(GET_RACTOR());
305 }
306}
307#endif
308
309static void
310ractor_queue_setup(struct rb_ractor_queue *rq)
311{
312 rq->size = 2;
313 rq->cnt = 0;
314 rq->start = 0;
315 rq->baskets = malloc(sizeof(struct rb_ractor_basket) * rq->size);
316}
317
318static struct rb_ractor_basket *
319ractor_queue_at(struct rb_ractor_queue *rq, int i)
320{
321 return &rq->baskets[(rq->start + i) % rq->size];
322}
323
324static void
325ractor_queue_advance(struct rb_ractor_queue *rq)
326{
327 ASSERT_ractor_locking(GET_RACTOR());
328
329 if (rq->reserved_cnt == 0) {
330 rq->cnt--;
331 rq->start = (rq->start + 1) % rq->size;
332 rq->serial++;
333 }
334 else {
335 ractor_queue_at(rq, 0)->type = basket_type_deleted;
336 }
337}
338
339static bool
340ractor_queue_skip_p(struct rb_ractor_queue *rq, int i)
341{
342 struct rb_ractor_basket *b = ractor_queue_at(rq, i);
343 return b->type == basket_type_deleted ||
344 b->type == basket_type_reserved;
345}
346
347static void
348ractor_queue_compact(rb_ractor_t *r, struct rb_ractor_queue *rq)
349{
350 ASSERT_ractor_locking(r);
351
352 while (rq->cnt > 0 && ractor_queue_at(rq, 0)->type == basket_type_deleted) {
353 ractor_queue_advance(rq);
354 }
355}
356
357static bool
358ractor_queue_empty_p(rb_ractor_t *r, struct rb_ractor_queue *rq)
359{
360 ASSERT_ractor_locking(r);
361
362 if (rq->cnt == 0) {
363 return true;
364 }
365
366 ractor_queue_compact(r, rq);
367
368 for (int i=0; i<rq->cnt; i++) {
369 if (!ractor_queue_skip_p(rq, i)) {
370 return false;
371 }
372 }
373
374 return true;
375}
376
377static bool
378ractor_queue_deq(rb_ractor_t *r, struct rb_ractor_queue *rq, struct rb_ractor_basket *basket)
379{
380 bool found = false;
381
382 RACTOR_LOCK(r);
383 {
384 if (!ractor_queue_empty_p(r, rq)) {
385 for (int i=0; i<rq->cnt; i++) {
386 if (!ractor_queue_skip_p(rq, i)) {
387 struct rb_ractor_basket *b = ractor_queue_at(rq, i);
388 *basket = *b;
389
390 // remove from queue
391 b->type = basket_type_deleted;
392 ractor_queue_compact(r, rq);
393 found = true;
394 break;
395 }
396 }
397 }
398 }
399 RACTOR_UNLOCK(r);
400
401 return found;
402}
403
404static void
405ractor_queue_enq(rb_ractor_t *r, struct rb_ractor_queue *rq, struct rb_ractor_basket *basket)
406{
407 ASSERT_ractor_locking(r);
408
409 if (rq->size <= rq->cnt) {
410 rq->baskets = realloc(rq->baskets, sizeof(struct rb_ractor_basket) * rq->size * 2);
411 for (int i=rq->size - rq->start; i<rq->cnt; i++) {
412 rq->baskets[i + rq->start] = rq->baskets[i + rq->start - rq->size];
413 }
414 rq->size *= 2;
415 }
416 rq->baskets[(rq->start + rq->cnt++) % rq->size] = *basket;
417 // fprintf(stderr, "%s %p->cnt:%d\n", RUBY_FUNCTION_NAME_STRING, (void *)rq, rq->cnt);
418}
419
420static void
421ractor_basket_clear(struct rb_ractor_basket *b)
422{
423 b->type = basket_type_none;
424 b->v = Qfalse;
425 b->sender = Qfalse;
426}
427
428static VALUE ractor_reset_belonging(VALUE obj); // in this file
429
430static VALUE
431ractor_basket_value(struct rb_ractor_basket *b)
432{
433 switch (b->type) {
434 case basket_type_ref:
435 break;
436 case basket_type_copy:
437 case basket_type_move:
438 case basket_type_will:
439 b->type = basket_type_ref;
440 b->v = ractor_reset_belonging(b->v);
441 break;
442 default:
443 rb_bug("unreachable");
444 }
445
446 return b->v;
447}
448
449static VALUE
450ractor_basket_accept(struct rb_ractor_basket *b)
451{
452 VALUE v = ractor_basket_value(b);
453
454 if (b->exception) {
455 VALUE cause = v;
456 VALUE err = rb_exc_new_cstr(rb_eRactorRemoteError, "thrown by remote Ractor.");
457 rb_ivar_set(err, rb_intern("@ractor"), b->sender);
458 ractor_basket_clear(b);
459 rb_ec_setup_exception(NULL, err, cause);
460 rb_exc_raise(err);
461 }
462
463 ractor_basket_clear(b);
464 return v;
465}
466
467static void
468ractor_recursive_receive_if(rb_ractor_t *r)
469{
470 if (r->receiving_mutex && rb_mutex_owned_p(r->receiving_mutex)) {
471 rb_raise(rb_eRactorError, "can not call receive/receive_if recursively");
472 }
473}
474
475static VALUE
476ractor_try_receive(rb_execution_context_t *ec, rb_ractor_t *r)
477{
478 struct rb_ractor_queue *rq = &r->sync.incoming_queue;
479 struct rb_ractor_basket basket;
480
481 ractor_recursive_receive_if(r);
482
483 if (ractor_queue_deq(r, rq, &basket) == false) {
484 if (r->sync.incoming_port_closed) {
485 rb_raise(rb_eRactorClosedError, "The incoming port is already closed");
486 }
487 else {
488 return Qundef;
489 }
490 }
491
492 return ractor_basket_accept(&basket);
493}
494
495static bool
496ractor_sleeping_by(const rb_ractor_t *r, enum ractor_wait_status wait_status)
497{
498 return (r->sync.wait.status & wait_status) && r->sync.wait.wakeup_status == wakeup_none;
499}
500
501static bool
502ractor_wakeup(rb_ractor_t *r, enum ractor_wait_status wait_status, enum ractor_wakeup_status wakeup_status)
503{
504 ASSERT_ractor_locking(r);
505
506 // fprintf(stderr, "%s r:%p status:%s/%s wakeup_status:%s/%s\n", RUBY_FUNCTION_NAME_STRING, (void *)r,
507 // wait_status_str(r->sync.wait.status), wait_status_str(wait_status),
508 // wakeup_status_str(r->sync.wait.wakeup_status), wakeup_status_str(wakeup_status));
509
510 if (ractor_sleeping_by(r, wait_status)) {
511 r->sync.wait.wakeup_status = wakeup_status;
512 rb_native_cond_signal(&r->sync.cond);
513 return true;
514 }
515 else {
516 return false;
517 }
518}
519
520static void *
521ractor_sleep_wo_gvl(void *ptr)
522{
523 rb_ractor_t *cr = ptr;
524 RACTOR_LOCK_SELF(cr);
525 {
526 VM_ASSERT(cr->sync.wait.status != wait_none);
527 if (cr->sync.wait.wakeup_status == wakeup_none) {
528 ractor_cond_wait(cr);
529 }
530 cr->sync.wait.status = wait_none;
531 }
532 RACTOR_UNLOCK_SELF(cr);
533 return NULL;
534}
535
536static void
537ractor_sleep_interrupt(void *ptr)
538{
539 rb_ractor_t *r = ptr;
540
541 RACTOR_LOCK(r);
542 {
543 ractor_wakeup(r, wait_receiving | wait_taking | wait_yielding, wakeup_by_interrupt);
544 }
545 RACTOR_UNLOCK(r);
546}
547
548#if defined(USE_RUBY_DEBUG_LOG) && USE_RUBY_DEBUG_LOG
549static const char *
550wait_status_str(enum ractor_wait_status wait_status)
551{
552 switch ((int)wait_status) {
553 case wait_none: return "none";
554 case wait_receiving: return "receiving";
555 case wait_taking: return "taking";
556 case wait_yielding: return "yielding";
557 case wait_receiving|wait_taking: return "receiving|taking";
558 case wait_receiving|wait_yielding: return "receiving|yielding";
559 case wait_taking|wait_yielding: return "taking|yielding";
560 case wait_receiving|wait_taking|wait_yielding: return "receiving|taking|yielding";
561 }
562 rb_bug("unreachable");
563}
564
565static const char *
566wakeup_status_str(enum ractor_wakeup_status wakeup_status)
567{
568 switch (wakeup_status) {
569 case wakeup_none: return "none";
570 case wakeup_by_send: return "by_send";
571 case wakeup_by_yield: return "by_yield";
572 case wakeup_by_take: return "by_take";
573 case wakeup_by_close: return "by_close";
574 case wakeup_by_interrupt: return "by_interrupt";
575 case wakeup_by_retry: return "by_retry";
576 }
577 rb_bug("unreachable");
578}
579#endif // USE_RUBY_DEBUG_LOG
580
581static void
582ractor_sleep(rb_execution_context_t *ec, rb_ractor_t *cr)
583{
584 VM_ASSERT(GET_RACTOR() == cr);
585 VM_ASSERT(cr->sync.wait.status != wait_none);
586 // fprintf(stderr, "%s r:%p status:%s, wakeup_status:%s\n", RUBY_FUNCTION_NAME_STRING, (void *)cr,
587 // wait_status_str(cr->sync.wait.status), wakeup_status_str(cr->sync.wait.wakeup_status));
588
589 RACTOR_UNLOCK(cr);
590 {
591 rb_nogvl(ractor_sleep_wo_gvl, cr,
592 ractor_sleep_interrupt, cr,
594 }
595 RACTOR_LOCK(cr);
596
597 // rb_nogvl() can be canceled by interrupts
598 if (cr->sync.wait.status != wait_none) {
599 cr->sync.wait.status = wait_none;
600 cr->sync.wait.wakeup_status = wakeup_by_interrupt;
601
602 RACTOR_UNLOCK(cr);
604 RACTOR_LOCK(cr); // reachable?
605 }
606}
607
608static void
609ractor_register_taking(rb_ractor_t *r, rb_ractor_t *cr)
610{
611 VM_ASSERT(cr == GET_RACTOR());
612 bool retry_try = false;
613
614 RACTOR_LOCK(r);
615 {
616 if (ractor_sleeping_by(r, wait_yielding)) {
617 // already waiting for yielding. retry try_take.
618 retry_try = true;
619 }
620 else {
621 // insert cr into taking list
622 struct rb_ractor_waiting_list *wl = &r->sync.taking_ractors;
623
624 for (int i=0; i<wl->cnt; i++) {
625 if (wl->ractors[i] == cr) {
626 // TODO: make it clean code.
627 rb_native_mutex_unlock(&r->sync.lock);
628 rb_raise(rb_eRuntimeError, "Already another thread of same ractor is waiting.");
629 }
630 }
631
632 if (wl->size == 0) {
633 wl->size = 1;
634 wl->ractors = malloc(sizeof(rb_ractor_t *) * wl->size);
635 if (wl->ractors == NULL) rb_bug("can't allocate buffer");
636 }
637 else if (wl->size <= wl->cnt + 1) {
638 wl->size *= 2;
639 wl->ractors = realloc(wl->ractors, sizeof(rb_ractor_t *) * wl->size);
640 if (wl->ractors == NULL) rb_bug("can't re-allocate buffer");
641 }
642 wl->ractors[wl->cnt++] = cr;
643 }
644 }
645 RACTOR_UNLOCK(r);
646
647 if (retry_try) {
648 RACTOR_LOCK(cr);
649 {
650 if (cr->sync.wait.wakeup_status == wakeup_none) {
651 VM_ASSERT(cr->sync.wait.status != wait_none);
652
653 cr->sync.wait.wakeup_status = wakeup_by_retry;
654 cr->sync.wait.status = wait_none;
655 }
656 }
657 RACTOR_UNLOCK(cr);
658 }
659}
660
661static void
662ractor_waiting_list_del(rb_ractor_t *r, struct rb_ractor_waiting_list *wl, rb_ractor_t *wr)
663{
664 RACTOR_LOCK(r);
665 {
666 int pos = -1;
667 for (int i=0; i<wl->cnt; i++) {
668 if (wl->ractors[i] == wr) {
669 pos = i;
670 break;
671 }
672 }
673 if (pos >= 0) { // found
674 wl->cnt--;
675 for (int i=pos; i<wl->cnt; i++) {
676 wl->ractors[i] = wl->ractors[i+1];
677 }
678 }
679 }
680 RACTOR_UNLOCK(r);
681}
682
683static rb_ractor_t *
684ractor_waiting_list_shift(rb_ractor_t *r, struct rb_ractor_waiting_list *wl)
685{
686 ASSERT_ractor_locking(r);
687 VM_ASSERT(&r->sync.taking_ractors == wl);
688
689 if (wl->cnt > 0) {
690 rb_ractor_t *tr = wl->ractors[0];
691 for (int i=1; i<wl->cnt; i++) {
692 wl->ractors[i-1] = wl->ractors[i];
693 }
694 wl->cnt--;
695 return tr;
696 }
697 else {
698 return NULL;
699 }
700}
701
702static void
703ractor_receive_wait(rb_execution_context_t *ec, rb_ractor_t *cr)
704{
705 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
706 ractor_recursive_receive_if(cr);
707
708 RACTOR_LOCK(cr);
709 {
710 if (ractor_queue_empty_p(cr, &cr->sync.incoming_queue)) {
711 VM_ASSERT(cr->sync.wait.status == wait_none);
712 cr->sync.wait.status = wait_receiving;
713 cr->sync.wait.wakeup_status = wakeup_none;
714 ractor_sleep(ec, cr);
715 cr->sync.wait.wakeup_status = wakeup_none;
716 }
717 }
718 RACTOR_UNLOCK(cr);
719}
720
721static VALUE
722ractor_receive(rb_execution_context_t *ec, rb_ractor_t *cr)
723{
724 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
725 VALUE v;
726
727 while ((v = ractor_try_receive(ec, cr)) == Qundef) {
728 ractor_receive_wait(ec, cr);
729 }
730
731 return v;
732}
733
734#if 0
735// for debug
736static const char *
737basket_type_name(enum rb_ractor_basket_type type)
738{
739 switch (type) {
740#define T(t) case basket_type_##t: return #t
741 T(none);
742 T(ref);
743 T(copy);
744 T(move);
745 T(will);
746 T(deleted);
747 T(reserved);
748 default: rb_bug("unreachable");
749 }
750}
751
752static void
753rq_dump(struct rb_ractor_queue *rq)
754{
755 bool bug = false;
756 for (int i=0; i<rq->cnt; i++) {
757 struct rb_ractor_basket *b = ractor_queue_at(rq, i);
758 fprintf(stderr, "%d (start:%d) type:%s %p %s\n", i, rq->start, basket_type_name(b->type),
759 (void *)b, RSTRING_PTR(RARRAY_AREF(b->v, 1)));
760 if (b->type == basket_type_reserved) bug = true;
761 }
762 if (bug) rb_bug("!!");
763}
764#endif
765
767 rb_ractor_t *cr;
768 struct rb_ractor_queue *rq;
769 VALUE v;
770 int index;
771 bool success;
772};
773
774static void
775ractor_receive_if_lock(rb_ractor_t *cr)
776{
777 VALUE m = cr->receiving_mutex;
778 if (m == Qfalse) {
779 m = cr->receiving_mutex = rb_mutex_new();
780 }
781 rb_mutex_lock(m);
782}
783
784static VALUE
785receive_if_body(VALUE ptr)
786{
787 struct receive_block_data *data = (struct receive_block_data *)ptr;
788
789 ractor_receive_if_lock(data->cr);
790 VALUE block_result = rb_yield(data->v);
791
792 RACTOR_LOCK_SELF(data->cr);
793 {
794 struct rb_ractor_basket *b = ractor_queue_at(data->rq, data->index);
795 VM_ASSERT(b->type == basket_type_reserved);
796 data->rq->reserved_cnt--;
797
798 if (RTEST(block_result)) {
799 b->type = basket_type_deleted;
800 ractor_queue_compact(data->cr, data->rq);
801 }
802 else {
803 b->type = basket_type_ref;
804 }
805 }
806 RACTOR_UNLOCK_SELF(data->cr);
807
808 data->success = true;
809
810 if (RTEST(block_result)) {
811 return data->v;
812 }
813 else {
814 return Qundef;
815 }
816}
817
818static VALUE
819receive_if_ensure(VALUE v)
820{
821 struct receive_block_data *data = (struct receive_block_data *)v;
822
823 if (!data->success) {
824 RACTOR_LOCK_SELF(data->cr);
825 {
826 struct rb_ractor_basket *b = ractor_queue_at(data->rq, data->index);
827 VM_ASSERT(b->type == basket_type_reserved);
828 b->type = basket_type_deleted;
829 data->rq->reserved_cnt--;
830 }
831 RACTOR_UNLOCK_SELF(data->cr);
832 }
833
834 rb_mutex_unlock(data->cr->receiving_mutex);
835 return Qnil;
836}
837
838static VALUE
839ractor_receive_if(rb_execution_context_t *ec, VALUE crv, VALUE b)
840{
841 if (!RTEST(b)) rb_raise(rb_eArgError, "no block given");
842
843 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
844 unsigned int serial = (unsigned int)-1;
845 int index = 0;
846 struct rb_ractor_queue *rq = &cr->sync.incoming_queue;
847
848 while (1) {
849 VALUE v = Qundef;
850
851 ractor_receive_wait(ec, cr);
852
853 RACTOR_LOCK_SELF(cr);
854 {
855 if (serial != rq->serial) {
856 serial = rq->serial;
857 index = 0;
858 }
859
860 // check newer version
861 for (int i=index; i<rq->cnt; i++) {
862 if (!ractor_queue_skip_p(rq, i)) {
863 struct rb_ractor_basket *b = ractor_queue_at(rq, i);
864 v = ractor_basket_value(b);
865 b->type = basket_type_reserved;
866 rq->reserved_cnt++;
867 index = i;
868 break;
869 }
870 }
871 }
872 RACTOR_UNLOCK_SELF(cr);
873
874 if (v != Qundef) {
875 struct receive_block_data data = {
876 .cr = cr,
877 .rq = rq,
878 .v = v,
879 .index = index,
880 .success = false,
881 };
882
883 VALUE result = rb_ensure(receive_if_body, (VALUE)&data,
884 receive_if_ensure, (VALUE)&data);
885
886 if (result != Qundef) return result;
887 index++;
888 }
889 }
890}
891
892static void
893ractor_send_basket(rb_execution_context_t *ec, rb_ractor_t *r, struct rb_ractor_basket *b)
894{
895 bool closed = false;
896 struct rb_ractor_queue *rq = &r->sync.incoming_queue;
897
898 RACTOR_LOCK(r);
899 {
900 if (r->sync.incoming_port_closed) {
901 closed = true;
902 }
903 else {
904 ractor_queue_enq(r, rq, b);
905 if (ractor_wakeup(r, wait_receiving, wakeup_by_send)) {
906 RUBY_DEBUG_LOG("wakeup");
907 }
908 }
909 }
910 RACTOR_UNLOCK(r);
911
912 if (closed) {
913 rb_raise(rb_eRactorClosedError, "The incoming-port is already closed");
914 }
915}
916
917static VALUE ractor_move(VALUE obj); // in this file
918static VALUE ractor_copy(VALUE obj); // in this file
919
920static void
921ractor_basket_setup(rb_execution_context_t *ec, struct rb_ractor_basket *basket, VALUE obj, VALUE move, bool exc, bool is_will, bool is_yield)
922{
923 basket->sender = rb_ec_ractor_ptr(ec)->pub.self;
924 basket->exception = exc;
925
926 if (is_will) {
927 basket->type = basket_type_will;
928 basket->v = obj;
929 }
930 else if (rb_ractor_shareable_p(obj)) {
931 basket->type = basket_type_ref;
932 basket->v = obj;
933 }
934 else if (!RTEST(move)) {
935 basket->v = ractor_copy(obj);
936 basket->type = basket_type_copy;
937 }
938 else {
939 basket->type = basket_type_move;
940
941 if (is_yield) {
942 basket->v = obj; // call ractor_move() when yielding timing.
943 }
944 else {
945 basket->v = ractor_move(obj);
946 }
947 }
948}
949
950static VALUE
951ractor_send(rb_execution_context_t *ec, rb_ractor_t *r, VALUE obj, VALUE move)
952{
953 struct rb_ractor_basket basket;
954 ractor_basket_setup(ec, &basket, obj, move, false, false, false);
955 ractor_send_basket(ec, r, &basket);
956 return r->pub.self;
957}
958
959static VALUE
960ractor_try_take(rb_execution_context_t *ec, rb_ractor_t *r)
961{
962 struct rb_ractor_basket basket = {
963 .type = basket_type_none,
964 };
965 bool closed = false;
966
967 RACTOR_LOCK(r);
968 {
969 if (ractor_sleeping_by(r, wait_yielding)) {
970 MAYBE_UNUSED(bool) wakeup_result;
971 VM_ASSERT(r->sync.wait.yielded_basket.type != basket_type_none);
972
973 if (r->sync.wait.yielded_basket.type == basket_type_move) {
974 wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_retry);
975 }
976 else {
977 wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_take);
978 basket = r->sync.wait.yielded_basket;
979 ractor_basket_clear(&r->sync.wait.yielded_basket);
980 }
981 VM_ASSERT(wakeup_result);
982 }
983 else if (r->sync.outgoing_port_closed) {
984 closed = true;
985 }
986 }
987 RACTOR_UNLOCK(r);
988
989 if (basket.type == basket_type_none) {
990 if (closed) {
991 rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
992 }
993 else {
994 return Qundef;
995 }
996 }
997 else {
998 return ractor_basket_accept(&basket);
999 }
1000}
1001
1002static VALUE
1003ractor_yield_move_body(VALUE v)
1004{
1005 return ractor_move(v);
1006}
1007
1008static bool
1009ractor_try_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_basket *basket)
1010{
1011 ASSERT_ractor_unlocking(cr);
1012 VM_ASSERT(basket->type != basket_type_none);
1013
1014 if (cr->sync.outgoing_port_closed) {
1015 rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
1016 }
1017
1018 rb_ractor_t *r;
1019
1020 retry_shift:
1021 RACTOR_LOCK(cr);
1022 {
1023 r = ractor_waiting_list_shift(cr, &cr->sync.taking_ractors);
1024 }
1025 RACTOR_UNLOCK(cr);
1026
1027 if (r) {
1028 bool retry_shift = false;
1029
1030 RACTOR_LOCK(r);
1031 {
1032 if (ractor_sleeping_by(r, wait_taking)) {
1033 VM_ASSERT(r->sync.wait.taken_basket.type == basket_type_none);
1034
1035 if (basket->type == basket_type_move) {
1036 enum ractor_wait_status prev_wait_status = r->sync.wait.status;
1037 r->sync.wait.status = wait_moving;
1038
1039 RACTOR_UNLOCK(r);
1040 {
1041 int state;
1042 VALUE moved_value = rb_protect(ractor_yield_move_body, basket->v, &state);
1043 if (state) {
1044 r->sync.wait.status = prev_wait_status;
1045 rb_jump_tag(state);
1046 }
1047 else {
1048 basket->v = moved_value;
1049 }
1050 }
1051 RACTOR_LOCK(r);
1052
1053 if (!ractor_wakeup(r, wait_moving, wakeup_by_yield)) {
1054 // terminating?
1055 }
1056 }
1057 else {
1058 ractor_wakeup(r, wait_taking, wakeup_by_yield);
1059 }
1060 r->sync.wait.taken_basket = *basket;
1061 }
1062 else {
1063 retry_shift = true;
1064 }
1065 }
1066 RACTOR_UNLOCK(r);
1067
1068 if (retry_shift) {
1069 // get candidate take-waiting ractor, but already woke up by another reason.
1070 // retry to check another ractor.
1071 goto retry_shift;
1072 }
1073 else {
1074 return true;
1075 }
1076 }
1077 else {
1078 return false;
1079 }
1080}
1081
1082// select(r1, r2, r3, receive: true, yield: obj)
1083static VALUE
1084ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VALUE yielded_value, bool move, VALUE *ret_r)
1085{
1086 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1087 VALUE crv = cr->pub.self;
1088 VALUE ret = Qundef;
1089 int i;
1090 bool interrupted = false;
1091 enum ractor_wait_status wait_status = 0;
1092 bool yield_p = (yielded_value != Qundef) ? true : false;
1093 const int alen = rs_len + (yield_p ? 1 : 0);
1094
1095 struct ractor_select_action {
1096 enum ractor_select_action_type {
1097 ractor_select_action_take,
1098 ractor_select_action_receive,
1099 ractor_select_action_yield,
1100 } type;
1101 VALUE v;
1102 } *actions = ALLOCA_N(struct ractor_select_action, alen);
1103
1104 VM_ASSERT(cr->sync.wait.status == wait_none);
1105 VM_ASSERT(cr->sync.wait.wakeup_status == wakeup_none);
1106 VM_ASSERT(cr->sync.wait.taken_basket.type == basket_type_none);
1107 VM_ASSERT(cr->sync.wait.yielded_basket.type == basket_type_none);
1108
1109 // setup actions
1110 for (i=0; i<rs_len; i++) {
1111 VALUE v = rs[i];
1112
1113 if (v == crv) {
1114 actions[i].type = ractor_select_action_receive;
1115 actions[i].v = Qnil;
1116 wait_status |= wait_receiving;
1117 }
1118 else if (rb_ractor_p(v)) {
1119 actions[i].type = ractor_select_action_take;
1120 actions[i].v = v;
1121 wait_status |= wait_taking;
1122 }
1123 else {
1124 rb_raise(rb_eArgError, "should be a ractor object, but %"PRIsVALUE, v);
1125 }
1126 }
1127 rs = NULL;
1128
1129 restart:
1130
1131 if (yield_p) {
1132 actions[rs_len].type = ractor_select_action_yield;
1133 actions[rs_len].v = Qundef;
1134 wait_status |= wait_yielding;
1135 ractor_basket_setup(ec, &cr->sync.wait.yielded_basket, yielded_value, move, false, false, true);
1136 }
1137
1138 // TODO: shuffle actions
1139
1140 while (1) {
1141 RUBY_DEBUG_LOG("try actions (%s)", wait_status_str(wait_status));
1142
1143 for (i=0; i<alen; i++) {
1144 VALUE v, rv;
1145 switch (actions[i].type) {
1146 case ractor_select_action_take:
1147 rv = actions[i].v;
1148 v = ractor_try_take(ec, RACTOR_PTR(rv));
1149 if (v != Qundef) {
1150 *ret_r = rv;
1151 ret = v;
1152 goto cleanup;
1153 }
1154 break;
1155 case ractor_select_action_receive:
1156 v = ractor_try_receive(ec, cr);
1157 if (v != Qundef) {
1158 *ret_r = ID2SYM(rb_intern("receive"));
1159 ret = v;
1160 goto cleanup;
1161 }
1162 break;
1163 case ractor_select_action_yield:
1164 {
1165 if (ractor_try_yield(ec, cr, &cr->sync.wait.yielded_basket)) {
1166 *ret_r = ID2SYM(rb_intern("yield"));
1167 ret = Qnil;
1168 goto cleanup;
1169 }
1170 }
1171 break;
1172 }
1173 }
1174
1175 RUBY_DEBUG_LOG("wait actions (%s)", wait_status_str(wait_status));
1176
1177 RACTOR_LOCK(cr);
1178 {
1179 VM_ASSERT(cr->sync.wait.status == wait_none);
1180 cr->sync.wait.status = wait_status;
1181 cr->sync.wait.wakeup_status = wakeup_none;
1182 }
1183 RACTOR_UNLOCK(cr);
1184
1185 // prepare waiting
1186 for (i=0; i<alen; i++) {
1187 rb_ractor_t *r;
1188 switch (actions[i].type) {
1189 case ractor_select_action_take:
1190 r = RACTOR_PTR(actions[i].v);
1191 ractor_register_taking(r, cr);
1192 break;
1193 case ractor_select_action_yield:
1194 case ractor_select_action_receive:
1195 break;
1196 }
1197 }
1198
1199 // wait
1200 RACTOR_LOCK(cr);
1201 {
1202 if (cr->sync.wait.wakeup_status == wakeup_none) {
1203 for (i=0; i<alen; i++) {
1204 rb_ractor_t *r;
1205
1206 switch (actions[i].type) {
1207 case ractor_select_action_take:
1208 r = RACTOR_PTR(actions[i].v);
1209 if (ractor_sleeping_by(r, wait_yielding)) {
1210 RUBY_DEBUG_LOG("wakeup_none, but r:%u is waiting for yielding", r->pub.id);
1211 cr->sync.wait.wakeup_status = wakeup_by_retry;
1212 goto skip_sleep;
1213 }
1214 break;
1215 case ractor_select_action_receive:
1216 if (cr->sync.incoming_queue.cnt > 0) {
1217 RUBY_DEBUG_LOG("wakeup_none, but incoming_queue has %u messages", cr->sync.incoming_queue.cnt);
1218 cr->sync.wait.wakeup_status = wakeup_by_retry;
1219 goto skip_sleep;
1220 }
1221 break;
1222 case ractor_select_action_yield:
1223 if (cr->sync.taking_ractors.cnt > 0) {
1224 RUBY_DEBUG_LOG("wakeup_none, but %u taking_ractors are waiting", cr->sync.taking_ractors.cnt);
1225 cr->sync.wait.wakeup_status = wakeup_by_retry;
1226 goto skip_sleep;
1227 }
1228 else if (cr->sync.outgoing_port_closed) {
1229 cr->sync.wait.wakeup_status = wakeup_by_close;
1230 goto skip_sleep;
1231 }
1232 break;
1233 }
1234 }
1235
1236 RUBY_DEBUG_LOG("sleep %s", wait_status_str(cr->sync.wait.status));
1237 ractor_sleep(ec, cr);
1238 RUBY_DEBUG_LOG("awaken %s", wakeup_status_str(cr->sync.wait.wakeup_status));
1239 }
1240 else {
1241 skip_sleep:
1242 RUBY_DEBUG_LOG("no need to sleep %s->%s",
1243 wait_status_str(cr->sync.wait.status),
1244 wakeup_status_str(cr->sync.wait.wakeup_status));
1245 cr->sync.wait.status = wait_none;
1246 }
1247 }
1248 RACTOR_UNLOCK(cr);
1249
1250 // cleanup waiting
1251 for (i=0; i<alen; i++) {
1252 rb_ractor_t *r;
1253 switch (actions[i].type) {
1254 case ractor_select_action_take:
1255 r = RACTOR_PTR(actions[i].v);
1256 ractor_waiting_list_del(r, &r->sync.taking_ractors, cr);
1257 break;
1258 case ractor_select_action_receive:
1259 case ractor_select_action_yield:
1260 break;
1261 }
1262 }
1263
1264 // check results
1265 enum ractor_wakeup_status wakeup_status = cr->sync.wait.wakeup_status;
1266 cr->sync.wait.wakeup_status = wakeup_none;
1267
1268 switch (wakeup_status) {
1269 case wakeup_none:
1270 // OK. something happens.
1271 // retry loop.
1272 break;
1273 case wakeup_by_retry:
1274 // Retry request.
1275 break;
1276 case wakeup_by_send:
1277 // OK.
1278 // retry loop and try_receive will succss.
1279 break;
1280 case wakeup_by_yield:
1281 // take was succeeded!
1282 // cr.wait.taken_basket contains passed block
1283 VM_ASSERT(cr->sync.wait.taken_basket.type != basket_type_none);
1284 *ret_r = cr->sync.wait.taken_basket.sender;
1285 VM_ASSERT(rb_ractor_p(*ret_r));
1286 ret = ractor_basket_accept(&cr->sync.wait.taken_basket);
1287 goto cleanup;
1288 case wakeup_by_take:
1289 *ret_r = ID2SYM(rb_intern("yield"));
1290 ret = Qnil;
1291 goto cleanup;
1292 case wakeup_by_close:
1293 // OK.
1294 // retry loop and will get CloseError.
1295 break;
1296 case wakeup_by_interrupt:
1297 ret = Qundef;
1298 interrupted = true;
1299 goto cleanup;
1300 }
1301 }
1302
1303 cleanup:
1304 RUBY_DEBUG_LOG("cleanup actions (%s)", wait_status_str(wait_status));
1305
1306 if (cr->sync.wait.yielded_basket.type != basket_type_none) {
1307 ractor_basket_clear(&cr->sync.wait.yielded_basket);
1308 }
1309
1310 VM_ASSERT(cr->sync.wait.status == wait_none);
1311 VM_ASSERT(cr->sync.wait.wakeup_status == wakeup_none);
1312 VM_ASSERT(cr->sync.wait.taken_basket.type == basket_type_none);
1313 VM_ASSERT(cr->sync.wait.yielded_basket.type == basket_type_none);
1314
1315 if (interrupted) {
1316 rb_vm_check_ints_blocking(ec);
1317 interrupted = false;
1318 goto restart;
1319 }
1320
1321 VM_ASSERT(ret != Qundef);
1322 return ret;
1323}
1324
1325static VALUE
1326ractor_yield(rb_execution_context_t *ec, rb_ractor_t *r, VALUE obj, VALUE move)
1327{
1328 VALUE ret_r;
1329 ractor_select(ec, NULL, 0, obj, RTEST(move) ? true : false, &ret_r);
1330 return Qnil;
1331}
1332
1333static VALUE
1334ractor_take(rb_execution_context_t *ec, rb_ractor_t *r)
1335{
1336 VALUE ret_r;
1337 VALUE v = ractor_select(ec, &r->pub.self, 1, Qundef, false, &ret_r);
1338 return v;
1339}
1340
1341static VALUE
1342ractor_close_incoming(rb_execution_context_t *ec, rb_ractor_t *r)
1343{
1344 VALUE prev;
1345
1346 RACTOR_LOCK(r);
1347 {
1348 if (!r->sync.incoming_port_closed) {
1349 prev = Qfalse;
1350 r->sync.incoming_port_closed = true;
1351 if (ractor_wakeup(r, wait_receiving, wakeup_by_close)) {
1352 VM_ASSERT(r->sync.incoming_queue.cnt == 0);
1353 RUBY_DEBUG_LOG("cancel receiving");
1354 }
1355 }
1356 else {
1357 prev = Qtrue;
1358 }
1359 }
1360 RACTOR_UNLOCK(r);
1361 return prev;
1362}
1363
1364static VALUE
1365ractor_close_outgoing(rb_execution_context_t *ec, rb_ractor_t *r)
1366{
1367 VALUE prev;
1368
1369 RACTOR_LOCK(r);
1370 {
1371 if (!r->sync.outgoing_port_closed) {
1372 prev = Qfalse;
1373 r->sync.outgoing_port_closed = true;
1374 }
1375 else {
1376 prev = Qtrue;
1377 }
1378
1379 // wakeup all taking ractors
1380 rb_ractor_t *taking_ractor;
1381 while ((taking_ractor = ractor_waiting_list_shift(r, &r->sync.taking_ractors)) != NULL) {
1382 RACTOR_LOCK(taking_ractor);
1383 ractor_wakeup(taking_ractor, wait_taking, wakeup_by_close);
1384 RACTOR_UNLOCK(taking_ractor);
1385 }
1386
1387 // raising yielding Ractor
1388 if (!r->yield_atexit &&
1389 ractor_wakeup(r, wait_yielding, wakeup_by_close)) {
1390 RUBY_DEBUG_LOG("cancel yielding");
1391 }
1392 }
1393 RACTOR_UNLOCK(r);
1394 return prev;
1395}
1396
1397// creation/termination
1398
1399static uint32_t
1400ractor_next_id(void)
1401{
1402 uint32_t id;
1403
1404 id = (uint32_t)(RUBY_ATOMIC_FETCH_ADD(ractor_last_id, 1) + 1);
1405
1406 return id;
1407}
1408
1409static void
1410vm_insert_ractor0(rb_vm_t *vm, rb_ractor_t *r, bool single_ractor_mode)
1411{
1412 RUBY_DEBUG_LOG("r:%u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
1413 VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
1414
1415 list_add_tail(&vm->ractor.set, &r->vmlr_node);
1416 vm->ractor.cnt++;
1417}
1418
1419static void
1420cancel_single_ractor_mode(void)
1421{
1422 // enable multi-ractor mode
1423 RUBY_DEBUG_LOG("enable multi-ractor mode");
1424
1425 VALUE was_disabled = rb_gc_enable();
1426
1427 rb_gc_start();
1428 rb_transient_heap_evacuate();
1429
1430 if (was_disabled) {
1431 rb_gc_disable();
1432 }
1433
1434 ruby_single_main_ractor = NULL;
1435}
1436
1437static void
1438vm_insert_ractor(rb_vm_t *vm, rb_ractor_t *r)
1439{
1440 VM_ASSERT(ractor_status_p(r, ractor_created));
1441
1442 if (rb_multi_ractor_p()) {
1443 RB_VM_LOCK();
1444 {
1445 vm_insert_ractor0(vm, r, false);
1446 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
1447 }
1448 RB_VM_UNLOCK();
1449 }
1450 else {
1451 if (vm->ractor.cnt == 0) {
1452 // main ractor
1453 vm_insert_ractor0(vm, r, true);
1454 ractor_status_set(r, ractor_blocking);
1455 ractor_status_set(r, ractor_running);
1456 }
1457 else {
1458 cancel_single_ractor_mode();
1459 vm_insert_ractor0(vm, r, true);
1460 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
1461 }
1462 }
1463}
1464
1465static void
1466vm_remove_ractor(rb_vm_t *vm, rb_ractor_t *cr)
1467{
1468 VM_ASSERT(ractor_status_p(cr, ractor_running));
1469 VM_ASSERT(vm->ractor.cnt > 1);
1470 VM_ASSERT(cr->threads.cnt == 1);
1471
1472 RB_VM_LOCK();
1473 {
1474 RUBY_DEBUG_LOG("ractor.cnt:%u-- terminate_waiting:%d",
1475 vm->ractor.cnt, vm->ractor.sync.terminate_waiting);
1476
1477 VM_ASSERT(vm->ractor.cnt > 0);
1478 list_del(&cr->vmlr_node);
1479
1480 if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
1481 rb_native_cond_signal(&vm->ractor.sync.terminate_cond);
1482 }
1483 vm->ractor.cnt--;
1484
1485 /* Clear the cached freelist to prevent a memory leak. */
1486 rb_gc_ractor_newobj_cache_clear(&cr->newobj_cache);
1487
1488 ractor_status_set(cr, ractor_terminated);
1489 }
1490 RB_VM_UNLOCK();
1491}
1492
1493static VALUE
1494ractor_alloc(VALUE klass)
1495{
1496 rb_ractor_t *r;
1497 VALUE rv = TypedData_Make_Struct(klass, rb_ractor_t, &ractor_data_type, r);
1499 r->pub.self = rv;
1500 VM_ASSERT(ractor_status_p(r, ractor_created));
1501 return rv;
1502}
1503
1505rb_ractor_main_alloc(void)
1506{
1507 rb_ractor_t *r = ruby_mimmalloc(sizeof(rb_ractor_t));
1508 if (r == NULL) {
1509 fprintf(stderr, "[FATAL] failed to allocate memory for main ractor\n");
1510 exit(EXIT_FAILURE);
1511 }
1512 MEMZERO(r, rb_ractor_t, 1);
1513 r->pub.id = ++ractor_last_id;
1514 r->loc = Qnil;
1515 r->name = Qnil;
1516 r->pub.self = Qnil;
1517 ruby_single_main_ractor = r;
1518
1519 return r;
1520}
1521
1522#if defined(HAVE_WORKING_FORK)
1523void
1524rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th)
1525{
1526 // initialize as a main ractor
1527 vm->ractor.cnt = 0;
1528 vm->ractor.blocking_cnt = 0;
1529 ruby_single_main_ractor = th->ractor;
1530 th->ractor->status_ = ractor_created;
1531
1532 rb_ractor_living_threads_init(th->ractor);
1533 rb_ractor_living_threads_insert(th->ractor, th);
1534
1535 VM_ASSERT(vm->ractor.blocking_cnt == 0);
1536 VM_ASSERT(vm->ractor.cnt == 1);
1537}
1538#endif
1539
1540void rb_gvl_init(rb_global_vm_lock_t *gvl);
1541
1542void
1543rb_ractor_living_threads_init(rb_ractor_t *r)
1544{
1545 list_head_init(&r->threads.set);
1546 r->threads.cnt = 0;
1547 r->threads.blocking_cnt = 0;
1548}
1549
1550static void
1551ractor_init(rb_ractor_t *r, VALUE name, VALUE loc)
1552{
1553 ractor_queue_setup(&r->sync.incoming_queue);
1554 rb_native_mutex_initialize(&r->sync.lock);
1555 rb_native_cond_initialize(&r->sync.cond);
1556 rb_native_cond_initialize(&r->barrier_wait_cond);
1557
1558 // thread management
1559 rb_gvl_init(&r->threads.gvl);
1560 rb_ractor_living_threads_init(r);
1561
1562 // naming
1563 if (!NIL_P(name)) {
1564 rb_encoding *enc;
1565 StringValueCStr(name);
1566 enc = rb_enc_get(name);
1567 if (!rb_enc_asciicompat(enc)) {
1568 rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
1569 rb_enc_name(enc));
1570 }
1571 name = rb_str_new_frozen(name);
1572 }
1573 r->name = name;
1574 r->loc = loc;
1575}
1576
1577void
1578rb_ractor_main_setup(rb_vm_t *vm, rb_ractor_t *r, rb_thread_t *th)
1579{
1580 r->pub.self = TypedData_Wrap_Struct(rb_cRactor, &ractor_data_type, r);
1581 FL_SET_RAW(r->pub.self, RUBY_FL_SHAREABLE);
1582 ractor_init(r, Qnil, Qnil);
1583 r->threads.main = th;
1584 rb_ractor_living_threads_insert(r, th);
1585}
1586
1587static VALUE
1588ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VALUE args, VALUE block)
1589{
1590 VALUE rv = ractor_alloc(self);
1591 rb_ractor_t *r = RACTOR_PTR(rv);
1592 ractor_init(r, name, loc);
1593
1594 // can block here
1595 r->pub.id = ractor_next_id();
1596 RUBY_DEBUG_LOG("r:%u", r->pub.id);
1597
1598 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1599 r->verbose = cr->verbose;
1600 r->debug = cr->debug;
1601
1602 rb_yjit_before_ractor_spawn();
1603 rb_thread_create_ractor(r, args, block);
1604
1605 RB_GC_GUARD(rv);
1606 return rv;
1607}
1608
1609static void
1610ractor_yield_atexit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE v, bool exc)
1611{
1612 if (cr->sync.outgoing_port_closed) {
1613 return;
1614 }
1615
1616 ASSERT_ractor_unlocking(cr);
1617
1618 struct rb_ractor_basket basket;
1619 ractor_basket_setup(ec, &basket, v, Qfalse, exc, true, true /* this flag is ignored because move is Qfalse */);
1620
1621 retry:
1622 if (ractor_try_yield(ec, cr, &basket)) {
1623 // OK.
1624 }
1625 else {
1626 bool retry = false;
1627 RACTOR_LOCK(cr);
1628 {
1629 if (cr->sync.taking_ractors.cnt == 0) {
1630 cr->sync.wait.yielded_basket = basket;
1631
1632 VM_ASSERT(cr->sync.wait.status == wait_none);
1633 cr->sync.wait.status = wait_yielding;
1634 cr->sync.wait.wakeup_status = wakeup_none;
1635
1636 VM_ASSERT(cr->yield_atexit == false);
1637 cr->yield_atexit = true;
1638 }
1639 else {
1640 retry = true; // another ractor is waiting for the yield.
1641 }
1642 }
1643 RACTOR_UNLOCK(cr);
1644
1645 if (retry) goto retry;
1646 }
1647}
1648
1649void
1650rb_ractor_teardown(rb_execution_context_t *ec)
1651{
1652 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1653 ractor_close_incoming(ec, cr);
1654 ractor_close_outgoing(ec, cr);
1655
1656 // sync with rb_ractor_terminate_interrupt_main_thread()
1657 RB_VM_LOCK_ENTER();
1658 {
1659 VM_ASSERT(cr->threads.main != NULL);
1660 cr->threads.main = NULL;
1661 }
1662 RB_VM_LOCK_LEAVE();
1663}
1664
1665void
1666rb_ractor_atexit(rb_execution_context_t *ec, VALUE result)
1667{
1668 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1669 ractor_yield_atexit(ec, cr, result, false);
1670}
1671
1672void
1673rb_ractor_atexit_exception(rb_execution_context_t *ec)
1674{
1675 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1676 ractor_yield_atexit(ec, cr, ec->errinfo, true);
1677}
1678
1679void
1680rb_ractor_receive_parameters(rb_execution_context_t *ec, rb_ractor_t *r, int len, VALUE *ptr)
1681{
1682 for (int i=0; i<len; i++) {
1683 ptr[i] = ractor_receive(ec, r);
1684 }
1685}
1686
1687void
1688rb_ractor_send_parameters(rb_execution_context_t *ec, rb_ractor_t *r, VALUE args)
1689{
1690 int len = RARRAY_LENINT(args);
1691 for (int i=0; i<len; i++) {
1692 ractor_send(ec, r, RARRAY_AREF(args, i), false);
1693 }
1694}
1695
1696MJIT_FUNC_EXPORTED bool
1697rb_ractor_main_p_(void)
1698{
1699 VM_ASSERT(rb_multi_ractor_p());
1700 rb_execution_context_t *ec = GET_EC();
1701 return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
1702}
1703
1704bool
1705rb_obj_is_main_ractor(VALUE gv)
1706{
1707 if (!rb_ractor_p(gv)) return false;
1708 rb_ractor_t *r = DATA_PTR(gv);
1709 return r == GET_VM()->ractor.main_ractor;
1710}
1711
1713rb_ractor_gvl(rb_ractor_t *r)
1714{
1715 return &r->threads.gvl;
1716}
1717
1718int
1719rb_ractor_living_thread_num(const rb_ractor_t *r)
1720{
1721 return r->threads.cnt;
1722}
1723
1724VALUE
1725rb_ractor_thread_list(rb_ractor_t *r)
1726{
1727 rb_thread_t *th = 0;
1728 VALUE *ts;
1729 int ts_cnt;
1730
1731 RACTOR_LOCK(r);
1732 {
1733 ts = ALLOCA_N(VALUE, r->threads.cnt);
1734 ts_cnt = 0;
1735
1736 list_for_each(&r->threads.set, th, lt_node) {
1737 switch (th->status) {
1738 case THREAD_RUNNABLE:
1739 case THREAD_STOPPED:
1740 case THREAD_STOPPED_FOREVER:
1741 ts[ts_cnt++] = th->self;
1742 default:
1743 break;
1744 }
1745 }
1746 }
1747 RACTOR_UNLOCK(r);
1748
1749 VALUE ary = rb_ary_new();
1750 for (int i=0; i<ts_cnt; i++) {
1751 rb_ary_push(ary, ts[i]);
1752 }
1753
1754 return ary;
1755}
1756
1757void
1758rb_ractor_living_threads_insert(rb_ractor_t *r, rb_thread_t *th)
1759{
1760 VM_ASSERT(th != NULL);
1761
1762 RACTOR_LOCK(r);
1763 {
1764 RUBY_DEBUG_LOG("r(%d)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
1765 list_add_tail(&r->threads.set, &th->lt_node);
1766 r->threads.cnt++;
1767 }
1768 RACTOR_UNLOCK(r);
1769
1770 // first thread for a ractor
1771 if (r->threads.cnt == 1) {
1772 VM_ASSERT(ractor_status_p(r, ractor_created));
1773 vm_insert_ractor(th->vm, r);
1774 }
1775}
1776
1777static void
1778vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line)
1779{
1780 ractor_status_set(r, ractor_blocking);
1781
1782 RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d++", vm->ractor.blocking_cnt);
1783 vm->ractor.blocking_cnt++;
1784 VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
1785}
1786
1787void
1788rb_vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
1789{
1790 ASSERT_vm_locking();
1791 VM_ASSERT(GET_RACTOR() == cr);
1792 vm_ractor_blocking_cnt_inc(vm, cr, file, line);
1793}
1794
1795void
1796rb_vm_ractor_blocking_cnt_dec(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
1797{
1798 ASSERT_vm_locking();
1799 VM_ASSERT(GET_RACTOR() == cr);
1800
1801 RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d--", vm->ractor.blocking_cnt);
1802 VM_ASSERT(vm->ractor.blocking_cnt > 0);
1803 vm->ractor.blocking_cnt--;
1804
1805 ractor_status_set(cr, ractor_running);
1806}
1807
1808static void
1809ractor_check_blocking(rb_ractor_t *cr, unsigned int remained_thread_cnt, const char *file, int line)
1810{
1811 VM_ASSERT(cr == GET_RACTOR());
1812
1813 RUBY_DEBUG_LOG2(file, line,
1814 "cr->threads.cnt:%u cr->threads.blocking_cnt:%u vm->ractor.cnt:%u vm->ractor.blocking_cnt:%u",
1815 cr->threads.cnt, cr->threads.blocking_cnt,
1816 GET_VM()->ractor.cnt, GET_VM()->ractor.blocking_cnt);
1817
1818 VM_ASSERT(cr->threads.cnt >= cr->threads.blocking_cnt + 1);
1819
1820 if (remained_thread_cnt > 0 &&
1821 // will be block
1822 cr->threads.cnt == cr->threads.blocking_cnt + 1) {
1823 // change ractor status: running -> blocking
1824 rb_vm_t *vm = GET_VM();
1825 ASSERT_vm_unlocking();
1826
1827 RB_VM_LOCK();
1828 {
1829 rb_vm_ractor_blocking_cnt_inc(vm, cr, file, line);
1830 }
1831 RB_VM_UNLOCK();
1832 }
1833}
1834
1835void
1836rb_ractor_living_threads_remove(rb_ractor_t *cr, rb_thread_t *th)
1837{
1838 VM_ASSERT(cr == GET_RACTOR());
1839 RUBY_DEBUG_LOG("r->threads.cnt:%d--", cr->threads.cnt);
1840 ractor_check_blocking(cr, cr->threads.cnt - 1, __FILE__, __LINE__);
1841
1842 if (cr->threads.cnt == 1) {
1843 vm_remove_ractor(th->vm, cr);
1844 }
1845 else {
1846 RACTOR_LOCK(cr);
1847 {
1848 list_del(&th->lt_node);
1849 cr->threads.cnt--;
1850 }
1851 RACTOR_UNLOCK(cr);
1852 }
1853}
1854
1855void
1856rb_ractor_blocking_threads_inc(rb_ractor_t *cr, const char *file, int line)
1857{
1858 RUBY_DEBUG_LOG2(file, line, "cr->threads.blocking_cnt:%d++", cr->threads.blocking_cnt);
1859
1860 VM_ASSERT(cr->threads.cnt > 0);
1861 VM_ASSERT(cr == GET_RACTOR());
1862
1863 ractor_check_blocking(cr, cr->threads.cnt, __FILE__, __LINE__);
1864 cr->threads.blocking_cnt++;
1865}
1866
1867void
1868rb_ractor_blocking_threads_dec(rb_ractor_t *cr, const char *file, int line)
1869{
1870 RUBY_DEBUG_LOG2(file, line,
1871 "r->threads.blocking_cnt:%d--, r->threads.cnt:%u",
1872 cr->threads.blocking_cnt, cr->threads.cnt);
1873
1874 VM_ASSERT(cr == GET_RACTOR());
1875
1876 if (cr->threads.cnt == cr->threads.blocking_cnt) {
1877 rb_vm_t *vm = GET_VM();
1878
1879 RB_VM_LOCK_ENTER();
1880 {
1881 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
1882 }
1883 RB_VM_LOCK_LEAVE();
1884 }
1885
1886 cr->threads.blocking_cnt--;
1887}
1888
1889void
1890rb_ractor_vm_barrier_interrupt_running_thread(rb_ractor_t *r)
1891{
1892 VM_ASSERT(r != GET_RACTOR());
1893 ASSERT_ractor_unlocking(r);
1894 ASSERT_vm_locking();
1895
1896 RACTOR_LOCK(r);
1897 {
1898 if (ractor_status_p(r, ractor_running)) {
1899 rb_execution_context_t *ec = r->threads.running_ec;
1900 if (ec) {
1901 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec);
1902 }
1903 }
1904 }
1905 RACTOR_UNLOCK(r);
1906}
1907
1908void
1909rb_ractor_terminate_interrupt_main_thread(rb_ractor_t *r)
1910{
1911 VM_ASSERT(r != GET_RACTOR());
1912 ASSERT_ractor_unlocking(r);
1913 ASSERT_vm_locking();
1914
1915 rb_thread_t *main_th = r->threads.main;
1916 if (main_th) {
1917 if (main_th->status != THREAD_KILLED) {
1918 RUBY_VM_SET_TERMINATE_INTERRUPT(main_th->ec);
1919 rb_threadptr_interrupt(main_th);
1920 }
1921 else {
1922 RUBY_DEBUG_LOG("killed (%p)", (void *)main_th);
1923 }
1924 }
1925}
1926
1927void rb_thread_terminate_all(rb_thread_t *th); // thread.c
1928
1929static void
1930ractor_terminal_interrupt_all(rb_vm_t *vm)
1931{
1932 if (vm->ractor.cnt > 1) {
1933 // send terminate notification to all ractors
1934 rb_ractor_t *r = 0;
1935 list_for_each(&vm->ractor.set, r, vmlr_node) {
1936 if (r != vm->ractor.main_ractor) {
1937 rb_ractor_terminate_interrupt_main_thread(r);
1938 }
1939 }
1940 }
1941}
1942
1943void
1944rb_ractor_terminate_all(void)
1945{
1946 rb_vm_t *vm = GET_VM();
1947 rb_ractor_t *cr = vm->ractor.main_ractor;
1948
1949 VM_ASSERT(cr == GET_RACTOR()); // only main-ractor's main-thread should kick it.
1950
1951 if (vm->ractor.cnt > 1) {
1952 RB_VM_LOCK();
1953 ractor_terminal_interrupt_all(vm); // kill all ractors
1954 RB_VM_UNLOCK();
1955 }
1956 rb_thread_terminate_all(GET_THREAD()); // kill other threads in main-ractor and wait
1957
1958 RB_VM_LOCK();
1959 {
1960 while (vm->ractor.cnt > 1) {
1961 RUBY_DEBUG_LOG("terminate_waiting:%d", vm->ractor.sync.terminate_waiting);
1962 vm->ractor.sync.terminate_waiting = true;
1963
1964 // wait for 1sec
1965 rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
1966 rb_vm_cond_timedwait(vm, &vm->ractor.sync.terminate_cond, 1000 /* ms */);
1967 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
1968
1969 ractor_terminal_interrupt_all(vm);
1970 }
1971 }
1972 RB_VM_UNLOCK();
1973}
1974
1976rb_vm_main_ractor_ec(rb_vm_t *vm)
1977{
1978 return vm->ractor.main_ractor->threads.running_ec;
1979}
1980
1981static VALUE
1982ractor_moved_missing(int argc, VALUE *argv, VALUE self)
1983{
1984 rb_raise(rb_eRactorMovedError, "can not send any methods to a moved object");
1985}
1986
1987/*
1988 * Document-class: Ractor::ClosedError
1989 *
1990 * Raised when an attempt is made to send a message to a closed port,
1991 * or to retrieve a message from a closed and empty port.
1992 * Ports may be closed explicitly with Ractor#close_outgoing/close_incoming
1993 * and are closed implicitly when a Ractor terminates.
1994 *
1995 * r = Ractor.new { sleep(500) }
1996 * r.close_outgoing
1997 * r.take # Ractor::ClosedError
1998 *
1999 * ClosedError is a descendant of StopIteration, so the closing of the ractor will break
2000 * the loops without propagating the error:
2001 *
2002 * r = Ractor.new do
2003 * loop do
2004 * msg = receive # raises ClosedError and loop traps it
2005 * puts "Received: #{msg}"
2006 * end
2007 * puts "loop exited"
2008 * end
2009 *
2010 * 3.times{|i| r << i}
2011 * r.close_incoming
2012 * r.take
2013 * puts "Continue successfully"
2014 *
2015 * This will print:
2016 *
2017 * Received: 0
2018 * Received: 1
2019 * Received: 2
2020 * loop exited
2021 * Continue successfully
2022 */
2023
2024/*
2025 * Document-class: Ractor::RemoteError
2026 *
2027 * Raised on attempt to Ractor#take if there was an uncaught exception in the Ractor.
2028 * Its +cause+ will contain the original exception, and +ractor+ is the original ractor
2029 * it was raised in.
2030 *
2031 * r = Ractor.new { raise "Something weird happened" }
2032 *
2033 * begin
2034 * r.take
2035 * rescue => e
2036 * p e # => #<Ractor::RemoteError: thrown by remote Ractor.>
2037 * p e.ractor == r # => true
2038 * p e.cause # => #<RuntimeError: Something weird happened>
2039 * end
2040 *
2041 */
2042
2043/*
2044 * Document-class: Ractor::MovedError
2045 *
2046 * Raised on an attempt to access an object which was moved in Ractor#send or Ractor.yield.
2047 *
2048 * r = Ractor.new { sleep }
2049 *
2050 * ary = [1, 2, 3]
2051 * r.send(ary, move: true)
2052 * ary.inspect
2053 * # Ractor::MovedError (can not send any methods to a moved object)
2054 *
2055 */
2056
2057/*
2058 * Document-class: Ractor::MovedObject
2059 *
2060 * A special object which replaces any value that was moved to another ractor in Ractor#send
2061 * or Ractor.yield. Any attempt to access the object results in Ractor::MovedError.
2062 *
2063 * r = Ractor.new { receive }
2064 *
2065 * ary = [1, 2, 3]
2066 * r.send(ary, move: true)
2067 * p Ractor::MovedObject === ary
2068 * # => true
2069 * ary.inspect
2070 * # Ractor::MovedError (can not send any methods to a moved object)
2071 */
2072
2073// Main docs are in ractor.rb, but without this clause there are weird artifacts
2074// in their rendering.
2075/*
2076 * Document-class: Ractor
2077 *
2078 */
2079
2080void
2081Init_Ractor(void)
2082{
2083 rb_cRactor = rb_define_class("Ractor", rb_cObject);
2085
2086 rb_eRactorError = rb_define_class_under(rb_cRactor, "Error", rb_eRuntimeError);
2087 rb_eRactorIsolationError = rb_define_class_under(rb_cRactor, "IsolationError", rb_eRactorError);
2088 rb_eRactorRemoteError = rb_define_class_under(rb_cRactor, "RemoteError", rb_eRactorError);
2089 rb_eRactorMovedError = rb_define_class_under(rb_cRactor, "MovedError", rb_eRactorError);
2090 rb_eRactorClosedError = rb_define_class_under(rb_cRactor, "ClosedError", rb_eStopIteration);
2091 rb_eRactorUnsafeError = rb_define_class_under(rb_cRactor, "UnsafeError", rb_eRactorError);
2092
2093 rb_cRactorMovedObject = rb_define_class_under(rb_cRactor, "MovedObject", rb_cBasicObject);
2094 rb_undef_alloc_func(rb_cRactorMovedObject);
2095 rb_define_method(rb_cRactorMovedObject, "method_missing", ractor_moved_missing, -1);
2096
2097 // override methods defined in BasicObject
2098 rb_define_method(rb_cRactorMovedObject, "__send__", ractor_moved_missing, -1);
2099 rb_define_method(rb_cRactorMovedObject, "!", ractor_moved_missing, -1);
2100 rb_define_method(rb_cRactorMovedObject, "==", ractor_moved_missing, -1);
2101 rb_define_method(rb_cRactorMovedObject, "!=", ractor_moved_missing, -1);
2102 rb_define_method(rb_cRactorMovedObject, "__id__", ractor_moved_missing, -1);
2103 rb_define_method(rb_cRactorMovedObject, "equal?", ractor_moved_missing, -1);
2104 rb_define_method(rb_cRactorMovedObject, "instance_eval", ractor_moved_missing, -1);
2105 rb_define_method(rb_cRactorMovedObject, "instance_exec", ractor_moved_missing, -1);
2106}
2107
2108void
2109rb_ractor_dump(void)
2110{
2111 rb_vm_t *vm = GET_VM();
2112 rb_ractor_t *r = 0;
2113
2114 list_for_each(&vm->ractor.set, r, vmlr_node) {
2115 if (r != vm->ractor.main_ractor) {
2116 fprintf(stderr, "r:%u (%s)\n", r->pub.id, ractor_status_str(r->status_));
2117 }
2118 }
2119}
2120
2121VALUE
2123{
2124 if (rb_ractor_main_p()) {
2125 return rb_stdin;
2126 }
2127 else {
2128 rb_ractor_t *cr = GET_RACTOR();
2129 return cr->r_stdin;
2130 }
2131}
2132
2133VALUE
2135{
2136 if (rb_ractor_main_p()) {
2137 return rb_stdout;
2138 }
2139 else {
2140 rb_ractor_t *cr = GET_RACTOR();
2141 return cr->r_stdout;
2142 }
2143}
2144
2145VALUE
2147{
2148 if (rb_ractor_main_p()) {
2149 return rb_stderr;
2150 }
2151 else {
2152 rb_ractor_t *cr = GET_RACTOR();
2153 return cr->r_stderr;
2154 }
2155}
2156
2157void
2159{
2160 if (rb_ractor_main_p()) {
2161 rb_stdin = in;
2162 }
2163 else {
2164 rb_ractor_t *cr = GET_RACTOR();
2165 RB_OBJ_WRITE(cr->pub.self, &cr->r_stdin, in);
2166 }
2167}
2168
2169void
2171{
2172 if (rb_ractor_main_p()) {
2173 rb_stdout = out;
2174 }
2175 else {
2176 rb_ractor_t *cr = GET_RACTOR();
2177 RB_OBJ_WRITE(cr->pub.self, &cr->r_stdout, out);
2178 }
2179}
2180
2181void
2183{
2184 if (rb_ractor_main_p()) {
2185 rb_stderr = err;
2186 }
2187 else {
2188 rb_ractor_t *cr = GET_RACTOR();
2189 RB_OBJ_WRITE(cr->pub.self, &cr->r_stderr, err);
2190 }
2191}
2192
2194rb_ractor_hooks(rb_ractor_t *cr)
2195{
2196 return &cr->pub.hooks;
2197}
2198
2200
2201// 2: stop search
2202// 1: skip child
2203// 0: continue
2204
2205enum obj_traverse_iterator_result {
2206 traverse_cont,
2207 traverse_skip,
2208 traverse_stop,
2209};
2210
2211typedef enum obj_traverse_iterator_result (*rb_obj_traverse_enter_func)(VALUE obj);
2212typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(VALUE obj);
2213typedef enum obj_traverse_iterator_result (*rb_obj_traverse_final_func)(VALUE obj);
2214
2215static enum obj_traverse_iterator_result null_leave(VALUE obj);
2216
2218 rb_obj_traverse_enter_func enter_func;
2219 rb_obj_traverse_leave_func leave_func;
2220
2221 st_table *rec;
2222 VALUE rec_hash;
2223};
2224
2225
2227 bool stop;
2228 struct obj_traverse_data *data;
2229};
2230
2231static int obj_traverse_i(VALUE obj, struct obj_traverse_data *data);
2232
2233static int
2234obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
2235{
2237
2238 if (obj_traverse_i(key, d->data)) {
2239 d->stop = true;
2240 return ST_STOP;
2241 }
2242
2243 if (obj_traverse_i(val, d->data)) {
2244 d->stop = true;
2245 return ST_STOP;
2246 }
2247
2248 return ST_CONTINUE;
2249}
2250
2251static void
2252obj_traverse_reachable_i(VALUE obj, void *ptr)
2253{
2255
2256 if (obj_traverse_i(obj, d->data)) {
2257 d->stop = true;
2258 }
2259}
2260
2261static struct st_table *
2262obj_traverse_rec(struct obj_traverse_data *data)
2263{
2264 if (UNLIKELY(!data->rec)) {
2265 data->rec_hash = rb_ident_hash_new();
2266 data->rec = rb_hash_st_table(data->rec_hash);
2267 }
2268 return data->rec;
2269}
2270
2271static int
2272obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
2273{
2274 if (RB_SPECIAL_CONST_P(obj)) return 0;
2275
2276 switch (data->enter_func(obj)) {
2277 case traverse_cont: break;
2278 case traverse_skip: return 0; // skip children
2279 case traverse_stop: return 1; // stop search
2280 }
2281
2282 if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
2283 // already traversed
2284 return 0;
2285 }
2286
2287 if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
2288 struct gen_ivtbl *ivtbl;
2289 rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
2290 for (uint32_t i = 0; i < ivtbl->numiv; i++) {
2291 VALUE val = ivtbl->ivptr[i];
2292 if (val != Qundef && obj_traverse_i(val, data)) return 1;
2293 }
2294 }
2295
2296 switch (BUILTIN_TYPE(obj)) {
2297 // no child node
2298 case T_STRING:
2299 case T_FLOAT:
2300 case T_BIGNUM:
2301 case T_REGEXP:
2302 case T_FILE:
2303 case T_SYMBOL:
2304 case T_MATCH:
2305 break;
2306
2307 case T_OBJECT:
2308 {
2309 uint32_t len = ROBJECT_NUMIV(obj);
2310 VALUE *ptr = ROBJECT_IVPTR(obj);
2311
2312 for (uint32_t i=0; i<len; i++) {
2313 VALUE val = ptr[i];
2314 if (val != Qundef && obj_traverse_i(val, data)) return 1;
2315 }
2316 }
2317 break;
2318
2319 case T_ARRAY:
2320 {
2321 for (int i = 0; i < RARRAY_LENINT(obj); i++) {
2322 VALUE e = rb_ary_entry(obj, i);
2323 if (obj_traverse_i(e, data)) return 1;
2324 }
2325 }
2326 break;
2327
2328 case T_HASH:
2329 {
2330 if (obj_traverse_i(RHASH_IFNONE(obj), data)) return 1;
2331
2332 struct obj_traverse_callback_data d = {
2333 .stop = false,
2334 .data = data,
2335 };
2336 rb_hash_foreach(obj, obj_hash_traverse_i, (VALUE)&d);
2337 if (d.stop) return 1;
2338 }
2339 break;
2340
2341 case T_STRUCT:
2342 {
2343 long len = RSTRUCT_LEN(obj);
2344 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2345
2346 for (long i=0; i<len; i++) {
2347 if (obj_traverse_i(ptr[i], data)) return 1;
2348 }
2349 }
2350 break;
2351
2352 case T_RATIONAL:
2353 if (obj_traverse_i(RRATIONAL(obj)->num, data)) return 1;
2354 if (obj_traverse_i(RRATIONAL(obj)->den, data)) return 1;
2355 break;
2356 case T_COMPLEX:
2357 if (obj_traverse_i(RCOMPLEX(obj)->real, data)) return 1;
2358 if (obj_traverse_i(RCOMPLEX(obj)->imag, data)) return 1;
2359 break;
2360
2361 case T_DATA:
2362 case T_IMEMO:
2363 {
2364 struct obj_traverse_callback_data d = {
2365 .stop = false,
2366 .data = data,
2367 };
2368 RB_VM_LOCK_ENTER_NO_BARRIER();
2369 {
2370 rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
2371 }
2372 RB_VM_LOCK_LEAVE_NO_BARRIER();
2373 if (d.stop) return 1;
2374 }
2375 break;
2376
2377 // unreachable
2378 case T_CLASS:
2379 case T_MODULE:
2380 case T_ICLASS:
2381 default:
2382 rp(obj);
2383 rb_bug("unreachable");
2384 }
2385
2386 if (data->leave_func(obj) == traverse_stop) {
2387 return 1;
2388 }
2389 else {
2390 return 0;
2391 }
2392}
2393
2395 rb_obj_traverse_final_func final_func;
2396 int stopped;
2397};
2398
2399static int
2400obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
2401{
2402 struct rb_obj_traverse_final_data *data = (void *)arg;
2403 if (data->final_func(key)) {
2404 data->stopped = 1;
2405 return ST_STOP;
2406 }
2407 return ST_CONTINUE;
2408}
2409
2410// 0: traverse all
2411// 1: stopped
2412static int
2413rb_obj_traverse(VALUE obj,
2414 rb_obj_traverse_enter_func enter_func,
2415 rb_obj_traverse_leave_func leave_func,
2416 rb_obj_traverse_final_func final_func)
2417{
2418 struct obj_traverse_data data = {
2419 .enter_func = enter_func,
2420 .leave_func = leave_func,
2421 .rec = NULL,
2422 };
2423
2424 if (obj_traverse_i(obj, &data)) return 1;
2425 if (final_func && data.rec) {
2426 struct rb_obj_traverse_final_data f = {final_func, 0};
2427 st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
2428 return f.stopped;
2429 }
2430 return 0;
2431}
2432
2433static int
2434frozen_shareable_p(VALUE obj, bool *made_shareable)
2435{
2436 if (!RB_TYPE_P(obj, T_DATA)) {
2437 return true;
2438 }
2439 else if (RTYPEDDATA_P(obj)) {
2440 const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
2441 if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
2442 return true;
2443 }
2444 else if (made_shareable && rb_obj_is_proc(obj)) {
2445 // special path to make shareable Proc.
2446 rb_proc_ractor_make_shareable(obj);
2447 *made_shareable = true;
2448 VM_ASSERT(RB_OBJ_SHAREABLE_P(obj));
2449 return false;
2450 }
2451 }
2452
2453 return false;
2454}
2455
2456static enum obj_traverse_iterator_result
2457make_shareable_check_shareable(VALUE obj)
2458{
2459 VM_ASSERT(!SPECIAL_CONST_P(obj));
2460 bool made_shareable = false;
2461
2462 if (rb_ractor_shareable_p(obj)) {
2463 return traverse_skip;
2464 }
2465 else if (!frozen_shareable_p(obj, &made_shareable)) {
2466 if (made_shareable) {
2467 return traverse_skip;
2468 }
2469 else {
2470 rb_raise(rb_eRactorError, "can not make shareable object for %"PRIsVALUE, obj);
2471 }
2472 }
2473
2474 if (!RB_OBJ_FROZEN_RAW(obj)) {
2475 rb_funcall(obj, idFreeze, 0);
2476
2477 if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
2478 rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
2479 }
2480
2481 if (RB_OBJ_SHAREABLE_P(obj)) {
2482 return traverse_skip;
2483 }
2484 }
2485
2486 return traverse_cont;
2487}
2488
2489static enum obj_traverse_iterator_result
2490mark_shareable(VALUE obj)
2491{
2493 return traverse_cont;
2494}
2495
2496VALUE
2498{
2499 rb_obj_traverse(obj,
2500 make_shareable_check_shareable,
2501 null_leave, mark_shareable);
2502 return obj;
2503}
2504
2505VALUE
2507{
2508 VALUE copy = ractor_copy(obj);
2509 rb_obj_traverse(copy,
2510 make_shareable_check_shareable,
2511 null_leave, mark_shareable);
2512 return copy;
2513}
2514
2515VALUE
2516rb_ractor_ensure_shareable(VALUE obj, VALUE name)
2517{
2518 if (!rb_ractor_shareable_p(obj)) {
2519 VALUE message = rb_sprintf("cannot assign unshareable object to %"PRIsVALUE,
2520 name);
2521 rb_exc_raise(rb_exc_new_str(rb_eRactorIsolationError, message));
2522 }
2523 return obj;
2524}
2525
2526static enum obj_traverse_iterator_result
2527shareable_p_enter(VALUE obj)
2528{
2529 if (RB_OBJ_SHAREABLE_P(obj)) {
2530 return traverse_skip;
2531 }
2532 else if (RB_TYPE_P(obj, T_CLASS) ||
2533 RB_TYPE_P(obj, T_MODULE) ||
2534 RB_TYPE_P(obj, T_ICLASS)) {
2535 // TODO: remove it
2536 mark_shareable(obj);
2537 return traverse_skip;
2538 }
2539 else if (RB_OBJ_FROZEN_RAW(obj) &&
2540 frozen_shareable_p(obj, NULL)) {
2541 return traverse_cont;
2542 }
2543
2544 return traverse_stop; // fail
2545}
2546
2547MJIT_FUNC_EXPORTED bool
2548rb_ractor_shareable_p_continue(VALUE obj)
2549{
2550 if (rb_obj_traverse(obj,
2551 shareable_p_enter, null_leave,
2552 mark_shareable)) {
2553 return false;
2554 }
2555 else {
2556 return true;
2557 }
2558}
2559
2560#if RACTOR_CHECK_MODE > 0
2561static enum obj_traverse_iterator_result
2562reset_belonging_enter(VALUE obj)
2563{
2564 if (rb_ractor_shareable_p(obj)) {
2565 return traverse_skip;
2566 }
2567 else {
2568 rb_ractor_setup_belonging(obj);
2569 return traverse_cont;
2570 }
2571}
2572#endif
2573
2574static enum obj_traverse_iterator_result
2575null_leave(VALUE obj)
2576{
2577 return traverse_cont;
2578}
2579
2580static VALUE
2581ractor_reset_belonging(VALUE obj)
2582{
2583#if RACTOR_CHECK_MODE > 0
2584 rb_obj_traverse(obj, reset_belonging_enter, null_leave, NULL);
2585#endif
2586 return obj;
2587}
2588
2589
2591
2592// 2: stop search
2593// 1: skip child
2594// 0: continue
2595
2597static int obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data);
2598typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_enter_func)(VALUE obj, struct obj_traverse_replace_data *data);
2599typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_leave_func)(VALUE obj, struct obj_traverse_replace_data *data);
2600
2602 rb_obj_traverse_replace_enter_func enter_func;
2603 rb_obj_traverse_replace_leave_func leave_func;
2604
2605 st_table *rec;
2606 VALUE rec_hash;
2607
2608 VALUE replacement;
2609 bool move;
2610};
2611
2613 bool stop;
2614 VALUE src;
2615 struct obj_traverse_replace_data *data;
2616};
2617
2618static int
2619obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp, int error)
2620{
2621 return ST_REPLACE;
2622}
2623
2624static int
2625obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr, int exists)
2626{
2628 struct obj_traverse_replace_data *data = d->data;
2629
2630 if (obj_traverse_replace_i(*key, data)) {
2631 d->stop = true;
2632 return ST_STOP;
2633 }
2634 else if (*key != data->replacement) {
2635 VALUE v = *key = data->replacement;
2636 RB_OBJ_WRITTEN(d->src, Qundef, v);
2637 }
2638
2639 if (obj_traverse_replace_i(*val, data)) {
2640 d->stop = true;
2641 return ST_STOP;
2642 }
2643 else if (*val != data->replacement) {
2644 VALUE v = *val = data->replacement;
2645 RB_OBJ_WRITTEN(d->src, Qundef, v);
2646 }
2647
2648 return ST_CONTINUE;
2649}
2650
2651static struct st_table *
2652obj_traverse_replace_rec(struct obj_traverse_replace_data *data)
2653{
2654 if (UNLIKELY(!data->rec)) {
2655 data->rec_hash = rb_ident_hash_new();
2656 data->rec = rb_hash_st_table(data->rec_hash);
2657 }
2658 return data->rec;
2659}
2660
2661#if USE_TRANSIENT_HEAP
2662void rb_ary_transient_heap_evacuate(VALUE ary, int promote);
2663void rb_obj_transient_heap_evacuate(VALUE obj, int promote);
2664void rb_hash_transient_heap_evacuate(VALUE hash, int promote);
2665void rb_struct_transient_heap_evacuate(VALUE st, int promote);
2666#endif
2667
2668static void
2669obj_refer_only_shareables_p_i(VALUE obj, void *ptr)
2670{
2671 int *pcnt = (int *)ptr;
2672
2673 if (!rb_ractor_shareable_p(obj)) {
2674 pcnt++;
2675 }
2676}
2677
2678static int
2679obj_refer_only_shareables_p(VALUE obj)
2680{
2681 int cnt = 0;
2682 RB_VM_LOCK_ENTER_NO_BARRIER();
2683 {
2684 rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
2685 }
2686 RB_VM_LOCK_LEAVE_NO_BARRIER();
2687 return cnt == 0;
2688}
2689
2690static int
2691obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
2692{
2693 VALUE replacement;
2694
2695 if (RB_SPECIAL_CONST_P(obj)) {
2696 data->replacement = obj;
2697 return 0;
2698 }
2699
2700 switch (data->enter_func(obj, data)) {
2701 case traverse_cont: break;
2702 case traverse_skip: return 0; // skip children
2703 case traverse_stop: return 1; // stop search
2704 }
2705
2706 replacement = data->replacement;
2707
2708 if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, (st_data_t *)&replacement))) {
2709 data->replacement = replacement;
2710 return 0;
2711 }
2712 else {
2713 st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, (st_data_t)replacement);
2714 }
2715
2716 if (!data->move) {
2717 obj = replacement;
2718 }
2719
2720#define CHECK_AND_REPLACE(v) do { \
2721 VALUE _val = (v); \
2722 if (obj_traverse_replace_i(_val, data)) { return 1; } \
2723 else if (data->replacement != _val) { RB_OBJ_WRITE(obj, &v, data->replacement); } \
2724} while (0)
2725
2726 if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
2727 struct gen_ivtbl *ivtbl;
2728 rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
2729 for (uint32_t i = 0; i < ivtbl->numiv; i++) {
2730 if (ivtbl->ivptr[i] != Qundef) {
2731 CHECK_AND_REPLACE(ivtbl->ivptr[i]);
2732 }
2733 }
2734 }
2735
2736 switch (BUILTIN_TYPE(obj)) {
2737 // no child node
2738 case T_FLOAT:
2739 case T_BIGNUM:
2740 case T_REGEXP:
2741 case T_FILE:
2742 case T_SYMBOL:
2743 case T_MATCH:
2744 break;
2745 case T_STRING:
2746 rb_str_make_independent(obj);
2747 break;
2748
2749 case T_OBJECT:
2750 {
2751#if USE_TRANSIENT_HEAP
2752 if (data->move) rb_obj_transient_heap_evacuate(obj, TRUE);
2753#endif
2754
2755 uint32_t len = ROBJECT_NUMIV(obj);
2756 VALUE *ptr = ROBJECT_IVPTR(obj);
2757
2758 for (uint32_t i=0; i<len; i++) {
2759 if (ptr[i] != Qundef) {
2760 CHECK_AND_REPLACE(ptr[i]);
2761 }
2762 }
2763 }
2764 break;
2765
2766 case T_ARRAY:
2767 {
2768 rb_ary_cancel_sharing(obj);
2769#if USE_TRANSIENT_HEAP
2770 if (data->move) rb_ary_transient_heap_evacuate(obj, TRUE);
2771#endif
2772
2773 for (int i = 0; i < RARRAY_LENINT(obj); i++) {
2774 VALUE e = rb_ary_entry(obj, i);
2775
2776 if (obj_traverse_replace_i(e, data)) {
2777 return 1;
2778 }
2779 else if (e != data->replacement) {
2780 RARRAY_ASET(obj, i, data->replacement);
2781 }
2782 }
2783 RB_GC_GUARD(obj);
2784 }
2785 break;
2786
2787 case T_HASH:
2788 {
2789#if USE_TRANSIENT_HEAP
2790 if (data->move) rb_hash_transient_heap_evacuate(obj, TRUE);
2791#endif
2793 .stop = false,
2794 .data = data,
2795 .src = obj,
2796 };
2797 rb_hash_stlike_foreach_with_replace(obj,
2798 obj_hash_traverse_replace_foreach_i,
2799 obj_hash_traverse_replace_i,
2800 (VALUE)&d);
2801 if (d.stop) return 1;
2802 // TODO: rehash here?
2803
2804 VALUE ifnone = RHASH_IFNONE(obj);
2805 if (obj_traverse_replace_i(ifnone, data)) {
2806 return 1;
2807 }
2808 else if (ifnone != data->replacement) {
2809 RHASH_SET_IFNONE(obj, data->replacement);
2810 }
2811 }
2812 break;
2813
2814 case T_STRUCT:
2815 {
2816#if USE_TRANSIENT_HEAP
2817 if (data->move) rb_struct_transient_heap_evacuate(obj, TRUE);
2818#endif
2819 long len = RSTRUCT_LEN(obj);
2820 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2821
2822 for (long i=0; i<len; i++) {
2823 CHECK_AND_REPLACE(ptr[i]);
2824 }
2825 }
2826 break;
2827
2828 case T_RATIONAL:
2829 CHECK_AND_REPLACE(RRATIONAL(obj)->num);
2830 CHECK_AND_REPLACE(RRATIONAL(obj)->den);
2831 break;
2832 case T_COMPLEX:
2833 CHECK_AND_REPLACE(RCOMPLEX(obj)->real);
2834 CHECK_AND_REPLACE(RCOMPLEX(obj)->imag);
2835 break;
2836
2837 case T_DATA:
2838 if (!data->move && obj_refer_only_shareables_p(obj)) {
2839 break;
2840 }
2841 else {
2842 rb_raise(rb_eRactorError, "can not %s %"PRIsVALUE" object.",
2843 data->move ? "move" : "copy", rb_class_of(obj));
2844 }
2845
2846 case T_IMEMO:
2847 // not supported yet
2848 return 1;
2849
2850 // unreachable
2851 case T_CLASS:
2852 case T_MODULE:
2853 case T_ICLASS:
2854 default:
2855 rp(obj);
2856 rb_bug("unreachable");
2857 }
2858
2859 data->replacement = replacement;
2860
2861 if (data->leave_func(obj, data) == traverse_stop) {
2862 return 1;
2863 }
2864 else {
2865 return 0;
2866 }
2867}
2868
2869// 0: traverse all
2870// 1: stopped
2871static VALUE
2872rb_obj_traverse_replace(VALUE obj,
2873 rb_obj_traverse_replace_enter_func enter_func,
2874 rb_obj_traverse_replace_leave_func leave_func,
2875 bool move)
2876{
2877 struct obj_traverse_replace_data data = {
2878 .enter_func = enter_func,
2879 .leave_func = leave_func,
2880 .rec = NULL,
2881 .replacement = Qundef,
2882 .move = move,
2883 };
2884
2885 if (obj_traverse_replace_i(obj, &data)) {
2886 return Qundef;
2887 }
2888 else {
2889 return data.replacement;
2890 }
2891}
2892
2893struct RVALUE {
2894 VALUE flags;
2895 VALUE klass;
2896 VALUE v1;
2897 VALUE v2;
2898 VALUE v3;
2899};
2900
2901static const VALUE fl_users = FL_USER1 | FL_USER2 | FL_USER3 |
2906
2907static void
2908ractor_moved_bang(VALUE obj)
2909{
2910 // invalidate src object
2911 struct RVALUE *rv = (void *)obj;
2912
2913 rv->klass = rb_cRactorMovedObject;
2914 rv->v1 = 0;
2915 rv->v2 = 0;
2916 rv->v3 = 0;
2917 rv->flags = rv->flags & ~fl_users;
2918
2919 // TODO: record moved location
2920}
2921
2922static enum obj_traverse_iterator_result
2923move_enter(VALUE obj, struct obj_traverse_replace_data *data)
2924{
2925 if (rb_ractor_shareable_p(obj)) {
2926 data->replacement = obj;
2927 return traverse_skip;
2928 }
2929 else {
2930 data->replacement = rb_obj_alloc(RBASIC_CLASS(obj));
2931 return traverse_cont;
2932 }
2933}
2934
2935void rb_replace_generic_ivar(VALUE clone, VALUE obj); // variable.c
2936
2937static enum obj_traverse_iterator_result
2938move_leave(VALUE obj, struct obj_traverse_replace_data *data)
2939{
2940 VALUE v = data->replacement;
2941 struct RVALUE *dst = (struct RVALUE *)v;
2942 struct RVALUE *src = (struct RVALUE *)obj;
2943
2944 dst->flags = (dst->flags & ~fl_users) | (src->flags & fl_users);
2945
2946 dst->v1 = src->v1;
2947 dst->v2 = src->v2;
2948 dst->v3 = src->v3;
2949
2950 if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
2951 rb_replace_generic_ivar(v, obj);
2952 }
2953
2954 // TODO: generic_ivar
2955
2956 ractor_moved_bang(obj);
2957 return traverse_cont;
2958}
2959
2960static VALUE
2961ractor_move(VALUE obj)
2962{
2963 VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave, true);
2964 if (val != Qundef) {
2965 return val;
2966 }
2967 else {
2968 rb_raise(rb_eRactorError, "can not move the object");
2969 }
2970}
2971
2972static enum obj_traverse_iterator_result
2973copy_enter(VALUE obj, struct obj_traverse_replace_data *data)
2974{
2975 if (rb_ractor_shareable_p(obj)) {
2976 data->replacement = obj;
2977 return traverse_skip;
2978 }
2979 else {
2980 data->replacement = rb_obj_clone(obj);
2981 return traverse_cont;
2982 }
2983}
2984
2985static enum obj_traverse_iterator_result
2986copy_leave(VALUE obj, struct obj_traverse_replace_data *data)
2987{
2988 return traverse_cont;
2989}
2990
2991static VALUE
2992ractor_copy(VALUE obj)
2993{
2994 VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave, false);
2995 if (val != Qundef) {
2996 return val;
2997 }
2998 else {
2999 rb_raise(rb_eRactorError, "can not copy the object");
3000 }
3001}
3002
3003// Ractor local storage
3004
3006 const struct rb_ractor_local_storage_type *type;
3007 void *main_cache;
3008};
3009
3011 int cnt;
3012 int capa;
3014} freed_ractor_local_keys;
3015
3016static int
3017ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
3018{
3020 if (k->type->mark) (*k->type->mark)((void *)val);
3021 return ST_CONTINUE;
3022}
3023
3024static enum rb_id_table_iterator_result
3025idkey_local_storage_mark_i(ID id, VALUE val, void *dmy)
3026{
3027 rb_gc_mark(val);
3028 return ID_TABLE_CONTINUE;
3029}
3030
3031static void
3032ractor_local_storage_mark(rb_ractor_t *r)
3033{
3034 if (r->local_storage) {
3035 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
3036
3037 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
3038 rb_ractor_local_key_t key = freed_ractor_local_keys.keys[i];
3039 st_data_t val;
3040 if (st_delete(r->local_storage, (st_data_t *)&key, &val) &&
3041 key->type->free) {
3042 (*key->type->free)((void *)val);
3043 }
3044 }
3045 }
3046
3047 if (r->idkey_local_storage) {
3048 rb_id_table_foreach(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
3049 }
3050}
3051
3052static int
3053ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
3054{
3056 if (k->type->free) (*k->type->free)((void *)val);
3057 return ST_CONTINUE;
3058}
3059
3060static void
3061ractor_local_storage_free(rb_ractor_t *r)
3062{
3063 if (r->local_storage) {
3064 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
3065 st_free_table(r->local_storage);
3066 }
3067
3068 if (r->idkey_local_storage) {
3069 rb_id_table_free(r->idkey_local_storage);
3070 }
3071}
3072
3073static void
3074rb_ractor_local_storage_value_mark(void *ptr)
3075{
3076 rb_gc_mark((VALUE)ptr);
3077}
3078
3079static const struct rb_ractor_local_storage_type ractor_local_storage_type_null = {
3080 NULL,
3081 NULL,
3082};
3083
3085 NULL,
3086 ruby_xfree,
3087};
3088
3089static const struct rb_ractor_local_storage_type ractor_local_storage_type_value = {
3090 rb_ractor_local_storage_value_mark,
3091 NULL,
3092};
3093
3096{
3098 key->type = type ? type : &ractor_local_storage_type_null;
3099 key->main_cache = (void *)Qundef;
3100 return key;
3101}
3102
3105{
3106 return rb_ractor_local_storage_ptr_newkey(&ractor_local_storage_type_value);
3107}
3108
3109void
3110rb_ractor_local_storage_delkey(rb_ractor_local_key_t key)
3111{
3112 RB_VM_LOCK_ENTER();
3113 {
3114 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
3115 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
3116 REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, freed_ractor_local_keys.capa);
3117 }
3118 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
3119 }
3120 RB_VM_LOCK_LEAVE();
3121}
3122
3123static bool
3124ractor_local_ref(rb_ractor_local_key_t key, void **pret)
3125{
3126 if (rb_ractor_main_p()) {
3127 if ((VALUE)key->main_cache != Qundef) {
3128 *pret = key->main_cache;
3129 return true;
3130 }
3131 else {
3132 return false;
3133 }
3134 }
3135 else {
3136 rb_ractor_t *cr = GET_RACTOR();
3137
3138 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
3139 return true;
3140 }
3141 else {
3142 return false;
3143 }
3144 }
3145}
3146
3147static void
3148ractor_local_set(rb_ractor_local_key_t key, void *ptr)
3149{
3150 rb_ractor_t *cr = GET_RACTOR();
3151
3152 if (cr->local_storage == NULL) {
3153 cr->local_storage = st_init_numtable();
3154 }
3155
3156 st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
3157
3158 if (rb_ractor_main_p()) {
3159 key->main_cache = ptr;
3160 }
3161}
3162
3163VALUE
3165{
3166 VALUE val;
3167 if (ractor_local_ref(key, (void **)&val)) {
3168 return val;
3169 }
3170 else {
3171 return Qnil;
3172 }
3173}
3174
3175bool
3177{
3178 if (ractor_local_ref(key, (void **)val)) {
3179 return true;
3180 }
3181 else {
3182 return false;
3183 }
3184}
3185
3186void
3188{
3189 ractor_local_set(key, (void *)val);
3190}
3191
3192void *
3194{
3195 void *ret;
3196 if (ractor_local_ref(key, &ret)) {
3197 return ret;
3198 }
3199 else {
3200 return NULL;
3201 }
3202}
3203
3204void
3206{
3207 ractor_local_set(key, ptr);
3208}
3209
3210#define DEFAULT_KEYS_CAPA 0x10
3211
3212void
3213rb_ractor_finish_marking(void)
3214{
3215 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
3216 ruby_xfree(freed_ractor_local_keys.keys[i]);
3217 }
3218 freed_ractor_local_keys.cnt = 0;
3219 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
3220 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
3221 REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, DEFAULT_KEYS_CAPA);
3222 }
3223}
3224
3225static VALUE
3226ractor_local_value(rb_execution_context_t *ec, VALUE self, VALUE sym)
3227{
3228 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3229 ID id = rb_check_id(&sym);
3230 struct rb_id_table *tbl = cr->idkey_local_storage;
3231 VALUE val;
3232
3233 if (id && tbl && rb_id_table_lookup(tbl, id, &val)) {
3234 return val;
3235 }
3236 else {
3237 return Qnil;
3238 }
3239}
3240
3241static VALUE
3242ractor_local_value_set(rb_execution_context_t *ec, VALUE self, VALUE sym, VALUE val)
3243{
3244 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3245 ID id = SYM2ID(rb_to_symbol(sym));
3246 struct rb_id_table *tbl = cr->idkey_local_storage;
3247
3248 if (tbl == NULL) {
3249 tbl = cr->idkey_local_storage = rb_id_table_create(2);
3250 }
3251 rb_id_table_insert(tbl, id, val);
3252 return val;
3253}
3254
3255#include "ractor.rbinc"
#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:91
static VALUE RB_OBJ_FROZEN_RAW(VALUE obj)
This is an implenentation detail of RB_OBJ_FROZEN().
Definition: fl_type.h:912
@ RUBY_FL_SHAREABLE
This flag has something to do with Ractor.
Definition: fl_type.h:298
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition: class.c:837
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:869
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a method.
Definition: class.c:1914
#define T_COMPLEX
Old name of RUBY_T_COMPLEX.
Definition: value_type.h:59
#define T_FILE
Old name of RUBY_T_FILE.
Definition: value_type.h:62
#define FL_EXIVAR
Old name of RUBY_FL_EXIVAR.
Definition: fl_type.h:67
#define FL_USER3
Old name of RUBY_FL_USER3.
Definition: fl_type.h:75
#define REALLOC_N
Old name of RB_REALLOC_N.
Definition: memory.h:397
#define ALLOC
Old name of RB_ALLOC.
Definition: memory.h:394
#define T_STRING
Old name of RUBY_T_STRING.
Definition: value_type.h:78
#define FL_USER7
Old name of RUBY_FL_USER7.
Definition: fl_type.h:79
#define FL_USER10
Old name of RUBY_FL_USER10.
Definition: fl_type.h:82
#define Qundef
Old name of RUBY_Qundef.
#define FL_USER6
Old name of RUBY_FL_USER6.
Definition: fl_type.h:78
#define T_FLOAT
Old name of RUBY_T_FLOAT.
Definition: value_type.h:64
#define T_IMEMO
Old name of RUBY_T_IMEMO.
Definition: value_type.h:67
#define FL_USER1
Old name of RUBY_FL_USER1.
Definition: fl_type.h:73
#define ID2SYM
Old name of RB_ID2SYM.
Definition: symbol.h:44
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
Definition: value_type.h:57
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define T_STRUCT
Old name of RUBY_T_STRUCT.
Definition: value_type.h:79
#define FL_USER19
Old name of RUBY_FL_USER19.
Definition: fl_type.h:91
#define SYM2ID
Old name of RB_SYM2ID.
Definition: symbol.h:45
#define FL_USER12
Old name of RUBY_FL_USER12.
Definition: fl_type.h:84
#define T_DATA
Old name of RUBY_T_DATA.
Definition: value_type.h:60
#define FL_USER11
Old name of RUBY_FL_USER11.
Definition: fl_type.h:83
#define FL_USER15
Old name of RUBY_FL_USER15.
Definition: fl_type.h:87
#define T_MODULE
Old name of RUBY_T_MODULE.
Definition: value_type.h:70
#define FL_USER8
Old name of RUBY_FL_USER8.
Definition: fl_type.h:80
#define FL_USER14
Old name of RUBY_FL_USER14.
Definition: fl_type.h:86
#define FL_USER17
Old name of RUBY_FL_USER17.
Definition: fl_type.h:89
#define T_RATIONAL
Old name of RUBY_T_RATIONAL.
Definition: value_type.h:76
#define T_ICLASS
Old name of RUBY_T_ICLASS.
Definition: value_type.h:66
#define T_HASH
Old name of RUBY_T_HASH.
Definition: value_type.h:65
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
Definition: fl_type.h:140
#define FL_USER18
Old name of RUBY_FL_USER18.
Definition: fl_type.h:90
#define FL_USER2
Old name of RUBY_FL_USER2.
Definition: fl_type.h:74
#define FL_USER9
Old name of RUBY_FL_USER9.
Definition: fl_type.h:81
#define Qtrue
Old name of RUBY_Qtrue.
#define FL_USER13
Old name of RUBY_FL_USER13.
Definition: fl_type.h:85
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition: value_type.h:56
#define T_OBJECT
Old name of RUBY_T_OBJECT.
Definition: value_type.h:75
#define NIL_P
Old name of RB_NIL_P.
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
Definition: value_type.h:80
#define T_MATCH
Old name of RUBY_T_MATCH.
Definition: value_type.h:69
#define FL_USER16
Old name of RUBY_FL_USER16.
Definition: fl_type.h:88
#define T_CLASS
Old name of RUBY_T_CLASS.
Definition: value_type.h:58
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition: value_type.h:85
#define FL_USER5
Old name of RUBY_FL_USER5.
Definition: fl_type.h:77
#define FL_USER4
Old name of RUBY_FL_USER4.
Definition: fl_type.h:76
#define FL_SET_RAW
Old name of RB_FL_SET_RAW.
Definition: fl_type.h:138
#define T_REGEXP
Old name of RUBY_T_REGEXP.
Definition: value_type.h:77
void rb_raise(VALUE exc, const char *fmt,...)
Exception entry point.
Definition: error.c:3021
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition: eval.c:671
void rb_bug(const char *fmt,...)
Interpreter panic switch.
Definition: error.c:802
VALUE rb_eStopIteration
StopIteration exception.
Definition: enumerator.c:141
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:979
VALUE rb_cRactor
Ractor class.
Definition: ractor.c:21
VALUE rb_stdin
STDIN constant.
Definition: io.c:198
VALUE rb_stderr
STDERR constant.
Definition: io.c:198
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
Definition: globals.h:172
VALUE rb_stdout
STDOUT constant.
Definition: io.c:198
#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: rgengc.h:232
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition: rgengc.h:220
static const char * rb_enc_name(rb_encoding *enc)
Queries the (canonical) name of the passed encoding.
Definition: encoding.h:433
rb_encoding * rb_enc_get(VALUE obj)
Identical to rb_enc_get_index(), except the return type.
Definition: encoding.c:1072
static bool rb_enc_asciicompat(rb_encoding *enc)
Queries if the passed encoding is in some sense compatible with ASCII.
Definition: encoding.h:782
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition: vm_eval.c:1102
VALUE rb_ary_new(void)
Allocates a new, empty array.
Definition: array.c:750
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
Definition: array.c:1308
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
Definition: array.c:1679
void rb_jump_tag(int state)
This function is to re-throw global escapes.
Definition: eval.c:841
void rb_gc_mark(VALUE obj)
Marks an object.
Definition: gc.c:6774
VALUE rb_gc_disable(void)
Disables GC.
Definition: gc.c:10723
VALUE rb_gc_start(void)
Identical to rb_gc(), except the return value.
Definition: gc.c:10285
VALUE rb_gc_enable(void)
(Re-) enables GC.
Definition: gc.c:10686
void rb_hash_foreach(VALUE hash, int(*func)(VALUE key, VALUE val, VALUE arg), VALUE arg)
Iterates over a hash.
VALUE rb_protect(VALUE(*func)(VALUE args), VALUE args, int *state)
Protects a function call from potential global escapes from the function.
VALUE rb_obj_is_proc(VALUE recv)
Queries if the given object is a proc.
Definition: proc.c:175
#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:1733
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition: string.c:1356
VALUE rb_mutex_new(void)
Creates a mutex.
Definition: thread_sync.c:172
void rb_thread_check_ints(void)
Checks for interrupts.
Definition: thread.c:1573
VALUE rb_mutex_unlock(VALUE mutex)
Releases the mutex.
Definition: thread_sync.c:472
VALUE rb_mutex_lock(VALUE mutex)
Attempts to lock the mutex.
Definition: thread_sync.c:399
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:1575
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition: vm_method.c:1117
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition: symbol.c:1066
ID rb_intern(const char *name)
Finds or creates a symbol of the given name.
Definition: symbol.c:782
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
Definition: string.c:11902
#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
#define RB_NOGVL_INTR_FAIL
Passing this flag to rb_nogvl() prevents it from checking interrupts.
Definition: thread.h:48
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:1666
VALUE rb_sprintf(const char *fmt,...)
Ruby's extended sprintf(3).
Definition: sprintf.c:1201
VALUE rb_yield(VALUE val)
Yields the block.
Definition: vm_eval.c:1357
#define ALLOCA_N(type, n)
Definition: memory.h:286
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition: memory.h:354
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition: memory.h:161
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
int st_foreach(st_table *q, int_type *w, st_data_t e)
Iteration over the given table.
Definition: cxxanyargs.hpp:432
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:3084
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:2506
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:3193
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:3205
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:3095
VALUE rb_ractor_stderr(void)
Queries the standard error of the current Ractor that is calling this function.
Definition: ractor.c:2146
VALUE rb_ractor_stdin(void)
Queries the standard input of the current Ractor that is calling this function.
Definition: ractor.c:2122
static bool rb_ractor_shareable_p(VALUE obj)
Queries if multiple Ractors can share the passed object or not.
Definition: ractor.h:249
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:2182
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:3187
bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
Queries the key.
Definition: ractor.c:3176
VALUE rb_ractor_stdout(void)
Queries the standard output of the current Ractor that is calling this function.
Definition: ractor.c:2134
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
Definition: ractor.h:235
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
Definition: ractor.c:2497
rb_ractor_local_key_t rb_ractor_local_storage_value_newkey(void)
Issues a new key.
Definition: ractor.c:3104
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:2170
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:2158
VALUE rb_ractor_local_storage_value(rb_ractor_local_key_t key)
Queries the key.
Definition: ractor.c:3164
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition: rarray.h:324
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition: rarray.h:571
#define RARRAY_AREF(a, i)
Definition: rarray.h:588
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
Definition: rbasic.h:152
#define DATA_PTR(obj)
Convenient getter macro.
Definition: rdata.h:71
#define RHASH_SET_IFNONE(h, ifnone)
Destructively updates the default value of the hash.
Definition: rhash.h:105
#define RHASH_IFNONE(h)
Definition: rhash.h:72
static uint32_t ROBJECT_NUMIV(VALUE obj)
Queries the number of instance variables.
Definition: robject.h:145
static VALUE * ROBJECT_IVPTR(VALUE obj)
Queries the instance variables.
Definition: robject.h:171
static char * RSTRING_PTR(VALUE str)
Queries the contents pointer of the string.
Definition: rstring.h:497
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition: rstring.h:95
static long RSTRUCT_LEN(VALUE st)
Returns the number of struct members.
Definition: rstruct.h:94
static bool RTYPEDDATA_P(VALUE obj)
Checks whether the passed object is RTypedData or RData.
Definition: rtypeddata.h:540
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition: rtypeddata.h:441
#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:489
static const struct rb_data_type_struct * RTYPEDDATA_TYPE(VALUE obj)
Queries for the type of given object.
Definition: rtypeddata.h:563
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
#define RTEST
This is an old name of RB_TEST.
Definition: gc.c:572
This is the struct that holds necessary info for a struct.
Type that defines a ractor-local storage.
Definition: string.c:7520
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_lock.
void rb_native_cond_initialize(rb_nativethread_cond_t *cond)
Fills the passed condition variable with an initial value.
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_initialize.
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_unlock.
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_destroy.
void rb_native_cond_destroy(rb_nativethread_cond_t *cond)
Destroys the passed condition variable.
void rb_native_cond_signal(rb_nativethread_cond_t *cond)
Signals a condition variable.
void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
Waits for the passed condition variable to be signalled.
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:375
void ruby_xfree(void *ptr)
Deallocates a storage instance.
Definition: gc.c:11772