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