8#include "eval_intern.h"
10#include "ractor_core.h"
11#include "internal/complex.h"
12#include "internal/error.h"
13#include "internal/gc.h"
14#include "internal/hash.h"
15#include "internal/object.h"
16#include "internal/ractor.h"
17#include "internal/rational.h"
18#include "internal/struct.h"
19#include "internal/thread.h"
24static VALUE rb_cRactorSelector;
26VALUE rb_eRactorUnsafeError;
27VALUE rb_eRactorIsolationError;
28static VALUE rb_eRactorError;
29static VALUE rb_eRactorRemoteError;
30static VALUE rb_eRactorMovedError;
31static VALUE rb_eRactorClosedError;
32static VALUE rb_cRactorMovedObject;
34static void vm_ractor_blocking_cnt_inc(
rb_vm_t *vm,
rb_ractor_t *r,
const char *file,
int line);
41#if RACTOR_CHECK_MODE > 0
43 if (ec != NULL && r->sync.locked_by == rb_ractor_self(rb_ec_ractor_ptr(ec))) {
44 rb_bug(
"recursive ractor locking");
52#if RACTOR_CHECK_MODE > 0
54 if (ec != NULL && r->sync.locked_by != rb_ractor_self(rb_ec_ractor_ptr(ec))) {
55 rp(r->sync.locked_by);
56 rb_bug(
"ractor lock is not acquired.");
62ractor_lock(
rb_ractor_t *r,
const char *file,
int line)
64 RUBY_DEBUG_LOG2(file, line,
"locking r:%u%s", r->pub.id, rb_current_ractor_raw(
false) == r ?
" (self)" :
"");
66 ASSERT_ractor_unlocking(r);
69#if RACTOR_CHECK_MODE > 0
70 if (rb_current_execution_context(
false) != NULL) {
72 r->sync.locked_by = cr ? rb_ractor_self(cr) :
Qundef;
76 RUBY_DEBUG_LOG2(file, line,
"locked r:%u%s", r->pub.id, rb_current_ractor_raw(
false) == r ?
" (self)" :
"");
80ractor_lock_self(
rb_ractor_t *cr,
const char *file,
int line)
82 VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline()));
83#if RACTOR_CHECK_MODE > 0
84 VM_ASSERT(cr->sync.locked_by != cr->pub.self);
86 ractor_lock(cr, file, line);
90ractor_unlock(
rb_ractor_t *r,
const char *file,
int line)
92 ASSERT_ractor_locking(r);
93#if RACTOR_CHECK_MODE > 0
94 r->sync.locked_by =
Qnil;
98 RUBY_DEBUG_LOG2(file, line,
"r:%u%s", r->pub.id, rb_current_ractor_raw(
false) == r ?
" (self)" :
"");
102ractor_unlock_self(
rb_ractor_t *cr,
const char *file,
int line)
104 VM_ASSERT(cr == rb_ec_ractor_ptr(rb_current_ec_noinline()));
105#if RACTOR_CHECK_MODE > 0
106 VM_ASSERT(cr->sync.locked_by == cr->pub.self);
108 ractor_unlock(cr, file, line);
111#define RACTOR_LOCK(r) ractor_lock(r, __FILE__, __LINE__)
112#define RACTOR_UNLOCK(r) ractor_unlock(r, __FILE__, __LINE__)
113#define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
114#define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
125 RACTOR_UNLOCK_SELF(r);
131ractor_status_str(
enum ractor_status status)
134 case ractor_created:
return "created";
135 case ractor_running:
return "running";
136 case ractor_blocking:
return "blocking";
137 case ractor_terminated:
return "terminated";
139 rb_bug(
"unreachable");
143ractor_status_set(
rb_ractor_t *r,
enum ractor_status status)
145 RUBY_DEBUG_LOG(
"r:%u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
148 if (r->status_ != ractor_created) {
149 VM_ASSERT(r == GET_RACTOR());
154 switch (r->status_) {
156 VM_ASSERT(status == ractor_blocking);
159 VM_ASSERT(status == ractor_blocking||
160 status == ractor_terminated);
162 case ractor_blocking:
163 VM_ASSERT(status == ractor_running);
165 case ractor_terminated:
166 rb_bug(
"unreachable");
174ractor_status_p(
rb_ractor_t *r,
enum ractor_status status)
176 return rb_ractor_status_p(r, status);
182static void ractor_local_storage_mark(
rb_ractor_t *r);
183static void ractor_local_storage_free(
rb_ractor_t *r);
188 for (
int i=0; i<rq->cnt; i++) {
190 rb_gc_mark(b->sender);
193 case basket_type_yielding:
194 case basket_type_take_basket:
195 case basket_type_deleted:
196 case basket_type_reserved:
200 rb_gc_mark(b->p.send.v);
206ractor_mark(
void *ptr)
210 ractor_queue_mark(&r->sync.recv_queue);
211 ractor_queue_mark(&r->sync.takers_queue);
215 rb_gc_mark(r->r_stdin);
216 rb_gc_mark(r->r_stdout);
217 rb_gc_mark(r->r_stderr);
218 rb_hook_list_mark(&r->pub.hooks);
220 if (r->threads.cnt > 0) {
222 ccan_list_for_each(&r->threads.set, th, lt_node) {
223 VM_ASSERT(th != NULL);
224 rb_gc_mark(th->self);
228 ractor_local_storage_mark(r);
238ractor_free(
void *ptr)
241 RUBY_DEBUG_LOG(
"free r:%d", rb_ractor_id(r));
243 ractor_queue_free(&r->sync.recv_queue);
244 ractor_queue_free(&r->sync.takers_queue);
245 ractor_local_storage_free(r);
246 rb_hook_list_free(&r->pub.hooks);
248 if (r->newobj_cache) {
251 rb_gc_ractor_cache_free(r->newobj_cache);
252 r->newobj_cache = NULL;
265ractor_memsize(
const void *ptr)
271 ractor_queue_memsize(&r->sync.recv_queue) +
272 ractor_queue_memsize(&r->sync.takers_queue);
283 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
298RACTOR_PTR(
VALUE self)
300 VM_ASSERT(rb_ractor_p(self));
307#if RACTOR_CHECK_MODE > 0
309rb_ractor_current_id(
void)
311 if (GET_THREAD()->ractor == NULL) {
315 return rb_ractor_id(GET_RACTOR());
334 if (r != NULL) ASSERT_ractor_locking(r);
335 return &rq->baskets[rq->start];
341 if (r != NULL) ASSERT_ractor_locking(r);
342 return &rq->baskets[(rq->start + i) % rq->size];
348 ASSERT_ractor_locking(r);
350 if (rq->reserved_cnt == 0) {
352 rq->start = (rq->start + 1) % rq->size;
356 ractor_queue_at(r, rq, 0)->type.e = basket_type_deleted;
364 return basket_type_p(b, basket_type_deleted) ||
365 basket_type_p(b, basket_type_reserved);
371 ASSERT_ractor_locking(r);
373 while (rq->cnt > 0 && basket_type_p(ractor_queue_at(r, rq, 0), basket_type_deleted)) {
374 ractor_queue_advance(r, rq);
381 ASSERT_ractor_locking(r);
387 ractor_queue_compact(r, rq);
389 for (
int i=0; i<rq->cnt; i++) {
390 if (!ractor_queue_skip_p(r, rq, i)) {
401 ASSERT_ractor_locking(r);
403 for (
int i=0; i<rq->cnt; i++) {
404 if (!ractor_queue_skip_p(r, rq, i)) {
409 b->type.e = basket_type_deleted;
410 ractor_queue_compact(r, rq);
421 ASSERT_ractor_locking(r);
423 if (rq->size <= rq->cnt) {
424 rq->baskets = realloc(rq->baskets,
sizeof(
struct rb_ractor_basket) * rq->size * 2);
425 for (
int i=rq->size - rq->start; i<rq->cnt; i++) {
426 rq->baskets[i + rq->start] = rq->baskets[i + rq->start - rq->size];
431 rq->baskets[(rq->start + rq->cnt++) % rq->size] = *basket;
438 basket->type.e = basket_type_deleted;
443static VALUE ractor_reset_belonging(
VALUE obj);
449 case basket_type_ref:
451 case basket_type_copy:
452 case basket_type_move:
453 case basket_type_will:
454 b->type.e = basket_type_ref;
455 b->p.send.v = ractor_reset_belonging(b->p.send.v);
458 rb_bug(
"unreachable");
467 VALUE v = ractor_basket_value(b);
470 if (b->p.send.exception) {
474 rb_ec_setup_exception(NULL, err, cause);
483#if USE_RUBY_DEBUG_LOG
485wait_status_str(
enum rb_ractor_wait_status wait_status)
487 switch ((
int)wait_status) {
488 case wait_none:
return "none";
489 case wait_receiving:
return "receiving";
490 case wait_taking:
return "taking";
491 case wait_yielding:
return "yielding";
492 case wait_receiving|wait_taking:
return "receiving|taking";
493 case wait_receiving|wait_yielding:
return "receiving|yielding";
494 case wait_taking|wait_yielding:
return "taking|yielding";
495 case wait_receiving|wait_taking|wait_yielding:
return "receiving|taking|yielding";
497 rb_bug(
"unreachable");
501wakeup_status_str(
enum rb_ractor_wakeup_status wakeup_status)
503 switch (wakeup_status) {
504 case wakeup_none:
return "none";
505 case wakeup_by_send:
return "by_send";
506 case wakeup_by_yield:
return "by_yield";
507 case wakeup_by_take:
return "by_take";
508 case wakeup_by_close:
return "by_close";
509 case wakeup_by_interrupt:
return "by_interrupt";
510 case wakeup_by_retry:
return "by_retry";
512 rb_bug(
"unreachable");
516basket_type_name(
enum rb_ractor_basket_type
type)
519 case basket_type_none:
return "none";
520 case basket_type_ref:
return "ref";
521 case basket_type_copy:
return "copy";
522 case basket_type_move:
return "move";
523 case basket_type_will:
return "will";
524 case basket_type_deleted:
return "deleted";
525 case basket_type_reserved:
return "reserved";
526 case basket_type_take_basket:
return "take_basket";
527 case basket_type_yielding:
return "yielding";
538 if ((th->ractor_waiting.wait_status & wait_status) && th->ractor_waiting.wakeup_status == wakeup_none) {
543 ccan_list_for_each(&r->sync.wait.waiting_threads, th, ractor_waiting.waiting_node) {
544 if ((th->ractor_waiting.wait_status & wait_status) && th->ractor_waiting.wakeup_status == wakeup_none) {
552#ifdef RUBY_THREAD_PTHREAD_H
562 ASSERT_ractor_locking(r);
574ractor_wakeup(
rb_ractor_t *r,
rb_thread_t *th ,
enum rb_ractor_wait_status wait_status,
enum rb_ractor_wakeup_status wakeup_status)
576 ASSERT_ractor_locking(r);
578 RUBY_DEBUG_LOG(
"r:%u wait_by:%s -> wait:%s wakeup:%s",
580 wait_status_str(th->ractor_waiting.wait_status),
581 wait_status_str(wait_status),
582 wakeup_status_str(wakeup_status));
584 if ((th = ractor_sleeping_by(r, th, wait_status)) != NULL) {
585 th->ractor_waiting.wakeup_status = wakeup_status;
586 rb_ractor_sched_wakeup(r, th);
597ractor_sleep_interrupt(
void *ptr)
605 ractor_wakeup(r, th, wait_receiving | wait_taking | wait_yielding, wakeup_by_interrupt);
610typedef void (*ractor_sleep_cleanup_function)(
rb_ractor_t *cr,
void *p);
617 if (cur_th->ractor_waiting.wait_status != wait_none) {
618 enum rb_ractor_wait_status prev_wait_status = cur_th->ractor_waiting.wait_status;
619 cur_th->ractor_waiting.wait_status = wait_none;
620 cur_th->ractor_waiting.wakeup_status = wakeup_by_interrupt;
625 enum ruby_tag_type state;
627 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
628 rb_ec_check_ints(ec);
633 (*cf_func)(cr, cf_data);
634 EC_JUMP_TAG(ec, state);
638 rb_ec_check_ints(ec);
643 cur_th->ractor_waiting.wait_status = prev_wait_status;
647#ifdef RUBY_THREAD_PTHREAD_H
654#if RACTOR_CHECK_MODE > 0
655 VALUE locked_by = r->sync.locked_by;
656 r->sync.locked_by =
Qnil;
660#if RACTOR_CHECK_MODE > 0
661 r->sync.locked_by = locked_by;
666ractor_sleep_wo_gvl(
void *ptr)
670 VM_ASSERT(GET_EC() == ec);
672 RACTOR_LOCK_SELF(cr);
674 VM_ASSERT(cur_th->ractor_waiting.wait_status != wait_none);
677 if (cur_th->ractor_waiting.wakeup_status == wakeup_none) {
678 cur_th->status = THREAD_STOPPED_FOREVER;
679 ractor_cond_wait(cr, cur_th);
680 cur_th->status = THREAD_RUNNABLE;
681 VM_ASSERT(cur_th->ractor_waiting.wakeup_status != wakeup_none);
683 RUBY_DEBUG_LOG(
"rare timing, no cond wait");
685 cur_th->ractor_waiting.wait_status = wait_none;
687 RACTOR_UNLOCK_SELF(cr);
694 ASSERT_ractor_locking(cr);
696 struct ccan_list_node *waitn = &th->ractor_waiting.waiting_node;
697 VM_ASSERT(waitn->next == waitn->prev && waitn->next == waitn);
698 ccan_list_add(&cr->sync.wait.waiting_threads, waitn);
704 ccan_list_del_init(waitn);
716static enum rb_ractor_wakeup_status
718 ractor_sleep_cleanup_function cf_func,
void *cf_data)
720 ASSERT_ractor_locking(cr);
721 enum rb_ractor_wakeup_status wakeup_status;
722 VM_ASSERT(GET_RACTOR() == cr);
724 VM_ASSERT(cur_th->ractor_waiting.wait_status == wait_none);
725 VM_ASSERT(wait_status != wait_none);
726 cur_th->ractor_waiting.wait_status = wait_status;
727 cur_th->ractor_waiting.wakeup_status = wakeup_none;
732 RUBY_DEBUG_LOG(
"sleep by %s", wait_status_str(wait_status));
734 while (cur_th->ractor_waiting.wakeup_status == wakeup_none) {
735 rb_ractor_sched_sleep(ec, cr, ractor_sleep_interrupt);
736 ractor_check_ints(ec, cr, cur_th, cf_func, cf_data);
739 cur_th->ractor_waiting.wait_status = wait_none;
741 wakeup_status = cur_th->ractor_waiting.wakeup_status;
742 cur_th->ractor_waiting.wakeup_status = wakeup_none;
744 RUBY_DEBUG_LOG(
"wakeup %s", wakeup_status_str(wakeup_status));
746 ASSERT_ractor_locking(cr);
747 return wakeup_status;
750static enum rb_ractor_wakeup_status
753 return ractor_sleep_with_cleanup(ec, cr, cur_th, wait_status, 0, NULL);
761 if (th->ractor_waiting.receiving_mutex && rb_mutex_owned_p(th->ractor_waiting.receiving_mutex)) {
762 rb_raise(rb_eRactorError,
"can not call receive/receive_if recursively");
770 ractor_recursive_receive_if(rb_ec_thread_ptr(ec));
771 bool received =
false;
773 RACTOR_LOCK_SELF(cr);
775 RUBY_DEBUG_LOG(
"rq->cnt:%d", rq->cnt);
776 received = ractor_queue_deq(cr, rq, &basket);
778 RACTOR_UNLOCK_SELF(cr);
781 if (cr->sync.incoming_port_closed) {
782 rb_raise(rb_eRactorClosedError,
"The incoming port is already closed");
787 return ractor_basket_accept(&basket);
794 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
796 ractor_recursive_receive_if(cur_th);
800 while (ractor_queue_empty_p(cr, rq) && !cr->sync.incoming_port_closed) {
801 ractor_sleep(ec, cr, cur_th, wait_receiving);
810 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
814 while (UNDEF_P(v = ractor_try_receive(ec, cr, rq))) {
815 ractor_wait_receive(ec, cr, rq);
826 for (
int i=0; i<rq->cnt; i++) {
828 fprintf(stderr,
"%d (start:%d) type:%s %p %s\n", i, rq->start, basket_type_name(b->type),
830 if (basket_type_p(b, basket_type_reserved) bug =
true;
832 if (bug) rb_bug(
"!!");
848 VALUE m = th->ractor_waiting.receiving_mutex;
850 m = th->ractor_waiting.receiving_mutex =
rb_mutex_new();
856receive_if_body(
VALUE ptr)
860 ractor_receive_if_lock(data->th);
864 RACTOR_LOCK_SELF(cr);
867 VM_ASSERT(basket_type_p(b, basket_type_reserved));
868 data->rq->reserved_cnt--;
870 if (
RTEST(block_result)) {
871 ractor_queue_delete(cr, data->rq, b);
872 ractor_queue_compact(cr, data->rq);
875 b->type.e = basket_type_ref;
878 RACTOR_UNLOCK_SELF(cr);
880 data->success =
true;
882 if (
RTEST(block_result)) {
891receive_if_ensure(
VALUE v)
897 if (!data->success) {
898 RACTOR_LOCK_SELF(cr);
901 VM_ASSERT(basket_type_p(b, basket_type_reserved));
902 b->type.e = basket_type_deleted;
903 data->rq->reserved_cnt--;
905 RACTOR_UNLOCK_SELF(cr);
915 if (!
RTEST(b)) rb_raise(rb_eArgError,
"no block given");
919 unsigned int serial = (
unsigned int)-1;
926 ractor_wait_receive(ec, cr, rq);
928 RACTOR_LOCK_SELF(cr);
930 if (serial != rq->serial) {
936 for (
int i=index; i<rq->cnt; i++) {
937 if (!ractor_queue_skip_p(cr, rq, i)) {
939 v = ractor_basket_value(b);
940 b->type.e = basket_type_reserved;
947 RACTOR_UNLOCK_SELF(cr);
960 receive_if_ensure, (
VALUE)&data);
962 if (!UNDEF_P(result))
return result;
966 RUBY_VM_CHECK_INTS(ec);
977 if (r->sync.incoming_port_closed) {
981 ractor_queue_enq(r, &r->sync.recv_queue, b);
983 ractor_wakeup(r, NULL, wait_receiving, wakeup_by_send);
989 rb_raise(rb_eRactorClosedError,
"The incoming-port is already closed");
999ractor_basket_prepare_contents(
VALUE obj,
VALUE move,
volatile VALUE *pobj,
enum rb_ractor_basket_type *ptype)
1002 enum rb_ractor_basket_type
type;
1005 type = basket_type_ref;
1008 else if (!
RTEST(move)) {
1009 v = ractor_copy(obj);
1010 type = basket_type_copy;
1013 type = basket_type_move;
1014 v = ractor_move(obj);
1024 VM_ASSERT(cr == GET_RACTOR());
1026 basket->sender = cr->pub.self;
1027 basket->sending_th = cur_th;
1028 basket->p.send.exception = exc;
1029 basket->p.send.v = obj;
1036 enum rb_ractor_basket_type
type;
1037 ractor_basket_prepare_contents(obj, move, &v, &
type);
1038 ractor_basket_fill_(cr, cur_th, basket, v, exc);
1039 basket->type.e =
type;
1045 ractor_basket_fill_(cr, cur_th, basket, obj, exc);
1046 basket->type.e = basket_type_will;
1056 ractor_basket_fill(cr, cur_th, &basket, obj, move,
false);
1057 ractor_send_basket(ec, recv_r, &basket);
1058 return recv_r->pub.self;
1066 ASSERT_ractor_locking(r);
1068 return basket_type_p(&r->sync.will_basket, basket_type_will);
1074 ASSERT_ractor_locking(r);
1076 if (ractor_take_has_will(r)) {
1077 *b = r->sync.will_basket;
1078 r->sync.will_basket.type.e = basket_type_none;
1082 VM_ASSERT(basket_type_p(&r->sync.will_basket, basket_type_none));
1090 ASSERT_ractor_unlocking(r);
1095 taken = ractor_take_will(r, b);
1107 .type.e = basket_type_take_basket,
1108 .sender = cr->pub.self,
1109 .sending_th = cur_th,
1112 .basket = take_basket,
1117 bool closed =
false;
1121 if (is_take && ractor_take_will(r, take_basket)) {
1122 RUBY_DEBUG_LOG(
"take over a will of r:%d", rb_ractor_id(r));
1124 else if (!is_take && ractor_take_has_will(r)) {
1125 RUBY_DEBUG_LOG(
"has_will");
1126 VM_ASSERT(config != NULL);
1127 config->closed =
true;
1129 else if (r->sync.outgoing_port_closed) {
1133 RUBY_DEBUG_LOG(
"register in r:%d", rb_ractor_id(r));
1134 ractor_queue_enq(r, &r->sync.takers_queue, &b);
1136 if (basket_none_p(take_basket)) {
1138 ractor_wakeup(r, NULL, wait_yielding, wakeup_by_take);
1145 if (!ignore_error) rb_raise(rb_eRactorClosedError,
"The outgoing-port is already closed");
1157 bool deleted =
false;
1161 if (r->sync.outgoing_port_closed) {
1165 for (
int i=0; i<ts->cnt; i++) {
1167 if (basket_type_p(b, basket_type_take_basket) && b->p.take.basket == take_basket) {
1168 ractor_queue_delete(r, ts, b);
1173 ractor_queue_compact(r, ts);
1187 RACTOR_LOCK_SELF(cr);
1190 if (basket_none_p(take_basket) || basket_type_p(take_basket, basket_type_yielding)) {
1197 RACTOR_UNLOCK_SELF(cr);
1200 RUBY_DEBUG_LOG(
"taken");
1201 if (basket_type_p(take_basket, basket_type_deleted)) {
1202 VM_ASSERT(recv_r->sync.outgoing_port_closed);
1203 rb_raise(rb_eRactorClosedError,
"The outgoing-port is already closed");
1205 return ractor_basket_accept(take_basket);
1208 RUBY_DEBUG_LOG(
"not taken");
1214#if VM_CHECK_MODE > 0
1223 for (
int i=0; i<ts->cnt; i++) {
1225 if (basket_type_p(b, basket_type_take_basket) && b->p.take.basket == tb) {
1242 if (basket_none_p(tb)) {
1243 if (!ractor_deregister_take(r, tb)) {
1250 VM_ASSERT(!ractor_check_specific_take_basket_lock(r, tb));
1260ractor_wait_take_cleanup(
rb_ractor_t *cr,
void *ptr)
1263 ractor_take_cleanup(cr, data->r, data->tb);
1274 RACTOR_LOCK_SELF(cr);
1276 if (basket_none_p(take_basket) || basket_type_p(take_basket, basket_type_yielding)) {
1277 ractor_sleep_with_cleanup(ec, cr, cur_th, wait_taking, ractor_wait_take_cleanup, &data);
1280 RACTOR_UNLOCK_SELF(cr);
1286 RUBY_DEBUG_LOG(
"from r:%u", rb_ractor_id(recv_r));
1292 .type.e = basket_type_none,
1296 ractor_register_take(cr, cur_th, recv_r, &take_basket,
true, NULL,
false);
1298 while (UNDEF_P(v = ractor_try_take(cr, cur_th, recv_r, &take_basket))) {
1299 ractor_wait_take(ec, cr, cur_th, recv_r, &take_basket);
1302 VM_ASSERT(!basket_none_p(&take_basket));
1303 VM_ASSERT(!ractor_check_specific_take_basket_lock(recv_r, &take_basket));
1313 ASSERT_ractor_locking(cr);
1315 for (
int i=0; i<rs->cnt; i++) {
1317 if (basket_type_p(b, basket_type_take_basket) &&
1318 basket_none_p(b->p.take.basket)) {
1330 ASSERT_ractor_unlocking(cr);
1334 RACTOR_LOCK_SELF(cr);
1336 while (ractor_queue_deq(cr, rs, b)) {
1337 if (basket_type_p(b, basket_type_take_basket)) {
1340 if (
RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_yielding) == basket_type_none) {
1345 ractor_queue_enq(cr, rs, b);
1346 if (first_tb == NULL) first_tb = tb;
1348 VM_ASSERT(head != NULL);
1349 if (basket_type_p(head, basket_type_take_basket) && head->p.take.basket == first_tb) {
1355 VM_ASSERT(basket_none_p(b));
1359 if (found && b->p.take.config && !b->p.take.config->oneshot) {
1360 ractor_queue_enq(cr, rs, b);
1363 RACTOR_UNLOCK_SELF(cr);
1374 ASSERT_ractor_unlocking(cr);
1379 if (ractor_deq_take_basket(cr, ts, &b)) {
1380 VM_ASSERT(basket_type_p(&b, basket_type_take_basket));
1381 VM_ASSERT(basket_type_p(b.p.take.basket, basket_type_yielding));
1386 enum rb_ractor_basket_type
type;
1388 RUBY_DEBUG_LOG(
"basket from r:%u", rb_ractor_id(
tr));
1391 type = basket_type_will;
1394 enum ruby_tag_type state;
1398 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1400 ractor_basket_prepare_contents(obj, move, &obj, &
type);
1405 RACTOR_LOCK_SELF(cr);
1407 b.p.take.basket->type.e = basket_type_none;
1408 ractor_queue_enq(cr, ts, &b);
1410 RACTOR_UNLOCK_SELF(cr);
1411 EC_JUMP_TAG(ec, state);
1417 VM_ASSERT(basket_type_p(tb, basket_type_yielding));
1419 RUBY_DEBUG_LOG(
"fill %sbasket from r:%u", is_will ?
"will " :
"", rb_ractor_id(
tr));
1420 ractor_basket_fill_(cr, cur_th, tb, obj, exc);
1421 if (
RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_yielding,
type) != basket_type_yielding) {
1422 rb_bug(
"unreachable");
1424 ractor_wakeup(
tr, tr_th, wait_taking, wakeup_by_yield);
1430 else if (cr->sync.outgoing_port_closed) {
1431 rb_raise(rb_eRactorClosedError,
"The outgoing-port is already closed");
1434 RUBY_DEBUG_LOG(
"no take basket");
1443 RACTOR_LOCK_SELF(cr);
1445 while (!ractor_check_take_basket(cr, ts) && !cr->sync.outgoing_port_closed) {
1446 ractor_sleep(ec, cr, cur_th, wait_yielding);
1449 RACTOR_UNLOCK_SELF(cr);
1458 while (!ractor_try_yield(ec, cr, ts, obj, move,
false,
false)) {
1459 ractor_wait_yield(ec, cr, ts);
1474ractor_selector_mark_ractors_i(st_data_t key, st_data_t value, st_data_t data)
1477 rb_gc_mark(r->pub.self);
1482ractor_selector_mark(
void *ptr)
1486 if (s->take_ractors) {
1487 st_foreach(s->take_ractors, ractor_selector_mark_ractors_i, 0);
1490 switch (s->take_basket.type.e) {
1491 case basket_type_ref:
1492 case basket_type_copy:
1493 case basket_type_move:
1494 case basket_type_will:
1495 rb_gc_mark(s->take_basket.sender);
1496 rb_gc_mark(s->take_basket.p.send.v);
1504ractor_selector_release_i(st_data_t key, st_data_t val, st_data_t data)
1509 if (!config->closed) {
1510 ractor_deregister_take((
rb_ractor_t *)key, &s->take_basket);
1517ractor_selector_free(
void *ptr)
1520 st_foreach(s->take_ractors, ractor_selector_release_i, (st_data_t)s);
1521 st_free_table(s->take_ractors);
1526ractor_selector_memsize(
const void *ptr)
1530 st_memsize(s->take_ractors) +
1537 ractor_selector_mark,
1538 ractor_selector_free,
1539 ractor_selector_memsize,
1542 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
1546RACTOR_SELECTOR_PTR(
VALUE selv)
1556ractor_selector_create(
VALUE klass)
1560 s->take_basket.type.e = basket_type_reserved;
1561 s->take_ractors = st_init_numtable();
1577 if (!rb_ractor_p(rv)) {
1578 rb_raise(rb_eArgError,
"Not a ractor object");
1584 if (st_lookup(s->take_ractors, (st_data_t)r, NULL)) {
1585 rb_raise(rb_eArgError,
"already added");
1589 VM_ASSERT(config != NULL);
1590 config->closed =
false;
1591 config->oneshot =
false;
1593 if (ractor_register_take(GET_RACTOR(), GET_THREAD(), r, &s->take_basket,
false, config,
true)) {
1594 st_insert(s->take_ractors, (st_data_t)r, (st_data_t)config);
1611 if (!rb_ractor_p(rv)) {
1612 rb_raise(rb_eArgError,
"Not a ractor object");
1618 RUBY_DEBUG_LOG(
"r:%u", rb_ractor_id(r));
1620 if (!st_lookup(s->take_ractors, (st_data_t)r, NULL)) {
1621 rb_raise(rb_eArgError,
"not added yet");
1624 ractor_deregister_take(r, &s->take_basket);
1626 st_delete(s->take_ractors, (st_data_t *)&r, (st_data_t *)&config);
1640ractor_selector_clear_i(st_data_t key, st_data_t val, st_data_t data)
1644 ractor_selector_remove(selv, r->pub.self);
1655ractor_selector_clear(
VALUE selv)
1659 st_foreach(s->take_ractors, ractor_selector_clear_i, (st_data_t)selv);
1660 st_clear(s->take_ractors);
1671ractor_selector_empty_p(
VALUE selv)
1674 return s->take_ractors->num_entries == 0 ?
Qtrue :
Qfalse;
1678ractor_selector_wait_i(st_data_t key, st_data_t val, st_data_t dat)
1684 if (!basket_none_p(tb)) {
1685 RUBY_DEBUG_LOG(
"already taken:%s", basket_type_name(tb->type.e));
1691 if (basket_type_p(&r->sync.will_basket, basket_type_will)) {
1692 RUBY_DEBUG_LOG(
"r:%u has will", rb_ractor_id(r));
1694 if (
RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_will) == basket_type_none) {
1695 ractor_take_will(r, tb);
1699 RUBY_DEBUG_LOG(
"has will, but already taken (%s)", basket_type_name(tb->type.e));
1703 else if (r->sync.outgoing_port_closed) {
1704 RUBY_DEBUG_LOG(
"r:%u is closed", rb_ractor_id(r));
1706 if (
RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_deleted) == basket_type_none) {
1707 tb->sender = r->pub.self;
1711 RUBY_DEBUG_LOG(
"closed, but already taken (%s)", basket_type_name(tb->type.e));
1716 RUBY_DEBUG_LOG(
"wakeup r:%u", rb_ractor_id(r));
1717 ractor_wakeup(r, NULL, wait_yielding, wakeup_by_take);
1730ractor_selector_wait_cleanup(
rb_ractor_t *cr,
void *ptr)
1734 RACTOR_LOCK_SELF(cr);
1736 while (basket_type_p(tb, basket_type_yielding)) {
1737 RACTOR_UNLOCK_SELF(cr);
1741 RACTOR_LOCK_SELF(cr);
1744 tb->type.e = basket_type_reserved;
1746 RACTOR_UNLOCK_SELF(cr);
1759 bool do_receive = !!
RTEST(do_receivev);
1760 bool do_yield = !!
RTEST(do_yieldv);
1762 enum rb_ractor_wait_status wait_status;
1766 RUBY_DEBUG_LOG(
"start");
1769 RUBY_DEBUG_LOG(
"takers:%ld", s->take_ractors->num_entries);
1772 wait_status = wait_none;
1773 if (s->take_ractors->num_entries > 0) wait_status |= wait_taking;
1774 if (do_receive) wait_status |= wait_receiving;
1775 if (do_yield) wait_status |= wait_yielding;
1777 RUBY_DEBUG_LOG(
"wait:%s", wait_status_str(wait_status));
1779 if (wait_status == wait_none) {
1780 rb_raise(rb_eRactorError,
"no taking ractors");
1784 if (do_receive && !UNDEF_P(ret_v = ractor_try_receive(ec, cr, rq))) {
1785 ret_r =
ID2SYM(rb_intern(
"receive"));
1790 if (do_yield && ractor_try_yield(ec, cr, ts, yield_value, move,
false,
false)) {
1792 ret_r =
ID2SYM(rb_intern(
"yield"));
1797 VM_ASSERT(basket_type_p(&s->take_basket, basket_type_reserved));
1798 s->take_basket.type.e = basket_type_none;
1800 st_foreach(s->take_ractors, ractor_selector_wait_i, (st_data_t)tb);
1802 RACTOR_LOCK_SELF(cr);
1806 if (!basket_none_p(tb)) {
1807 RUBY_DEBUG_LOG(
"taken:%s from r:%u", basket_type_name(tb->type.e),
1808 tb->sender ? rb_ractor_id(RACTOR_PTR(tb->sender)) : 0);
1811 if (do_receive && !ractor_queue_empty_p(cr, rq)) {
1812 RUBY_DEBUG_LOG(
"can receive (%d)", rq->cnt);
1815 if (do_yield && ractor_check_take_basket(cr, ts)) {
1816 RUBY_DEBUG_LOG(
"can yield");
1820 ractor_sleep_with_cleanup(ec, cr, cur_th, wait_status, ractor_selector_wait_cleanup, tb);
1827 if (taken_basket.type.e == basket_type_yielding ||
1828 RUBY_ATOMIC_CAS(tb->type.atomic, taken_basket.type.e, basket_type_reserved) != taken_basket.type.e) {
1830 if (basket_type_p(tb, basket_type_yielding)) {
1831 RACTOR_UNLOCK_SELF(cr);
1835 RACTOR_LOCK_SELF(cr);
1840 RACTOR_UNLOCK_SELF(cr);
1843 switch (taken_basket.type.e) {
1844 case basket_type_none:
1845 VM_ASSERT(do_receive || do_yield);
1847 case basket_type_yielding:
1848 rb_bug(
"unreachable");
1849 case basket_type_deleted: {
1850 ractor_selector_remove(selv, taken_basket.sender);
1853 if (ractor_take_will_lock(r, &taken_basket)) {
1854 RUBY_DEBUG_LOG(
"has_will");
1857 RUBY_DEBUG_LOG(
"no will");
1864 case basket_type_will:
1866 ractor_selector_remove(selv, taken_basket.sender);
1872 RUBY_DEBUG_LOG(
"taken_basket:%s", basket_type_name(taken_basket.type.e));
1874 ret_v = ractor_basket_accept(&taken_basket);
1875 ret_r = taken_basket.sender;
1877 return rb_ary_new_from_args(2, ret_r, ret_v);
1887ractor_selector_wait(
int argc,
VALUE *argv,
VALUE selector)
1893 keywords[0] = rb_intern(
"receive");
1894 keywords[1] = rb_intern(
"yield_value");
1895 keywords[2] = rb_intern(
"move");
1898 rb_get_kwargs(options, keywords, 0, numberof(values), values);
1899 return ractor_selector__wait(selector,
1901 values[1] !=
Qundef, values[1], values[2]);
1905ractor_selector_new(
int argc,
VALUE *ractors,
VALUE klass)
1907 VALUE selector = ractor_selector_create(klass);
1909 for (
int i=0; i<argc; i++) {
1910 ractor_selector_add(selector, ractors[i]);
1924 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1925 result = ractor_selector__wait(selector, do_receive, do_yield, yield_value, move);
1928 if (state != TAG_NONE) {
1930 ractor_selector_clear(selector);
1933 EC_JUMP_TAG(ec, state);
1947 if (r == rb_ec_ractor_ptr(ec)) {
1948 r_th = rb_ec_thread_ptr(ec);
1953 if (!r->sync.incoming_port_closed) {
1955 r->sync.incoming_port_closed =
true;
1956 if (ractor_wakeup(r, r_th, wait_receiving, wakeup_by_close)) {
1957 VM_ASSERT(ractor_queue_empty_p(r, &r->sync.recv_queue));
1958 RUBY_DEBUG_LOG(
"cancel receiving");
1982 if (!r->sync.outgoing_port_closed) {
1984 r->sync.outgoing_port_closed =
true;
1987 VM_ASSERT(ractor_queue_empty_p(r, ts));
1992 while (ractor_queue_deq(r, ts, &b)) {
1993 if (basket_type_p(&b, basket_type_take_basket)) {
1994 tr = RACTOR_PTR(b.sender);
1998 if (
RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_yielding) == basket_type_none) {
1999 b.p.take.basket->sender = r->pub.self;
2000 if (
RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_yielding, basket_type_deleted) != basket_type_yielding) {
2001 rb_bug(
"unreachable");
2003 RUBY_DEBUG_LOG(
"set delete for r:%u", rb_ractor_id(RACTOR_PTR(b.sender)));
2006 if (b.p.take.config) {
2007 b.p.take.config->closed =
true;
2013 ractor_wakeup(
tr, tr_th, wait_taking, wakeup_by_close);
2020 ractor_wakeup(r, NULL, wait_yielding, wakeup_by_close);
2022 VM_ASSERT(ractor_queue_empty_p(r, ts));
2043 RUBY_DEBUG_LOG(
"r:%u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
2044 VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
2046 ccan_list_add_tail(&vm->ractor.set, &r->vmlr_node);
2049 if (r->newobj_cache) {
2050 VM_ASSERT(r == ruby_single_main_ractor);
2053 r->newobj_cache = rb_gc_ractor_cache_alloc(r);
2058cancel_single_ractor_mode(
void)
2061 RUBY_DEBUG_LOG(
"enable multi-ractor mode");
2063 ruby_single_main_ractor = NULL;
2070 VM_ASSERT(ractor_status_p(r, ractor_created));
2072 if (rb_multi_ractor_p()) {
2075 vm_insert_ractor0(vm, r,
false);
2076 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
2081 if (vm->ractor.cnt == 0) {
2083 vm_insert_ractor0(vm, r,
true);
2084 ractor_status_set(r, ractor_blocking);
2085 ractor_status_set(r, ractor_running);
2088 cancel_single_ractor_mode();
2089 vm_insert_ractor0(vm, r,
true);
2090 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
2098 VM_ASSERT(ractor_status_p(cr, ractor_running));
2099 VM_ASSERT(vm->ractor.cnt > 1);
2100 VM_ASSERT(cr->threads.cnt == 1);
2104 RUBY_DEBUG_LOG(
"ractor.cnt:%u-- terminate_waiting:%d",
2105 vm->ractor.cnt, vm->ractor.sync.terminate_waiting);
2107 VM_ASSERT(vm->ractor.cnt > 0);
2108 ccan_list_del(&cr->vmlr_node);
2110 if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
2115 rb_gc_ractor_cache_free(cr->newobj_cache);
2116 cr->newobj_cache = NULL;
2118 ractor_status_set(cr, ractor_terminated);
2124ractor_alloc(
VALUE klass)
2130 VM_ASSERT(ractor_status_p(r, ractor_created));
2135rb_ractor_main_alloc(
void)
2139 fprintf(stderr,
"[FATAL] failed to allocate memory for main ractor\n");
2142 r->pub.id = ++ractor_last_id;
2146 r->newobj_cache = rb_gc_ractor_cache_alloc(r);
2147 ruby_single_main_ractor = r;
2152#if defined(HAVE_WORKING_FORK)
2160 vm->ractor.blocking_cnt = 0;
2161 ruby_single_main_ractor = th->ractor;
2162 th->ractor->status_ = ractor_created;
2164 rb_ractor_living_threads_init(th->ractor);
2165 rb_ractor_living_threads_insert(th->ractor, th);
2167 VM_ASSERT(vm->ractor.blocking_cnt == 0);
2168 VM_ASSERT(vm->ractor.cnt == 1);
2174 rb_gc_ractor_cache_free(r->newobj_cache);
2175 r->newobj_cache = NULL;
2176 r->status_ = ractor_terminated;
2177 r->sync.outgoing_port_closed =
true;
2178 r->sync.incoming_port_closed =
true;
2179 r->sync.will_basket.type.e = basket_type_none;
2188 ccan_list_head_init(&r->threads.set);
2190 r->threads.blocking_cnt = 0;
2196 ractor_queue_setup(&r->sync.recv_queue);
2197 ractor_queue_setup(&r->sync.takers_queue);
2201#ifdef RUBY_THREAD_WIN32_H
2204 ccan_list_head_init(&r->sync.wait.waiting_threads);
2207 rb_thread_sched_init(&r->threads.sched,
false);
2208 rb_ractor_living_threads_init(r);
2214 enc = rb_enc_get(name);
2215 if (!rb_enc_asciicompat(enc)) {
2216 rb_raise(rb_eArgError,
"ASCII incompatible encoding (%s)",
2231 r->threads.main = th;
2232 rb_ractor_living_threads_insert(r, th);
2238 VALUE rv = ractor_alloc(self);
2240 ractor_init(r, name, loc);
2243 r->pub.id = ractor_next_id();
2244 RUBY_DEBUG_LOG(
"r:%u", r->pub.id);
2247 r->verbose = cr->verbose;
2248 r->debug = cr->debug;
2250 rb_yjit_before_ractor_spawn();
2251 rb_thread_create_ractor(r, args, block);
2261 return ractor_create(rb_current_ec_noinline(), klass, loc, name, args, block);
2267 if (cr->sync.outgoing_port_closed) {
2271 ASSERT_ractor_unlocking(cr);
2277 if (ractor_try_yield(ec, cr, ts, v,
Qfalse, exc,
true)) {
2284 if (!ractor_check_take_basket(cr, ts)) {
2285 VM_ASSERT(cur_th->ractor_waiting.wait_status == wait_none);
2286 RUBY_DEBUG_LOG(
"leave a will");
2287 ractor_basket_fill_will(cr, cur_th, &cr->sync.will_basket, v, exc);
2290 RUBY_DEBUG_LOG(
"rare timing!");
2296 if (retry)
goto retry;
2304 ractor_yield_atexit(ec, cr, result,
false);
2311 ractor_yield_atexit(ec, cr, ec->errinfo,
true);
2318 ractor_close_incoming(ec, cr);
2319 ractor_close_outgoing(ec, cr);
2324 VM_ASSERT(cr->threads.main != NULL);
2325 cr->threads.main = NULL;
2333 for (
int i=0; i<
len; i++) {
2334 ptr[i] = ractor_receive(ec, r);
2342 for (
int i=0; i<
len; i++) {
2348rb_ractor_main_p_(
void)
2350 VM_ASSERT(rb_multi_ractor_p());
2352 return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
2356rb_obj_is_main_ractor(
VALUE gv)
2358 if (!rb_ractor_p(gv))
return false;
2360 return r == GET_VM()->ractor.main_ractor;
2366 return r->threads.cnt;
2371rb_ractor_thread_list(
void)
2377 ccan_list_for_each(&r->threads.set, th, lt_node) {
2378 switch (th->status) {
2379 case THREAD_RUNNABLE:
2380 case THREAD_STOPPED:
2381 case THREAD_STOPPED_FOREVER:
2394 VM_ASSERT(th != NULL);
2398 RUBY_DEBUG_LOG(
"r(%d)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
2399 ccan_list_add_tail(&r->threads.set, &th->lt_node);
2405 if (r->threads.cnt == 1) {
2406 VM_ASSERT(ractor_status_p(r, ractor_created));
2407 vm_insert_ractor(th->vm, r);
2414 ractor_status_set(r, ractor_blocking);
2416 RUBY_DEBUG_LOG2(file, line,
"vm->ractor.blocking_cnt:%d++", vm->ractor.blocking_cnt);
2417 vm->ractor.blocking_cnt++;
2418 VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
2422rb_vm_ractor_blocking_cnt_inc(
rb_vm_t *vm,
rb_ractor_t *cr,
const char *file,
int line)
2424 ASSERT_vm_locking();
2425 VM_ASSERT(GET_RACTOR() == cr);
2426 vm_ractor_blocking_cnt_inc(vm, cr, file, line);
2430rb_vm_ractor_blocking_cnt_dec(
rb_vm_t *vm,
rb_ractor_t *cr,
const char *file,
int line)
2432 ASSERT_vm_locking();
2433 VM_ASSERT(GET_RACTOR() == cr);
2435 RUBY_DEBUG_LOG2(file, line,
"vm->ractor.blocking_cnt:%d--", vm->ractor.blocking_cnt);
2436 VM_ASSERT(vm->ractor.blocking_cnt > 0);
2437 vm->ractor.blocking_cnt--;
2439 ractor_status_set(cr, ractor_running);
2443ractor_check_blocking(
rb_ractor_t *cr,
unsigned int remained_thread_cnt,
const char *file,
int line)
2445 VM_ASSERT(cr == GET_RACTOR());
2447 RUBY_DEBUG_LOG2(file, line,
2448 "cr->threads.cnt:%u cr->threads.blocking_cnt:%u vm->ractor.cnt:%u vm->ractor.blocking_cnt:%u",
2449 cr->threads.cnt, cr->threads.blocking_cnt,
2450 GET_VM()->ractor.cnt, GET_VM()->ractor.blocking_cnt);
2452 VM_ASSERT(cr->threads.cnt >= cr->threads.blocking_cnt + 1);
2454 if (remained_thread_cnt > 0 &&
2456 cr->threads.cnt == cr->threads.blocking_cnt + 1) {
2462 rb_vm_ractor_blocking_cnt_inc(vm, cr, file, line);
2473 VM_ASSERT(cr == GET_RACTOR());
2474 RUBY_DEBUG_LOG(
"r->threads.cnt:%d--", cr->threads.cnt);
2475 ractor_check_blocking(cr, cr->threads.cnt - 1, __FILE__, __LINE__);
2477 rb_threadptr_remove(th);
2479 if (cr->threads.cnt == 1) {
2480 vm_remove_ractor(th->vm, cr);
2485 ccan_list_del(&th->lt_node);
2493rb_ractor_blocking_threads_inc(
rb_ractor_t *cr,
const char *file,
int line)
2495 RUBY_DEBUG_LOG2(file, line,
"cr->threads.blocking_cnt:%d++", cr->threads.blocking_cnt);
2497 VM_ASSERT(cr->threads.cnt > 0);
2498 VM_ASSERT(cr == GET_RACTOR());
2500 ractor_check_blocking(cr, cr->threads.cnt, __FILE__, __LINE__);
2501 cr->threads.blocking_cnt++;
2505rb_ractor_blocking_threads_dec(
rb_ractor_t *cr,
const char *file,
int line)
2507 RUBY_DEBUG_LOG2(file, line,
2508 "r->threads.blocking_cnt:%d--, r->threads.cnt:%u",
2509 cr->threads.blocking_cnt, cr->threads.cnt);
2511 VM_ASSERT(cr == GET_RACTOR());
2513 if (cr->threads.cnt == cr->threads.blocking_cnt) {
2518 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
2523 cr->threads.blocking_cnt--;
2527rb_ractor_vm_barrier_interrupt_running_thread(
rb_ractor_t *r)
2529 VM_ASSERT(r != GET_RACTOR());
2530 ASSERT_ractor_unlocking(r);
2531 ASSERT_vm_locking();
2535 if (ractor_status_p(r, ractor_running)) {
2538 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec);
2546rb_ractor_terminate_interrupt_main_thread(
rb_ractor_t *r)
2548 VM_ASSERT(r != GET_RACTOR());
2549 ASSERT_ractor_unlocking(r);
2550 ASSERT_vm_locking();
2554 if (main_th->status != THREAD_KILLED) {
2555 RUBY_VM_SET_TERMINATE_INTERRUPT(main_th->ec);
2556 rb_threadptr_interrupt(main_th);
2559 RUBY_DEBUG_LOG(
"killed (%p)", (
void *)main_th);
2567ractor_terminal_interrupt_all(
rb_vm_t *vm)
2569 if (vm->ractor.cnt > 1) {
2572 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
2573 if (r != vm->ractor.main_ractor) {
2574 RUBY_DEBUG_LOG(
"r:%d", rb_ractor_id(r));
2575 rb_ractor_terminate_interrupt_main_thread(r);
2585rb_ractor_terminate_all(
void)
2590 RUBY_DEBUG_LOG(
"ractor.cnt:%d", (
int)vm->ractor.cnt);
2592 VM_ASSERT(cr == GET_RACTOR());
2594 if (vm->ractor.cnt > 1) {
2597 ractor_terminal_interrupt_all(vm);
2601 rb_thread_terminate_all(GET_THREAD());
2605 while (vm->ractor.cnt > 1) {
2606 RUBY_DEBUG_LOG(
"terminate_waiting:%d", vm->ractor.sync.terminate_waiting);
2607 vm->ractor.sync.terminate_waiting =
true;
2610 rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
2611 rb_del_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
2612 rb_vm_cond_timedwait(vm, &vm->ractor.sync.terminate_cond, 1000 );
2613 rb_add_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
2614 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
2616 ractor_terminal_interrupt_all(vm);
2623rb_vm_main_ractor_ec(
rb_vm_t *vm)
2640 if (running_ec) {
return running_ec; }
2641 return vm->ractor.main_thread->ec;
2645ractor_moved_missing(
int argc,
VALUE *argv,
VALUE self)
2647 rb_raise(rb_eRactorMovedError,
"can not send any methods to a moved object");
2650#ifndef USE_RACTOR_SELECTOR
2651#define USE_RACTOR_SELECTOR 0
2654RUBY_SYMBOL_EXPORT_BEGIN
2655void rb_init_ractor_selector(
void);
2656RUBY_SYMBOL_EXPORT_END
2665rb_init_ractor_selector(
void)
2674 rb_define_method(rb_cRactorSelector,
"empty?", ractor_selector_empty_p, 0);
2787 rb_define_method(rb_cRactorMovedObject,
"method_missing", ractor_moved_missing, -1);
2790 rb_define_method(rb_cRactorMovedObject,
"__send__", ractor_moved_missing, -1);
2794 rb_define_method(rb_cRactorMovedObject,
"__id__", ractor_moved_missing, -1);
2795 rb_define_method(rb_cRactorMovedObject,
"equal?", ractor_moved_missing, -1);
2796 rb_define_method(rb_cRactorMovedObject,
"instance_eval", ractor_moved_missing, -1);
2797 rb_define_method(rb_cRactorMovedObject,
"instance_exec", ractor_moved_missing, -1);
2801#if USE_RACTOR_SELECTOR
2802 rb_init_ractor_selector();
2812 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
2813 if (r != vm->ractor.main_ractor) {
2814 fprintf(stderr,
"r:%u (%s)\n", r->pub.id, ractor_status_str(r->status_));
2822 if (rb_ractor_main_p()) {
2832rb_ractor_stdout(
void)
2834 if (rb_ractor_main_p()) {
2839 return cr->r_stdout;
2844rb_ractor_stderr(
void)
2846 if (rb_ractor_main_p()) {
2851 return cr->r_stderr;
2858 if (rb_ractor_main_p()) {
2870 if (rb_ractor_main_p()) {
2882 if (rb_ractor_main_p()) {
2894 return &cr->pub.hooks;
2903enum obj_traverse_iterator_result {
2909typedef enum obj_traverse_iterator_result (*rb_obj_traverse_enter_func)(
VALUE obj);
2910typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(
VALUE obj);
2911typedef enum obj_traverse_iterator_result (*rb_obj_traverse_final_func)(
VALUE obj);
2913static enum obj_traverse_iterator_result null_leave(
VALUE obj);
2916 rb_obj_traverse_enter_func enter_func;
2917 rb_obj_traverse_leave_func leave_func;
2936 if (obj_traverse_i(key, d->data)) {
2941 if (obj_traverse_i(val, d->data)) {
2950obj_traverse_reachable_i(
VALUE obj,
void *ptr)
2954 if (obj_traverse_i(obj, d->data)) {
2962 if (UNLIKELY(!data->rec)) {
2963 data->rec_hash = rb_ident_hash_new();
2964 data->rec = RHASH_ST_TABLE(data->rec_hash);
2970obj_traverse_ivar_foreach_i(
ID key,
VALUE val, st_data_t ptr)
2974 if (obj_traverse_i(val, d->data)) {
2987 switch (data->enter_func(obj)) {
2988 case traverse_cont:
break;
2989 case traverse_skip:
return 0;
2990 case traverse_stop:
return 1;
2993 if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
3003 if (d.stop)
return 1;
3024 if (obj_traverse_i(e, data))
return 1;
3038 if (d.stop)
return 1;
3044 long len = RSTRUCT_LEN(obj);
3045 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
3047 for (
long i=0; i<
len; i++) {
3048 if (obj_traverse_i(ptr[i], data))
return 1;
3054 if (obj_traverse_i(RRATIONAL(obj)->num, data))
return 1;
3055 if (obj_traverse_i(RRATIONAL(obj)->den, data))
return 1;
3058 if (obj_traverse_i(RCOMPLEX(obj)->real, data))
return 1;
3059 if (obj_traverse_i(RCOMPLEX(obj)->imag, data))
return 1;
3069 RB_VM_LOCK_ENTER_NO_BARRIER();
3071 rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
3073 RB_VM_LOCK_LEAVE_NO_BARRIER();
3074 if (d.stop)
return 1;
3084 rb_bug(
"unreachable");
3087 if (data->leave_func(obj) == traverse_stop) {
3096 rb_obj_traverse_final_func final_func;
3101obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
3104 if (data->final_func(key)) {
3114rb_obj_traverse(
VALUE obj,
3115 rb_obj_traverse_enter_func enter_func,
3116 rb_obj_traverse_leave_func leave_func,
3117 rb_obj_traverse_final_func final_func)
3120 .enter_func = enter_func,
3121 .leave_func = leave_func,
3125 if (obj_traverse_i(obj, &data))
return 1;
3126 if (final_func && data.rec) {
3128 st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
3135allow_frozen_shareable_p(
VALUE obj)
3142 if (
type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
3150static enum obj_traverse_iterator_result
3151make_shareable_check_shareable(
VALUE obj)
3156 return traverse_skip;
3158 else if (!allow_frozen_shareable_p(obj)) {
3160 rb_proc_ractor_make_shareable(obj);
3161 return traverse_cont;
3164 rb_raise(rb_eRactorError,
"can not make shareable object for %"PRIsVALUE, obj);
3169 return traverse_skip;
3176 rb_raise(rb_eRactorError,
"#freeze does not freeze object correctly");
3180 return traverse_skip;
3184 return traverse_cont;
3187static enum obj_traverse_iterator_result
3188mark_shareable(
VALUE obj)
3191 return traverse_cont;
3197 rb_obj_traverse(obj,
3198 make_shareable_check_shareable,
3199 null_leave, mark_shareable);
3206 VALUE copy = ractor_copy(obj);
3211rb_ractor_ensure_shareable(
VALUE obj,
VALUE name)
3214 VALUE message = rb_sprintf(
"cannot assign unshareable object to %"PRIsVALUE,
3222rb_ractor_ensure_main_ractor(
const char *msg)
3224 if (!rb_ractor_main_p()) {
3225 rb_raise(rb_eRactorIsolationError,
"%s", msg);
3229static enum obj_traverse_iterator_result
3230shareable_p_enter(
VALUE obj)
3233 return traverse_skip;
3239 mark_shareable(obj);
3240 return traverse_skip;
3243 allow_frozen_shareable_p(obj)) {
3244 return traverse_cont;
3247 return traverse_stop;
3251rb_ractor_shareable_p_continue(
VALUE obj)
3253 if (rb_obj_traverse(obj,
3254 shareable_p_enter, null_leave,
3263#if RACTOR_CHECK_MODE > 0
3265rb_ractor_setup_belonging(
VALUE obj)
3267 rb_ractor_setup_belonging_to(obj, rb_ractor_current_id());
3270static enum obj_traverse_iterator_result
3271reset_belonging_enter(
VALUE obj)
3274 return traverse_skip;
3277 rb_ractor_setup_belonging(obj);
3278 return traverse_cont;
3283static enum obj_traverse_iterator_result
3284null_leave(
VALUE obj)
3286 return traverse_cont;
3290ractor_reset_belonging(
VALUE obj)
3292#if RACTOR_CHECK_MODE > 0
3293 rb_obj_traverse(obj, reset_belonging_enter, null_leave, NULL);
3311 rb_obj_traverse_replace_enter_func enter_func;
3312 rb_obj_traverse_replace_leave_func leave_func;
3328obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp,
int error)
3334obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr,
int exists)
3339 if (obj_traverse_replace_i(*key, data)) {
3343 else if (*key != data->replacement) {
3344 VALUE v = *key = data->replacement;
3348 if (obj_traverse_replace_i(*val, data)) {
3352 else if (*val != data->replacement) {
3353 VALUE v = *val = data->replacement;
3361obj_iv_hash_traverse_replace_foreach_i(st_data_t _key, st_data_t _val, st_data_t _data,
int _x)
3367obj_iv_hash_traverse_replace_i(st_data_t * _key, st_data_t * val, st_data_t ptr,
int exists)
3372 if (obj_traverse_replace_i(*(
VALUE *)val, data)) {
3376 else if (*(
VALUE *)val != data->replacement) {
3387 if (UNLIKELY(!data->rec)) {
3388 data->rec_hash = rb_ident_hash_new();
3389 data->rec = RHASH_ST_TABLE(data->rec_hash);
3395obj_refer_only_shareables_p_i(
VALUE obj,
void *ptr)
3397 int *pcnt = (
int *)ptr;
3405obj_refer_only_shareables_p(
VALUE obj)
3408 RB_VM_LOCK_ENTER_NO_BARRIER();
3410 rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
3412 RB_VM_LOCK_LEAVE_NO_BARRIER();
3419 st_data_t replacement;
3422 data->replacement = obj;
3426 switch (data->enter_func(obj, data)) {
3427 case traverse_cont:
break;
3428 case traverse_skip:
return 0;
3429 case traverse_stop:
return 1;
3432 replacement = (st_data_t)data->replacement;
3434 if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, &replacement))) {
3435 data->replacement = (
VALUE)replacement;
3439 st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, replacement);
3446#define CHECK_AND_REPLACE(v) do { \
3448 if (obj_traverse_replace_i(_val, data)) { return 1; } \
3449 else if (data->replacement != _val) { RB_OBJ_WRITE(obj, &v, data->replacement); } \
3454 rb_ivar_generic_fields_tbl_lookup(obj, &fields_tbl);
3456 if (UNLIKELY(rb_shape_obj_too_complex_p(obj))) {
3462 rb_st_foreach_with_replace(
3463 fields_tbl->as.complex.table,
3464 obj_iv_hash_traverse_replace_foreach_i,
3465 obj_iv_hash_traverse_replace_i,
3468 if (d.stop)
return 1;
3471 for (uint32_t i = 0; i < fields_tbl->as.shape.fields_count; i++) {
3472 if (!UNDEF_P(fields_tbl->as.shape.fields[i])) {
3473 CHECK_AND_REPLACE(fields_tbl->as.shape.fields[i]);
3489 rb_str_make_independent(obj);
3494 if (rb_shape_obj_too_complex_p(obj)) {
3500 rb_st_foreach_with_replace(
3501 ROBJECT_FIELDS_HASH(obj),
3502 obj_iv_hash_traverse_replace_foreach_i,
3503 obj_iv_hash_traverse_replace_i,
3506 if (d.stop)
return 1;
3509 uint32_t
len = ROBJECT_FIELDS_COUNT(obj);
3512 for (uint32_t i = 0; i <
len; i++) {
3513 CHECK_AND_REPLACE(ptr[i]);
3521 rb_ary_cancel_sharing(obj);
3526 if (obj_traverse_replace_i(e, data)) {
3529 else if (e != data->replacement) {
3543 rb_hash_stlike_foreach_with_replace(obj,
3544 obj_hash_traverse_replace_foreach_i,
3545 obj_hash_traverse_replace_i,
3547 if (d.stop)
return 1;
3551 if (obj_traverse_replace_i(ifnone, data)) {
3554 else if (ifnone != data->replacement) {
3562 long len = RSTRUCT_LEN(obj);
3563 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
3565 for (
long i=0; i<
len; i++) {
3566 CHECK_AND_REPLACE(ptr[i]);
3572 CHECK_AND_REPLACE(RRATIONAL(obj)->num);
3573 CHECK_AND_REPLACE(RRATIONAL(obj)->den);
3576 CHECK_AND_REPLACE(RCOMPLEX(obj)->real);
3577 CHECK_AND_REPLACE(RCOMPLEX(obj)->imag);
3581 if (!data->move && obj_refer_only_shareables_p(obj)) {
3585 rb_raise(rb_eRactorError,
"can not %s %"PRIsVALUE
" object.",
3599 rb_bug(
"unreachable");
3602 data->replacement = (
VALUE)replacement;
3604 if (data->leave_func(obj, data) == traverse_stop) {
3615rb_obj_traverse_replace(
VALUE obj,
3616 rb_obj_traverse_replace_enter_func enter_func,
3617 rb_obj_traverse_replace_leave_func leave_func,
3621 .enter_func = enter_func,
3622 .leave_func = leave_func,
3628 if (obj_traverse_replace_i(obj, &data)) {
3632 return data.replacement;
3636static const bool wb_protected_types[
RUBY_T_MASK] = {
3649static enum obj_traverse_iterator_result
3653 data->replacement = obj;
3654 return traverse_skip;
3659 NEWOBJ_OF(moved,
struct RBasic, 0,
type, rb_gc_obj_slot_size(obj), 0);
3660 data->replacement = (
VALUE)moved;
3661 return traverse_cont;
3665static enum obj_traverse_iterator_result
3668 size_t size = rb_gc_obj_slot_size(obj);
3669 memcpy((
void *)data->replacement, (
void *)obj, size);
3671 void rb_replace_generic_ivar(
VALUE clone,
VALUE obj);
3673 rb_gc_obj_id_moved(data->replacement);
3676 rb_replace_generic_ivar(data->replacement, obj);
3682 RBASIC_SET_CLASS_RAW(obj, rb_cRactorMovedObject);
3683 return traverse_cont;
3687ractor_move(
VALUE obj)
3689 VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave,
true);
3690 if (!UNDEF_P(val)) {
3694 rb_raise(rb_eRactorError,
"can not move the object");
3698static enum obj_traverse_iterator_result
3702 data->replacement = obj;
3703 return traverse_skip;
3707 return traverse_cont;
3711static enum obj_traverse_iterator_result
3714 return traverse_cont;
3718ractor_copy(
VALUE obj)
3720 VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave,
false);
3721 if (!UNDEF_P(val)) {
3725 rb_raise(rb_eRactorError,
"can not copy the object");
3740} freed_ractor_local_keys;
3743ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
3746 if (k->type->
mark) (*k->type->
mark)((
void *)val);
3750static enum rb_id_table_iterator_result
3751idkey_local_storage_mark_i(
VALUE val,
void *dmy)
3754 return ID_TABLE_CONTINUE;
3760 if (r->local_storage) {
3761 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
3763 for (
int i=0; i<freed_ractor_local_keys.cnt; i++) {
3765 st_data_t val, k = (st_data_t)key;
3766 if (st_delete(r->local_storage, &k, &val) &&
3768 (*key->type->free)((
void *)val);
3773 if (r->idkey_local_storage) {
3774 rb_id_table_foreach_values(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
3777 rb_gc_mark(r->local_storage_store_lock);
3781ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
3784 if (k->type->
free) (*k->type->
free)((
void *)val);
3791 if (r->local_storage) {
3792 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
3793 st_free_table(r->local_storage);
3796 if (r->idkey_local_storage) {
3797 rb_id_table_free(r->idkey_local_storage);
3802rb_ractor_local_storage_value_mark(
void *ptr)
3804 rb_gc_mark((
VALUE)ptr);
3818 rb_ractor_local_storage_value_mark,
3826 key->type =
type ?
type : &ractor_local_storage_type_null;
3827 key->main_cache = (
void *)
Qundef;
3842 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
3843 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
3846 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
3854 if (rb_ractor_main_p()) {
3855 if (!UNDEF_P((
VALUE)key->main_cache)) {
3856 *pret = key->main_cache;
3866 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
3880 if (cr->local_storage == NULL) {
3881 cr->local_storage = st_init_numtable();
3884 st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
3886 if (rb_ractor_main_p()) {
3887 key->main_cache = ptr;
3895 if (ractor_local_ref(key, &val)) {
3906 if (ractor_local_ref(key, (
void **)val)) {
3917 ractor_local_set(key, (
void *)val);
3924 if (ractor_local_ref(key, &ret)) {
3935 ractor_local_set(key, ptr);
3938#define DEFAULT_KEYS_CAPA 0x10
3941rb_ractor_finish_marking(
void)
3943 for (
int i=0; i<freed_ractor_local_keys.cnt; i++) {
3944 ruby_xfree(freed_ractor_local_keys.keys[i]);
3946 freed_ractor_local_keys.cnt = 0;
3947 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
3948 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
3958 struct rb_id_table *tbl = cr->idkey_local_storage;
3961 if (
id && tbl && rb_id_table_lookup(tbl,
id, &val)) {
3974 struct rb_id_table *tbl = cr->idkey_local_storage;
3977 tbl = cr->idkey_local_storage = rb_id_table_create(2);
3979 rb_id_table_insert(tbl,
id, val);
3991ractor_local_value_store_i(
VALUE ptr)
3996 if (rb_id_table_lookup(data->tbl, data->id, &val)) {
4001 ractor_local_value_set(data->ec,
Qnil, data->sym, val);
4014 .tbl = cr->idkey_local_storage,
4018 if (data.tbl == NULL) {
4019 data.tbl = cr->idkey_local_storage = rb_id_table_create(2);
4021 else if (rb_id_table_lookup(data.tbl, data.id, &val)) {
4026 if (!cr->local_storage_store_lock) {
4047 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
4048 VALUE obj = ractor_receive(ec, cr);
4049 ractor_yield(ec, cr, obj,
Qfalse);
4063rb_ractor_channel_new(
void)
4088 VM_ASSERT(ec == rb_current_ec_noinline());
4098 VM_ASSERT(ec == rb_current_ec_noinline());
4107 VM_ASSERT(ec == rb_current_ec_noinline());
4111 return ractor_close_outgoing(ec, (
rb_ractor_t *)ch);
4130require_body(
VALUE data)
4136 crr->result = rb_funcallv(
Qnil, require, 1, &crr->feature);
4145 crr->exception = errinfo;
4150require_result_copy_body(
VALUE data)
4154 if (crr->exception !=
Qundef) {
4155 VM_ASSERT(crr->result ==
Qundef);
4156 crr->exception = ractor_copy(crr->exception);
4159 VM_ASSERT(crr->result !=
Qundef);
4160 crr->result = ractor_copy(crr->result);
4167require_result_copy_resuce(
VALUE data,
VALUE errinfo)
4170 crr->exception = errinfo;
4184 rb_ractor_channel_yield(GET_EC(), crr->ch,
Qtrue);
4190ractore_require_func(
void *data)
4193 return ractor_require_protect(crr, require_body);
4197rb_ractor_require(
VALUE feature)
4202 .ch = rb_ractor_channel_new(),
4208 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
4209 rb_ractor_interrupt_exec(main_r, ractore_require_func, &crr, 0);
4212 rb_ractor_channel_take(ec, crr.ch);
4213 rb_ractor_channel_close(ec, crr.ch);
4215 if (crr.exception !=
Qundef) {
4226 return rb_ractor_require(feature);
4230autoload_load_body(
VALUE data)
4238ractor_autoload_load_func(
void *data)
4241 return ractor_require_protect(crr, autoload_load_body);
4245rb_ractor_autoload_load(
VALUE module,
ID name)
4250 .ch = rb_ractor_channel_new(),
4256 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
4257 rb_ractor_interrupt_exec(main_r, ractor_autoload_load_func, &crr, 0);
4260 rb_ractor_channel_take(ec, crr.ch);
4261 rb_ractor_channel_close(ec, crr.ch);
4263 if (crr.exception !=
Qundef) {
4271#include "ractor.rbinc"
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
#define RUBY_ATOMIC_CAS(var, oldval, newval)
Atomic compare-and-swap.
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
#define RUBY_ATOMIC_FETCH_ADD(var, val)
Atomically replaces the value pointed by var with the result of addition of val to the old value of v...
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
static VALUE RB_OBJ_FROZEN_RAW(VALUE obj)
This is an implementation detail of RB_OBJ_FROZEN().
@ RUBY_FL_SHAREABLE
This flag has something to do with Ractor.
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)
Keyword argument deconstructor.
#define rb_str_new2
Old name of rb_str_new_cstr.
#define T_COMPLEX
Old name of RUBY_T_COMPLEX.
#define T_FILE
Old name of RUBY_T_FILE.
#define FL_EXIVAR
Old name of RUBY_FL_EXIVAR.
#define REALLOC_N
Old name of RB_REALLOC_N.
#define ALLOC
Old name of RB_ALLOC.
#define T_STRING
Old name of RUBY_T_STRING.
#define Qundef
Old name of RUBY_Qundef.
#define T_FLOAT
Old name of RUBY_T_FLOAT.
#define T_IMEMO
Old name of RUBY_T_IMEMO.
#define ID2SYM
Old name of RB_ID2SYM.
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define T_STRUCT
Old name of RUBY_T_STRUCT.
#define SYM2ID
Old name of RB_SYM2ID.
#define T_DATA
Old name of RUBY_T_DATA.
#define T_MODULE
Old name of RUBY_T_MODULE.
#define T_RATIONAL
Old name of RUBY_T_RATIONAL.
#define T_ICLASS
Old name of RUBY_T_ICLASS.
#define T_HASH
Old name of RUBY_T_HASH.
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
#define Qtrue
Old name of RUBY_Qtrue.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
#define T_OBJECT
Old name of RUBY_T_OBJECT.
#define NIL_P
Old name of RB_NIL_P.
#define FL_WB_PROTECTED
Old name of RUBY_FL_WB_PROTECTED.
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
#define T_MATCH
Old name of RUBY_T_MATCH.
#define T_CLASS
Old name of RUBY_T_CLASS.
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
#define FL_FREEZE
Old name of RUBY_FL_FREEZE.
#define CONST_ID
Old name of RUBY_CONST_ID.
#define FL_SET_RAW
Old name of RB_FL_SET_RAW.
#define T_REGEXP
Old name of RUBY_T_REGEXP.
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Checks if the given object is of given kind.
VALUE rb_eRuntimeError
RuntimeError exception.
VALUE rb_eStopIteration
StopIteration exception.
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of C's.
VALUE rb_eException
Mother of all exceptions.
VALUE rb_cRactor
Ractor class.
VALUE rb_stdin
STDIN constant.
VALUE rb_stderr
STDERR constant.
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
VALUE rb_cBasicObject
BasicObject class.
VALUE rb_obj_clone(VALUE obj)
Produces a shallow copy of the given object.
VALUE rb_stdout
STDOUT constant.
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
#define RGENGC_WB_PROTECTED_STRUCT
This is a compile-time flag to enable/disable write barrier for struct RStruct.
#define RGENGC_WB_PROTECTED_STRING
This is a compile-time flag to enable/disable write barrier for struct RString.
#define RGENGC_WB_PROTECTED_HASH
This is a compile-time flag to enable/disable write barrier for struct RHash.
#define RGENGC_WB_PROTECTED_MATCH
This is a compile-time flag to enable/disable write barrier for struct RMatch.
#define RGENGC_WB_PROTECTED_ARRAY
This is a compile-time flag to enable/disable write barrier for struct RArray.
#define RGENGC_WB_PROTECTED_COMPLEX
This is a compile-time flag to enable/disable write barrier for struct RComplex.
#define RGENGC_WB_PROTECTED_FLOAT
This is a compile-time flag to enable/disable write barrier for struct RFloat.
#define RGENGC_WB_PROTECTED_RATIONAL
This is a compile-time flag to enable/disable write barrier for struct RRational.
#define RGENGC_WB_PROTECTED_REGEXP
This is a compile-time flag to enable/disable write barrier for struct RRegexp.
#define RGENGC_WB_PROTECTED_OBJECT
This is a compile-time flag to enable/disable write barrier for struct RObject.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
VALUE rb_obj_is_proc(VALUE recv)
Queries if the given object is a proc.
#define rb_exc_new_cstr(exc, str)
Identical to rb_exc_new(), except it assumes the passed pointer is a pointer to a C string.
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
VALUE rb_mutex_new(void)
Creates a mutex.
VALUE rb_mutex_synchronize(VALUE mutex, VALUE(*func)(VALUE arg), VALUE arg)
Obtains the lock, runs the passed function, and releases the lock when it completes.
void rb_unblock_function_t(void *)
This is the type of UBFs.
VALUE rb_mutex_unlock(VALUE mutex)
Releases the mutex.
VALUE rb_mutex_lock(VALUE mutex)
Attempts to lock the mutex.
void rb_thread_sleep(int sec)
Blocks for the given period of time.
VALUE rb_const_get(VALUE space, ID name)
Identical to rb_const_defined(), except it returns the actual defined value.
VALUE rb_ivar_set(VALUE obj, ID name, VALUE val)
Identical to rb_iv_set(), except it accepts the name as an ID instead of a C string.
VALUE rb_autoload_load(VALUE space, ID name)
Kicks the autoload procedure as if it was "touched".
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
int len
Length of the buffer.
const struct rb_ractor_local_storage_type rb_ractor_local_storage_type_free
A type of ractor-local storage that destructs itself using ruby_xfree.
VALUE rb_ractor_make_shareable_copy(VALUE obj)
Identical to rb_ractor_make_shareable(), except it returns a (deep) copy of the passed one instead of...
struct rb_ractor_local_key_struct * rb_ractor_local_key_t
(Opaque) struct that holds a ractor-local storage key.
void * rb_ractor_local_storage_ptr(rb_ractor_local_key_t key)
Identical to rb_ractor_local_storage_value() except the return type.
void rb_ractor_local_storage_ptr_set(rb_ractor_local_key_t key, void *ptr)
Identical to rb_ractor_local_storage_value_set() except the parameter type.
rb_ractor_local_key_t rb_ractor_local_storage_ptr_newkey(const struct rb_ractor_local_storage_type *type)
Extended version of rb_ractor_local_storage_value_newkey().
VALUE rb_ractor_stdin(void)
Queries the standard input of the current Ractor that is calling this function.
static bool rb_ractor_shareable_p(VALUE obj)
Queries if multiple Ractors can share the passed object or not.
void rb_ractor_stderr_set(VALUE io)
Assigns an IO to the standard error of the Ractor that is calling this function.
void rb_ractor_local_storage_value_set(rb_ractor_local_key_t key, VALUE val)
Associates the passed value to the passed key.
bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
Queries the key.
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
rb_ractor_local_key_t rb_ractor_local_storage_value_newkey(void)
Issues a new key.
void rb_ractor_stdout_set(VALUE io)
Assigns an IO to the standard output of the Ractor that is calling this function.
void rb_ractor_stdin_set(VALUE io)
Assigns an IO to the standard input of the Ractor that is calling this function.
VALUE rb_ractor_local_storage_value(rb_ractor_local_key_t key)
Queries the key.
#define RB_NOGVL_INTR_FAIL
Passing this flag to rb_nogvl() prevents it from checking interrupts.
void * rb_nogvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2, int flags)
Identical to rb_thread_call_without_gvl(), except it additionally takes "flags" that change the behav...
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
Shim for block function parameters.
VALUE rb_yield(VALUE val)
Yields the block.
rb_block_call_func * rb_block_call_func_t
Shorthand type that represents an iterator-written-in-C function pointer.
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
VALUE rb_proc_new(type *q, VALUE w)
Creates a rb_cProc instance.
VALUE type(ANYARGS)
ANYARGS-ed function type.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
void rb_ivar_foreach(VALUE q, int_type *w, VALUE e)
Iteration over each instance variable of the object.
VALUE rb_rescue2(type *q, VALUE w, type *e, VALUE r,...)
An equivalent of rescue clause.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
#define RARRAY_AREF(a, i)
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
#define RBASIC(obj)
Convenient casting macro.
#define DATA_PTR(obj)
Convenient getter macro.
#define RHASH_SET_IFNONE(h, ifnone)
Destructively updates the default value of the hash.
static VALUE * ROBJECT_FIELDS(VALUE obj)
Queries the instance variables.
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
static bool RTYPEDDATA_P(VALUE obj)
Checks whether the passed object is RTypedData or RData.
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
static const struct rb_data_type_struct * RTYPEDDATA_TYPE(VALUE obj)
Queries for the type of given object.
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
#define RTEST
This is an old name of RB_TEST.
Ruby object's base components.
This is the struct that holds necessary info for a struct.
Type that defines a ractor-local storage.
void(* free)(void *ptr)
A function to destruct a ractor-local storage.
void(* mark)(void *ptr)
A function to mark a ractor-local storage.
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_lock.
void rb_native_cond_initialize(rb_nativethread_cond_t *cond)
Fills the passed condition variable with an initial value.
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_initialize.
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_unlock.
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_destroy.
void rb_native_cond_signal(rb_nativethread_cond_t *cond)
Signals a condition variable.
void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
Waits for the passed condition variable to be signalled.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.
static enum ruby_value_type RB_BUILTIN_TYPE(VALUE obj)
Queries the type of the object.
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
@ RUBY_T_MASK
Bitmask of ruby_value_type.