Ruby 3.0.5p211 (2022-11-24 revision ba5cf0f7c52d4d35cc6a173c89eda98ceffa2dcf)
ractor.c
Go to the documentation of this file.
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
21
24static VALUE rb_eRactorError;
25static VALUE rb_eRactorRemoteError;
26static VALUE rb_eRactorMovedError;
27static VALUE rb_eRactorClosedError;
28static VALUE rb_cRactorMovedObject;
29
30static void vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line);
31
32static void
33ASSERT_ractor_unlocking(rb_ractor_t *r)
34{
35#if RACTOR_CHECK_MODE > 0
36 // GET_EC is NULL in an MJIT worker
37 if (rb_current_execution_context(false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) {
38 rb_bug("recursive ractor locking");
39 }
40#endif
41}
42
43static void
44ASSERT_ractor_locking(rb_ractor_t *r)
45{
46#if RACTOR_CHECK_MODE > 0
47 // GET_EC is NULL in an MJIT worker
48 if (rb_current_execution_context(false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) {
49 rp(r->sync.locked_by);
50 rb_bug("ractor lock is not acquired.");
51 }
52#endif
53}
54
55static void
56ractor_lock(rb_ractor_t *r, const char *file, int line)
57{
58 RUBY_DEBUG_LOG2(file, line, "locking r:%u%s", r->pub.id, GET_RACTOR() == r ? " (self)" : "");
59
60 ASSERT_ractor_unlocking(r);
62
63#if RACTOR_CHECK_MODE > 0
64 if (rb_current_execution_context(false) != NULL) { // GET_EC is NULL in an MJIT worker
65 r->sync.locked_by = rb_ractor_self(GET_RACTOR());
66 }
67#endif
68
69 RUBY_DEBUG_LOG2(file, line, "locked r:%u%s", r->pub.id, GET_RACTOR() == r ? " (self)" : "");
70}
71
72static void
73ractor_lock_self(rb_ractor_t *cr, const char *file, int line)
74{
75 VM_ASSERT(cr == GET_RACTOR());
76 VM_ASSERT(cr->sync.locked_by != cr->pub.self);
77 ractor_lock(cr, file, line);
78}
79
80static void
81ractor_unlock(rb_ractor_t *r, const char *file, int line)
82{
83 ASSERT_ractor_locking(r);
84#if RACTOR_CHECK_MODE > 0
85 r->sync.locked_by = Qnil;
86#endif
88
89 RUBY_DEBUG_LOG2(file, line, "r:%u%s", r->pub.id, GET_RACTOR() == r ? " (self)" : "");
90}
91
92static void
93ractor_unlock_self(rb_ractor_t *cr, const char *file, int line)
94{
95 VM_ASSERT(cr == GET_RACTOR());
96 VM_ASSERT(cr->sync.locked_by == cr->pub.self);
97 ractor_unlock(cr, file, line);
98}
99
100#define RACTOR_LOCK(r) ractor_lock(r, __FILE__, __LINE__)
101#define RACTOR_UNLOCK(r) ractor_unlock(r, __FILE__, __LINE__)
102#define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
103#define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
104
105static void
106ractor_cond_wait(rb_ractor_t *r)
107{
108#if RACTOR_CHECK_MODE > 0
109 VALUE locked_by = r->sync.locked_by;
110 r->sync.locked_by = Qnil;
111#endif
113
114#if RACTOR_CHECK_MODE > 0
115 r->sync.locked_by = locked_by;
116#endif
117}
118
119static const char *
120ractor_status_str(enum ractor_status status)
121{
122 switch (status) {
123 case ractor_created: return "created";
124 case ractor_running: return "running";
125 case ractor_blocking: return "blocking";
126 case ractor_terminated: return "terminated";
127 }
128 rb_bug("unreachable");
129}
130
131static void
132ractor_status_set(rb_ractor_t *r, enum ractor_status status)
133{
134 RUBY_DEBUG_LOG("r:%u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
135
136 // check 1
137 if (r->status_ != ractor_created) {
138 VM_ASSERT(r == GET_RACTOR()); // only self-modification is allowed.
140 }
141
142 // check2: transition check. assume it will be vanished on non-debug build.
143 switch (r->status_) {
144 case ractor_created:
145 VM_ASSERT(status == ractor_blocking);
146 break;
147 case ractor_running:
148 VM_ASSERT(status == ractor_blocking||
149 status == ractor_terminated);
150 break;
151 case ractor_blocking:
152 VM_ASSERT(status == ractor_running);
153 break;
154 case ractor_terminated:
155 VM_ASSERT(0); // unreachable
156 break;
157 }
158
159 r->status_ = status;
160}
161
162static bool
163ractor_status_p(rb_ractor_t *r, enum ractor_status status)
164{
165 return rb_ractor_status_p(r, status);
166}
167
168static struct rb_ractor_basket *ractor_queue_at(struct rb_ractor_queue *rq, int i);
169
170static void
171ractor_queue_mark(struct rb_ractor_queue *rq)
172{
173 for (int i=0; i<rq->cnt; i++) {
174 struct rb_ractor_basket *b = ractor_queue_at(rq, i);
175 rb_gc_mark(b->v);
176 rb_gc_mark(b->sender);
177 }
178}
179
180static void ractor_local_storage_mark(rb_ractor_t *r);
181static void ractor_local_storage_free(rb_ractor_t *r);
182
183static void
184ractor_mark(void *ptr)
185{
187
188 ractor_queue_mark(&r->sync.incoming_queue);
194
195 rb_gc_mark(r->loc);
196 rb_gc_mark(r->name);
201
202 if (r->threads.cnt > 0) {
203 rb_thread_t *th = 0;
204 list_for_each(&r->threads.set, th, lt_node) {
205 VM_ASSERT(th != NULL);
206 rb_gc_mark(th->self);
207 }
208 }
209
210 ractor_local_storage_mark(r);
211}
212
213static void
214ractor_queue_free(struct rb_ractor_queue *rq)
215{
216 free(rq->baskets);
217}
218
219static void
220ractor_waiting_list_free(struct rb_ractor_waiting_list *wl)
221{
222 free(wl->ractors);
223}
224
225static void
226ractor_free(void *ptr)
227{
231 ractor_queue_free(&r->sync.incoming_queue);
232 ractor_waiting_list_free(&r->sync.taking_ractors);
233 ractor_local_storage_free(r);
235 ruby_xfree(r);
236}
237
238static size_t
239ractor_queue_memsize(const struct rb_ractor_queue *rq)
240{
241 return sizeof(struct rb_ractor_basket) * rq->size;
242}
243
244static size_t
245ractor_waiting_list_memsize(const struct rb_ractor_waiting_list *wl)
246{
247 return sizeof(rb_ractor_t *) * wl->size;
248}
249
250static size_t
251ractor_memsize(const void *ptr)
252{
254
255 // TODO
256 return sizeof(rb_ractor_t) +
257 ractor_queue_memsize(&r->sync.incoming_queue) +
258 ractor_waiting_list_memsize(&r->sync.taking_ractors);
259}
260
261static const rb_data_type_t ractor_data_type = {
262 "ractor",
263 {
264 ractor_mark,
265 ractor_free,
266 ractor_memsize,
267 NULL, // update
268 },
269 0, 0, RUBY_TYPED_FREE_IMMEDIATELY /* | RUBY_TYPED_WB_PROTECTED */
270};
271
272bool
274{
275 if (rb_typeddata_is_kind_of(gv, &ractor_data_type)) {
276 return true;
277 }
278 else {
279 return false;
280 }
281}
282
283static inline rb_ractor_t *
284RACTOR_PTR(VALUE self)
285{
286 VM_ASSERT(rb_ractor_p(self));
287
288 rb_ractor_t *r = DATA_PTR(self);
289 // TODO: check
290 return r;
291}
292
293static uint32_t ractor_last_id;
294
295#if RACTOR_CHECK_MODE > 0
297rb_ractor_current_id(void)
298{
299 if (GET_THREAD()->ractor == NULL) {
300 return 1; // main ractor
301 }
302 else {
303 return rb_ractor_id(GET_RACTOR());
304 }
305}
306#endif
307
308static void
309ractor_queue_setup(struct rb_ractor_queue *rq)
310{
311 rq->size = 2;
312 rq->cnt = 0;
313 rq->start = 0;
314 rq->baskets = malloc(sizeof(struct rb_ractor_basket) * rq->size);
315}
316
317static struct rb_ractor_basket *
318ractor_queue_at(struct rb_ractor_queue *rq, int i)
319{
320 return &rq->baskets[(rq->start + i) % rq->size];
321}
322
323static void
324ractor_queue_advance(struct rb_ractor_queue *rq)
325{
326 ASSERT_ractor_locking(GET_RACTOR());
327
328 if (rq->reserved_cnt == 0) {
329 rq->cnt--;
330 rq->start = (rq->start + 1) % rq->size;
331 rq->serial++;
332 }
333 else {
334 ractor_queue_at(rq, 0)->type = basket_type_deleted;
335 }
336}
337
338static bool
339ractor_queue_skip_p(struct rb_ractor_queue *rq, int i)
340{
341 struct rb_ractor_basket *b = ractor_queue_at(rq, i);
342 return b->type == basket_type_deleted ||
344}
345
346static void
347ractor_queue_compact(rb_ractor_t *r, struct rb_ractor_queue *rq)
348{
349 ASSERT_ractor_locking(r);
350
351 while (rq->cnt > 0 && ractor_queue_at(rq, 0)->type == basket_type_deleted) {
352 ractor_queue_advance(rq);
353 }
354}
355
356static bool
357ractor_queue_empty_p(rb_ractor_t *r, struct rb_ractor_queue *rq)
358{
359 ASSERT_ractor_locking(r);
360
361 if (rq->cnt == 0) {
362 return true;
363 }
364
365 ractor_queue_compact(r, rq);
366
367 for (int i=0; i<rq->cnt; i++) {
368 if (!ractor_queue_skip_p(rq, i)) {
369 return false;
370 }
371 }
372
373 return true;
374}
375
376static bool
377ractor_queue_deq(rb_ractor_t *r, struct rb_ractor_queue *rq, struct rb_ractor_basket *basket)
378{
379 bool found = false;
380
381 RACTOR_LOCK(r);
382 {
383 if (!ractor_queue_empty_p(r, rq)) {
384 for (int i=0; i<rq->cnt; i++) {
385 if (!ractor_queue_skip_p(rq, i)) {
386 struct rb_ractor_basket *b = ractor_queue_at(rq, i);
387 *basket = *b;
388
389 // remove from queue
391 ractor_queue_compact(r, rq);
392 found = true;
393 break;
394 }
395 }
396 }
397 }
398 RACTOR_UNLOCK(r);
399
400 return found;
401}
402
403static void
404ractor_queue_enq(rb_ractor_t *r, struct rb_ractor_queue *rq, struct rb_ractor_basket *basket)
405{
406 ASSERT_ractor_locking(r);
407
408 if (rq->size <= rq->cnt) {
409 rq->baskets = realloc(rq->baskets, sizeof(struct rb_ractor_basket) * rq->size * 2);
410 for (int i=rq->size - rq->start; i<rq->cnt; i++) {
411 rq->baskets[i + rq->start] = rq->baskets[i + rq->start - rq->size];
412 }
413 rq->size *= 2;
414 }
415 rq->baskets[(rq->start + rq->cnt++) % rq->size] = *basket;
416 // fprintf(stderr, "%s %p->cnt:%d\n", __func__, rq, rq->cnt);
417}
418
419static void
420ractor_basket_clear(struct rb_ractor_basket *b)
421{
423 b->v = Qfalse;
424 b->sender = Qfalse;
425}
426
427static VALUE ractor_reset_belonging(VALUE obj); // in this file
428
429static VALUE
430ractor_basket_value(struct rb_ractor_basket *b)
431{
432 switch (b->type) {
433 case basket_type_ref:
434 break;
435 case basket_type_copy:
436 case basket_type_move:
437 case basket_type_will:
439 b->v = ractor_reset_belonging(b->v);
440 break;
441 default:
442 rb_bug("unreachable");
443 }
444
445 return b->v;
446}
447
448static VALUE
449ractor_basket_accept(struct rb_ractor_basket *b)
450{
451 VALUE v = ractor_basket_value(b);
452
453 if (b->exception) {
454 VALUE cause = v;
455 VALUE err = rb_exc_new_cstr(rb_eRactorRemoteError, "thrown by remote Ractor.");
456 rb_ivar_set(err, rb_intern("@ractor"), b->sender);
457 ractor_basket_clear(b);
458 rb_ec_setup_exception(NULL, err, cause);
460 }
461
462 ractor_basket_clear(b);
463 return v;
464}
465
466static void
467ractor_recursive_receive_if(rb_ractor_t *r)
468{
470 rb_raise(rb_eRactorError, "can not call receive/receive_if recursively");
471 }
472}
473
474static VALUE
475ractor_try_receive(rb_execution_context_t *ec, rb_ractor_t *r)
476{
477 struct rb_ractor_queue *rq = &r->sync.incoming_queue;
478 struct rb_ractor_basket basket;
479
480 ractor_recursive_receive_if(r);
481
482 if (ractor_queue_deq(r, rq, &basket) == false) {
483 if (r->sync.incoming_port_closed) {
484 rb_raise(rb_eRactorClosedError, "The incoming port is already closed");
485 }
486 else {
487 return Qundef;
488 }
489 }
490
491 return ractor_basket_accept(&basket);
492}
493
494static void *
495ractor_sleep_wo_gvl(void *ptr)
496{
497 rb_ractor_t *cr = ptr;
499 {
500 VM_ASSERT(cr->sync.wait.status != wait_none);
501 if (cr->sync.wait.wakeup_status == wakeup_none) {
502 ractor_cond_wait(cr);
503 }
504 cr->sync.wait.status = wait_none;
505 }
507 return NULL;
508}
509
510static void
511ractor_sleep_interrupt(void *ptr)
512{
513 rb_ractor_t *r = ptr;
514
515 RACTOR_LOCK(r);
516 if (r->sync.wait.wakeup_status == wakeup_none) {
517 r->sync.wait.wakeup_status = wakeup_by_interrupt;
519 }
520 RACTOR_UNLOCK(r);
521}
522
523#if USE_RUBY_DEBUG_LOG
524static const char *
525wait_status_str(enum ractor_wait_status wait_status)
526{
527 switch ((int)wait_status) {
528 case wait_none: return "none";
529 case wait_receiving: return "receiving";
530 case wait_taking: return "taking";
531 case wait_yielding: return "yielding";
532 case wait_receiving|wait_taking: return "receiving|taking";
533 case wait_receiving|wait_yielding: return "receiving|yielding";
534 case wait_taking|wait_yielding: return "taking|yielding";
535 case wait_receiving|wait_taking|wait_yielding: return "receiving|taking|yielding";
536 }
537 rb_bug("unrechable");
538}
539
540static const char *
541wakeup_status_str(enum ractor_wakeup_status wakeup_status)
542{
543 switch (wakeup_status) {
544 case wakeup_none: return "none";
545 case wakeup_by_send: return "by_send";
546 case wakeup_by_yield: return "by_yield";
547 case wakeup_by_take: return "by_take";
548 case wakeup_by_close: return "by_close";
549 case wakeup_by_interrupt: return "by_interrupt";
550 case wakeup_by_retry: return "by_retry";
551 }
552 rb_bug("unrechable");
553}
554#endif // USE_RUBY_DEBUG_LOG
555
556static void
557ractor_sleep(rb_execution_context_t *ec, rb_ractor_t *cr)
558{
559 VM_ASSERT(GET_RACTOR() == cr);
560 VM_ASSERT(cr->sync.wait.status != wait_none);
561 // fprintf(stderr, "%s r:%p status:%s, wakeup_status:%s\n", __func__, cr,
562 // wait_status_str(cr->sync.wait.status), wakeup_status_str(cr->sync.wait.wakeup_status));
563
564 RACTOR_UNLOCK(cr);
565 {
566 rb_nogvl(ractor_sleep_wo_gvl, cr,
567 ractor_sleep_interrupt, cr,
569 }
570 RACTOR_LOCK(cr);
571
572 // rb_nogvl() can be canceled by interrupts
573 if (cr->sync.wait.status != wait_none) {
574 cr->sync.wait.status = wait_none;
575 cr->sync.wait.wakeup_status = wakeup_by_interrupt;
576
577 RACTOR_UNLOCK(cr);
579 RACTOR_LOCK(cr); // reachable?
580 }
581}
582
583static bool
584ractor_sleeping_by(const rb_ractor_t *r, enum ractor_wait_status wait_status)
585{
586 return (r->sync.wait.status & wait_status) && r->sync.wait.wakeup_status == wakeup_none;
587}
588
589static bool
590ractor_wakeup(rb_ractor_t *r, enum ractor_wait_status wait_status, enum ractor_wakeup_status wakeup_status)
591{
592 ASSERT_ractor_locking(r);
593
594 // fprintf(stderr, "%s r:%p status:%s/%s wakeup_status:%s/%s\n", __func__, r,
595 // wait_status_str(r->sync.wait.status), wait_status_str(wait_status),
596 // wakeup_status_str(r->sync.wait.wakeup_status), wakeup_status_str(wakeup_status));
597
598 if (ractor_sleeping_by(r, wait_status)) {
599 r->sync.wait.wakeup_status = wakeup_status;
601 return true;
602 }
603 else {
604 return false;
605 }
606}
607
608static void
609ractor_register_taking(rb_ractor_t *r, rb_ractor_t *cr)
610{
611 VM_ASSERT(cr == GET_RACTOR());
612 bool retry_try = false;
613
614 RACTOR_LOCK(r);
615 {
616 if (ractor_sleeping_by(r, wait_yielding)) {
617 // already waiting for yielding. retry try_take.
618 retry_try = true;
619 }
620 else {
621 // insert cr into taking list
623
624 for (int i=0; i<wl->cnt; i++) {
625 if (wl->ractors[i] == cr) {
626 // TODO: make it clean code.
628 rb_raise(rb_eRuntimeError, "Already another thread of same ractor is waiting.");
629 }
630 }
631
632 if (wl->size == 0) {
633 wl->size = 1;
634 wl->ractors = malloc(sizeof(rb_ractor_t *) * wl->size);
635 if (wl->ractors == NULL) rb_bug("can't allocate buffer");
636 }
637 else if (wl->size <= wl->cnt + 1) {
638 wl->size *= 2;
639 wl->ractors = realloc(wl->ractors, sizeof(rb_ractor_t *) * wl->size);
640 if (wl->ractors == NULL) rb_bug("can't re-allocate buffer");
641 }
642 wl->ractors[wl->cnt++] = cr;
643 }
644 }
645 RACTOR_UNLOCK(r);
646
647 if (retry_try) {
648 RACTOR_LOCK(cr);
649 {
650 if (cr->sync.wait.wakeup_status == wakeup_none) {
651 VM_ASSERT(cr->sync.wait.status != wait_none);
652
653 cr->sync.wait.wakeup_status = wakeup_by_retry;
654 cr->sync.wait.status = wait_none;
655 }
656 }
657 RACTOR_UNLOCK(cr);
658 }
659}
660
661static void
662ractor_waiting_list_del(rb_ractor_t *r, struct rb_ractor_waiting_list *wl, rb_ractor_t *wr)
663{
664 RACTOR_LOCK(r);
665 {
666 int pos = -1;
667 for (int i=0; i<wl->cnt; i++) {
668 if (wl->ractors[i] == wr) {
669 pos = i;
670 break;
671 }
672 }
673 if (pos >= 0) { // found
674 wl->cnt--;
675 for (int i=pos; i<wl->cnt; i++) {
676 wl->ractors[i] = wl->ractors[i+1];
677 }
678 }
679 }
680 RACTOR_UNLOCK(r);
681}
682
683static rb_ractor_t *
684ractor_waiting_list_shift(rb_ractor_t *r, struct rb_ractor_waiting_list *wl)
685{
686 ASSERT_ractor_locking(r);
687 VM_ASSERT(&r->sync.taking_ractors == wl);
688
689 if (wl->cnt > 0) {
690 rb_ractor_t *tr = wl->ractors[0];
691 for (int i=1; i<wl->cnt; i++) {
692 wl->ractors[i-1] = wl->ractors[i];
693 }
694 wl->cnt--;
695 return tr;
696 }
697 else {
698 return NULL;
699 }
700}
701
702static void
703ractor_receive_wait(rb_execution_context_t *ec, rb_ractor_t *cr)
704{
705 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
706 ractor_recursive_receive_if(cr);
707
708 RACTOR_LOCK(cr);
709 {
710 if (ractor_queue_empty_p(cr, &cr->sync.incoming_queue)) {
711 VM_ASSERT(cr->sync.wait.status == wait_none);
712 cr->sync.wait.status = wait_receiving;
713 cr->sync.wait.wakeup_status = wakeup_none;
714 ractor_sleep(ec, cr);
715 cr->sync.wait.wakeup_status = wakeup_none;
716 }
717 }
718 RACTOR_UNLOCK(cr);
719}
720
721static VALUE
722ractor_receive(rb_execution_context_t *ec, rb_ractor_t *cr)
723{
724 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
725 VALUE v;
726
727 while ((v = ractor_try_receive(ec, cr)) == Qundef) {
728 ractor_receive_wait(ec, cr);
729 }
730
731 return v;
732}
733
734#if 0
735// for debug
736static const char *
737basket_type_name(enum rb_ractor_basket_type type)
738{
739 switch (type) {
740#define T(t) case basket_type_##t: return #t
741 T(none);
742 T(ref);
743 T(copy);
744 T(move);
745 T(will);
746 T(deleted);
747 T(reserved);
748 default: rb_bug("unreachable");
749 }
750}
751
752static void
753rq_dump(struct rb_ractor_queue *rq)
754{
755 bool bug = false;
756 for (int i=0; i<rq->cnt; i++) {
757 struct rb_ractor_basket *b = ractor_queue_at(rq, i);
758 fprintf(stderr, "%d (start:%d) type:%s %p %s\n", i, rq->start, basket_type_name(b->type), b, RSTRING_PTR(RARRAY_AREF(b->v, 1)));
759 if (b->type == basket_type_reserved) bug = true;
760 }
761 if (bug) rb_bug("!!");
762}
763#endif
764
769 int index;
771};
772
773static void
774ractor_receive_if_lock(rb_ractor_t *cr)
775{
776 VALUE m = cr->receiving_mutex;
777 if (m == Qfalse) {
778 m = cr->receiving_mutex = rb_mutex_new();
779 }
780 rb_mutex_lock(m);
781}
782
783static VALUE
784receive_if_body(VALUE ptr)
785{
786 struct receive_block_data *data = (struct receive_block_data *)ptr;
787
788 ractor_receive_if_lock(data->cr);
789 VALUE block_result = rb_yield(data->v);
790
791 RACTOR_LOCK_SELF(data->cr);
792 {
793 struct rb_ractor_basket *b = ractor_queue_at(data->rq, data->index);
795 data->rq->reserved_cnt--;
796
797 if (RTEST(block_result)) {
799 ractor_queue_compact(data->cr, data->rq);
800 }
801 else {
803 }
804 }
805 RACTOR_UNLOCK_SELF(data->cr);
806
807 data->success = true;
808
809 if (RTEST(block_result)) {
810 return data->v;
811 }
812 else {
813 return Qundef;
814 }
815}
816
817static VALUE
818receive_if_ensure(VALUE v)
819{
820 struct receive_block_data *data = (struct receive_block_data *)v;
821
822 if (!data->success) {
823 RACTOR_LOCK_SELF(data->cr);
824 {
825 struct rb_ractor_basket *b = ractor_queue_at(data->rq, data->index);
828 data->rq->reserved_cnt--;
829 }
830 RACTOR_UNLOCK_SELF(data->cr);
831 }
832
834 return Qnil;
835}
836
837static VALUE
838ractor_receive_if(rb_execution_context_t *ec, VALUE crv, VALUE b)
839{
840 if (!RTEST(b)) rb_raise(rb_eArgError, "no block given");
841
842 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
843 unsigned int serial = (unsigned int)-1;
844 int index = 0;
845 struct rb_ractor_queue *rq = &cr->sync.incoming_queue;
846
847 while (1) {
848 VALUE v = Qundef;
849
850 ractor_receive_wait(ec, cr);
851
853 {
854 if (serial != rq->serial) {
855 serial = rq->serial;
856 index = 0;
857 }
858
859 // check newer version
860 for (int i=index; i<rq->cnt; i++) {
861 if (!ractor_queue_skip_p(rq, i)) {
862 struct rb_ractor_basket *b = ractor_queue_at(rq, i);
863 v = ractor_basket_value(b);
865 rq->reserved_cnt++;
866 index = i;
867 break;
868 }
869 }
870 }
872
873 if (v != Qundef) {
874 struct receive_block_data data = {
875 .cr = cr,
876 .rq = rq,
877 .v = v,
878 .index = index,
879 .success = false,
880 };
881
882 VALUE result = rb_ensure(receive_if_body, (VALUE)&data,
883 receive_if_ensure, (VALUE)&data);
884
885 if (result != Qundef) return result;
886 index++;
887 }
888 }
889}
890
891static void
892ractor_send_basket(rb_execution_context_t *ec, rb_ractor_t *r, struct rb_ractor_basket *b)
893{
894 bool closed = false;
895 struct rb_ractor_queue *rq = &r->sync.incoming_queue;
896
897 RACTOR_LOCK(r);
898 {
899 if (r->sync.incoming_port_closed) {
900 closed = true;
901 }
902 else {
903 ractor_queue_enq(r, rq, b);
904 if (ractor_wakeup(r, wait_receiving, wakeup_by_send)) {
905 RUBY_DEBUG_LOG("wakeup", 0);
906 }
907 }
908 }
909 RACTOR_UNLOCK(r);
910
911 if (closed) {
912 rb_raise(rb_eRactorClosedError, "The incoming-port is already closed");
913 }
914}
915
916static VALUE ractor_move(VALUE obj); // in this file
917static VALUE ractor_copy(VALUE obj); // in this file
918
919static void
920ractor_basket_setup(rb_execution_context_t *ec, struct rb_ractor_basket *basket, VALUE obj, VALUE move, bool exc, bool is_will, bool is_yield)
921{
922 basket->sender = rb_ec_ractor_ptr(ec)->pub.self;
923 basket->exception = exc;
924
925 if (is_will) {
926 basket->type = basket_type_will;
927 basket->v = obj;
928 }
929 else if (rb_ractor_shareable_p(obj)) {
930 basket->type = basket_type_ref;
931 basket->v = obj;
932 }
933 else if (!RTEST(move)) {
934 basket->v = ractor_copy(obj);
935 basket->type = basket_type_copy;
936 }
937 else {
938 basket->type = basket_type_move;
939
940 if (is_yield) {
941 basket->v = obj; // call ractor_move() when yielding timing.
942 }
943 else {
944 basket->v = ractor_move(obj);
945 }
946 }
947}
948
949static VALUE
950ractor_send(rb_execution_context_t *ec, rb_ractor_t *r, VALUE obj, VALUE move)
951{
952 struct rb_ractor_basket basket;
953 ractor_basket_setup(ec, &basket, obj, move, false, false, false);
954 ractor_send_basket(ec, r, &basket);
955 return r->pub.self;
956}
957
958static VALUE
959ractor_try_take(rb_execution_context_t *ec, rb_ractor_t *r)
960{
961 struct rb_ractor_basket basket = {
963 };
964 bool closed = false;
965
966 RACTOR_LOCK(r);
967 {
968 if (ractor_sleeping_by(r, wait_yielding)) {
969 MAYBE_UNUSED(bool) wakeup_result;
971
973 wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_retry);
974 }
975 else {
976 wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_take);
977 basket = r->sync.wait.yielded_basket;
978 ractor_basket_clear(&r->sync.wait.yielded_basket);
979 }
980 VM_ASSERT(wakeup_result);
981 }
982 else if (r->sync.outgoing_port_closed) {
983 closed = true;
984 }
985 }
986 RACTOR_UNLOCK(r);
987
988 if (basket.type == basket_type_none) {
989 if (closed) {
990 rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
991 }
992 else {
993 return Qundef;
994 }
995 }
996 else {
997 return ractor_basket_accept(&basket);
998 }
999}
1000
1001static VALUE
1002ractor_yield_move_body(VALUE v)
1003{
1004 return ractor_move(v);
1005}
1006
1007static bool
1008ractor_try_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_basket *basket)
1009{
1010 ASSERT_ractor_unlocking(cr);
1011 VM_ASSERT(basket->type != basket_type_none);
1012
1013 if (cr->sync.outgoing_port_closed) {
1014 rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
1015 }
1016
1017 rb_ractor_t *r;
1018
1019 retry_shift:
1020 RACTOR_LOCK(cr);
1021 {
1022 r = ractor_waiting_list_shift(cr, &cr->sync.taking_ractors);
1023 }
1024 RACTOR_UNLOCK(cr);
1025
1026 if (r) {
1027 bool retry_shift = false;
1028
1029 RACTOR_LOCK(r);
1030 {
1031 if (ractor_sleeping_by(r, wait_taking)) {
1033
1034 if (basket->type == basket_type_move) {
1035 enum ractor_wait_status prev_wait_status = r->sync.wait.status;
1036 r->sync.wait.status = wait_moving;
1037
1038 RACTOR_UNLOCK(r);
1039 {
1040 int state;
1041 VALUE moved_value = rb_protect(ractor_yield_move_body, basket->v, &state);
1042 if (state) {
1043 r->sync.wait.status = prev_wait_status;
1045 }
1046 else {
1047 basket->v = moved_value;
1048 }
1049 }
1050 RACTOR_LOCK(r);
1051
1052 if (!ractor_wakeup(r, wait_moving, wakeup_by_yield)) {
1053 // terminating?
1054 }
1055 }
1056 else {
1057 ractor_wakeup(r, wait_taking, wakeup_by_yield);
1058 }
1059 r->sync.wait.taken_basket = *basket;
1060 }
1061 else {
1062 retry_shift = true;
1063 }
1064 }
1065 RACTOR_UNLOCK(r);
1066
1067 if (retry_shift) {
1068 // get candidate take-waiting ractor, but already woke up by another reason.
1069 // retry to check another ractor.
1070 goto retry_shift;
1071 }
1072 else {
1073 return true;
1074 }
1075 }
1076 else {
1077 return false;
1078 }
1079}
1080
1081// select(r1, r2, r3, receive: true, yield: obj)
1082static VALUE
1083ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VALUE yielded_value, bool move, VALUE *ret_r)
1084{
1085 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1086 VALUE crv = cr->pub.self;
1087 VALUE ret = Qundef;
1088 int i;
1089 bool interrupted = false;
1090 enum ractor_wait_status wait_status = 0;
1091 bool yield_p = (yielded_value != Qundef) ? true : false;
1092 const int alen = rs_len + (yield_p ? 1 : 0);
1093
1094 struct ractor_select_action {
1095 enum ractor_select_action_type {
1096 ractor_select_action_take,
1097 ractor_select_action_receive,
1098 ractor_select_action_yield,
1099 } type;
1100 VALUE v;
1101 } *actions = ALLOCA_N(struct ractor_select_action, alen);
1102
1103 VM_ASSERT(cr->sync.wait.status == wait_none);
1104 VM_ASSERT(cr->sync.wait.wakeup_status == wakeup_none);
1107
1108 // setup actions
1109 for (i=0; i<rs_len; i++) {
1110 VALUE v = rs[i];
1111
1112 if (v == crv) {
1113 actions[i].type = ractor_select_action_receive;
1114 actions[i].v = Qnil;
1115 wait_status |= wait_receiving;
1116 }
1117 else if (rb_ractor_p(v)) {
1118 actions[i].type = ractor_select_action_take;
1119 actions[i].v = v;
1120 wait_status |= wait_taking;
1121 }
1122 else {
1123 rb_raise(rb_eArgError, "should be a ractor object, but %"PRIsVALUE, v);
1124 }
1125 }
1126 rs = NULL;
1127
1128 restart:
1129
1130 if (yield_p) {
1131 actions[rs_len].type = ractor_select_action_yield;
1132 actions[rs_len].v = Qundef;
1133 wait_status |= wait_yielding;
1134 ractor_basket_setup(ec, &cr->sync.wait.yielded_basket, yielded_value, move, false, false, true);
1135 }
1136
1137 // TODO: shuffle actions
1138
1139 while (1) {
1140 RUBY_DEBUG_LOG("try actions (%s)", wait_status_str(wait_status));
1141
1142 for (i=0; i<alen; i++) {
1143 VALUE v, rv;
1144 switch (actions[i].type) {
1145 case ractor_select_action_take:
1146 rv = actions[i].v;
1147 v = ractor_try_take(ec, RACTOR_PTR(rv));
1148 if (v != Qundef) {
1149 *ret_r = rv;
1150 ret = v;
1151 goto cleanup;
1152 }
1153 break;
1154 case ractor_select_action_receive:
1155 v = ractor_try_receive(ec, cr);
1156 if (v != Qundef) {
1157 *ret_r = ID2SYM(rb_intern("receive"));
1158 ret = v;
1159 goto cleanup;
1160 }
1161 break;
1162 case ractor_select_action_yield:
1163 {
1164 if (ractor_try_yield(ec, cr, &cr->sync.wait.yielded_basket)) {
1165 *ret_r = ID2SYM(rb_intern("yield"));
1166 ret = Qnil;
1167 goto cleanup;
1168 }
1169 }
1170 break;
1171 }
1172 }
1173
1174 RUBY_DEBUG_LOG("wait actions (%s)", wait_status_str(wait_status));
1175
1176 RACTOR_LOCK(cr);
1177 {
1178 VM_ASSERT(cr->sync.wait.status == wait_none);
1179 cr->sync.wait.status = wait_status;
1180 cr->sync.wait.wakeup_status = wakeup_none;
1181 }
1182 RACTOR_UNLOCK(cr);
1183
1184 // prepare waiting
1185 for (i=0; i<alen; i++) {
1186 rb_ractor_t *r;
1187 switch (actions[i].type) {
1188 case ractor_select_action_take:
1189 r = RACTOR_PTR(actions[i].v);
1190 ractor_register_taking(r, cr);
1191 break;
1192 case ractor_select_action_yield:
1193 case ractor_select_action_receive:
1194 break;
1195 }
1196 }
1197
1198 // wait
1199 RACTOR_LOCK(cr);
1200 {
1201 if (cr->sync.wait.wakeup_status == wakeup_none) {
1202 for (i=0; i<alen; i++) {
1203 rb_ractor_t *r;
1204
1205 switch (actions[i].type) {
1206 case ractor_select_action_take:
1207 r = RACTOR_PTR(actions[i].v);
1208 if (ractor_sleeping_by(r, wait_yielding)) {
1209 RUBY_DEBUG_LOG("wakeup_none, but r:%u is waiting for yielding", r->pub.id);
1210 cr->sync.wait.wakeup_status = wakeup_by_retry;
1211 goto skip_sleep;
1212 }
1213 break;
1214 case ractor_select_action_receive:
1215 if (cr->sync.incoming_queue.cnt > 0) {
1216 RUBY_DEBUG_LOG("wakeup_none, but incoming_queue has %u messages", cr->sync.incoming_queue.cnt);
1217 cr->sync.wait.wakeup_status = wakeup_by_retry;
1218 goto skip_sleep;
1219 }
1220 break;
1221 case ractor_select_action_yield:
1222 if (cr->sync.taking_ractors.cnt > 0) {
1223 RUBY_DEBUG_LOG("wakeup_none, but %u taking_ractors are waiting", cr->sync.taking_ractors.cnt);
1224 cr->sync.wait.wakeup_status = wakeup_by_retry;
1225 goto skip_sleep;
1226 }
1227 else if (cr->sync.outgoing_port_closed) {
1228 cr->sync.wait.wakeup_status = wakeup_by_close;
1229 goto skip_sleep;
1230 }
1231 break;
1232 }
1233 }
1234
1235 RUBY_DEBUG_LOG("sleep %s", wait_status_str(cr->sync.wait.status));
1236 ractor_sleep(ec, cr);
1237 RUBY_DEBUG_LOG("awaken %s", wakeup_status_str(cr->sync.wait.wakeup_status));
1238 }
1239 else {
1240 skip_sleep:
1241 RUBY_DEBUG_LOG("no need to sleep %s->%s",
1242 wait_status_str(cr->sync.wait.status),
1243 wakeup_status_str(cr->sync.wait.wakeup_status));
1244 cr->sync.wait.status = wait_none;
1245 }
1246 }
1247 RACTOR_UNLOCK(cr);
1248
1249 // cleanup waiting
1250 for (i=0; i<alen; i++) {
1251 rb_ractor_t *r;
1252 switch (actions[i].type) {
1253 case ractor_select_action_take:
1254 r = RACTOR_PTR(actions[i].v);
1255 ractor_waiting_list_del(r, &r->sync.taking_ractors, cr);
1256 break;
1257 case ractor_select_action_receive:
1258 case ractor_select_action_yield:
1259 break;
1260 }
1261 }
1262
1263 // check results
1264 enum ractor_wakeup_status wakeup_status = cr->sync.wait.wakeup_status;
1265 cr->sync.wait.wakeup_status = wakeup_none;
1266
1267 switch (wakeup_status) {
1268 case wakeup_none:
1269 // OK. something happens.
1270 // retry loop.
1271 break;
1272 case wakeup_by_retry:
1273 // Retry request.
1274 break;
1275 case wakeup_by_send:
1276 // OK.
1277 // retry loop and try_receive will succss.
1278 break;
1279 case wakeup_by_yield:
1280 // take was succeeded!
1281 // cr.wait.taken_basket contains passed block
1283 *ret_r = cr->sync.wait.taken_basket.sender;
1284 VM_ASSERT(rb_ractor_p(*ret_r));
1285 ret = ractor_basket_accept(&cr->sync.wait.taken_basket);
1286 goto cleanup;
1287 case wakeup_by_take:
1288 *ret_r = ID2SYM(rb_intern("yield"));
1289 ret = Qnil;
1290 goto cleanup;
1291 case wakeup_by_close:
1292 // OK.
1293 // retry loop and will get CloseError.
1294 break;
1295 case wakeup_by_interrupt:
1296 ret = Qundef;
1297 interrupted = true;
1298 goto cleanup;
1299 }
1300 }
1301
1302 cleanup:
1303 RUBY_DEBUG_LOG("cleanup actions (%s)", wait_status_str(wait_status));
1304
1306 ractor_basket_clear(&cr->sync.wait.yielded_basket);
1307 }
1308
1309 VM_ASSERT(cr->sync.wait.status == wait_none);
1310 VM_ASSERT(cr->sync.wait.wakeup_status == wakeup_none);
1313
1314 if (interrupted) {
1316 interrupted = false;
1317 goto restart;
1318 }
1319
1320 VM_ASSERT(ret != Qundef);
1321 return ret;
1322}
1323
1324static VALUE
1325ractor_yield(rb_execution_context_t *ec, rb_ractor_t *r, VALUE obj, VALUE move)
1326{
1327 VALUE ret_r;
1328 ractor_select(ec, NULL, 0, obj, RTEST(move) ? true : false, &ret_r);
1329 return Qnil;
1330}
1331
1332static VALUE
1333ractor_take(rb_execution_context_t *ec, rb_ractor_t *r)
1334{
1335 VALUE ret_r;
1336 VALUE v = ractor_select(ec, &r->pub.self, 1, Qundef, false, &ret_r);
1337 return v;
1338}
1339
1340static VALUE
1341ractor_close_incoming(rb_execution_context_t *ec, rb_ractor_t *r)
1342{
1343 VALUE prev;
1344
1345 RACTOR_LOCK(r);
1346 {
1347 if (!r->sync.incoming_port_closed) {
1348 prev = Qfalse;
1349 r->sync.incoming_port_closed = true;
1350 if (ractor_wakeup(r, wait_receiving, wakeup_by_close)) {
1352 RUBY_DEBUG_LOG("cancel receiving", 0);
1353 }
1354 }
1355 else {
1356 prev = Qtrue;
1357 }
1358 }
1359 RACTOR_UNLOCK(r);
1360 return prev;
1361}
1362
1363static VALUE
1364ractor_close_outgoing(rb_execution_context_t *ec, rb_ractor_t *r)
1365{
1366 VALUE prev;
1367
1368 RACTOR_LOCK(r);
1369 {
1370 if (!r->sync.outgoing_port_closed) {
1371 prev = Qfalse;
1372 r->sync.outgoing_port_closed = true;
1373 }
1374 else {
1375 prev = Qtrue;
1376 }
1377
1378 // wakeup all taking ractors
1379 rb_ractor_t *taking_ractor;
1380 while ((taking_ractor = ractor_waiting_list_shift(r, &r->sync.taking_ractors)) != NULL) {
1381 RACTOR_LOCK(taking_ractor);
1382 ractor_wakeup(taking_ractor, wait_taking, wakeup_by_close);
1383 RACTOR_UNLOCK(taking_ractor);
1384 }
1385
1386 // raising yielding Ractor
1387 if (!r->yield_atexit &&
1388 ractor_wakeup(r, wait_yielding, wakeup_by_close)) {
1389 RUBY_DEBUG_LOG("cancel yielding", 0);
1390 }
1391 }
1392 RACTOR_UNLOCK(r);
1393 return prev;
1394}
1395
1396// creation/termination
1397
1398static uint32_t
1399ractor_next_id(void)
1400{
1401 uint32_t id;
1402
1403 RB_VM_LOCK();
1404 {
1405 id = ++ractor_last_id;
1406 }
1407 RB_VM_UNLOCK();
1408
1409 return id;
1410}
1411
1412static void
1413vm_insert_ractor0(rb_vm_t *vm, rb_ractor_t *r, bool single_ractor_mode)
1414{
1415 RUBY_DEBUG_LOG("r:%u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
1416 VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
1417
1418 list_add_tail(&vm->ractor.set, &r->vmlr_node);
1419 vm->ractor.cnt++;
1420}
1421
1422static void
1423cancel_single_ractor_mode(void)
1424{
1425 // enable multi-ractor mode
1426 RUBY_DEBUG_LOG("enable multi-ractor mode", 0);
1427
1428 rb_gc_start();
1430
1432
1435 "Ractor is experimental, and the behavior may change in future versions of Ruby! "
1436 "Also there are many implementation issues.");
1437 }
1438}
1439
1440static void
1441vm_insert_ractor(rb_vm_t *vm, rb_ractor_t *r)
1442{
1443 VM_ASSERT(ractor_status_p(r, ractor_created));
1444
1445 if (rb_multi_ractor_p()) {
1446 RB_VM_LOCK();
1447 {
1448 vm_insert_ractor0(vm, r, false);
1449 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
1450 }
1451 RB_VM_UNLOCK();
1452 }
1453 else {
1454 if (vm->ractor.cnt == 0) {
1455 // main ractor
1456 vm_insert_ractor0(vm, r, true);
1457 ractor_status_set(r, ractor_blocking);
1458 ractor_status_set(r, ractor_running);
1459 }
1460 else {
1461 cancel_single_ractor_mode();
1462 vm_insert_ractor0(vm, r, true);
1463 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
1464 }
1465 }
1466}
1467
1468static void
1469vm_remove_ractor(rb_vm_t *vm, rb_ractor_t *cr)
1470{
1471 VM_ASSERT(ractor_status_p(cr, ractor_running));
1472 VM_ASSERT(vm->ractor.cnt > 1);
1473 VM_ASSERT(cr->threads.cnt == 1);
1474
1475 RB_VM_LOCK();
1476 {
1477 RUBY_DEBUG_LOG("ractor.cnt:%u-- terminate_waiting:%d",
1479
1480 VM_ASSERT(vm->ractor.cnt > 0);
1481 list_del(&cr->vmlr_node);
1482
1483 if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
1485 }
1486 vm->ractor.cnt--;
1487
1488 /* Clear the cached freelist to prevent a memory leak. */
1490
1491 ractor_status_set(cr, ractor_terminated);
1492 }
1493 RB_VM_UNLOCK();
1494}
1495
1496static VALUE
1497ractor_alloc(VALUE klass)
1498{
1499 rb_ractor_t *r;
1500 VALUE rv = TypedData_Make_Struct(klass, rb_ractor_t, &ractor_data_type, r);
1502 r->pub.self = rv;
1503 VM_ASSERT(ractor_status_p(r, ractor_created));
1504 return rv;
1505}
1506
1509{
1511 if (r == NULL) {
1512 fprintf(stderr, "[FATAL] failed to allocate memory for main ractor\n");
1513 exit(EXIT_FAILURE);
1514 }
1515 MEMZERO(r, rb_ractor_t, 1);
1516 r->pub.id = ++ractor_last_id;
1517 r->loc = Qnil;
1518 r->name = Qnil;
1519 r->pub.self = Qnil;
1521
1522 return r;
1523}
1524
1525#if defined(HAVE_WORKING_FORK)
1526void
1528{
1529 // initialize as a main ractor
1530 vm->ractor.cnt = 0;
1531 vm->ractor.blocking_cnt = 0;
1533 th->ractor->status_ = ractor_created;
1534
1537
1538 VM_ASSERT(vm->ractor.blocking_cnt == 0);
1539 VM_ASSERT(vm->ractor.cnt == 1);
1540}
1541#endif
1542
1544
1545void
1547{
1548 list_head_init(&r->threads.set);
1549 r->threads.cnt = 0;
1550 r->threads.blocking_cnt = 0;
1551}
1552
1553static void
1554ractor_init(rb_ractor_t *r, VALUE name, VALUE loc)
1555{
1556 ractor_queue_setup(&r->sync.incoming_queue);
1560
1561 // thread management
1562 rb_gvl_init(&r->threads.gvl);
1564
1565 // naming
1566 if (!NIL_P(name)) {
1567 rb_encoding *enc;
1569 enc = rb_enc_get(name);
1570 if (!rb_enc_asciicompat(enc)) {
1571 rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
1572 rb_enc_name(enc));
1573 }
1575 }
1576 r->name = name;
1577 r->loc = loc;
1578}
1579
1580void
1582{
1583 r->pub.self = TypedData_Wrap_Struct(rb_cRactor, &ractor_data_type, r);
1585 ractor_init(r, Qnil, Qnil);
1586 r->threads.main = th;
1588}
1589
1590static VALUE
1591ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VALUE args, VALUE block)
1592{
1593 VALUE rv = ractor_alloc(self);
1594 rb_ractor_t *r = RACTOR_PTR(rv);
1595 ractor_init(r, name, loc);
1596
1597 // can block here
1598 r->pub.id = ractor_next_id();
1599 RUBY_DEBUG_LOG("r:%u", r->pub.id);
1600
1601 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1602 r->verbose = cr->verbose;
1603 r->debug = cr->debug;
1604
1605 rb_thread_create_ractor(r, args, block);
1606
1607 RB_GC_GUARD(rv);
1608 return rv;
1609}
1610
1611static void
1612ractor_yield_atexit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE v, bool exc)
1613{
1614 if (cr->sync.outgoing_port_closed) {
1615 return;
1616 }
1617
1618 ASSERT_ractor_unlocking(cr);
1619
1620 struct rb_ractor_basket basket;
1621 ractor_basket_setup(ec, &basket, v, Qfalse, exc, true, true /* this flag is ignored because move is Qfalse */);
1622
1623 retry:
1624 if (ractor_try_yield(ec, cr, &basket)) {
1625 // OK.
1626 }
1627 else {
1628 bool retry = false;
1629 RACTOR_LOCK(cr);
1630 {
1631 if (cr->sync.taking_ractors.cnt == 0) {
1632 cr->sync.wait.yielded_basket = basket;
1633
1634 VM_ASSERT(cr->sync.wait.status == wait_none);
1635 cr->sync.wait.status = wait_yielding;
1636 cr->sync.wait.wakeup_status = wakeup_none;
1637
1638 VM_ASSERT(cr->yield_atexit == false);
1639 cr->yield_atexit = true;
1640 }
1641 else {
1642 retry = true; // another ractor is waiting for the yield.
1643 }
1644 }
1645 RACTOR_UNLOCK(cr);
1646
1647 if (retry) goto retry;
1648 }
1649}
1650
1651void
1653{
1654 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1655 ractor_close_incoming(ec, cr);
1656 ractor_close_outgoing(ec, cr);
1657
1658 // sync with rb_ractor_terminate_interrupt_main_thread()
1660 {
1661 VM_ASSERT(cr->threads.main != NULL);
1662 cr->threads.main = NULL;
1663 }
1665}
1666
1667void
1669{
1670 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1671 ractor_yield_atexit(ec, cr, result, false);
1672}
1673
1674void
1676{
1677 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1678 ractor_yield_atexit(ec, cr, ec->errinfo, true);
1679}
1680
1681void
1683{
1684 for (int i=0; i<len; i++) {
1685 ptr[i] = ractor_receive(ec, r);
1686 }
1687}
1688
1689void
1691{
1692 int len = RARRAY_LENINT(args);
1693 for (int i=0; i<len; i++) {
1694 ractor_send(ec, r, RARRAY_AREF(args, i), false);
1695 }
1696}
1697
1700{
1701 VM_ASSERT(rb_multi_ractor_p());
1702 rb_execution_context_t *ec = GET_EC();
1703 return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
1704}
1705
1706bool
1708{
1709 if (!rb_ractor_p(gv)) return false;
1710 rb_ractor_t *r = DATA_PTR(gv);
1711 return r == GET_VM()->ractor.main_ractor;
1712}
1713
1716{
1717 return &r->threads.gvl;
1718}
1719
1720int
1722{
1723 return r->threads.cnt;
1724}
1725
1726VALUE
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 list_for_each(&r->threads.set, th, lt_node) {
1739 switch (th->status) {
1740 case THREAD_RUNNABLE:
1741 case THREAD_STOPPED:
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
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 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++;
1787}
1788
1789void
1791{
1793 VM_ASSERT(GET_RACTOR() == cr);
1794 vm_ractor_blocking_cnt_inc(vm, cr, file, line);
1795}
1796
1797void
1799{
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",
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();
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
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 list_del(&th->lt_node);
1851 cr->threads.cnt--;
1852 }
1853 RACTOR_UNLOCK(cr);
1854 }
1855}
1856
1857void
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
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
1882 {
1883 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
1884 }
1886 }
1887
1888 cr->threads.blocking_cnt--;
1889}
1890
1891void
1893{
1894 VM_ASSERT(r != GET_RACTOR());
1895 ASSERT_ractor_unlocking(r);
1897
1898 RACTOR_LOCK(r);
1899 {
1900 if (ractor_status_p(r, ractor_running)) {
1902 if (ec) {
1904 }
1905 }
1906 }
1907 RACTOR_UNLOCK(r);
1908}
1909
1910void
1912{
1913 VM_ASSERT(r != GET_RACTOR());
1914 ASSERT_ractor_unlocking(r);
1916
1917 rb_thread_t *main_th = r->threads.main;
1918 if (main_th) {
1919 if (main_th->status != THREAD_KILLED) {
1921 rb_threadptr_interrupt(main_th);
1922 }
1923 else {
1924 RUBY_DEBUG_LOG("killed (%p)", 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 list_for_each(&vm->ractor.set, r, vmlr_node) {
1938 if (r != vm->ractor.main_ractor) {
1940 }
1941 }
1942 }
1943}
1944
1945void
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
1979{
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
2084{
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
2112{
2113 rb_vm_t *vm = GET_VM();
2114 rb_ractor_t *r = 0;
2115
2116 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
2197{
2198 return &cr->pub.hooks;
2199}
2200
2202
2203// 2: stop search
2204// 1: skip child
2205// 0: continue
2206
2211};
2212
2216
2217static enum obj_traverse_iterator_result null_leave(VALUE obj);
2218
2222
2225};
2226
2227
2229 bool stop;
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 void
2254obj_traverse_reachable_i(VALUE obj, void *ptr)
2255{
2257
2258 if (obj_traverse_i(obj, d->data)) {
2259 d->stop = true;
2260 }
2261}
2262
2263static struct st_table *
2264obj_traverse_rec(struct obj_traverse_data *data)
2265{
2266 if (UNLIKELY(!data->rec)) {
2267 data->rec_hash = rb_ident_hash_new();
2268 data->rec = rb_hash_st_table(data->rec_hash);
2269 }
2270 return data->rec;
2271}
2272
2273static int
2274obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
2275{
2276 if (RB_SPECIAL_CONST_P(obj)) return 0;
2277
2278 switch (data->enter_func(obj)) {
2279 case traverse_cont: break;
2280 case traverse_skip: return 0; // skip children
2281 case traverse_stop: return 1; // stop search
2282 }
2283
2284 if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
2285 // already traversed
2286 return 0;
2287 }
2288
2289 if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
2290 struct gen_ivtbl *ivtbl;
2291 rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
2292 for (uint32_t i = 0; i < ivtbl->numiv; i++) {
2293 VALUE val = ivtbl->ivptr[i];
2294 if (val != Qundef && obj_traverse_i(val, data)) return 1;
2295 }
2296 }
2297
2298 switch (BUILTIN_TYPE(obj)) {
2299 // no child node
2300 case T_STRING:
2301 case T_FLOAT:
2302 case T_BIGNUM:
2303 case T_REGEXP:
2304 case T_FILE:
2305 case T_SYMBOL:
2306 case T_MATCH:
2307 break;
2308
2309 case T_OBJECT:
2310 {
2311 uint32_t len = ROBJECT_NUMIV(obj);
2312 VALUE *ptr = ROBJECT_IVPTR(obj);
2313
2314 for (uint32_t i=0; i<len; i++) {
2315 VALUE val = ptr[i];
2316 if (val != Qundef && obj_traverse_i(val, data)) return 1;
2317 }
2318 }
2319 break;
2320
2321 case T_ARRAY:
2322 {
2323 for (int i = 0; i < RARRAY_LENINT(obj); i++) {
2324 VALUE e = rb_ary_entry(obj, i);
2325 if (obj_traverse_i(e, data)) return 1;
2326 }
2327 }
2328 break;
2329
2330 case T_HASH:
2331 {
2332 if (obj_traverse_i(RHASH_IFNONE(obj), data)) return 1;
2333
2334 struct obj_traverse_callback_data d = {
2335 .stop = false,
2336 .data = data,
2337 };
2338 rb_hash_foreach(obj, obj_hash_traverse_i, (VALUE)&d);
2339 if (d.stop) return 1;
2340 }
2341 break;
2342
2343 case T_STRUCT:
2344 {
2345 long len = RSTRUCT_LEN(obj);
2346 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2347
2348 for (long i=0; i<len; i++) {
2349 if (obj_traverse_i(ptr[i], data)) return 1;
2350 }
2351 }
2352 break;
2353
2354 case T_RATIONAL:
2355 if (obj_traverse_i(RRATIONAL(obj)->num, data)) return 1;
2356 if (obj_traverse_i(RRATIONAL(obj)->den, data)) return 1;
2357 break;
2358 case T_COMPLEX:
2359 if (obj_traverse_i(RCOMPLEX(obj)->real, data)) return 1;
2360 if (obj_traverse_i(RCOMPLEX(obj)->imag, data)) return 1;
2361 break;
2362
2363 case T_DATA:
2364 case T_IMEMO:
2365 {
2366 struct obj_traverse_callback_data d = {
2367 .stop = false,
2368 .data = data,
2369 };
2371 {
2372 rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
2373 }
2375 if (d.stop) return 1;
2376 }
2377 break;
2378
2379 // unreachable
2380 case T_CLASS:
2381 case T_MODULE:
2382 case T_ICLASS:
2383 default:
2384 rp(obj);
2385 rb_bug("unreachable");
2386 }
2387
2388 if (data->leave_func(obj) == traverse_stop) {
2389 return 1;
2390 }
2391 else {
2392 return 0;
2393 }
2394}
2395
2399};
2400
2401static int
2402obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
2403{
2404 struct rb_obj_traverse_final_data *data = (void *)arg;
2405 if (data->final_func(key)) {
2406 data->stopped = 1;
2407 return ST_STOP;
2408 }
2409 return ST_CONTINUE;
2410}
2411
2412// 0: traverse all
2413// 1: stopped
2414static int
2415rb_obj_traverse(VALUE obj,
2416 rb_obj_traverse_enter_func enter_func,
2417 rb_obj_traverse_leave_func leave_func,
2419{
2420 struct obj_traverse_data data = {
2422 .leave_func = leave_func,
2423 .rec = NULL,
2424 };
2425
2426 if (obj_traverse_i(obj, &data)) return 1;
2427 if (final_func && data.rec) {
2429 st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
2430 return f.stopped;
2431 }
2432 return 0;
2433}
2434
2435static int
2436frozen_shareable_p(VALUE obj, bool *made_shareable)
2437{
2438 if (!RB_TYPE_P(obj, T_DATA)) {
2439 return true;
2440 }
2441 else if (RTYPEDDATA_P(obj)) {
2442 const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
2443 if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
2444 return true;
2445 }
2446 else if (made_shareable && rb_obj_is_proc(obj)) {
2447 // special path to make shareable Proc.
2449 *made_shareable = true;
2451 return false;
2452 }
2453 }
2454
2455 return false;
2456}
2457
2459make_shareable_check_shareable(VALUE obj)
2460{
2462 bool made_shareable = false;
2463
2464 if (rb_ractor_shareable_p(obj)) {
2465 return traverse_skip;
2466 }
2467 else if (!frozen_shareable_p(obj, &made_shareable)) {
2468 if (made_shareable) {
2469 return traverse_skip;
2470 }
2471 else {
2472 rb_raise(rb_eRactorError, "can not make shareable object for %"PRIsVALUE, obj);
2473 }
2474 }
2475
2476 if (!RB_OBJ_FROZEN_RAW(obj)) {
2477 rb_funcall(obj, idFreeze, 0);
2478
2479 if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
2480 rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
2481 }
2482
2483 if (RB_OBJ_SHAREABLE_P(obj)) {
2484 return traverse_skip;
2485 }
2486 }
2487
2488 return traverse_cont;
2489}
2490
2492mark_shareable(VALUE obj)
2493{
2495 return traverse_cont;
2496}
2497
2498VALUE
2500{
2501 rb_obj_traverse(obj,
2502 make_shareable_check_shareable,
2503 null_leave, mark_shareable);
2504 return obj;
2505}
2506
2507VALUE
2509{
2510 VALUE copy = ractor_copy(obj);
2511 rb_obj_traverse(copy,
2512 make_shareable_check_shareable,
2513 null_leave, mark_shareable);
2514 return copy;
2515}
2516
2517VALUE
2519{
2520 if (!rb_ractor_shareable_p(obj)) {
2521 VALUE message = rb_sprintf("cannot assign unshareable object to %"PRIsVALUE,
2522 name);
2524 }
2525 return obj;
2526}
2527
2529shareable_p_enter(VALUE obj)
2530{
2531 if (RB_OBJ_SHAREABLE_P(obj)) {
2532 return traverse_skip;
2533 }
2534 else if (RB_TYPE_P(obj, T_CLASS) ||
2535 RB_TYPE_P(obj, T_MODULE) ||
2536 RB_TYPE_P(obj, T_ICLASS)) {
2537 // TODO: remove it
2538 mark_shareable(obj);
2539 return traverse_skip;
2540 }
2541 else if (RB_OBJ_FROZEN_RAW(obj) &&
2542 frozen_shareable_p(obj, NULL)) {
2543 return traverse_cont;
2544 }
2545
2546 return traverse_stop; // fail
2547}
2548
2551{
2552 if (rb_obj_traverse(obj,
2553 shareable_p_enter, null_leave,
2554 mark_shareable)) {
2555 return false;
2556 }
2557 else {
2558 return true;
2559 }
2560}
2561
2562#if RACTOR_CHECK_MODE > 0
2564reset_belonging_enter(VALUE obj)
2565{
2566 if (rb_ractor_shareable_p(obj)) {
2567 return traverse_skip;
2568 }
2569 else {
2570 rb_ractor_setup_belonging(obj);
2571 return traverse_cont;
2572 }
2573}
2574#endif
2575
2577null_leave(VALUE obj)
2578{
2579 return traverse_cont;
2580}
2581
2582static VALUE
2583ractor_reset_belonging(VALUE obj)
2584{
2585#if RACTOR_CHECK_MODE > 0
2586 rb_obj_traverse(obj, reset_belonging_enter, null_leave, NULL);
2587#endif
2588 return obj;
2589}
2590
2591
2593
2594// 2: stop search
2595// 1: skip child
2596// 0: continue
2597
2599static int obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data);
2602
2606
2609
2611 bool move;
2612};
2613
2615 bool stop;
2618};
2619
2620static int
2621obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp, int error)
2622{
2623 return ST_REPLACE;
2624}
2625
2626static int
2627obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr, int exists)
2628{
2630 struct obj_traverse_replace_data *data = d->data;
2631
2632 if (obj_traverse_replace_i(*key, data)) {
2633 d->stop = true;
2634 return ST_STOP;
2635 }
2636 else if (*key != data->replacement) {
2637 VALUE v = *key = data->replacement;
2638 RB_OBJ_WRITTEN(d->src, Qundef, v);
2639 }
2640
2641 if (obj_traverse_replace_i(*val, data)) {
2642 d->stop = true;
2643 return ST_STOP;
2644 }
2645 else if (*val != data->replacement) {
2646 VALUE v = *val = data->replacement;
2647 RB_OBJ_WRITTEN(d->src, Qundef, v);
2648 }
2649
2650 return ST_CONTINUE;
2651}
2652
2653static struct st_table *
2654obj_traverse_replace_rec(struct obj_traverse_replace_data *data)
2655{
2656 if (UNLIKELY(!data->rec)) {
2657 data->rec_hash = rb_ident_hash_new();
2658 data->rec = rb_hash_st_table(data->rec_hash);
2659 }
2660 return data->rec;
2661}
2662
2663#if USE_TRANSIENT_HEAP
2664void rb_ary_transient_heap_evacuate(VALUE ary, int promote);
2665void rb_obj_transient_heap_evacuate(VALUE obj, int promote);
2666void rb_hash_transient_heap_evacuate(VALUE hash, int promote);
2667void rb_struct_transient_heap_evacuate(VALUE st, int promote);
2668#endif
2669
2670static void
2671obj_refer_only_shareables_p_i(VALUE obj, void *ptr)
2672{
2673 int *pcnt = (int *)ptr;
2674
2675 if (!rb_ractor_shareable_p(obj)) {
2676 pcnt++;
2677 }
2678}
2679
2680static int
2681obj_refer_only_shareables_p(VALUE obj)
2682{
2683 int cnt = 0;
2685 {
2686 rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
2687 }
2689 return cnt == 0;
2690}
2691
2692static int
2693obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
2694{
2695 VALUE replacement;
2696
2697 if (RB_SPECIAL_CONST_P(obj)) {
2698 data->replacement = obj;
2699 return 0;
2700 }
2701
2702 switch (data->enter_func(obj, data)) {
2703 case traverse_cont: break;
2704 case traverse_skip: return 0; // skip children
2705 case traverse_stop: return 1; // stop search
2706 }
2707
2708 replacement = data->replacement;
2709
2710 if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, (st_data_t *)&replacement))) {
2711 data->replacement = replacement;
2712 return 0;
2713 }
2714 else {
2715 st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, (st_data_t)replacement);
2716 }
2717
2718 if (!data->move) {
2719 obj = replacement;
2720 }
2721
2722#define CHECK_AND_REPLACE(v) do { \
2723 VALUE _val = (v); \
2724 if (obj_traverse_replace_i(_val, data)) { return 1; } \
2725 else if (data->replacement != _val) { RB_OBJ_WRITE(obj, &v, data->replacement); } \
2726} while (0)
2727
2728 if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
2729 struct gen_ivtbl *ivtbl;
2730 rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
2731 for (uint32_t i = 0; i < ivtbl->numiv; i++) {
2732 if (ivtbl->ivptr[i] != Qundef) {
2733 CHECK_AND_REPLACE(ivtbl->ivptr[i]);
2734 }
2735 }
2736 }
2737
2738 switch (BUILTIN_TYPE(obj)) {
2739 // no child node
2740 case T_FLOAT:
2741 case T_BIGNUM:
2742 case T_REGEXP:
2743 case T_FILE:
2744 case T_SYMBOL:
2745 case T_MATCH:
2746 break;
2747 case T_STRING:
2749 break;
2750
2751 case T_OBJECT:
2752 {
2753#if USE_TRANSIENT_HEAP
2754 if (data->move) rb_obj_transient_heap_evacuate(obj, TRUE);
2755#endif
2756
2757 uint32_t len = ROBJECT_NUMIV(obj);
2758 VALUE *ptr = ROBJECT_IVPTR(obj);
2759
2760 for (uint32_t i=0; i<len; i++) {
2761 if (ptr[i] != Qundef) {
2763 }
2764 }
2765 }
2766 break;
2767
2768 case T_ARRAY:
2769 {
2771#if USE_TRANSIENT_HEAP
2772 if (data->move) rb_ary_transient_heap_evacuate(obj, TRUE);
2773#endif
2774
2775 for (int i = 0; i < RARRAY_LENINT(obj); i++) {
2776 VALUE e = rb_ary_entry(obj, i);
2777
2778 if (obj_traverse_replace_i(e, data)) {
2779 return 1;
2780 }
2781 else if (e != data->replacement) {
2782 RARRAY_ASET(obj, i, data->replacement);
2783 }
2784 }
2785 RB_GC_GUARD(obj);
2786 }
2787 break;
2788
2789 case T_HASH:
2790 {
2791#if USE_TRANSIENT_HEAP
2792 if (data->move) rb_hash_transient_heap_evacuate(obj, TRUE);
2793#endif
2795 .stop = false,
2796 .data = data,
2797 .src = obj,
2798 };
2800 obj_hash_traverse_replace_foreach_i,
2801 obj_hash_traverse_replace_i,
2802 (VALUE)&d);
2803 if (d.stop) return 1;
2804 // TODO: rehash here?
2805
2806 VALUE ifnone = RHASH_IFNONE(obj);
2807 if (obj_traverse_replace_i(ifnone, data)) {
2808 return 1;
2809 }
2810 else if (ifnone != data->replacement) {
2812 }
2813 }
2814 break;
2815
2816 case T_STRUCT:
2817 {
2818#if USE_TRANSIENT_HEAP
2820#endif
2821 long len = RSTRUCT_LEN(obj);
2822 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2823
2824 for (long i=0; i<len; i++) {
2826 }
2827 }
2828 break;
2829
2830 case T_RATIONAL:
2832 CHECK_AND_REPLACE(RRATIONAL(obj)->den);
2833 break;
2834 case T_COMPLEX:
2835 CHECK_AND_REPLACE(RCOMPLEX(obj)->real);
2836 CHECK_AND_REPLACE(RCOMPLEX(obj)->imag);
2837 break;
2838
2839 case T_DATA:
2840 if (!data->move && obj_refer_only_shareables_p(obj)) {
2841 break;
2842 }
2843 else {
2844 rb_raise(rb_eRactorError, "can not %s %"PRIsVALUE" object.",
2845 data->move ? "move" : "copy", rb_class_of(obj));
2846 }
2847
2848 case T_IMEMO:
2849 // not supported yet
2850 return 1;
2851
2852 // unreachable
2853 case T_CLASS:
2854 case T_MODULE:
2855 case T_ICLASS:
2856 default:
2857 rp(obj);
2858 rb_bug("unreachable");
2859 }
2860
2861 data->replacement = replacement;
2862
2863 if (data->leave_func(obj, data) == traverse_stop) {
2864 return 1;
2865 }
2866 else {
2867 return 0;
2868 }
2869}
2870
2871// 0: traverse all
2872// 1: stopped
2873static VALUE
2874rb_obj_traverse_replace(VALUE obj,
2877 bool move)
2878{
2879 struct obj_traverse_replace_data data = {
2881 .leave_func = leave_func,
2882 .rec = NULL,
2883 .replacement = Qundef,
2884 .move = move,
2885 };
2886
2887 if (obj_traverse_replace_i(obj, &data)) {
2888 return Qundef;
2889 }
2890 else {
2891 return data.replacement;
2892 }
2893}
2894
2895struct RVALUE {
2896 VALUE flags;
2898 VALUE v1;
2899 VALUE v2;
2900 VALUE v3;
2901};
2902
2903static const VALUE fl_users = FL_USER1 | FL_USER2 | FL_USER3 |
2908
2909static void
2910ractor_moved_bang(VALUE obj)
2911{
2912 // invalidate src object
2913 struct RVALUE *rv = (void *)obj;
2914
2915 rv->klass = rb_cRactorMovedObject;
2916 rv->v1 = 0;
2917 rv->v2 = 0;
2918 rv->v3 = 0;
2919 rv->flags = rv->flags & ~fl_users;
2920
2921 // TODO: record moved location
2922}
2923
2925move_enter(VALUE obj, struct obj_traverse_replace_data *data)
2926{
2927 if (rb_ractor_shareable_p(obj)) {
2928 data->replacement = obj;
2929 return traverse_skip;
2930 }
2931 else {
2932 data->replacement = rb_obj_alloc(RBASIC_CLASS(obj));
2933 return traverse_cont;
2934 }
2935}
2936
2937void rb_replace_generic_ivar(VALUE clone, VALUE obj); // variable.c
2938
2940move_leave(VALUE obj, struct obj_traverse_replace_data *data)
2941{
2942 VALUE v = data->replacement;
2943 struct RVALUE *dst = (struct RVALUE *)v;
2944 struct RVALUE *src = (struct RVALUE *)obj;
2945
2946 dst->flags = (dst->flags & ~fl_users) | (src->flags & fl_users);
2947
2948 dst->v1 = src->v1;
2949 dst->v2 = src->v2;
2950 dst->v3 = src->v3;
2951
2952 if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
2954 }
2955
2956 // TODO: generic_ivar
2957
2958 ractor_moved_bang(obj);
2959 return traverse_cont;
2960}
2961
2962static VALUE
2963ractor_move(VALUE obj)
2964{
2965 VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave, true);
2966 if (val != Qundef) {
2967 return val;
2968 }
2969 else {
2970 rb_raise(rb_eRactorError, "can not move the object");
2971 }
2972}
2973
2975copy_enter(VALUE obj, struct obj_traverse_replace_data *data)
2976{
2977 if (rb_ractor_shareable_p(obj)) {
2978 data->replacement = obj;
2979 return traverse_skip;
2980 }
2981 else {
2982 data->replacement = rb_obj_clone(obj);
2983 return traverse_cont;
2984 }
2985}
2986
2988copy_leave(VALUE obj, struct obj_traverse_replace_data *data)
2989{
2990 return traverse_cont;
2991}
2992
2993static VALUE
2994ractor_copy(VALUE obj)
2995{
2996 VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave, false);
2997 if (val != Qundef) {
2998 return val;
2999 }
3000 else {
3001 rb_raise(rb_eRactorError, "can not copy the object");
3002 }
3003}
3004
3005// Ractor local storage
3006
3010};
3011
3012static struct freed_ractor_local_keys_struct {
3013 int cnt;
3014 int capa;
3016} freed_ractor_local_keys;
3017
3018static int
3019ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
3020{
3022 if (k->type->mark) (*k->type->mark)((void *)val);
3023 return ST_CONTINUE;
3024}
3025
3027idkey_local_storage_mark_i(ID id, VALUE val, void *dmy)
3028{
3029 rb_gc_mark(val);
3030 return ID_TABLE_CONTINUE;
3031}
3032
3033static void
3034ractor_local_storage_mark(rb_ractor_t *r)
3035{
3036 if (r->local_storage) {
3037 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
3038
3039 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
3040 rb_ractor_local_key_t key = freed_ractor_local_keys.keys[i];
3041 st_data_t val;
3042 if (st_delete(r->local_storage, (st_data_t *)&key, &val) &&
3043 key->type->free) {
3044 (*key->type->free)((void *)val);
3045 }
3046 }
3047 }
3048
3049 if (r->idkey_local_storage) {
3050 rb_id_table_foreach(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
3051 }
3052}
3053
3054static int
3055ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
3056{
3058 if (k->type->free) (*k->type->free)((void *)val);
3059 return ST_CONTINUE;
3060}
3061
3062static void
3063ractor_local_storage_free(rb_ractor_t *r)
3064{
3065 if (r->local_storage) {
3066 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
3068 }
3069
3070 if (r->idkey_local_storage) {
3072 }
3073}
3074
3075static void
3076rb_ractor_local_storage_value_mark(void *ptr)
3077{
3079}
3080
3081static const struct rb_ractor_local_storage_type ractor_local_storage_type_null = {
3082 NULL,
3083 NULL,
3084};
3085
3087 NULL,
3088 ruby_xfree,
3089};
3090
3091static const struct rb_ractor_local_storage_type ractor_local_storage_type_value = {
3092 rb_ractor_local_storage_value_mark,
3093 NULL,
3094};
3095
3098{
3100 key->type = type ? type : &ractor_local_storage_type_null;
3101 key->main_cache = (void *)Qundef;
3102 return key;
3103}
3104
3107{
3108 return rb_ractor_local_storage_ptr_newkey(&ractor_local_storage_type_value);
3109}
3110
3111void
3113{
3115 {
3116 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
3117 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
3118 REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, freed_ractor_local_keys.capa);
3119 }
3120 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
3121 }
3123}
3124
3125static bool
3126ractor_local_ref(rb_ractor_local_key_t key, void **pret)
3127{
3128 if (rb_ractor_main_p()) {
3129 if ((VALUE)key->main_cache != Qundef) {
3130 *pret = key->main_cache;
3131 return true;
3132 }
3133 else {
3134 return false;
3135 }
3136 }
3137 else {
3138 rb_ractor_t *cr = GET_RACTOR();
3139
3140 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
3141 return true;
3142 }
3143 else {
3144 return false;
3145 }
3146 }
3147}
3148
3149static void
3150ractor_local_set(rb_ractor_local_key_t key, void *ptr)
3151{
3152 rb_ractor_t *cr = GET_RACTOR();
3153
3154 if (cr->local_storage == NULL) {
3156 }
3157
3159
3160 if (rb_ractor_main_p()) {
3161 key->main_cache = ptr;
3162 }
3163}
3164
3165VALUE
3167{
3168 VALUE val;
3169 if (ractor_local_ref(key, (void **)&val)) {
3170 return val;
3171 }
3172 else {
3173 return Qnil;
3174 }
3175}
3176
3177bool
3179{
3180 if (ractor_local_ref(key, (void **)val)) {
3181 return true;
3182 }
3183 else {
3184 return false;
3185 }
3186}
3187
3188void
3190{
3191 ractor_local_set(key, (void *)val);
3192}
3193
3194void *
3196{
3197 void *ret;
3198 if (ractor_local_ref(key, &ret)) {
3199 return ret;
3200 }
3201 else {
3202 return NULL;
3203 }
3204}
3205
3206void
3208{
3209 ractor_local_set(key, ptr);
3210}
3211
3212#define DEFAULT_KEYS_CAPA 0x10
3213
3214void
3216{
3217 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
3218 ruby_xfree(freed_ractor_local_keys.keys[i]);
3219 }
3220 freed_ractor_local_keys.cnt = 0;
3221 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
3222 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
3223 REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, DEFAULT_KEYS_CAPA);
3224 }
3225}
3226
3227static VALUE
3228ractor_local_value(rb_execution_context_t *ec, VALUE self, VALUE sym)
3229{
3230 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3231 ID id = rb_check_id(&sym);
3232 struct rb_id_table *tbl = cr->idkey_local_storage;
3233 VALUE val;
3234
3235 if (id && tbl && rb_id_table_lookup(tbl, id, &val)) {
3236 return val;
3237 }
3238 else {
3239 return Qnil;
3240 }
3241}
3242
3243static VALUE
3244ractor_local_value_set(rb_execution_context_t *ec, VALUE self, VALUE sym, VALUE val)
3245{
3246 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3247 ID id = SYM2ID(rb_to_symbol(sym));
3248 struct rb_id_table *tbl = cr->idkey_local_storage;
3249
3250 if (tbl == NULL) {
3252 }
3253 rb_id_table_insert(tbl, id, val);
3254 return val;
3255}
3256
3257#include "ractor.rbinc"
VALUE rb_ary_push(VALUE ary, VALUE item)
Definition: array.c:1301
VALUE rb_ary_new(void)
Definition: array.c:749
void rb_ary_cancel_sharing(VALUE ary)
Definition: array.c:565
VALUE rb_ary_entry(VALUE ary, long offset)
Definition: array.c:1672
#define rb_category_warn(category,...)
Definition: bigdecimal.h:163
unsigned char T
Definition: cls_uchar_va.c:10
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
Definition: cxxanyargs.hpp:653
struct RIMemo * ptr
Definition: debug.c:88
#define MJIT_FUNC_EXPORTED
Definition: dllexport.h:55
#define free(x)
Definition: dln.c:52
rb_encoding * rb_enc_get(VALUE obj)
Definition: encoding.c:1070
big_t * num
Definition: enough.c:232
string_t out
Definition: enough.c:230
void cleanup(void)
Definition: enough.c:244
VALUE rb_eStopIteration
Definition: enumerator.c:141
#define sym(name)
Definition: enumerator.c:4007
uint8_t len
Definition: escape.c:17
#define EXIT_FAILURE
Definition: eval_intern.h:32
#define RSTRING_PTR(string)
Definition: fbuffer.h:19
#define MAYBE_UNUSED
Definition: ffi_common.h:30
#define UNLIKELY(x)
Definition: ffi_common.h:126
#define FL_EXIVAR
Definition: fl_type.h:58
#define FL_USER3
Definition: fl_type.h:66
#define FL_USER7
Definition: fl_type.h:70
#define FL_USER10
Definition: fl_type.h:73
#define FL_USER6
Definition: fl_type.h:69
#define FL_USER1
Definition: fl_type.h:64
#define FL_USER19
Definition: fl_type.h:82
#define FL_USER12
Definition: fl_type.h:75
#define FL_USER11
Definition: fl_type.h:74
#define FL_USER15
Definition: fl_type.h:78
#define FL_USER8
Definition: fl_type.h:71
#define FL_USER14
Definition: fl_type.h:77
#define FL_USER17
Definition: fl_type.h:80
#define FL_USER18
Definition: fl_type.h:81
#define FL_USER2
Definition: fl_type.h:65
#define FL_USER9
Definition: fl_type.h:72
#define FL_USER13
Definition: fl_type.h:76
#define FL_USER16
Definition: fl_type.h:79
#define FL_USER5
Definition: fl_type.h:68
@ RUBY_FL_SHAREABLE
Definition: fl_type.h:169
#define FL_USER4
Definition: fl_type.h:67
#define PRIsVALUE
Definition: function.c:10
void ruby_xfree(void *x)
Deallocates a storage instance.
Definition: gc.c:10914
VALUE rb_gc_start(void)
Definition: gc.c:9490
void * ruby_mimmalloc(size_t size)
Definition: gc.c:10951
void rb_objspace_reachable_objects_from(VALUE obj, void(func)(VALUE, void *), void *data)
Definition: gc.c:10169
void rb_gc_ractor_newobj_cache_clear(rb_ractor_newobj_cache_t *newobj_cache)
Definition: gc.c:7937
void rb_gc_mark(VALUE ptr)
Definition: gc.c:6112
VALUE rb_stdin
Definition: io.c:196
VALUE rb_stderr
Definition: globals.h:118
VALUE rb_stdout
Definition: globals.h:118
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition: class.c:748
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:797
#define FL_TEST_RAW
Definition: fl_type.h:131
#define FL_SET_RAW
Definition: fl_type.h:129
void rb_raise(VALUE exc, const char *fmt,...)
Definition: error.c:2917
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition: eval.c:712
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Definition: error.c:1007
void rb_bug(const char *fmt,...)
Definition: error.c:768
VALUE rb_ident_hash_new(void)
Definition: hash.c:4443
VALUE rb_protect(VALUE(*proc)(VALUE), VALUE data, int *pstate)
Protects a function call from potential global escapes from the function.
Definition: eval.c:1105
VALUE rb_eRuntimeError
Definition: error.c:1055
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Definition: error.c:1107
VALUE rb_eArgError
Definition: error.c:1058
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:1148
void rb_jump_tag(int tag)
Continues the exception caught by rb_protect() and rb_eval_string_protect().
Definition: eval.c:921
bool rb_warning_category_enabled_p(rb_warning_category_t category)
Definition: error.c:182
VALUE rb_cObject
Object class.
Definition: object.c:49
VALUE rb_obj_alloc(VALUE)
Allocates an instance of klass.
Definition: object.c:1900
VALUE rb_cBasicObject
BasicObject class.
Definition: object.c:47
VALUE rb_obj_clone(VALUE)
Almost same as Object::clone.
Definition: object.c:457
unsigned in(void *in_desc, z_const unsigned char **buf)
Definition: gun.c:89
int rb_hash_stlike_foreach_with_replace(VALUE hash, st_foreach_check_callback_func *func, st_update_callback_func *replace, st_data_t arg)
Definition: hash.c:1468
void rb_hash_foreach(VALUE hash, rb_foreach_func *func, VALUE farg)
Definition: hash.c:1498
st_table * rb_hash_st_table(VALUE hash)
Definition: hash.c:567
int rb_id_table_insert(struct rb_id_table *tbl, ID id, VALUE val)
Definition: id_table.c:257
int rb_id_table_lookup(struct rb_id_table *tbl, ID id, VALUE *valp)
Definition: id_table.c:227
void rb_id_table_free(struct rb_id_table *tbl)
Definition: id_table.c:103
struct rb_id_table * rb_id_table_create(size_t capa)
Definition: id_table.c:96
void rb_id_table_foreach(struct rb_id_table *tbl, rb_id_table_foreach_func_t *func, void *data)
Definition: id_table.c:292
rb_id_table_iterator_result
Definition: id_table.h:10
@ ID_TABLE_CONTINUE
Definition: id_table.h:11
#define rb_enc_name(enc)
Definition: encoding.h:168
#define rb_enc_asciicompat(enc)
Definition: encoding.h:236
@ RB_WARN_CATEGORY_EXPERIMENTAL
Definition: error.h:35
VALUE rb_funcall(VALUE, ID, int,...)
Calls a method.
Definition: vm_eval.c:1077
VALUE rb_obj_is_proc(VALUE)
Definition: proc.c:152
#define rb_exc_new_cstr(exc, str)
Definition: string.h:271
VALUE rb_str_new_frozen(VALUE)
Definition: string.c:1273
VALUE rb_mutex_new(void)
Definition: thread_sync.c:177
void rb_thread_check_ints(void)
Definition: thread.c:1577
VALUE rb_mutex_unlock(VALUE mutex)
Definition: thread_sync.c:474
VALUE rb_mutex_lock(VALUE mutex)
Definition: thread_sync.c:402
VALUE rb_ivar_set(VALUE, ID, VALUE)
Definition: variable.c:1493
void rb_undef_alloc_func(VALUE)
Definition: vm_method.c:954
#define ID2SYM
Definition: symbol.h:44
#define SYM2ID
Definition: symbol.h:45
ID rb_intern(const char *)
Definition: symbol.c:785
ID rb_check_id(volatile VALUE *)
Returns ID for the given name if it is interned already, or 0.
Definition: symbol.c:1069
VALUE rb_to_symbol(VALUE name)
Definition: string.c:11511
#define RB_NOGVL_UBF_ASYNC_SAFE
Definition: thread.h:18
#define RB_NOGVL_INTR_FAIL
Definition: thread.h:17
void * rb_nogvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2, int flags)
Definition: thread.c:1670
Internal header for Complex.
#define RCOMPLEX(obj)
Definition: complex.h:20
Internal header for GC.
Internal header for Hash.
Internal header for Rational.
#define RRATIONAL(obj)
Definition: rational.h:24
void rb_str_make_independent(VALUE str)
Definition: string.c:220
Internal header for Struct.
#define RSTRUCT_LEN
Definition: struct.h:52
Internal header for Thread.
VALUE rb_mutex_owned_p(VALUE self)
Definition: thread_sync.c:414
#define rp(obj)
Definition: internal.h:95
typedef int(ZCALLBACK *close_file_func) OF((voidpf opaque
VALUE rb_yield(VALUE)
Definition: vm_eval.c:1341
#define REALLOC_N
Definition: memory.h:137
#define ALLOCA_N(type, n)
Definition: memory.h:112
#define MEMZERO(p, type, n)
Definition: memory.h:128
#define RB_GC_GUARD(v)
Definition: memory.h:91
VALUE type(ANYARGS)
ANYARGS-ed function type.
Definition: cxxanyargs.hpp:56
const int id
Definition: nkf.c:209
const char * name
Definition: nkf.c:208
#define TRUE
Definition: nkf.h:175
#define RARRAY_AREF(a, i)
Definition: psych_emitter.c:7
bool rb_obj_is_main_ractor(VALUE gv)
Definition: ractor.c:1707
const struct rb_ractor_local_storage_type rb_ractor_local_storage_type_free
Definition: ractor.c:3086
rb_ractor_t * rb_ractor_main_alloc(void)
Definition: ractor.c:1508
VALUE rb_ractor_make_shareable_copy(VALUE obj)
Definition: ractor.c:2508
#define RACTOR_LOCK(r)
Definition: ractor.c:100
bool rb_ractor_p(VALUE gv)
Definition: ractor.c:273
void rb_ractor_local_storage_delkey(rb_ractor_local_key_t key)
Definition: ractor.c:3112
VALUE rb_ractor_ensure_shareable(VALUE obj, VALUE name)
Definition: ractor.c:2518
void * rb_ractor_local_storage_ptr(rb_ractor_local_key_t key)
Definition: ractor.c:3195
rb_execution_context_t * rb_vm_main_ractor_ec(rb_vm_t *vm)
Definition: ractor.c:1978
void rb_ractor_blocking_threads_dec(rb_ractor_t *cr, const char *file, int line)
Definition: ractor.c:1870
void rb_ractor_teardown(rb_execution_context_t *ec)
Definition: ractor.c:1652
void rb_ractor_stdin_set(VALUE in)
Definition: ractor.c:2160
rb_global_vm_lock_t * rb_ractor_gvl(rb_ractor_t *r)
Definition: ractor.c:1715
void rb_ractor_terminate_all(void)
Definition: ractor.c:1946
enum obj_traverse_iterator_result(* rb_obj_traverse_final_func)(VALUE obj)
Definition: ractor.c:2215
void rb_ractor_local_storage_ptr_set(rb_ractor_local_key_t key, void *ptr)
Definition: ractor.c:3207
void rb_ractor_stdout_set(VALUE out)
Definition: ractor.c:2172
enum obj_traverse_iterator_result(* rb_obj_traverse_replace_enter_func)(VALUE obj, struct obj_traverse_replace_data *data)
Definition: ractor.c:2600
#define RACTOR_UNLOCK_SELF(r)
Definition: ractor.c:103
void rb_ractor_living_threads_insert(rb_ractor_t *r, rb_thread_t *th)
Definition: ractor.c:1760
void rb_ractor_living_threads_remove(rb_ractor_t *cr, rb_thread_t *th)
Definition: ractor.c:1838
rb_ractor_local_key_t rb_ractor_local_storage_ptr_newkey(const struct rb_ractor_local_storage_type *type)
Definition: ractor.c:3097
void rb_gvl_init(rb_global_vm_lock_t *gvl)
void Init_Ractor(void)
Definition: ractor.c:2083
obj_traverse_iterator_result
traverse function
Definition: ractor.c:2207
@ traverse_cont
Definition: ractor.c:2208
@ traverse_stop
Definition: ractor.c:2210
@ traverse_skip
Definition: ractor.c:2209
void rb_ractor_dump(void)
Definition: ractor.c:2111
VALUE rb_ractor_stderr(void)
Definition: ractor.c:2148
void rb_vm_ractor_blocking_cnt_dec(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
Definition: ractor.c:1798
VALUE rb_cRactor
Definition: ractor.c:20
#define RACTOR_LOCK_SELF(r)
Definition: ractor.c:102
VALUE rb_ractor_stdin(void)
Definition: ractor.c:2124
bool rb_ractor_main_p_(void)
Definition: ractor.c:1699
void rb_ractor_finish_marking(void)
Definition: ractor.c:3215
#define CHECK_AND_REPLACE(v)
enum obj_traverse_iterator_result(* rb_obj_traverse_replace_leave_func)(VALUE obj, struct obj_traverse_replace_data *data)
Definition: ractor.c:2601
void rb_ractor_local_storage_value_set(rb_ractor_local_key_t key, VALUE val)
Definition: ractor.c:3189
void rb_ractor_atexit_exception(rb_execution_context_t *ec)
Definition: ractor.c:1675
void rb_ractor_vm_barrier_interrupt_running_thread(rb_ractor_t *r)
Definition: ractor.c:1892
void rb_ractor_send_parameters(rb_execution_context_t *ec, rb_ractor_t *r, VALUE args)
Definition: ractor.c:1690
VALUE rb_eRactorUnsafeError
Definition: ractor.c:22
void rb_ractor_blocking_threads_inc(rb_ractor_t *cr, const char *file, int line)
Definition: ractor.c:1858
bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
Definition: ractor.c:3178
VALUE rb_ractor_stdout(void)
Definition: ractor.c:2136
void rb_ractor_atexit(rb_execution_context_t *ec, VALUE result)
Definition: ractor.c:1668
void rb_thread_terminate_all(rb_thread_t *th)
Definition: thread.c:581
void rb_ractor_stderr_set(VALUE err)
Definition: ractor.c:2184
VALUE rb_ractor_thread_list(rb_ractor_t *r)
Definition: ractor.c:1727
rb_hook_list_t * rb_ractor_hooks(rb_ractor_t *cr)
Definition: ractor.c:2196
VALUE rb_ractor_make_shareable(VALUE obj)
Definition: ractor.c:2499
enum obj_traverse_iterator_result(* rb_obj_traverse_enter_func)(VALUE obj)
Definition: ractor.c:2213
void rb_ractor_terminate_interrupt_main_thread(rb_ractor_t *r)
Definition: ractor.c:1911
void rb_replace_generic_ivar(VALUE clone, VALUE obj)
Definition: variable.c:1689
#define DEFAULT_KEYS_CAPA
Definition: ractor.c:3212
VALUE rb_eRactorIsolationError
Definition: ractor.c:23
void rb_ractor_living_threads_init(rb_ractor_t *r)
Definition: ractor.c:1546
rb_ractor_local_key_t rb_ractor_local_storage_value_newkey(void)
Definition: ractor.c:3106
#define RACTOR_UNLOCK(r)
Definition: ractor.c:101
void rb_ractor_main_setup(rb_vm_t *vm, rb_ractor_t *r, rb_thread_t *th)
Definition: ractor.c:1581
bool rb_ractor_shareable_p_continue(VALUE obj)
Definition: ractor.c:2550
enum obj_traverse_iterator_result(* rb_obj_traverse_leave_func)(VALUE obj)
Definition: ractor.c:2214
void rb_ractor_receive_parameters(rb_execution_context_t *ec, rb_ractor_t *r, int len, VALUE *ptr)
Definition: ractor.c:1682
void rb_vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
Definition: ractor.c:1790
VALUE rb_ractor_local_storage_value(rb_ractor_local_key_t key)
Definition: ractor.c:3166
int rb_ractor_living_thread_num(const rb_ractor_t *r)
Definition: ractor.c:1721
#define RB_OBJ_SHAREABLE_P(obj)
Definition: ractor.h:50
rb_ractor_basket_type
Definition: ractor_core.h:11
@ basket_type_will
Definition: ractor_core.h:16
@ basket_type_copy
Definition: ractor_core.h:14
@ basket_type_deleted
Definition: ractor_core.h:17
@ basket_type_reserved
Definition: ractor_core.h:18
@ basket_type_none
Definition: ractor_core.h:12
@ basket_type_ref
Definition: ractor_core.h:13
@ basket_type_move
Definition: ractor_core.h:15
VALUE rb_thread_create_ractor(rb_ractor_t *g, VALUE args, VALUE proc)
Definition: thread.c:1130
void rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th)
#define RBASIC_CLASS
Definition: rbasic.h:35
#define DATA_PTR(obj)
Definition: rdata.h:56
#define NULL
Definition: regenc.h:69
#define RB_OBJ_WRITE(a, slot, b)
WB for new reference from ‘a’ to ‘b’.
Definition: rgengc.h:107
#define RB_OBJ_WRITTEN(a, oldv, b)
WB for new reference from ‘a’ to ‘b’.
Definition: rgengc.h:114
#define RHASH_SET_IFNONE(h, ifnone)
Definition: rhash.h:52
#define RHASH_IFNONE(h)
Definition: rhash.h:49
#define StringValueCStr(v)
Definition: rstring.h:52
#define TypedData_Wrap_Struct(klass, data_type, sval)
Definition: rtypeddata.h:101
@ RUBY_TYPED_FREE_IMMEDIATELY
Definition: rtypeddata.h:62
@ RUBY_TYPED_FROZEN_SHAREABLE
Definition: rtypeddata.h:63
#define TypedData_Make_Struct(klass, type, data_type, sval)
Definition: rtypeddata.h:122
int argc
Definition: ruby.c:240
char ** argv
Definition: ruby.c:241
unsigned int uint32_t
Definition: sha2.h:101
rb_atomic_t cnt[RUBY_NSIG]
Definition: signal.c:508
#define Qundef
#define SPECIAL_CONST_P
#define Qtrue
#define RTEST
#define Qnil
#define Qfalse
#define NIL_P
#define f
VALUE rb_sprintf(const char *,...)
Definition: sprintf.c:1203
#define realloc
Definition: st.c:172
#define malloc
Definition: st.c:170
@ ST_STOP
Definition: st.h:99
@ ST_REPLACE
Definition: st.h:99
@ ST_CONTINUE
Definition: st.h:99
unsigned long st_data_t
Definition: st.h:22
#define st_foreach
Definition: st.h:142
#define st_init_numtable
Definition: st.h:106
#define st_lookup
Definition: st.h:128
#define st_delete
Definition: st.h:118
#define st_insert
Definition: st.h:124
#define st_free_table
Definition: st.h:156
Definition: gc.c:557
struct RData data
Definition: gc.c:572
struct RClass klass
Definition: gc.c:566
VALUE flags
Definition: gc.c:560
VALUE v3
Definition: gc.c:596
VALUE v1
Definition: gc.c:594
VALUE klass
Definition: ractor.c:2897
VALUE v2
Definition: gc.c:595
Definition: gzappend.c:170
uint32_t numiv
Definition: variable.h:15
VALUE ivptr[FLEX_ARY_LEN]
Definition: variable.h:16
struct obj_traverse_data * data
Definition: ractor.c:2230
rb_obj_traverse_enter_func enter_func
Definition: ractor.c:2220
st_table * rec
Definition: ractor.c:2223
rb_obj_traverse_leave_func leave_func
Definition: ractor.c:2221
struct obj_traverse_replace_data * data
Definition: ractor.c:2617
rb_obj_traverse_replace_leave_func leave_func
Definition: ractor.c:2605
rb_obj_traverse_replace_enter_func enter_func
Definition: ractor.c:2604
rb_obj_traverse_final_func final_func
Definition: ractor.c:2397
enum rb_ractor_basket_type type
Definition: ractor_core.h:23
const struct rb_ractor_local_storage_type * type
Definition: ractor.c:3008
void(* free)(void *ptr)
Definition: ractor.h:17
void(* mark)(void *ptr)
Definition: ractor.h:16
uint32_t id
Definition: vm_core.h:1990
VALUE self
Definition: vm_core.h:1989
rb_hook_list_t hooks
Definition: vm_core.h:1991
struct rb_ractor_basket * baskets
Definition: ractor_core.h:29
unsigned int serial
Definition: ractor_core.h:33
unsigned int reserved_cnt
Definition: ractor_core.h:34
rb_nativethread_cond_t barrier_wait_cond
Definition: ractor_core.h:90
rb_global_vm_lock_t gvl
Definition: ractor_core.h:98
struct list_node vmlr_node
Definition: ractor_core.h:129
rb_thread_t * main
Definition: ractor_core.h:100
unsigned int blocking_cnt
Definition: ractor_core.h:96
unsigned int cnt
Definition: ractor_core.h:95
rb_execution_context_t * running_ec
Definition: ractor_core.h:99
struct list_head set
Definition: ractor_core.h:94
st_table * local_storage
Definition: ractor_core.h:133
struct rb_ractor_struct::@141 threads
enum rb_ractor_struct::ractor_status status_
struct rb_ractor_sync sync
Definition: ractor_core.h:85
struct rb_ractor_pub pub
Definition: ractor_core.h:83
VALUE receiving_mutex
Definition: ractor_core.h:86
struct rb_id_table * idkey_local_storage
Definition: ractor_core.h:134
rb_ractor_newobj_cache_t newobj_cache
Definition: ractor_core.h:142
struct rb_ractor_basket taken_basket
Definition: ractor_core.h:78
enum rb_ractor_sync::ractor_wait::ractor_wait_status status
enum rb_ractor_sync::ractor_wait::ractor_wakeup_status wakeup_status
struct rb_ractor_basket yielded_basket
Definition: ractor_core.h:77
struct rb_ractor_waiting_list taking_ractors
Definition: ractor_core.h:53
struct rb_ractor_sync::ractor_wait wait
bool incoming_port_closed
Definition: ractor_core.h:55
struct rb_ractor_queue incoming_queue
Definition: ractor_core.h:52
rb_nativethread_cond_t cond
Definition: ractor_core.h:49
bool outgoing_port_closed
Definition: ractor_core.h:56
rb_nativethread_lock_t lock
Definition: ractor_core.h:45
rb_ractor_t ** ractors
Definition: ractor_core.h:40
rb_execution_context_t * ec
Definition: vm_core.h:941
rb_vm_t * vm
Definition: vm_core.h:939
rb_ractor_t * ractor
Definition: vm_core.h:938
struct list_node lt_node
Definition: vm_core.h:936
struct list_head set
Definition: vm_core.h:568
struct rb_vm_struct::@194::@197 sync
struct rb_vm_struct::@194 ractor
bool terminate_waiting
Definition: vm_core.h:588
struct rb_ractor_struct * main_ractor
Definition: vm_core.h:572
unsigned int cnt
Definition: vm_core.h:569
unsigned int blocking_cnt
Definition: vm_core.h:570
rb_nativethread_cond_t terminate_cond
Definition: vm_core.h:587
struct rb_ractor_queue * rq
Definition: ractor.c:767
rb_ractor_t * cr
Definition: ractor.c:766
Definition: st.h:79
Definition: blast.c:41
Definition: string.c:7257
void rb_threadptr_interrupt(rb_thread_t *th)
Definition: thread.c:508
int rb_vm_check_ints_blocking(rb_execution_context_t *ec)
Definition: thread.c:222
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
void rb_native_cond_initialize(rb_nativethread_cond_t *cond)
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
void rb_native_cond_destroy(rb_nativethread_cond_t *cond)
void rb_native_cond_signal(rb_nativethread_cond_t *cond)
void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
#define rb_obj_transient_heap_evacuate(x, y)
#define rb_ary_transient_heap_evacuate(x, y)
#define rb_hash_transient_heap_evacuate(x, y)
#define rb_struct_transient_heap_evacuate(x, y)
#define rb_transient_heap_evacuate()
void error(const char *msg)
Definition: untgz.c:593
#define ALLOC(size)
Definition: unzip.c:112
unsigned long VALUE
Definition: value.h:38
unsigned long ID
Definition: value.h:39
#define T_COMPLEX
Definition: value_type.h:58
#define T_FILE
Definition: value_type.h:61
#define T_STRING
Definition: value_type.h:77
#define T_FLOAT
Definition: value_type.h:63
#define T_IMEMO
Definition: value_type.h:66
#define T_BIGNUM
Definition: value_type.h:56
#define T_STRUCT
Definition: value_type.h:78
#define T_DATA
Definition: value_type.h:59
#define T_MODULE
Definition: value_type.h:69
#define T_RATIONAL
Definition: value_type.h:75
#define T_ICLASS
Definition: value_type.h:65
#define T_HASH
Definition: value_type.h:64
#define T_ARRAY
Definition: value_type.h:55
#define T_OBJECT
Definition: value_type.h:74
#define T_SYMBOL
Definition: value_type.h:79
#define T_MATCH
Definition: value_type.h:68
#define T_CLASS
Definition: value_type.h:57
#define BUILTIN_TYPE
Definition: value_type.h:84
#define T_REGEXP
Definition: value_type.h:76
int rb_ivar_generic_ivtbl_lookup(VALUE obj, struct gen_ivtbl **ivtbl)
Definition: variable.c:960
rb_ractor_t * ruby_single_main_ractor
Definition: vm.c:381
VALUE rb_proc_ractor_make_shareable(VALUE self)
Definition: vm.c:1105
@ THREAD_KILLED
Definition: vm_core.h:795
@ THREAD_STOPPED
Definition: vm_core.h:793
@ THREAD_RUNNABLE
Definition: vm_core.h:792
@ THREAD_STOPPED_FOREVER
Definition: vm_core.h:794
#define RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec)
Definition: vm_core.h:1880
#define VM_ASSERT(expr)
Definition: vm_core.h:61
struct rb_ractor_struct rb_ractor_t
Definition: vm_core.h:933
void rb_hook_list_free(rb_hook_list_t *hooks)
Definition: vm_trace.c:69
#define RUBY_VM_SET_TERMINATE_INTERRUPT(ec)
Definition: vm_core.h:1879
void rb_hook_list_mark(rb_hook_list_t *hooks)
Definition: vm_trace.c:56
void rb_vm_cond_timedwait(rb_vm_t *vm, rb_nativethread_cond_t *cond, unsigned long msec)
Definition: vm_sync.c:211
#define RUBY_DEBUG_LOG2(file, line, fmt,...)
Definition: vm_debug.h:113
#define RUBY_DEBUG_LOG(fmt,...)
Definition: vm_debug.h:112
#define RB_VM_LOCKED_P()
Definition: vm_sync.h:111
#define RB_VM_LOCK_ENTER_NO_BARRIER()
Definition: vm_sync.h:125
#define RB_VM_LOCK()
Definition: vm_sync.h:113
#define ASSERT_vm_locking()
Definition: vm_sync.h:134
#define RB_VM_UNLOCK()
Definition: vm_sync.h:114
#define RB_VM_LOCK_LEAVE_NO_BARRIER()
Definition: vm_sync.h:126
#define RB_VM_LOCK_ENTER()
Definition: vm_sync.h:121
#define RB_VM_LOCK_LEAVE()
Definition: vm_sync.h:122
#define ASSERT_vm_unlocking()
Definition: vm_sync.h:135
int err
Definition: win32.c:142