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"
18#include "transient_heap.h"
24VALUE rb_eRactorUnsafeError;
25VALUE rb_eRactorIsolationError;
26static VALUE rb_eRactorError;
27static VALUE rb_eRactorRemoteError;
28static VALUE rb_eRactorMovedError;
29static VALUE rb_eRactorClosedError;
30static VALUE rb_cRactorMovedObject;
32static void vm_ractor_blocking_cnt_inc(
rb_vm_t *vm,
rb_ractor_t *r,
const char *file,
int line);
37#if RACTOR_CHECK_MODE > 0
39 if (rb_current_execution_context(
false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) {
40 rb_bug(
"recursive ractor locking");
48#if RACTOR_CHECK_MODE > 0
50 if (rb_current_execution_context(
false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) {
51 rp(r->sync.locked_by);
52 rb_bug(
"ractor lock is not acquired.");
58ractor_lock(
rb_ractor_t *r,
const char *file,
int line)
60 RUBY_DEBUG_LOG2(file, line,
"locking r:%u%s", r->pub.id, GET_RACTOR() == r ?
" (self)" :
"");
62 ASSERT_ractor_unlocking(r);
65#if RACTOR_CHECK_MODE > 0
66 if (rb_current_execution_context(
false) != NULL) {
67 r->sync.locked_by = rb_ractor_self(GET_RACTOR());
71 RUBY_DEBUG_LOG2(file, line,
"locked r:%u%s", r->pub.id, GET_RACTOR() == r ?
" (self)" :
"");
75ractor_lock_self(
rb_ractor_t *cr,
const char *file,
int line)
77 VM_ASSERT(cr == GET_RACTOR());
78#if RACTOR_CHECK_MODE > 0
79 VM_ASSERT(cr->sync.locked_by != cr->pub.self);
81 ractor_lock(cr, file, line);
85ractor_unlock(
rb_ractor_t *r,
const char *file,
int line)
87 ASSERT_ractor_locking(r);
88#if RACTOR_CHECK_MODE > 0
89 r->sync.locked_by =
Qnil;
93 RUBY_DEBUG_LOG2(file, line,
"r:%u%s", r->pub.id, GET_RACTOR() == r ?
" (self)" :
"");
97ractor_unlock_self(
rb_ractor_t *cr,
const char *file,
int line)
99 VM_ASSERT(cr == GET_RACTOR());
100#if RACTOR_CHECK_MODE > 0
101 VM_ASSERT(cr->sync.locked_by == cr->pub.self);
103 ractor_unlock(cr, file, line);
106#define RACTOR_LOCK(r) ractor_lock(r, __FILE__, __LINE__)
107#define RACTOR_UNLOCK(r) ractor_unlock(r, __FILE__, __LINE__)
108#define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
109#define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
114#if RACTOR_CHECK_MODE > 0
115 VALUE locked_by = r->sync.locked_by;
116 r->sync.locked_by =
Qnil;
120#if RACTOR_CHECK_MODE > 0
121 r->sync.locked_by = locked_by;
126ractor_status_str(
enum ractor_status status)
129 case ractor_created:
return "created";
130 case ractor_running:
return "running";
131 case ractor_blocking:
return "blocking";
132 case ractor_terminated:
return "terminated";
138ractor_status_set(
rb_ractor_t *r,
enum ractor_status status)
140 RUBY_DEBUG_LOG(
"r:%u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
143 if (r->status_ != ractor_created) {
144 VM_ASSERT(r == GET_RACTOR());
149 switch (r->status_) {
151 VM_ASSERT(status == ractor_blocking);
154 VM_ASSERT(status == ractor_blocking||
155 status == ractor_terminated);
157 case ractor_blocking:
158 VM_ASSERT(status == ractor_running);
160 case ractor_terminated:
169ractor_status_p(
rb_ractor_t *r,
enum ractor_status status)
171 return rb_ractor_status_p(r, status);
179 for (
int i=0; i<rq->cnt; i++) {
182 rb_gc_mark(b->sender);
186static void ractor_local_storage_mark(
rb_ractor_t *r);
187static void ractor_local_storage_free(
rb_ractor_t *r);
190ractor_mark(
void *ptr)
194 ractor_queue_mark(&r->sync.incoming_queue);
195 rb_gc_mark(r->sync.wait.taken_basket.v);
196 rb_gc_mark(r->sync.wait.taken_basket.sender);
197 rb_gc_mark(r->sync.wait.yielded_basket.v);
198 rb_gc_mark(r->sync.wait.yielded_basket.sender);
199 rb_gc_mark(r->receiving_mutex);
203 rb_gc_mark(r->r_stdin);
204 rb_gc_mark(r->r_stdout);
205 rb_gc_mark(r->r_stderr);
206 rb_hook_list_mark(&r->pub.hooks);
208 if (r->threads.cnt > 0) {
210 ccan_list_for_each(&r->threads.set, th, lt_node) {
211 VM_ASSERT(th != NULL);
212 rb_gc_mark(th->self);
216 ractor_local_storage_mark(r);
232ractor_free(
void *ptr)
237 ractor_queue_free(&r->sync.incoming_queue);
238 ractor_waiting_list_free(&r->sync.taking_ractors);
239 ractor_local_storage_free(r);
240 rb_hook_list_free(&r->pub.hooks);
257ractor_memsize(
const void *ptr)
263 ractor_queue_memsize(&r->sync.incoming_queue) +
264 ractor_waiting_list_memsize(&r->sync.taking_ractors);
275 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
290RACTOR_PTR(
VALUE self)
292 VM_ASSERT(rb_ractor_p(self));
301#if RACTOR_CHECK_MODE > 0
302MJIT_FUNC_EXPORTED uint32_t
303rb_ractor_current_id(
void)
305 if (GET_THREAD()->ractor == NULL) {
309 return rb_ractor_id(GET_RACTOR());
326 return &rq->baskets[(rq->start + i) % rq->size];
332 ASSERT_ractor_locking(GET_RACTOR());
334 if (rq->reserved_cnt == 0) {
336 rq->start = (rq->start + 1) % rq->size;
340 ractor_queue_at(rq, 0)->type = basket_type_deleted;
348 return b->type == basket_type_deleted ||
349 b->type == basket_type_reserved;
355 ASSERT_ractor_locking(r);
357 while (rq->cnt > 0 && ractor_queue_at(rq, 0)->
type == basket_type_deleted) {
358 ractor_queue_advance(rq);
365 ASSERT_ractor_locking(r);
371 ractor_queue_compact(r, rq);
373 for (
int i=0; i<rq->cnt; i++) {
374 if (!ractor_queue_skip_p(rq, i)) {
389 if (!ractor_queue_empty_p(r, rq)) {
390 for (
int i=0; i<rq->cnt; i++) {
391 if (!ractor_queue_skip_p(rq, i)) {
396 b->type = basket_type_deleted;
397 ractor_queue_compact(r, rq);
412 ASSERT_ractor_locking(r);
414 if (rq->size <= rq->cnt) {
415 rq->baskets = realloc(rq->baskets,
sizeof(
struct rb_ractor_basket) * rq->size * 2);
416 for (
int i=rq->size - rq->start; i<rq->cnt; i++) {
417 rq->baskets[i + rq->start] = rq->baskets[i + rq->start - rq->size];
421 rq->baskets[(rq->start + rq->cnt++) % rq->size] = *basket;
428 b->type = basket_type_none;
433static VALUE ractor_reset_belonging(
VALUE obj);
439 case basket_type_ref:
441 case basket_type_copy:
442 case basket_type_move:
443 case basket_type_will:
444 b->type = basket_type_ref;
445 b->v = ractor_reset_belonging(b->v);
457 VALUE v = ractor_basket_value(b);
463 ractor_basket_clear(b);
464 rb_ec_setup_exception(NULL, err, cause);
468 ractor_basket_clear(b);
475 if (r->receiving_mutex && rb_mutex_owned_p(r->receiving_mutex)) {
476 rb_raise(rb_eRactorError,
"can not call receive/receive_if recursively");
486 ractor_recursive_receive_if(r);
488 if (ractor_queue_deq(r, rq, &basket) ==
false) {
489 if (r->sync.incoming_port_closed) {
490 rb_raise(rb_eRactorClosedError,
"The incoming port is already closed");
497 return ractor_basket_accept(&basket);
501ractor_sleeping_by(
const rb_ractor_t *r,
enum rb_ractor_wait_status wait_status)
503 return (r->sync.wait.status & wait_status) && r->sync.wait.wakeup_status == wakeup_none;
507ractor_wakeup(
rb_ractor_t *r,
enum rb_ractor_wait_status wait_status,
enum rb_ractor_wakeup_status wakeup_status)
509 ASSERT_ractor_locking(r);
515 if (ractor_sleeping_by(r, wait_status)) {
516 r->sync.wait.wakeup_status = wakeup_status;
526ractor_sleep_wo_gvl(
void *ptr)
529 RACTOR_LOCK_SELF(cr);
531 VM_ASSERT(cr->sync.wait.status != wait_none);
532 if (cr->sync.wait.wakeup_status == wakeup_none) {
533 ractor_cond_wait(cr);
535 cr->sync.wait.status = wait_none;
537 RACTOR_UNLOCK_SELF(cr);
542ractor_sleep_interrupt(
void *ptr)
548 ractor_wakeup(r, wait_receiving | wait_taking | wait_yielding, wakeup_by_interrupt);
553#if USE_RUBY_DEBUG_LOG
555wait_status_str(
enum rb_ractor_wait_status wait_status)
557 switch ((
int)wait_status) {
558 case wait_none:
return "none";
559 case wait_receiving:
return "receiving";
560 case wait_taking:
return "taking";
561 case wait_yielding:
return "yielding";
562 case wait_receiving|wait_taking:
return "receiving|taking";
563 case wait_receiving|wait_yielding:
return "receiving|yielding";
564 case wait_taking|wait_yielding:
return "taking|yielding";
565 case wait_receiving|wait_taking|wait_yielding:
return "receiving|taking|yielding";
571wakeup_status_str(
enum rb_ractor_wakeup_status wakeup_status)
573 switch (wakeup_status) {
574 case wakeup_none:
return "none";
575 case wakeup_by_send:
return "by_send";
576 case wakeup_by_yield:
return "by_yield";
577 case wakeup_by_take:
return "by_take";
578 case wakeup_by_close:
return "by_close";
579 case wakeup_by_interrupt:
return "by_interrupt";
580 case wakeup_by_retry:
return "by_retry";
589 VM_ASSERT(GET_RACTOR() == cr);
590 VM_ASSERT(cr->sync.wait.status != wait_none);
597 ractor_sleep_interrupt, cr,
603 if (cr->sync.wait.status != wait_none) {
604 cr->sync.wait.status = wait_none;
605 cr->sync.wait.wakeup_status = wakeup_by_interrupt;
616 VM_ASSERT(cr == GET_RACTOR());
617 bool retry_try =
false;
621 if (ractor_sleeping_by(r, wait_yielding)) {
629 for (
int i=0; i<wl->cnt; i++) {
630 if (wl->ractors[i] == cr) {
639 wl->ractors = malloc(
sizeof(
rb_ractor_t *) * wl->size);
640 if (wl->ractors == NULL)
rb_bug(
"can't allocate buffer");
642 else if (wl->size <= wl->cnt + 1) {
644 wl->ractors = realloc(wl->ractors,
sizeof(
rb_ractor_t *) * wl->size);
645 if (wl->ractors == NULL)
rb_bug(
"can't re-allocate buffer");
647 wl->ractors[wl->cnt++] = cr;
655 if (cr->sync.wait.wakeup_status == wakeup_none) {
656 VM_ASSERT(cr->sync.wait.status != wait_none);
658 cr->sync.wait.wakeup_status = wakeup_by_retry;
659 cr->sync.wait.status = wait_none;
672 for (
int i=0; i<wl->cnt; i++) {
673 if (wl->ractors[i] == wr) {
680 for (
int i=pos; i<wl->cnt; i++) {
681 wl->ractors[i] = wl->ractors[i+1];
691 ASSERT_ractor_locking(r);
692 VM_ASSERT(&r->sync.taking_ractors == wl);
696 for (
int i=1; i<wl->cnt; i++) {
697 wl->ractors[i-1] = wl->ractors[i];
710 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
711 ractor_recursive_receive_if(cr);
715 if (ractor_queue_empty_p(cr, &cr->sync.incoming_queue)) {
716 VM_ASSERT(cr->sync.wait.status == wait_none);
717 cr->sync.wait.status = wait_receiving;
718 cr->sync.wait.wakeup_status = wakeup_none;
719 ractor_sleep(ec, cr);
720 cr->sync.wait.wakeup_status = wakeup_none;
729 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
732 while (UNDEF_P(v = ractor_try_receive(ec, cr))) {
733 ractor_receive_wait(ec, cr);
742basket_type_name(
enum rb_ractor_basket_type
type)
745#define T(t) case basket_type_##t: return #t
753 default:
rb_bug(
"unreachable");
761 for (
int i=0; i<rq->cnt; i++) {
763 fprintf(stderr,
"%d (start:%d) type:%s %p %s\n", i, rq->start, basket_type_name(b->type),
765 if (b->type == basket_type_reserved) bug =
true;
782 VALUE m = cr->receiving_mutex;
790receive_if_body(
VALUE ptr)
794 ractor_receive_if_lock(data->cr);
797 RACTOR_LOCK_SELF(data->cr);
800 VM_ASSERT(b->type == basket_type_reserved);
801 data->rq->reserved_cnt--;
803 if (
RTEST(block_result)) {
804 b->type = basket_type_deleted;
805 ractor_queue_compact(data->cr, data->rq);
808 b->type = basket_type_ref;
811 RACTOR_UNLOCK_SELF(data->cr);
813 data->success =
true;
815 if (
RTEST(block_result)) {
824receive_if_ensure(
VALUE v)
828 if (!data->success) {
829 RACTOR_LOCK_SELF(data->cr);
832 VM_ASSERT(b->type == basket_type_reserved);
833 b->type = basket_type_deleted;
834 data->rq->reserved_cnt--;
836 RACTOR_UNLOCK_SELF(data->cr);
849 unsigned int serial = (
unsigned int)-1;
856 ractor_receive_wait(ec, cr);
858 RACTOR_LOCK_SELF(cr);
860 if (serial != rq->serial) {
866 for (
int i=index; i<rq->cnt; i++) {
867 if (!ractor_queue_skip_p(rq, i)) {
869 v = ractor_basket_value(b);
870 b->type = basket_type_reserved;
877 RACTOR_UNLOCK_SELF(cr);
889 receive_if_ensure, (
VALUE)&data);
891 if (!UNDEF_P(result))
return result;
895 RUBY_VM_CHECK_INTS(ec);
907 if (r->sync.incoming_port_closed) {
911 ractor_queue_enq(r, rq, b);
912 if (ractor_wakeup(r, wait_receiving, wakeup_by_send)) {
913 RUBY_DEBUG_LOG(
"wakeup");
920 rb_raise(rb_eRactorClosedError,
"The incoming-port is already closed");
930 basket->sender = rb_ec_ractor_ptr(ec)->pub.self;
931 basket->exception = exc;
934 basket->type = basket_type_will;
938 basket->type = basket_type_ref;
941 else if (!
RTEST(move)) {
942 basket->v = ractor_copy(obj);
943 basket->type = basket_type_copy;
946 basket->type = basket_type_move;
952 basket->v = ractor_move(obj);
961 ractor_basket_setup(ec, &basket, obj, move,
false,
false,
false);
962 ractor_send_basket(ec, r, &basket);
970 .type = basket_type_none,
976 if (ractor_sleeping_by(r, wait_yielding)) {
977 MAYBE_UNUSED(
bool) wakeup_result;
978 VM_ASSERT(r->sync.wait.yielded_basket.type != basket_type_none);
980 if (r->sync.wait.yielded_basket.type == basket_type_move) {
981 wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_retry);
984 wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_take);
985 basket = r->sync.wait.yielded_basket;
986 ractor_basket_clear(&r->sync.wait.yielded_basket);
988 VM_ASSERT(wakeup_result);
990 else if (r->sync.outgoing_port_closed) {
996 if (basket.type == basket_type_none) {
998 rb_raise(rb_eRactorClosedError,
"The outgoing-port is already closed");
1005 return ractor_basket_accept(&basket);
1010ractor_yield_move_body(
VALUE v)
1012 return ractor_move(v);
1018 ASSERT_ractor_unlocking(cr);
1019 VM_ASSERT(basket->type != basket_type_none);
1021 if (cr->sync.outgoing_port_closed) {
1022 rb_raise(rb_eRactorClosedError,
"The outgoing-port is already closed");
1030 r = ractor_waiting_list_shift(cr, &cr->sync.taking_ractors);
1035 bool retry_shift =
false;
1039 if (ractor_sleeping_by(r, wait_taking)) {
1040 VM_ASSERT(r->sync.wait.taken_basket.type == basket_type_none);
1042 if (basket->type == basket_type_move) {
1043 enum rb_ractor_wait_status prev_wait_status = r->sync.wait.status;
1044 r->sync.wait.status = wait_moving;
1049 VALUE moved_value = rb_protect(ractor_yield_move_body, basket->v, &state);
1051 r->sync.wait.status = prev_wait_status;
1055 basket->v = moved_value;
1060 if (!ractor_wakeup(r, wait_moving, wakeup_by_yield)) {
1065 ractor_wakeup(r, wait_taking, wakeup_by_yield);
1067 r->sync.wait.taken_basket = *basket;
1094 VALUE crv = cr->pub.self;
1097 bool interrupted =
false;
1098 enum rb_ractor_wait_status wait_status = 0;
1099 bool yield_p = !UNDEF_P(yielded_value) ? true :
false;
1100 const int alen = rs_len + (yield_p ? 1 : 0);
1102 struct ractor_select_action {
1103 enum ractor_select_action_type {
1104 ractor_select_action_take,
1105 ractor_select_action_receive,
1106 ractor_select_action_yield,
1109 } *actions =
ALLOCA_N(
struct ractor_select_action, alen);
1111 VM_ASSERT(cr->sync.wait.status == wait_none);
1112 VM_ASSERT(cr->sync.wait.wakeup_status == wakeup_none);
1113 VM_ASSERT(cr->sync.wait.taken_basket.type == basket_type_none);
1114 VM_ASSERT(cr->sync.wait.yielded_basket.type == basket_type_none);
1117 for (i=0; i<rs_len; i++) {
1121 actions[i].type = ractor_select_action_receive;
1122 actions[i].v =
Qnil;
1123 wait_status |= wait_receiving;
1125 else if (rb_ractor_p(v)) {
1126 actions[i].type = ractor_select_action_take;
1128 wait_status |= wait_taking;
1139 actions[rs_len].type = ractor_select_action_yield;
1140 actions[rs_len].v =
Qundef;
1141 wait_status |= wait_yielding;
1142 ractor_basket_setup(ec, &cr->sync.wait.yielded_basket, yielded_value, move,
false,
false,
true);
1148 RUBY_DEBUG_LOG(
"try actions (%s)", wait_status_str(wait_status));
1150 for (i=0; i<alen; i++) {
1152 switch (actions[i].
type) {
1153 case ractor_select_action_take:
1155 v = ractor_try_take(ec, RACTOR_PTR(rv));
1162 case ractor_select_action_receive:
1163 v = ractor_try_receive(ec, cr);
1165 *ret_r =
ID2SYM(rb_intern(
"receive"));
1170 case ractor_select_action_yield:
1172 if (ractor_try_yield(ec, cr, &cr->sync.wait.yielded_basket)) {
1173 *ret_r =
ID2SYM(rb_intern(
"yield"));
1182 RUBY_DEBUG_LOG(
"wait actions (%s)", wait_status_str(wait_status));
1186 VM_ASSERT(cr->sync.wait.status == wait_none);
1187 cr->sync.wait.status = wait_status;
1188 cr->sync.wait.wakeup_status = wakeup_none;
1193 for (i=0; i<alen; i++) {
1195 switch (actions[i].
type) {
1196 case ractor_select_action_take:
1197 r = RACTOR_PTR(actions[i].v);
1198 ractor_register_taking(r, cr);
1200 case ractor_select_action_yield:
1201 case ractor_select_action_receive:
1209 if (cr->sync.wait.wakeup_status == wakeup_none) {
1210 for (i=0; i<alen; i++) {
1213 switch (actions[i].
type) {
1214 case ractor_select_action_take:
1215 r = RACTOR_PTR(actions[i].v);
1216 if (ractor_sleeping_by(r, wait_yielding)) {
1217 RUBY_DEBUG_LOG(
"wakeup_none, but r:%u is waiting for yielding", r->pub.id);
1218 cr->sync.wait.wakeup_status = wakeup_by_retry;
1222 case ractor_select_action_receive:
1223 if (cr->sync.incoming_queue.cnt > 0) {
1224 RUBY_DEBUG_LOG(
"wakeup_none, but incoming_queue has %u messages", cr->sync.incoming_queue.cnt);
1225 cr->sync.wait.wakeup_status = wakeup_by_retry;
1229 case ractor_select_action_yield:
1230 if (cr->sync.taking_ractors.cnt > 0) {
1231 RUBY_DEBUG_LOG(
"wakeup_none, but %u taking_ractors are waiting", cr->sync.taking_ractors.cnt);
1232 cr->sync.wait.wakeup_status = wakeup_by_retry;
1235 else if (cr->sync.outgoing_port_closed) {
1236 cr->sync.wait.wakeup_status = wakeup_by_close;
1243 RUBY_DEBUG_LOG(
"sleep %s", wait_status_str(cr->sync.wait.status));
1244 ractor_sleep(ec, cr);
1245 RUBY_DEBUG_LOG(
"awaken %s", wakeup_status_str(cr->sync.wait.wakeup_status));
1249 RUBY_DEBUG_LOG(
"no need to sleep %s->%s",
1250 wait_status_str(cr->sync.wait.status),
1251 wakeup_status_str(cr->sync.wait.wakeup_status));
1252 cr->sync.wait.status = wait_none;
1258 for (i=0; i<alen; i++) {
1260 switch (actions[i].
type) {
1261 case ractor_select_action_take:
1262 r = RACTOR_PTR(actions[i].v);
1263 ractor_waiting_list_del(r, &r->sync.taking_ractors, cr);
1265 case ractor_select_action_receive:
1266 case ractor_select_action_yield:
1272 enum rb_ractor_wakeup_status wakeup_status = cr->sync.wait.wakeup_status;
1273 cr->sync.wait.wakeup_status = wakeup_none;
1275 switch (wakeup_status) {
1280 case wakeup_by_retry:
1283 case wakeup_by_send:
1287 case wakeup_by_yield:
1290 VM_ASSERT(cr->sync.wait.taken_basket.type != basket_type_none);
1291 *ret_r = cr->sync.wait.taken_basket.sender;
1292 VM_ASSERT(rb_ractor_p(*ret_r));
1293 ret = ractor_basket_accept(&cr->sync.wait.taken_basket);
1295 case wakeup_by_take:
1296 *ret_r =
ID2SYM(rb_intern(
"yield"));
1299 case wakeup_by_close:
1303 case wakeup_by_interrupt:
1311 RUBY_DEBUG_LOG(
"cleanup actions (%s)", wait_status_str(wait_status));
1313 if (cr->sync.wait.yielded_basket.type != basket_type_none) {
1314 ractor_basket_clear(&cr->sync.wait.yielded_basket);
1317 VM_ASSERT(cr->sync.wait.status == wait_none);
1318 VM_ASSERT(cr->sync.wait.wakeup_status == wakeup_none);
1319 VM_ASSERT(cr->sync.wait.taken_basket.type == basket_type_none);
1320 VM_ASSERT(cr->sync.wait.yielded_basket.type == basket_type_none);
1323 rb_vm_check_ints_blocking(ec);
1324 interrupted =
false;
1328 VM_ASSERT(!UNDEF_P(ret));
1336 ractor_select(ec, NULL, 0, obj,
RTEST(move) ?
true : false, &ret_r);
1344 VALUE v = ractor_select(ec, &r->pub.self, 1,
Qundef,
false, &ret_r);
1355 if (!r->sync.incoming_port_closed) {
1357 r->sync.incoming_port_closed =
true;
1358 if (ractor_wakeup(r, wait_receiving, wakeup_by_close)) {
1359 VM_ASSERT(r->sync.incoming_queue.cnt == 0);
1360 RUBY_DEBUG_LOG(
"cancel receiving");
1378 if (!r->sync.outgoing_port_closed) {
1380 r->sync.outgoing_port_closed =
true;
1388 while ((taking_ractor = ractor_waiting_list_shift(r, &r->sync.taking_ractors)) != NULL) {
1389 RACTOR_LOCK(taking_ractor);
1390 ractor_wakeup(taking_ractor, wait_taking, wakeup_by_close);
1391 RACTOR_UNLOCK(taking_ractor);
1395 if (!r->yield_atexit &&
1396 ractor_wakeup(r, wait_yielding, wakeup_by_close)) {
1397 RUBY_DEBUG_LOG(
"cancel yielding");
1419 RUBY_DEBUG_LOG(
"r:%u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
1420 VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
1422 ccan_list_add_tail(&vm->ractor.set, &r->vmlr_node);
1427cancel_single_ractor_mode(
void)
1430 RUBY_DEBUG_LOG(
"enable multi-ractor mode");
1432 VALUE was_disabled = rb_gc_enable();
1435 rb_transient_heap_evacuate();
1441 ruby_single_main_ractor = NULL;
1447 VM_ASSERT(ractor_status_p(r, ractor_created));
1449 if (rb_multi_ractor_p()) {
1452 vm_insert_ractor0(vm, r,
false);
1453 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
1458 if (vm->ractor.cnt == 0) {
1460 vm_insert_ractor0(vm, r,
true);
1461 ractor_status_set(r, ractor_blocking);
1462 ractor_status_set(r, ractor_running);
1465 cancel_single_ractor_mode();
1466 vm_insert_ractor0(vm, r,
true);
1467 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
1475 VM_ASSERT(ractor_status_p(cr, ractor_running));
1476 VM_ASSERT(vm->ractor.cnt > 1);
1477 VM_ASSERT(cr->threads.cnt == 1);
1481 RUBY_DEBUG_LOG(
"ractor.cnt:%u-- terminate_waiting:%d",
1482 vm->ractor.cnt, vm->ractor.sync.terminate_waiting);
1484 VM_ASSERT(vm->ractor.cnt > 0);
1485 ccan_list_del(&cr->vmlr_node);
1487 if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
1493 rb_gc_ractor_newobj_cache_clear(&cr->newobj_cache);
1495 ractor_status_set(cr, ractor_terminated);
1501ractor_alloc(
VALUE klass)
1507 VM_ASSERT(ractor_status_p(r, ractor_created));
1512rb_ractor_main_alloc(
void)
1516 fprintf(stderr,
"[FATAL] failed to allocate memory for main ractor\n");
1520 r->pub.id = ++ractor_last_id;
1524 ruby_single_main_ractor = r;
1529#if defined(HAVE_WORKING_FORK)
1535 vm->ractor.blocking_cnt = 0;
1536 ruby_single_main_ractor = th->ractor;
1537 th->ractor->status_ = ractor_created;
1539 rb_ractor_living_threads_init(th->ractor);
1540 rb_ractor_living_threads_insert(th->ractor, th);
1542 VM_ASSERT(vm->ractor.blocking_cnt == 0);
1543 VM_ASSERT(vm->ractor.cnt == 1);
1552 ccan_list_head_init(&r->threads.set);
1554 r->threads.blocking_cnt = 0;
1560 ractor_queue_setup(&r->sync.incoming_queue);
1566 rb_thread_sched_init(&r->threads.sched);
1567 rb_ractor_living_threads_init(r);
1573 enc = rb_enc_get(name);
1574 if (!rb_enc_asciicompat(enc)) {
1590 r->threads.main = th;
1591 rb_ractor_living_threads_insert(r, th);
1597 VALUE rv = ractor_alloc(self);
1599 ractor_init(r, name, loc);
1602 r->pub.id = ractor_next_id();
1603 RUBY_DEBUG_LOG(
"r:%u", r->pub.id);
1606 r->verbose = cr->verbose;
1607 r->debug = cr->debug;
1609 rb_yjit_before_ractor_spawn();
1610 rb_mjit_before_ractor_spawn();
1611 rb_thread_create_ractor(r, args, block);
1620 if (cr->sync.outgoing_port_closed) {
1624 ASSERT_ractor_unlocking(cr);
1627 ractor_basket_setup(ec, &basket, v,
Qfalse, exc,
true,
true );
1630 if (ractor_try_yield(ec, cr, &basket)) {
1637 if (cr->sync.taking_ractors.cnt == 0) {
1638 cr->sync.wait.yielded_basket = basket;
1640 VM_ASSERT(cr->sync.wait.status == wait_none);
1641 cr->sync.wait.status = wait_yielding;
1642 cr->sync.wait.wakeup_status = wakeup_none;
1644 VM_ASSERT(cr->yield_atexit ==
false);
1645 cr->yield_atexit =
true;
1653 if (retry)
goto retry;
1661 ractor_close_incoming(ec, cr);
1662 ractor_close_outgoing(ec, cr);
1667 VM_ASSERT(cr->threads.main != NULL);
1668 cr->threads.main = NULL;
1677 ractor_yield_atexit(ec, cr, result,
false);
1684 ractor_yield_atexit(ec, cr, ec->errinfo,
true);
1690 for (
int i=0; i<len; i++) {
1691 ptr[i] = ractor_receive(ec, r);
1698 int len = RARRAY_LENINT(args);
1699 for (
int i=0; i<len; i++) {
1704MJIT_FUNC_EXPORTED
bool
1705rb_ractor_main_p_(
void)
1707 VM_ASSERT(rb_multi_ractor_p());
1709 return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
1713rb_obj_is_main_ractor(
VALUE gv)
1715 if (!rb_ractor_p(gv))
return false;
1717 return r == GET_VM()->ractor.main_ractor;
1723 return r->threads.cnt;
1738 ccan_list_for_each(&r->threads.set, th, lt_node) {
1739 switch (th->status) {
1740 case THREAD_RUNNABLE:
1741 case THREAD_STOPPED:
1742 case THREAD_STOPPED_FOREVER:
1743 ts[ts_cnt++] = th->self;
1751 VALUE ary = rb_ary_new();
1752 for (
int i=0; i<ts_cnt; i++) {
1753 rb_ary_push(ary, ts[i]);
1762 VM_ASSERT(th != NULL);
1766 RUBY_DEBUG_LOG(
"r(%d)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
1767 ccan_list_add_tail(&r->threads.set, &th->lt_node);
1773 if (r->threads.cnt == 1) {
1774 VM_ASSERT(ractor_status_p(r, ractor_created));
1775 vm_insert_ractor(th->vm, r);
1782 ractor_status_set(r, ractor_blocking);
1784 RUBY_DEBUG_LOG2(file, line,
"vm->ractor.blocking_cnt:%d++", vm->ractor.blocking_cnt);
1785 vm->ractor.blocking_cnt++;
1786 VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
1790rb_vm_ractor_blocking_cnt_inc(
rb_vm_t *vm,
rb_ractor_t *cr,
const char *file,
int line)
1792 ASSERT_vm_locking();
1793 VM_ASSERT(GET_RACTOR() == cr);
1794 vm_ractor_blocking_cnt_inc(vm, cr, file, line);
1798rb_vm_ractor_blocking_cnt_dec(
rb_vm_t *vm,
rb_ractor_t *cr,
const char *file,
int line)
1800 ASSERT_vm_locking();
1801 VM_ASSERT(GET_RACTOR() == cr);
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--;
1807 ractor_status_set(cr, ractor_running);
1811ractor_check_blocking(
rb_ractor_t *cr,
unsigned int remained_thread_cnt,
const char *file,
int line)
1813 VM_ASSERT(cr == GET_RACTOR());
1815 RUBY_DEBUG_LOG2(file, line,
1816 "cr->threads.cnt:%u cr->threads.blocking_cnt:%u vm->ractor.cnt:%u vm->ractor.blocking_cnt:%u",
1817 cr->threads.cnt, cr->threads.blocking_cnt,
1818 GET_VM()->ractor.cnt, GET_VM()->ractor.blocking_cnt);
1820 VM_ASSERT(cr->threads.cnt >= cr->threads.blocking_cnt + 1);
1822 if (remained_thread_cnt > 0 &&
1824 cr->threads.cnt == cr->threads.blocking_cnt + 1) {
1827 ASSERT_vm_unlocking();
1831 rb_vm_ractor_blocking_cnt_inc(vm, cr, file, line);
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__);
1844 if (cr->threads.cnt == 1) {
1845 vm_remove_ractor(th->vm, cr);
1850 ccan_list_del(&th->lt_node);
1858rb_ractor_blocking_threads_inc(
rb_ractor_t *cr,
const char *file,
int line)
1860 RUBY_DEBUG_LOG2(file, line,
"cr->threads.blocking_cnt:%d++", cr->threads.blocking_cnt);
1862 VM_ASSERT(cr->threads.cnt > 0);
1863 VM_ASSERT(cr == GET_RACTOR());
1865 ractor_check_blocking(cr, cr->threads.cnt, __FILE__, __LINE__);
1866 cr->threads.blocking_cnt++;
1870rb_ractor_blocking_threads_dec(
rb_ractor_t *cr,
const char *file,
int line)
1872 RUBY_DEBUG_LOG2(file, line,
1873 "r->threads.blocking_cnt:%d--, r->threads.cnt:%u",
1874 cr->threads.blocking_cnt, cr->threads.cnt);
1876 VM_ASSERT(cr == GET_RACTOR());
1878 if (cr->threads.cnt == cr->threads.blocking_cnt) {
1883 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
1888 cr->threads.blocking_cnt--;
1892rb_ractor_vm_barrier_interrupt_running_thread(
rb_ractor_t *r)
1894 VM_ASSERT(r != GET_RACTOR());
1895 ASSERT_ractor_unlocking(r);
1896 ASSERT_vm_locking();
1900 if (ractor_status_p(r, ractor_running)) {
1903 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec);
1911rb_ractor_terminate_interrupt_main_thread(
rb_ractor_t *r)
1913 VM_ASSERT(r != GET_RACTOR());
1914 ASSERT_ractor_unlocking(r);
1915 ASSERT_vm_locking();
1919 if (main_th->status != THREAD_KILLED) {
1920 RUBY_VM_SET_TERMINATE_INTERRUPT(main_th->ec);
1921 rb_threadptr_interrupt(main_th);
1924 RUBY_DEBUG_LOG(
"killed (%p)", (
void *)main_th);
1932ractor_terminal_interrupt_all(
rb_vm_t *vm)
1934 if (vm->ractor.cnt > 1) {
1937 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
1938 if (r != vm->ractor.main_ractor) {
1939 rb_ractor_terminate_interrupt_main_thread(r);
1946rb_ractor_terminate_all(
void)
1951 VM_ASSERT(cr == GET_RACTOR());
1953 if (vm->ractor.cnt > 1) {
1955 ractor_terminal_interrupt_all(vm);
1958 rb_thread_terminate_all(GET_THREAD());
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;
1967 rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
1968 rb_vm_cond_timedwait(vm, &vm->ractor.sync.terminate_cond, 1000 );
1969 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
1971 ractor_terminal_interrupt_all(vm);
1978rb_vm_main_ractor_ec(
rb_vm_t *vm)
1980 return vm->ractor.main_ractor->threads.running_ec;
1984ractor_moved_missing(
int argc,
VALUE *argv,
VALUE self)
1986 rb_raise(rb_eRactorMovedError,
"can not send any methods to a moved object");
2097 rb_define_method(rb_cRactorMovedObject,
"method_missing", ractor_moved_missing, -1);
2100 rb_define_method(rb_cRactorMovedObject,
"__send__", 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);
2116 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
2117 if (r != vm->ractor.main_ractor) {
2118 fprintf(stderr,
"r:%u (%s)\n", r->pub.id, ractor_status_str(r->status_));
2126 if (rb_ractor_main_p()) {
2138 if (rb_ractor_main_p()) {
2143 return cr->r_stdout;
2150 if (rb_ractor_main_p()) {
2155 return cr->r_stderr;
2162 if (rb_ractor_main_p()) {
2174 if (rb_ractor_main_p()) {
2186 if (rb_ractor_main_p()) {
2198 return &cr->pub.hooks;
2207enum obj_traverse_iterator_result {
2213typedef enum obj_traverse_iterator_result (*rb_obj_traverse_enter_func)(
VALUE obj);
2214typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(
VALUE obj);
2215typedef enum obj_traverse_iterator_result (*rb_obj_traverse_final_func)(
VALUE obj);
2217static enum obj_traverse_iterator_result null_leave(
VALUE obj);
2220 rb_obj_traverse_enter_func enter_func;
2221 rb_obj_traverse_leave_func leave_func;
2240 if (obj_traverse_i(key, d->data)) {
2245 if (obj_traverse_i(val, d->data)) {
2253static enum rb_id_table_iterator_result
2254obj_hash_iv_traverse_i(
VALUE val,
void *ptr)
2258 if (obj_traverse_i(val, d->data)) {
2260 return ID_TABLE_STOP;
2263 return ID_TABLE_CONTINUE;
2267obj_traverse_reachable_i(
VALUE obj,
void *ptr)
2271 if (obj_traverse_i(obj, d->data)) {
2279 if (UNLIKELY(!data->rec)) {
2280 data->rec_hash = rb_ident_hash_new();
2281 data->rec = rb_hash_st_table(data->rec_hash);
2289 if (RB_SPECIAL_CONST_P(obj))
return 0;
2291 switch (data->enter_func(obj)) {
2292 case traverse_cont:
break;
2293 case traverse_skip:
return 0;
2294 case traverse_stop:
return 1;
2297 if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
2304 rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
2305 for (uint32_t i = 0; i < ivtbl->numiv; i++) {
2306 VALUE val = ivtbl->ivptr[i];
2307 if (!UNDEF_P(val) && obj_traverse_i(val, data))
return 1;
2324 if (rb_shape_obj_too_complex(obj)) {
2329 rb_id_table_foreach_values(ROBJECT_IV_HASH(obj), obj_hash_iv_traverse_i, &d);
2330 if (d.stop)
return 1;
2333 uint32_t len = ROBJECT_IV_COUNT(obj);
2334 VALUE *ptr = ROBJECT_IVPTR(obj);
2336 for (uint32_t i=0; i<len; i++) {
2338 if (!UNDEF_P(val) && obj_traverse_i(val, data))
return 1;
2346 for (
int i = 0; i < RARRAY_LENINT(obj); i++) {
2347 VALUE e = rb_ary_entry(obj, i);
2348 if (obj_traverse_i(e, data))
return 1;
2362 if (d.stop)
return 1;
2368 long len = RSTRUCT_LEN(obj);
2369 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2371 for (
long i=0; i<len; i++) {
2372 if (obj_traverse_i(ptr[i], data))
return 1;
2378 if (obj_traverse_i(RRATIONAL(obj)->num, data))
return 1;
2379 if (obj_traverse_i(RRATIONAL(obj)->den, data))
return 1;
2382 if (obj_traverse_i(RCOMPLEX(obj)->real, data))
return 1;
2383 if (obj_traverse_i(RCOMPLEX(obj)->imag, data))
return 1;
2393 RB_VM_LOCK_ENTER_NO_BARRIER();
2395 rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
2397 RB_VM_LOCK_LEAVE_NO_BARRIER();
2398 if (d.stop)
return 1;
2411 if (data->leave_func(obj) == traverse_stop) {
2420 rb_obj_traverse_final_func final_func;
2425obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
2428 if (data->final_func(key)) {
2438rb_obj_traverse(
VALUE obj,
2439 rb_obj_traverse_enter_func enter_func,
2440 rb_obj_traverse_leave_func leave_func,
2441 rb_obj_traverse_final_func final_func)
2444 .enter_func = enter_func,
2445 .leave_func = leave_func,
2449 if (obj_traverse_i(obj, &data))
return 1;
2450 if (final_func && data.rec) {
2452 st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
2459frozen_shareable_p(
VALUE obj,
bool *made_shareable)
2461 if (!RB_TYPE_P(obj,
T_DATA)) {
2464 else if (RTYPEDDATA_P(obj)) {
2466 if (
type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
2471 rb_proc_ractor_make_shareable(obj);
2472 *made_shareable =
true;
2481static enum obj_traverse_iterator_result
2482make_shareable_check_shareable(
VALUE obj)
2485 bool made_shareable =
false;
2488 return traverse_skip;
2490 else if (!frozen_shareable_p(obj, &made_shareable)) {
2491 if (made_shareable) {
2492 return traverse_skip;
2495 rb_raise(rb_eRactorError,
"can not make shareable object for %"PRIsVALUE, obj);
2499 if (!RB_OBJ_FROZEN_RAW(obj)) {
2500 rb_funcall(obj, idFreeze, 0);
2502 if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
2503 rb_raise(rb_eRactorError,
"#freeze does not freeze object correctly");
2507 return traverse_skip;
2511 return traverse_cont;
2514static enum obj_traverse_iterator_result
2515mark_shareable(
VALUE obj)
2518 return traverse_cont;
2524 rb_obj_traverse(obj,
2525 make_shareable_check_shareable,
2526 null_leave, mark_shareable);
2533 VALUE copy = ractor_copy(obj);
2534 rb_obj_traverse(copy,
2535 make_shareable_check_shareable,
2536 null_leave, mark_shareable);
2541rb_ractor_ensure_shareable(
VALUE obj,
VALUE name)
2544 VALUE message =
rb_sprintf(
"cannot assign unshareable object to %"PRIsVALUE,
2552rb_ractor_ensure_main_ractor(
const char *msg)
2554 if (!rb_ractor_main_p()) {
2555 rb_raise(rb_eRactorIsolationError,
"%s", msg);
2559static enum obj_traverse_iterator_result
2560shareable_p_enter(
VALUE obj)
2563 return traverse_skip;
2565 else if (RB_TYPE_P(obj,
T_CLASS) ||
2569 mark_shareable(obj);
2570 return traverse_skip;
2572 else if (RB_OBJ_FROZEN_RAW(obj) &&
2573 frozen_shareable_p(obj, NULL)) {
2574 return traverse_cont;
2577 return traverse_stop;
2580MJIT_FUNC_EXPORTED
bool
2581rb_ractor_shareable_p_continue(
VALUE obj)
2583 if (rb_obj_traverse(obj,
2584 shareable_p_enter, null_leave,
2593#if RACTOR_CHECK_MODE > 0
2594static enum obj_traverse_iterator_result
2595reset_belonging_enter(
VALUE obj)
2598 return traverse_skip;
2601 rb_ractor_setup_belonging(obj);
2602 return traverse_cont;
2607static enum obj_traverse_iterator_result
2608null_leave(
VALUE obj)
2610 return traverse_cont;
2614ractor_reset_belonging(
VALUE obj)
2616#if RACTOR_CHECK_MODE > 0
2617 rb_obj_traverse(obj, reset_belonging_enter, null_leave, NULL);
2635 rb_obj_traverse_replace_enter_func enter_func;
2636 rb_obj_traverse_replace_leave_func leave_func;
2652obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp,
int error)
2658obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr,
int exists)
2663 if (obj_traverse_replace_i(*key, data)) {
2667 else if (*key != data->replacement) {
2668 VALUE v = *key = data->replacement;
2672 if (obj_traverse_replace_i(*val, data)) {
2676 else if (*val != data->replacement) {
2677 VALUE v = *val = data->replacement;
2684static enum rb_id_table_iterator_result
2685obj_iv_hash_traverse_replace_foreach_i(
VALUE val,
void *data)
2687 return ID_TABLE_REPLACE;
2690static enum rb_id_table_iterator_result
2691obj_iv_hash_traverse_replace_i(
VALUE *val,
void *ptr,
int exists)
2696 if (obj_traverse_replace_i(*val, data)) {
2698 return ID_TABLE_STOP;
2700 else if (*val != data->replacement) {
2701 VALUE v = *val = data->replacement;
2705 return ID_TABLE_CONTINUE;
2711 if (UNLIKELY(!data->rec)) {
2712 data->rec_hash = rb_ident_hash_new();
2713 data->rec = rb_hash_st_table(data->rec_hash);
2718#if USE_TRANSIENT_HEAP
2719void rb_ary_transient_heap_evacuate(
VALUE ary,
int promote);
2720void rb_obj_transient_heap_evacuate(
VALUE obj,
int promote);
2721void rb_hash_transient_heap_evacuate(
VALUE hash,
int promote);
2722void rb_struct_transient_heap_evacuate(
VALUE st,
int promote);
2726obj_refer_only_shareables_p_i(
VALUE obj,
void *ptr)
2728 int *pcnt = (
int *)ptr;
2736obj_refer_only_shareables_p(
VALUE obj)
2739 RB_VM_LOCK_ENTER_NO_BARRIER();
2741 rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
2743 RB_VM_LOCK_LEAVE_NO_BARRIER();
2750 st_data_t replacement;
2752 if (RB_SPECIAL_CONST_P(obj)) {
2753 data->replacement = obj;
2757 switch (data->enter_func(obj, data)) {
2758 case traverse_cont:
break;
2759 case traverse_skip:
return 0;
2760 case traverse_stop:
return 1;
2763 replacement = (st_data_t)data->replacement;
2765 if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, &replacement))) {
2766 data->replacement = (
VALUE)replacement;
2770 st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, replacement);
2777#define CHECK_AND_REPLACE(v) do { \
2779 if (obj_traverse_replace_i(_val, data)) { return 1; } \
2780 else if (data->replacement != _val) { RB_OBJ_WRITE(obj, &v, data->replacement); } \
2785 rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
2786 for (uint32_t i = 0; i < ivtbl->numiv; i++) {
2787 if (!UNDEF_P(ivtbl->ivptr[i])) {
2788 CHECK_AND_REPLACE(ivtbl->ivptr[i]);
2803 rb_str_make_independent(obj);
2808 if (rb_shape_obj_too_complex(obj)) {
2809 struct rb_id_table * table = ROBJECT_IV_HASH(obj);
2815 rb_id_table_foreach_values_with_replace(table,
2816 obj_iv_hash_traverse_replace_foreach_i,
2817 obj_iv_hash_traverse_replace_i,
2821#if USE_TRANSIENT_HEAP
2822 if (data->move) rb_obj_transient_heap_evacuate(obj, TRUE);
2825 uint32_t len = ROBJECT_IV_COUNT(obj);
2826 VALUE *ptr = ROBJECT_IVPTR(obj);
2828 for (uint32_t i=0; i<len; i++) {
2829 if (!UNDEF_P(ptr[i])) {
2830 CHECK_AND_REPLACE(ptr[i]);
2839 rb_ary_cancel_sharing(obj);
2840#if USE_TRANSIENT_HEAP
2841 if (data->move) rb_ary_transient_heap_evacuate(obj, TRUE);
2844 for (
int i = 0; i < RARRAY_LENINT(obj); i++) {
2845 VALUE e = rb_ary_entry(obj, i);
2847 if (obj_traverse_replace_i(e, data)) {
2850 else if (e != data->replacement) {
2851 RARRAY_ASET(obj, i, data->replacement);
2860#if USE_TRANSIENT_HEAP
2861 if (data->move) rb_hash_transient_heap_evacuate(obj, TRUE);
2868 rb_hash_stlike_foreach_with_replace(obj,
2869 obj_hash_traverse_replace_foreach_i,
2870 obj_hash_traverse_replace_i,
2872 if (d.stop)
return 1;
2876 if (obj_traverse_replace_i(ifnone, data)) {
2879 else if (ifnone != data->replacement) {
2887#if USE_TRANSIENT_HEAP
2888 if (data->move) rb_struct_transient_heap_evacuate(obj, TRUE);
2890 long len = RSTRUCT_LEN(obj);
2891 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2893 for (
long i=0; i<len; i++) {
2894 CHECK_AND_REPLACE(ptr[i]);
2900 CHECK_AND_REPLACE(RRATIONAL(obj)->num);
2901 CHECK_AND_REPLACE(RRATIONAL(obj)->den);
2904 CHECK_AND_REPLACE(RCOMPLEX(obj)->real);
2905 CHECK_AND_REPLACE(RCOMPLEX(obj)->imag);
2909 if (!data->move && obj_refer_only_shareables_p(obj)) {
2913 rb_raise(rb_eRactorError,
"can not %s %"PRIsVALUE
" object.",
2930 data->replacement = (
VALUE)replacement;
2932 if (data->leave_func(obj, data) == traverse_stop) {
2943rb_obj_traverse_replace(
VALUE obj,
2944 rb_obj_traverse_replace_enter_func enter_func,
2945 rb_obj_traverse_replace_leave_func leave_func,
2949 .enter_func = enter_func,
2950 .leave_func = leave_func,
2956 if (obj_traverse_replace_i(obj, &data)) {
2960 return data.replacement;
2979ractor_moved_bang(
VALUE obj)
2982 struct RVALUE *rv = (
void *)obj;
2984 rv->klass = rb_cRactorMovedObject;
2988 rv->flags = rv->flags & ~fl_users;
2993static enum obj_traverse_iterator_result
2997 data->replacement = obj;
2998 return traverse_skip;
3002 return traverse_cont;
3006void rb_replace_generic_ivar(
VALUE clone,
VALUE obj);
3008static enum obj_traverse_iterator_result
3011 VALUE v = data->replacement;
3015 dst->flags = (dst->flags & ~fl_users) | (src->flags & fl_users);
3022 rb_replace_generic_ivar(v, obj);
3027 ractor_moved_bang(obj);
3028 return traverse_cont;
3032ractor_move(
VALUE obj)
3034 VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave,
true);
3035 if (!UNDEF_P(val)) {
3039 rb_raise(rb_eRactorError,
"can not move the object");
3043static enum obj_traverse_iterator_result
3047 data->replacement = obj;
3048 return traverse_skip;
3052 return traverse_cont;
3056static enum obj_traverse_iterator_result
3059 return traverse_cont;
3063ractor_copy(
VALUE obj)
3065 VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave,
false);
3066 if (!UNDEF_P(val)) {
3070 rb_raise(rb_eRactorError,
"can not copy the object");
3085} freed_ractor_local_keys;
3088ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
3091 if (k->type->mark) (*k->type->mark)((
void *)val);
3095static enum rb_id_table_iterator_result
3096idkey_local_storage_mark_i(
ID id,
VALUE val,
void *dmy)
3099 return ID_TABLE_CONTINUE;
3105 if (r->local_storage) {
3106 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
3108 for (
int i=0; i<freed_ractor_local_keys.cnt; i++) {
3110 st_data_t val, k = (st_data_t)key;
3111 if (st_delete(r->local_storage, &k, &val) &&
3113 (*key->type->free)((
void *)val);
3118 if (r->idkey_local_storage) {
3119 rb_id_table_foreach(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
3124ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
3127 if (k->type->free) (*k->type->free)((
void *)val);
3134 if (r->local_storage) {
3135 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
3136 st_free_table(r->local_storage);
3139 if (r->idkey_local_storage) {
3140 rb_id_table_free(r->idkey_local_storage);
3145rb_ractor_local_storage_value_mark(
void *ptr)
3147 rb_gc_mark((
VALUE)ptr);
3161 rb_ractor_local_storage_value_mark,
3169 key->type =
type ?
type : &ractor_local_storage_type_null;
3170 key->main_cache = (
void *)
Qundef;
3185 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
3186 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
3189 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
3197 if (rb_ractor_main_p()) {
3198 if (!UNDEF_P((
VALUE)key->main_cache)) {
3199 *pret = key->main_cache;
3209 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
3223 if (cr->local_storage == NULL) {
3224 cr->local_storage = st_init_numtable();
3227 st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
3229 if (rb_ractor_main_p()) {
3230 key->main_cache = ptr;
3238 if (ractor_local_ref(key, &val)) {
3249 if (ractor_local_ref(key, (
void **)val)) {
3260 ractor_local_set(key, (
void *)val);
3267 if (ractor_local_ref(key, &ret)) {
3278 ractor_local_set(key, ptr);
3281#define DEFAULT_KEYS_CAPA 0x10
3284rb_ractor_finish_marking(
void)
3286 for (
int i=0; i<freed_ractor_local_keys.cnt; i++) {
3287 ruby_xfree(freed_ractor_local_keys.keys[i]);
3289 freed_ractor_local_keys.cnt = 0;
3290 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
3291 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
3301 struct rb_id_table *tbl = cr->idkey_local_storage;
3304 if (
id && tbl && rb_id_table_lookup(tbl,
id, &val)) {
3317 struct rb_id_table *tbl = cr->idkey_local_storage;
3320 tbl = cr->idkey_local_storage = rb_id_table_create(2);
3322 rb_id_table_insert(tbl,
id, val);
3326#include "ractor.rbinc"
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.
@ 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.
#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 FL_USER3
Old name of RUBY_FL_USER3.
#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 FL_USER7
Old name of RUBY_FL_USER7.
#define FL_USER10
Old name of RUBY_FL_USER10.
#define Qundef
Old name of RUBY_Qundef.
#define FL_USER6
Old name of RUBY_FL_USER6.
#define T_FLOAT
Old name of RUBY_T_FLOAT.
#define T_IMEMO
Old name of RUBY_T_IMEMO.
#define FL_USER1
Old name of RUBY_FL_USER1.
#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 FL_USER19
Old name of RUBY_FL_USER19.
#define SYM2ID
Old name of RB_SYM2ID.
#define FL_USER12
Old name of RUBY_FL_USER12.
#define T_DATA
Old name of RUBY_T_DATA.
#define FL_USER11
Old name of RUBY_FL_USER11.
#define FL_USER15
Old name of RUBY_FL_USER15.
#define T_MODULE
Old name of RUBY_T_MODULE.
#define FL_USER8
Old name of RUBY_FL_USER8.
#define FL_USER14
Old name of RUBY_FL_USER14.
#define FL_USER17
Old name of RUBY_FL_USER17.
#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 FL_USER18
Old name of RUBY_FL_USER18.
#define FL_USER2
Old name of RUBY_FL_USER2.
#define FL_USER9
Old name of RUBY_FL_USER9.
#define Qtrue
Old name of RUBY_Qtrue.
#define FL_USER13
Old name of RUBY_FL_USER13.
#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 T_SYMBOL
Old name of RUBY_T_SYMBOL.
#define T_MATCH
Old name of RUBY_T_MATCH.
#define FL_USER16
Old name of RUBY_FL_USER16.
#define T_CLASS
Old name of RUBY_T_CLASS.
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
#define FL_USER5
Old name of RUBY_FL_USER5.
#define FL_USER4
Old name of RUBY_FL_USER4.
#define FL_SET_RAW
Old name of RB_FL_SET_RAW.
#define T_REGEXP
Old name of RUBY_T_REGEXP.
void rb_raise(VALUE exc, const char *fmt,...)
Exception entry point.
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.
void rb_bug(const char *fmt,...)
Interpreter panic switch.
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_eArgError
ArgumentError exception.
VALUE rb_cObject
Documented in include/ruby/internal/globals.h.
VALUE rb_obj_alloc(VALUE klass)
Allocates an instance of the given class.
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_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.
void rb_thread_check_ints(void)
Checks for interrupts.
VALUE rb_mutex_unlock(VALUE mutex)
Releases the mutex.
VALUE rb_mutex_lock(VALUE mutex)
Attempts to lock the mutex.
VALUE rb_ivar_set(VALUE obj, ID name, VALUE val)
Identical to rb_iv_set(), except it accepts the name as an ID instead of a C string.
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.
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_stderr(void)
Queries the standard error of the current Ractor that is calling this function.
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.
VALUE rb_ractor_stdout(void)
Queries the standard output of the current Ractor that is calling this function.
#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_UBF_ASYNC_SAFE
Passing this flag to rb_nogvl() indicates that the passed UBF is async-signal-safe.
#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...
VALUE rb_sprintf(const char *fmt,...)
Ruby's extended sprintf(3).
VALUE rb_yield(VALUE val)
Yields the block.
#define ALLOCA_N(type, n)
#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 type(ANYARGS)
ANYARGS-ed function type.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
#define RARRAY_AREF(a, i)
#define DATA_PTR(obj)
Convenient getter macro.
#define RHASH_SET_IFNONE(h, ifnone)
Destructively updates the default value of the hash.
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
#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...
#define RTEST
This is an old name of RB_TEST.
Type that defines 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_destroy(rb_nativethread_cond_t *cond)
Destroys the passed condition variable.
void rb_native_cond_signal(rb_nativethread_cond_t *cond)
Signals a condition variable.
void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
Waits for the passed condition variable to be signalled.
uintptr_t VALUE
Type that represents a Ruby object.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.