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"
23VALUE rb_eRactorUnsafeError;
24VALUE rb_eRactorIsolationError;
25static VALUE rb_eRactorError;
26static VALUE rb_eRactorRemoteError;
27static VALUE rb_eRactorMovedError;
28static VALUE rb_eRactorClosedError;
29static VALUE rb_cRactorMovedObject;
31static void vm_ractor_blocking_cnt_inc(
rb_vm_t *vm,
rb_ractor_t *r,
const char *file,
int line);
36#if RACTOR_CHECK_MODE > 0
38 if (rb_current_execution_context(
false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) {
39 rb_bug(
"recursive ractor locking");
47#if RACTOR_CHECK_MODE > 0
49 if (rb_current_execution_context(
false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) {
50 rp(r->sync.locked_by);
51 rb_bug(
"ractor lock is not acquired.");
57ractor_lock(
rb_ractor_t *r,
const char *file,
int line)
59 RUBY_DEBUG_LOG2(file, line,
"locking r:%u%s", r->pub.id, GET_RACTOR() == r ?
" (self)" :
"");
61 ASSERT_ractor_unlocking(r);
64#if RACTOR_CHECK_MODE > 0
65 if (rb_current_execution_context(
false) != NULL) {
66 r->sync.locked_by = rb_ractor_self(GET_RACTOR());
70 RUBY_DEBUG_LOG2(file, line,
"locked r:%u%s", r->pub.id, GET_RACTOR() == r ?
" (self)" :
"");
74ractor_lock_self(
rb_ractor_t *cr,
const char *file,
int line)
76 VM_ASSERT(cr == GET_RACTOR());
77 VM_ASSERT(cr->sync.locked_by != cr->pub.self);
78 ractor_lock(cr, file, line);
82ractor_unlock(
rb_ractor_t *r,
const char *file,
int line)
84 ASSERT_ractor_locking(r);
85#if RACTOR_CHECK_MODE > 0
86 r->sync.locked_by =
Qnil;
90 RUBY_DEBUG_LOG2(file, line,
"r:%u%s", r->pub.id, GET_RACTOR() == r ?
" (self)" :
"");
94ractor_unlock_self(
rb_ractor_t *cr,
const char *file,
int line)
96 VM_ASSERT(cr == GET_RACTOR());
97 VM_ASSERT(cr->sync.locked_by == cr->pub.self);
98 ractor_unlock(cr, file, line);
101#define RACTOR_LOCK(r) ractor_lock(r, __FILE__, __LINE__)
102#define RACTOR_UNLOCK(r) ractor_unlock(r, __FILE__, __LINE__)
103#define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
104#define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
109#if RACTOR_CHECK_MODE > 0
110 VALUE locked_by = r->sync.locked_by;
111 r->sync.locked_by =
Qnil;
115#if RACTOR_CHECK_MODE > 0
116 r->sync.locked_by = locked_by;
121ractor_status_str(
enum ractor_status status)
124 case ractor_created:
return "created";
125 case ractor_running:
return "running";
126 case ractor_blocking:
return "blocking";
127 case ractor_terminated:
return "terminated";
133ractor_status_set(
rb_ractor_t *r,
enum ractor_status status)
135 RUBY_DEBUG_LOG(
"r:%u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
138 if (r->status_ != ractor_created) {
139 VM_ASSERT(r == GET_RACTOR());
144 switch (r->status_) {
146 VM_ASSERT(status == ractor_blocking);
149 VM_ASSERT(status == ractor_blocking||
150 status == ractor_terminated);
152 case ractor_blocking:
153 VM_ASSERT(status == ractor_running);
155 case ractor_terminated:
164ractor_status_p(
rb_ractor_t *r,
enum ractor_status status)
166 return rb_ractor_status_p(r, status);
174 for (
int i=0; i<rq->cnt; i++) {
181static void ractor_local_storage_mark(
rb_ractor_t *r);
182static void ractor_local_storage_free(
rb_ractor_t *r);
185ractor_mark(
void *ptr)
189 ractor_queue_mark(&r->sync.incoming_queue);
193 rb_gc_mark(r->sync.wait.yielded_basket.sender);
201 rb_hook_list_mark(&r->pub.hooks);
203 if (r->threads.cnt > 0) {
205 list_for_each(&r->threads.set, th, lt_node) {
206 VM_ASSERT(th != NULL);
211 ractor_local_storage_mark(r);
227ractor_free(
void *ptr)
232 ractor_queue_free(&r->sync.incoming_queue);
233 ractor_waiting_list_free(&r->sync.taking_ractors);
234 ractor_local_storage_free(r);
235 rb_hook_list_free(&r->pub.hooks);
252ractor_memsize(
const void *ptr)
258 ractor_queue_memsize(&r->sync.incoming_queue) +
259 ractor_waiting_list_memsize(&r->sync.taking_ractors);
270 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
276 if (rb_typeddata_is_kind_of(gv, &ractor_data_type)) {
285RACTOR_PTR(VALUE
self)
287 VM_ASSERT(rb_ractor_p(
self));
294static rb_atomic_t ractor_last_id;
296#if RACTOR_CHECK_MODE > 0
297MJIT_FUNC_EXPORTED uint32_t
298rb_ractor_current_id(
void)
300 if (GET_THREAD()->ractor == NULL) {
304 return rb_ractor_id(GET_RACTOR());
321 return &rq->baskets[(rq->start + i) % rq->size];
327 ASSERT_ractor_locking(GET_RACTOR());
329 if (rq->reserved_cnt == 0) {
331 rq->start = (rq->start + 1) % rq->size;
335 ractor_queue_at(rq, 0)->type = basket_type_deleted;
343 return b->type == basket_type_deleted ||
344 b->type == basket_type_reserved;
350 ASSERT_ractor_locking(r);
352 while (rq->cnt > 0 && ractor_queue_at(rq, 0)->
type == basket_type_deleted) {
353 ractor_queue_advance(rq);
360 ASSERT_ractor_locking(r);
366 ractor_queue_compact(r, rq);
368 for (
int i=0; i<rq->cnt; i++) {
369 if (!ractor_queue_skip_p(rq, i)) {
384 if (!ractor_queue_empty_p(r, rq)) {
385 for (
int i=0; i<rq->cnt; i++) {
386 if (!ractor_queue_skip_p(rq, i)) {
391 b->type = basket_type_deleted;
392 ractor_queue_compact(r, rq);
407 ASSERT_ractor_locking(r);
409 if (rq->size <= rq->cnt) {
410 rq->baskets = realloc(rq->baskets,
sizeof(
struct rb_ractor_basket) * rq->size * 2);
411 for (
int i=rq->size - rq->start; i<rq->cnt; i++) {
412 rq->baskets[i + rq->start] = rq->baskets[i + rq->start - rq->size];
416 rq->baskets[(rq->start + rq->cnt++) % rq->size] = *basket;
423 b->type = basket_type_none;
428static VALUE ractor_reset_belonging(VALUE obj);
434 case basket_type_ref:
436 case basket_type_copy:
437 case basket_type_move:
438 case basket_type_will:
439 b->type = basket_type_ref;
440 b->v = ractor_reset_belonging(b->v);
452 VALUE v = ractor_basket_value(b);
456 VALUE err =
rb_exc_new_cstr(rb_eRactorRemoteError,
"thrown by remote Ractor.");
458 ractor_basket_clear(b);
459 rb_ec_setup_exception(NULL, err, cause);
463 ractor_basket_clear(b);
470 if (r->receiving_mutex && rb_mutex_owned_p(r->receiving_mutex)) {
471 rb_raise(rb_eRactorError,
"can not call receive/receive_if recursively");
481 ractor_recursive_receive_if(r);
483 if (ractor_queue_deq(r, rq, &basket) ==
false) {
484 if (r->sync.incoming_port_closed) {
485 rb_raise(rb_eRactorClosedError,
"The incoming port is already closed");
492 return ractor_basket_accept(&basket);
496ractor_sleeping_by(
const rb_ractor_t *r,
enum ractor_wait_status wait_status)
498 return (r->sync.wait.status & wait_status) && r->sync.wait.wakeup_status == wakeup_none;
502ractor_wakeup(
rb_ractor_t *r,
enum ractor_wait_status wait_status,
enum ractor_wakeup_status wakeup_status)
504 ASSERT_ractor_locking(r);
510 if (ractor_sleeping_by(r, wait_status)) {
511 r->sync.wait.wakeup_status = wakeup_status;
521ractor_sleep_wo_gvl(
void *ptr)
524 RACTOR_LOCK_SELF(cr);
526 VM_ASSERT(cr->sync.wait.status != wait_none);
527 if (cr->sync.wait.wakeup_status == wakeup_none) {
528 ractor_cond_wait(cr);
530 cr->sync.wait.status = wait_none;
532 RACTOR_UNLOCK_SELF(cr);
537ractor_sleep_interrupt(
void *ptr)
543 ractor_wakeup(r, wait_receiving | wait_taking | wait_yielding, wakeup_by_interrupt);
548#if defined(USE_RUBY_DEBUG_LOG) && USE_RUBY_DEBUG_LOG
550wait_status_str(
enum ractor_wait_status wait_status)
552 switch ((
int)wait_status) {
553 case wait_none:
return "none";
554 case wait_receiving:
return "receiving";
555 case wait_taking:
return "taking";
556 case wait_yielding:
return "yielding";
557 case wait_receiving|wait_taking:
return "receiving|taking";
558 case wait_receiving|wait_yielding:
return "receiving|yielding";
559 case wait_taking|wait_yielding:
return "taking|yielding";
560 case wait_receiving|wait_taking|wait_yielding:
return "receiving|taking|yielding";
566wakeup_status_str(
enum ractor_wakeup_status wakeup_status)
568 switch (wakeup_status) {
569 case wakeup_none:
return "none";
570 case wakeup_by_send:
return "by_send";
571 case wakeup_by_yield:
return "by_yield";
572 case wakeup_by_take:
return "by_take";
573 case wakeup_by_close:
return "by_close";
574 case wakeup_by_interrupt:
return "by_interrupt";
575 case wakeup_by_retry:
return "by_retry";
584 VM_ASSERT(GET_RACTOR() == cr);
585 VM_ASSERT(cr->sync.wait.status != wait_none);
592 ractor_sleep_interrupt, cr,
598 if (cr->sync.wait.status != wait_none) {
599 cr->sync.wait.status = wait_none;
600 cr->sync.wait.wakeup_status = wakeup_by_interrupt;
611 VM_ASSERT(cr == GET_RACTOR());
612 bool retry_try =
false;
616 if (ractor_sleeping_by(r, wait_yielding)) {
624 for (
int i=0; i<wl->cnt; i++) {
625 if (wl->ractors[i] == cr) {
628 rb_raise(rb_eRuntimeError,
"Already another thread of same ractor is waiting.");
634 wl->ractors = malloc(
sizeof(
rb_ractor_t *) * wl->size);
635 if (wl->ractors == NULL)
rb_bug(
"can't allocate buffer");
637 else if (wl->size <= wl->cnt + 1) {
639 wl->ractors = realloc(wl->ractors,
sizeof(
rb_ractor_t *) * wl->size);
640 if (wl->ractors == NULL)
rb_bug(
"can't re-allocate buffer");
642 wl->ractors[wl->cnt++] = cr;
650 if (cr->sync.wait.wakeup_status == wakeup_none) {
651 VM_ASSERT(cr->sync.wait.status != wait_none);
653 cr->sync.wait.wakeup_status = wakeup_by_retry;
654 cr->sync.wait.status = wait_none;
667 for (
int i=0; i<wl->cnt; i++) {
668 if (wl->ractors[i] == wr) {
675 for (
int i=pos; i<wl->cnt; i++) {
676 wl->ractors[i] = wl->ractors[i+1];
686 ASSERT_ractor_locking(r);
687 VM_ASSERT(&r->sync.taking_ractors == wl);
691 for (
int i=1; i<wl->cnt; i++) {
692 wl->ractors[i-1] = wl->ractors[i];
705 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
706 ractor_recursive_receive_if(cr);
710 if (ractor_queue_empty_p(cr, &cr->sync.incoming_queue)) {
711 VM_ASSERT(cr->sync.wait.status == wait_none);
712 cr->sync.wait.status = wait_receiving;
713 cr->sync.wait.wakeup_status = wakeup_none;
714 ractor_sleep(ec, cr);
715 cr->sync.wait.wakeup_status = wakeup_none;
724 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
727 while ((v = ractor_try_receive(ec, cr)) ==
Qundef) {
728 ractor_receive_wait(ec, cr);
737basket_type_name(
enum rb_ractor_basket_type
type)
740#define T(t) case basket_type_##t: return #t
748 default:
rb_bug(
"unreachable");
756 for (
int i=0; i<rq->cnt; i++) {
758 fprintf(stderr,
"%d (start:%d) type:%s %p %s\n", i, rq->start, basket_type_name(b->type),
760 if (b->type == basket_type_reserved) bug =
true;
777 VALUE m = cr->receiving_mutex;
785receive_if_body(VALUE ptr)
789 ractor_receive_if_lock(data->cr);
790 VALUE block_result =
rb_yield(data->v);
792 RACTOR_LOCK_SELF(data->cr);
795 VM_ASSERT(b->type == basket_type_reserved);
796 data->rq->reserved_cnt--;
798 if (
RTEST(block_result)) {
799 b->type = basket_type_deleted;
800 ractor_queue_compact(data->cr, data->rq);
803 b->type = basket_type_ref;
806 RACTOR_UNLOCK_SELF(data->cr);
808 data->success =
true;
810 if (
RTEST(block_result)) {
819receive_if_ensure(VALUE v)
823 if (!data->success) {
824 RACTOR_LOCK_SELF(data->cr);
827 VM_ASSERT(b->type == basket_type_reserved);
828 b->type = basket_type_deleted;
829 data->rq->reserved_cnt--;
831 RACTOR_UNLOCK_SELF(data->cr);
844 unsigned int serial = (
unsigned int)-1;
851 ractor_receive_wait(ec, cr);
853 RACTOR_LOCK_SELF(cr);
855 if (serial != rq->serial) {
861 for (
int i=index; i<rq->cnt; i++) {
862 if (!ractor_queue_skip_p(rq, i)) {
864 v = ractor_basket_value(b);
865 b->type = basket_type_reserved;
872 RACTOR_UNLOCK_SELF(cr);
883 VALUE result =
rb_ensure(receive_if_body, (VALUE)&data,
884 receive_if_ensure, (VALUE)&data);
886 if (result !=
Qundef)
return result;
900 if (r->sync.incoming_port_closed) {
904 ractor_queue_enq(r, rq, b);
905 if (ractor_wakeup(r, wait_receiving, wakeup_by_send)) {
906 RUBY_DEBUG_LOG(
"wakeup");
913 rb_raise(rb_eRactorClosedError,
"The incoming-port is already closed");
917static VALUE ractor_move(VALUE obj);
918static VALUE ractor_copy(VALUE obj);
923 basket->sender = rb_ec_ractor_ptr(ec)->pub.self;
924 basket->exception = exc;
927 basket->type = basket_type_will;
931 basket->type = basket_type_ref;
934 else if (!
RTEST(move)) {
935 basket->v = ractor_copy(obj);
936 basket->type = basket_type_copy;
939 basket->type = basket_type_move;
945 basket->v = ractor_move(obj);
954 ractor_basket_setup(ec, &basket, obj, move,
false,
false,
false);
955 ractor_send_basket(ec, r, &basket);
963 .type = basket_type_none,
969 if (ractor_sleeping_by(r, wait_yielding)) {
970 MAYBE_UNUSED(
bool) wakeup_result;
971 VM_ASSERT(r->sync.wait.yielded_basket.type != basket_type_none);
973 if (r->sync.wait.yielded_basket.type == basket_type_move) {
974 wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_retry);
977 wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_take);
978 basket = r->sync.wait.yielded_basket;
979 ractor_basket_clear(&r->sync.wait.yielded_basket);
981 VM_ASSERT(wakeup_result);
983 else if (r->sync.outgoing_port_closed) {
989 if (basket.type == basket_type_none) {
991 rb_raise(rb_eRactorClosedError,
"The outgoing-port is already closed");
998 return ractor_basket_accept(&basket);
1003ractor_yield_move_body(VALUE v)
1005 return ractor_move(v);
1011 ASSERT_ractor_unlocking(cr);
1012 VM_ASSERT(basket->type != basket_type_none);
1014 if (cr->sync.outgoing_port_closed) {
1015 rb_raise(rb_eRactorClosedError,
"The outgoing-port is already closed");
1023 r = ractor_waiting_list_shift(cr, &cr->sync.taking_ractors);
1028 bool retry_shift =
false;
1032 if (ractor_sleeping_by(r, wait_taking)) {
1033 VM_ASSERT(r->sync.wait.taken_basket.type == basket_type_none);
1035 if (basket->type == basket_type_move) {
1036 enum ractor_wait_status prev_wait_status = r->sync.wait.status;
1037 r->sync.wait.status = wait_moving;
1042 VALUE moved_value =
rb_protect(ractor_yield_move_body, basket->v, &state);
1044 r->sync.wait.status = prev_wait_status;
1048 basket->v = moved_value;
1053 if (!ractor_wakeup(r, wait_moving, wakeup_by_yield)) {
1058 ractor_wakeup(r, wait_taking, wakeup_by_yield);
1060 r->sync.wait.taken_basket = *basket;
1084ractor_select(
rb_execution_context_t *ec,
const VALUE *rs,
const int rs_len, VALUE yielded_value,
bool move, VALUE *ret_r)
1087 VALUE crv = cr->pub.self;
1090 bool interrupted =
false;
1091 enum ractor_wait_status wait_status = 0;
1092 bool yield_p = (yielded_value !=
Qundef) ?
true :
false;
1093 const int alen = rs_len + (yield_p ? 1 : 0);
1095 struct ractor_select_action {
1096 enum ractor_select_action_type {
1097 ractor_select_action_take,
1098 ractor_select_action_receive,
1099 ractor_select_action_yield,
1102 } *actions =
ALLOCA_N(
struct ractor_select_action, alen);
1104 VM_ASSERT(cr->sync.wait.status == wait_none);
1105 VM_ASSERT(cr->sync.wait.wakeup_status == wakeup_none);
1106 VM_ASSERT(cr->sync.wait.taken_basket.type == basket_type_none);
1107 VM_ASSERT(cr->sync.wait.yielded_basket.type == basket_type_none);
1110 for (i=0; i<rs_len; i++) {
1114 actions[i].type = ractor_select_action_receive;
1115 actions[i].v =
Qnil;
1116 wait_status |= wait_receiving;
1118 else if (rb_ractor_p(v)) {
1119 actions[i].type = ractor_select_action_take;
1121 wait_status |= wait_taking;
1124 rb_raise(rb_eArgError,
"should be a ractor object, but %"PRIsVALUE, v);
1132 actions[rs_len].type = ractor_select_action_yield;
1133 actions[rs_len].v =
Qundef;
1134 wait_status |= wait_yielding;
1135 ractor_basket_setup(ec, &cr->sync.wait.yielded_basket, yielded_value, move,
false,
false,
true);
1141 RUBY_DEBUG_LOG(
"try actions (%s)", wait_status_str(wait_status));
1143 for (i=0; i<alen; i++) {
1145 switch (actions[i].
type) {
1146 case ractor_select_action_take:
1148 v = ractor_try_take(ec, RACTOR_PTR(rv));
1155 case ractor_select_action_receive:
1156 v = ractor_try_receive(ec, cr);
1163 case ractor_select_action_yield:
1165 if (ractor_try_yield(ec, cr, &cr->sync.wait.yielded_basket)) {
1175 RUBY_DEBUG_LOG(
"wait actions (%s)", wait_status_str(wait_status));
1179 VM_ASSERT(cr->sync.wait.status == wait_none);
1180 cr->sync.wait.status = wait_status;
1181 cr->sync.wait.wakeup_status = wakeup_none;
1186 for (i=0; i<alen; i++) {
1188 switch (actions[i].
type) {
1189 case ractor_select_action_take:
1190 r = RACTOR_PTR(actions[i].v);
1191 ractor_register_taking(r, cr);
1193 case ractor_select_action_yield:
1194 case ractor_select_action_receive:
1202 if (cr->sync.wait.wakeup_status == wakeup_none) {
1203 for (i=0; i<alen; i++) {
1206 switch (actions[i].
type) {
1207 case ractor_select_action_take:
1208 r = RACTOR_PTR(actions[i].v);
1209 if (ractor_sleeping_by(r, wait_yielding)) {
1210 RUBY_DEBUG_LOG(
"wakeup_none, but r:%u is waiting for yielding", r->pub.id);
1211 cr->sync.wait.wakeup_status = wakeup_by_retry;
1215 case ractor_select_action_receive:
1216 if (cr->sync.incoming_queue.cnt > 0) {
1217 RUBY_DEBUG_LOG(
"wakeup_none, but incoming_queue has %u messages", cr->sync.incoming_queue.cnt);
1218 cr->sync.wait.wakeup_status = wakeup_by_retry;
1222 case ractor_select_action_yield:
1223 if (cr->sync.taking_ractors.cnt > 0) {
1224 RUBY_DEBUG_LOG(
"wakeup_none, but %u taking_ractors are waiting", cr->sync.taking_ractors.cnt);
1225 cr->sync.wait.wakeup_status = wakeup_by_retry;
1228 else if (cr->sync.outgoing_port_closed) {
1229 cr->sync.wait.wakeup_status = wakeup_by_close;
1236 RUBY_DEBUG_LOG(
"sleep %s", wait_status_str(cr->sync.wait.status));
1237 ractor_sleep(ec, cr);
1238 RUBY_DEBUG_LOG(
"awaken %s", wakeup_status_str(cr->sync.wait.wakeup_status));
1242 RUBY_DEBUG_LOG(
"no need to sleep %s->%s",
1243 wait_status_str(cr->sync.wait.status),
1244 wakeup_status_str(cr->sync.wait.wakeup_status));
1245 cr->sync.wait.status = wait_none;
1251 for (i=0; i<alen; i++) {
1253 switch (actions[i].
type) {
1254 case ractor_select_action_take:
1255 r = RACTOR_PTR(actions[i].v);
1256 ractor_waiting_list_del(r, &r->sync.taking_ractors, cr);
1258 case ractor_select_action_receive:
1259 case ractor_select_action_yield:
1265 enum ractor_wakeup_status wakeup_status = cr->sync.wait.wakeup_status;
1266 cr->sync.wait.wakeup_status = wakeup_none;
1268 switch (wakeup_status) {
1273 case wakeup_by_retry:
1276 case wakeup_by_send:
1280 case wakeup_by_yield:
1283 VM_ASSERT(cr->sync.wait.taken_basket.type != basket_type_none);
1284 *ret_r = cr->sync.wait.taken_basket.sender;
1285 VM_ASSERT(rb_ractor_p(*ret_r));
1286 ret = ractor_basket_accept(&cr->sync.wait.taken_basket);
1288 case wakeup_by_take:
1292 case wakeup_by_close:
1296 case wakeup_by_interrupt:
1304 RUBY_DEBUG_LOG(
"cleanup actions (%s)", wait_status_str(wait_status));
1306 if (cr->sync.wait.yielded_basket.type != basket_type_none) {
1307 ractor_basket_clear(&cr->sync.wait.yielded_basket);
1310 VM_ASSERT(cr->sync.wait.status == wait_none);
1311 VM_ASSERT(cr->sync.wait.wakeup_status == wakeup_none);
1312 VM_ASSERT(cr->sync.wait.taken_basket.type == basket_type_none);
1313 VM_ASSERT(cr->sync.wait.yielded_basket.type == basket_type_none);
1316 rb_vm_check_ints_blocking(ec);
1317 interrupted =
false;
1321 VM_ASSERT(ret !=
Qundef);
1329 ractor_select(ec, NULL, 0, obj,
RTEST(move) ?
true :
false, &ret_r);
1337 VALUE v = ractor_select(ec, &r->pub.self, 1,
Qundef,
false, &ret_r);
1348 if (!r->sync.incoming_port_closed) {
1350 r->sync.incoming_port_closed =
true;
1351 if (ractor_wakeup(r, wait_receiving, wakeup_by_close)) {
1352 VM_ASSERT(r->sync.incoming_queue.cnt == 0);
1353 RUBY_DEBUG_LOG(
"cancel receiving");
1371 if (!r->sync.outgoing_port_closed) {
1373 r->sync.outgoing_port_closed =
true;
1381 while ((taking_ractor = ractor_waiting_list_shift(r, &r->sync.taking_ractors)) != NULL) {
1382 RACTOR_LOCK(taking_ractor);
1383 ractor_wakeup(taking_ractor, wait_taking, wakeup_by_close);
1384 RACTOR_UNLOCK(taking_ractor);
1388 if (!r->yield_atexit &&
1389 ractor_wakeup(r, wait_yielding, wakeup_by_close)) {
1390 RUBY_DEBUG_LOG(
"cancel yielding");
1412 RUBY_DEBUG_LOG(
"r:%u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
1413 VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
1415 list_add_tail(&vm->ractor.set, &r->vmlr_node);
1420cancel_single_ractor_mode(
void)
1423 RUBY_DEBUG_LOG(
"enable multi-ractor mode");
1428 rb_transient_heap_evacuate();
1434 ruby_single_main_ractor = NULL;
1440 VM_ASSERT(ractor_status_p(r, ractor_created));
1442 if (rb_multi_ractor_p()) {
1445 vm_insert_ractor0(vm, r,
false);
1446 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
1451 if (vm->ractor.cnt == 0) {
1453 vm_insert_ractor0(vm, r,
true);
1454 ractor_status_set(r, ractor_blocking);
1455 ractor_status_set(r, ractor_running);
1458 cancel_single_ractor_mode();
1459 vm_insert_ractor0(vm, r,
true);
1460 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
1468 VM_ASSERT(ractor_status_p(cr, ractor_running));
1469 VM_ASSERT(vm->ractor.cnt > 1);
1470 VM_ASSERT(cr->threads.cnt == 1);
1474 RUBY_DEBUG_LOG(
"ractor.cnt:%u-- terminate_waiting:%d",
1475 vm->ractor.cnt, vm->ractor.sync.terminate_waiting);
1477 VM_ASSERT(vm->ractor.cnt > 0);
1478 list_del(&cr->vmlr_node);
1480 if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
1486 rb_gc_ractor_newobj_cache_clear(&cr->newobj_cache);
1488 ractor_status_set(cr, ractor_terminated);
1494ractor_alloc(VALUE klass)
1500 VM_ASSERT(ractor_status_p(r, ractor_created));
1505rb_ractor_main_alloc(
void)
1509 fprintf(stderr,
"[FATAL] failed to allocate memory for main ractor\n");
1513 r->pub.id = ++ractor_last_id;
1517 ruby_single_main_ractor = r;
1522#if defined(HAVE_WORKING_FORK)
1528 vm->ractor.blocking_cnt = 0;
1529 ruby_single_main_ractor = th->ractor;
1530 th->ractor->status_ = ractor_created;
1532 rb_ractor_living_threads_init(th->ractor);
1533 rb_ractor_living_threads_insert(th->ractor, th);
1535 VM_ASSERT(vm->ractor.blocking_cnt == 0);
1536 VM_ASSERT(vm->ractor.cnt == 1);
1545 list_head_init(&r->threads.set);
1547 r->threads.blocking_cnt = 0;
1553 ractor_queue_setup(&r->sync.incoming_queue);
1559 rb_gvl_init(&r->threads.gvl);
1560 rb_ractor_living_threads_init(r);
1568 rb_raise(rb_eArgError,
"ASCII incompatible encoding (%s)",
1583 r->threads.main = th;
1584 rb_ractor_living_threads_insert(r, th);
1590 VALUE rv = ractor_alloc(
self);
1592 ractor_init(r, name, loc);
1595 r->pub.id = ractor_next_id();
1596 RUBY_DEBUG_LOG(
"r:%u", r->pub.id);
1599 r->verbose = cr->verbose;
1600 r->debug = cr->debug;
1602 rb_yjit_before_ractor_spawn();
1603 rb_thread_create_ractor(r, args, block);
1612 if (cr->sync.outgoing_port_closed) {
1616 ASSERT_ractor_unlocking(cr);
1619 ractor_basket_setup(ec, &basket, v,
Qfalse, exc,
true,
true );
1622 if (ractor_try_yield(ec, cr, &basket)) {
1629 if (cr->sync.taking_ractors.cnt == 0) {
1630 cr->sync.wait.yielded_basket = basket;
1632 VM_ASSERT(cr->sync.wait.status == wait_none);
1633 cr->sync.wait.status = wait_yielding;
1634 cr->sync.wait.wakeup_status = wakeup_none;
1636 VM_ASSERT(cr->yield_atexit ==
false);
1637 cr->yield_atexit =
true;
1645 if (retry)
goto retry;
1653 ractor_close_incoming(ec, cr);
1654 ractor_close_outgoing(ec, cr);
1659 VM_ASSERT(cr->threads.main != NULL);
1660 cr->threads.main = NULL;
1669 ractor_yield_atexit(ec, cr, result,
false);
1676 ractor_yield_atexit(ec, cr, ec->errinfo,
true);
1682 for (
int i=0; i<len; i++) {
1683 ptr[i] = ractor_receive(ec, r);
1691 for (
int i=0; i<len; i++) {
1696MJIT_FUNC_EXPORTED
bool
1697rb_ractor_main_p_(
void)
1699 VM_ASSERT(rb_multi_ractor_p());
1701 return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
1705rb_obj_is_main_ractor(VALUE gv)
1707 if (!rb_ractor_p(gv))
return false;
1709 return r == GET_VM()->ractor.main_ractor;
1715 return &r->threads.gvl;
1721 return r->threads.cnt;
1733 ts =
ALLOCA_N(VALUE, r->threads.cnt);
1736 list_for_each(&r->threads.set, th, lt_node) {
1737 switch (th->status) {
1738 case THREAD_RUNNABLE:
1739 case THREAD_STOPPED:
1740 case THREAD_STOPPED_FOREVER:
1741 ts[ts_cnt++] = th->self;
1750 for (
int i=0; i<ts_cnt; i++) {
1760 VM_ASSERT(th != NULL);
1764 RUBY_DEBUG_LOG(
"r(%d)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
1765 list_add_tail(&r->threads.set, &th->lt_node);
1771 if (r->threads.cnt == 1) {
1772 VM_ASSERT(ractor_status_p(r, ractor_created));
1773 vm_insert_ractor(th->vm, r);
1780 ractor_status_set(r, ractor_blocking);
1782 RUBY_DEBUG_LOG2(file, line,
"vm->ractor.blocking_cnt:%d++", vm->ractor.blocking_cnt);
1783 vm->ractor.blocking_cnt++;
1784 VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
1788rb_vm_ractor_blocking_cnt_inc(
rb_vm_t *vm,
rb_ractor_t *cr,
const char *file,
int line)
1790 ASSERT_vm_locking();
1791 VM_ASSERT(GET_RACTOR() == cr);
1792 vm_ractor_blocking_cnt_inc(vm, cr, file, line);
1796rb_vm_ractor_blocking_cnt_dec(
rb_vm_t *vm,
rb_ractor_t *cr,
const char *file,
int line)
1798 ASSERT_vm_locking();
1799 VM_ASSERT(GET_RACTOR() == cr);
1801 RUBY_DEBUG_LOG2(file, line,
"vm->ractor.blocking_cnt:%d--", vm->ractor.blocking_cnt);
1802 VM_ASSERT(vm->ractor.blocking_cnt > 0);
1803 vm->ractor.blocking_cnt--;
1805 ractor_status_set(cr, ractor_running);
1809ractor_check_blocking(
rb_ractor_t *cr,
unsigned int remained_thread_cnt,
const char *file,
int line)
1811 VM_ASSERT(cr == GET_RACTOR());
1813 RUBY_DEBUG_LOG2(file, line,
1814 "cr->threads.cnt:%u cr->threads.blocking_cnt:%u vm->ractor.cnt:%u vm->ractor.blocking_cnt:%u",
1815 cr->threads.cnt, cr->threads.blocking_cnt,
1816 GET_VM()->ractor.cnt, GET_VM()->ractor.blocking_cnt);
1818 VM_ASSERT(cr->threads.cnt >= cr->threads.blocking_cnt + 1);
1820 if (remained_thread_cnt > 0 &&
1822 cr->threads.cnt == cr->threads.blocking_cnt + 1) {
1825 ASSERT_vm_unlocking();
1829 rb_vm_ractor_blocking_cnt_inc(vm, cr, file, line);
1838 VM_ASSERT(cr == GET_RACTOR());
1839 RUBY_DEBUG_LOG(
"r->threads.cnt:%d--", cr->threads.cnt);
1840 ractor_check_blocking(cr, cr->threads.cnt - 1, __FILE__, __LINE__);
1842 if (cr->threads.cnt == 1) {
1843 vm_remove_ractor(th->vm, cr);
1848 list_del(&th->lt_node);
1856rb_ractor_blocking_threads_inc(
rb_ractor_t *cr,
const char *file,
int line)
1858 RUBY_DEBUG_LOG2(file, line,
"cr->threads.blocking_cnt:%d++", cr->threads.blocking_cnt);
1860 VM_ASSERT(cr->threads.cnt > 0);
1861 VM_ASSERT(cr == GET_RACTOR());
1863 ractor_check_blocking(cr, cr->threads.cnt, __FILE__, __LINE__);
1864 cr->threads.blocking_cnt++;
1868rb_ractor_blocking_threads_dec(
rb_ractor_t *cr,
const char *file,
int line)
1870 RUBY_DEBUG_LOG2(file, line,
1871 "r->threads.blocking_cnt:%d--, r->threads.cnt:%u",
1872 cr->threads.blocking_cnt, cr->threads.cnt);
1874 VM_ASSERT(cr == GET_RACTOR());
1876 if (cr->threads.cnt == cr->threads.blocking_cnt) {
1881 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
1886 cr->threads.blocking_cnt--;
1890rb_ractor_vm_barrier_interrupt_running_thread(
rb_ractor_t *r)
1892 VM_ASSERT(r != GET_RACTOR());
1893 ASSERT_ractor_unlocking(r);
1894 ASSERT_vm_locking();
1898 if (ractor_status_p(r, ractor_running)) {
1901 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec);
1909rb_ractor_terminate_interrupt_main_thread(
rb_ractor_t *r)
1911 VM_ASSERT(r != GET_RACTOR());
1912 ASSERT_ractor_unlocking(r);
1913 ASSERT_vm_locking();
1917 if (main_th->status != THREAD_KILLED) {
1918 RUBY_VM_SET_TERMINATE_INTERRUPT(main_th->ec);
1919 rb_threadptr_interrupt(main_th);
1922 RUBY_DEBUG_LOG(
"killed (%p)", (
void *)main_th);
1930ractor_terminal_interrupt_all(
rb_vm_t *vm)
1932 if (vm->ractor.cnt > 1) {
1935 list_for_each(&vm->ractor.set, r, vmlr_node) {
1936 if (r != vm->ractor.main_ractor) {
1937 rb_ractor_terminate_interrupt_main_thread(r);
1944rb_ractor_terminate_all(
void)
1949 VM_ASSERT(cr == GET_RACTOR());
1951 if (vm->ractor.cnt > 1) {
1953 ractor_terminal_interrupt_all(vm);
1956 rb_thread_terminate_all(GET_THREAD());
1960 while (vm->ractor.cnt > 1) {
1961 RUBY_DEBUG_LOG(
"terminate_waiting:%d", vm->ractor.sync.terminate_waiting);
1962 vm->ractor.sync.terminate_waiting =
true;
1965 rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
1966 rb_vm_cond_timedwait(vm, &vm->ractor.sync.terminate_cond, 1000 );
1967 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
1969 ractor_terminal_interrupt_all(vm);
1976rb_vm_main_ractor_ec(
rb_vm_t *vm)
1978 return vm->ractor.main_ractor->threads.running_ec;
1982ractor_moved_missing(
int argc, VALUE *argv, VALUE
self)
1984 rb_raise(rb_eRactorMovedError,
"can not send any methods to a moved object");
2095 rb_define_method(rb_cRactorMovedObject,
"method_missing", ractor_moved_missing, -1);
2098 rb_define_method(rb_cRactorMovedObject,
"__send__", ractor_moved_missing, -1);
2102 rb_define_method(rb_cRactorMovedObject,
"__id__", ractor_moved_missing, -1);
2103 rb_define_method(rb_cRactorMovedObject,
"equal?", ractor_moved_missing, -1);
2104 rb_define_method(rb_cRactorMovedObject,
"instance_eval", ractor_moved_missing, -1);
2105 rb_define_method(rb_cRactorMovedObject,
"instance_exec", ractor_moved_missing, -1);
2114 list_for_each(&vm->ractor.set, r, vmlr_node) {
2115 if (r != vm->ractor.main_ractor) {
2116 fprintf(stderr,
"r:%u (%s)\n", r->pub.id, ractor_status_str(r->status_));
2124 if (rb_ractor_main_p()) {
2136 if (rb_ractor_main_p()) {
2141 return cr->r_stdout;
2148 if (rb_ractor_main_p()) {
2153 return cr->r_stderr;
2160 if (rb_ractor_main_p()) {
2172 if (rb_ractor_main_p()) {
2184 if (rb_ractor_main_p()) {
2196 return &cr->pub.hooks;
2205enum obj_traverse_iterator_result {
2211typedef enum obj_traverse_iterator_result (*rb_obj_traverse_enter_func)(VALUE obj);
2212typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(VALUE obj);
2213typedef enum obj_traverse_iterator_result (*rb_obj_traverse_final_func)(VALUE obj);
2215static enum obj_traverse_iterator_result null_leave(VALUE obj);
2218 rb_obj_traverse_enter_func enter_func;
2219 rb_obj_traverse_leave_func leave_func;
2234obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
2238 if (obj_traverse_i(key, d->data)) {
2243 if (obj_traverse_i(val, d->data)) {
2252obj_traverse_reachable_i(VALUE obj,
void *ptr)
2256 if (obj_traverse_i(obj, d->data)) {
2264 if (UNLIKELY(!data->rec)) {
2265 data->rec_hash = rb_ident_hash_new();
2266 data->rec = rb_hash_st_table(data->rec_hash);
2276 switch (data->enter_func(obj)) {
2277 case traverse_cont:
break;
2278 case traverse_skip:
return 0;
2279 case traverse_stop:
return 1;
2282 if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
2289 rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
2290 for (uint32_t i = 0; i < ivtbl->numiv; i++) {
2291 VALUE val = ivtbl->ivptr[i];
2292 if (val !=
Qundef && obj_traverse_i(val, data))
return 1;
2312 for (uint32_t i=0; i<len; i++) {
2314 if (val !=
Qundef && obj_traverse_i(val, data))
return 1;
2323 if (obj_traverse_i(e, data))
return 1;
2337 if (d.stop)
return 1;
2344 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2346 for (
long i=0; i<len; i++) {
2347 if (obj_traverse_i(ptr[i], data))
return 1;
2353 if (obj_traverse_i(RRATIONAL(obj)->num, data))
return 1;
2354 if (obj_traverse_i(RRATIONAL(obj)->den, data))
return 1;
2357 if (obj_traverse_i(RCOMPLEX(obj)->real, data))
return 1;
2358 if (obj_traverse_i(RCOMPLEX(obj)->imag, data))
return 1;
2368 RB_VM_LOCK_ENTER_NO_BARRIER();
2370 rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
2372 RB_VM_LOCK_LEAVE_NO_BARRIER();
2373 if (d.stop)
return 1;
2386 if (data->leave_func(obj) == traverse_stop) {
2395 rb_obj_traverse_final_func final_func;
2400obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
2403 if (data->final_func(key)) {
2413rb_obj_traverse(VALUE obj,
2414 rb_obj_traverse_enter_func enter_func,
2415 rb_obj_traverse_leave_func leave_func,
2416 rb_obj_traverse_final_func final_func)
2419 .enter_func = enter_func,
2420 .leave_func = leave_func,
2424 if (obj_traverse_i(obj, &data))
return 1;
2425 if (final_func && data.rec) {
2427 st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
2434frozen_shareable_p(VALUE obj,
bool *made_shareable)
2441 if (
type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
2446 rb_proc_ractor_make_shareable(obj);
2447 *made_shareable =
true;
2456static enum obj_traverse_iterator_result
2457make_shareable_check_shareable(VALUE obj)
2460 bool made_shareable =
false;
2463 return traverse_skip;
2465 else if (!frozen_shareable_p(obj, &made_shareable)) {
2466 if (made_shareable) {
2467 return traverse_skip;
2470 rb_raise(rb_eRactorError,
"can not make shareable object for %"PRIsVALUE, obj);
2478 rb_raise(rb_eRactorError,
"#freeze does not freeze object correctly");
2482 return traverse_skip;
2486 return traverse_cont;
2489static enum obj_traverse_iterator_result
2490mark_shareable(VALUE obj)
2493 return traverse_cont;
2499 rb_obj_traverse(obj,
2500 make_shareable_check_shareable,
2501 null_leave, mark_shareable);
2508 VALUE copy = ractor_copy(obj);
2509 rb_obj_traverse(copy,
2510 make_shareable_check_shareable,
2511 null_leave, mark_shareable);
2516rb_ractor_ensure_shareable(VALUE obj, VALUE name)
2519 VALUE message =
rb_sprintf(
"cannot assign unshareable object to %"PRIsVALUE,
2521 rb_exc_raise(rb_exc_new_str(rb_eRactorIsolationError, message));
2526static enum obj_traverse_iterator_result
2527shareable_p_enter(VALUE obj)
2530 return traverse_skip;
2536 mark_shareable(obj);
2537 return traverse_skip;
2540 frozen_shareable_p(obj, NULL)) {
2541 return traverse_cont;
2544 return traverse_stop;
2547MJIT_FUNC_EXPORTED
bool
2548rb_ractor_shareable_p_continue(VALUE obj)
2550 if (rb_obj_traverse(obj,
2551 shareable_p_enter, null_leave,
2560#if RACTOR_CHECK_MODE > 0
2561static enum obj_traverse_iterator_result
2562reset_belonging_enter(VALUE obj)
2565 return traverse_skip;
2568 rb_ractor_setup_belonging(obj);
2569 return traverse_cont;
2574static enum obj_traverse_iterator_result
2575null_leave(VALUE obj)
2577 return traverse_cont;
2581ractor_reset_belonging(VALUE obj)
2583#if RACTOR_CHECK_MODE > 0
2584 rb_obj_traverse(obj, reset_belonging_enter, null_leave, NULL);
2598typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_enter_func)(VALUE obj,
struct obj_traverse_replace_data *data);
2599typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_leave_func)(VALUE obj,
struct obj_traverse_replace_data *data);
2602 rb_obj_traverse_replace_enter_func enter_func;
2603 rb_obj_traverse_replace_leave_func leave_func;
2619obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp,
int error)
2625obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr,
int exists)
2630 if (obj_traverse_replace_i(*key, data)) {
2634 else if (*key != data->replacement) {
2635 VALUE v = *key = data->replacement;
2639 if (obj_traverse_replace_i(*val, data)) {
2643 else if (*val != data->replacement) {
2644 VALUE v = *val = data->replacement;
2654 if (UNLIKELY(!data->rec)) {
2655 data->rec_hash = rb_ident_hash_new();
2656 data->rec = rb_hash_st_table(data->rec_hash);
2661#if USE_TRANSIENT_HEAP
2662void rb_ary_transient_heap_evacuate(VALUE ary,
int promote);
2663void rb_obj_transient_heap_evacuate(VALUE obj,
int promote);
2664void rb_hash_transient_heap_evacuate(VALUE hash,
int promote);
2665void rb_struct_transient_heap_evacuate(VALUE st,
int promote);
2669obj_refer_only_shareables_p_i(VALUE obj,
void *ptr)
2671 int *pcnt = (
int *)ptr;
2679obj_refer_only_shareables_p(VALUE obj)
2682 RB_VM_LOCK_ENTER_NO_BARRIER();
2684 rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
2686 RB_VM_LOCK_LEAVE_NO_BARRIER();
2696 data->replacement = obj;
2700 switch (data->enter_func(obj, data)) {
2701 case traverse_cont:
break;
2702 case traverse_skip:
return 0;
2703 case traverse_stop:
return 1;
2706 replacement = data->replacement;
2708 if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, (st_data_t *)&replacement))) {
2709 data->replacement = replacement;
2713 st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, (st_data_t)replacement);
2720#define CHECK_AND_REPLACE(v) do { \
2722 if (obj_traverse_replace_i(_val, data)) { return 1; } \
2723 else if (data->replacement != _val) { RB_OBJ_WRITE(obj, &v, data->replacement); } \
2728 rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
2729 for (uint32_t i = 0; i < ivtbl->numiv; i++) {
2730 if (ivtbl->ivptr[i] !=
Qundef) {
2731 CHECK_AND_REPLACE(ivtbl->ivptr[i]);
2746 rb_str_make_independent(obj);
2751#if USE_TRANSIENT_HEAP
2752 if (data->move) rb_obj_transient_heap_evacuate(obj, TRUE);
2758 for (uint32_t i=0; i<len; i++) {
2760 CHECK_AND_REPLACE(ptr[i]);
2768 rb_ary_cancel_sharing(obj);
2769#if USE_TRANSIENT_HEAP
2770 if (data->move) rb_ary_transient_heap_evacuate(obj, TRUE);
2776 if (obj_traverse_replace_i(e, data)) {
2779 else if (e != data->replacement) {
2789#if USE_TRANSIENT_HEAP
2790 if (data->move) rb_hash_transient_heap_evacuate(obj, TRUE);
2797 rb_hash_stlike_foreach_with_replace(obj,
2798 obj_hash_traverse_replace_foreach_i,
2799 obj_hash_traverse_replace_i,
2801 if (d.stop)
return 1;
2805 if (obj_traverse_replace_i(ifnone, data)) {
2808 else if (ifnone != data->replacement) {
2816#if USE_TRANSIENT_HEAP
2817 if (data->move) rb_struct_transient_heap_evacuate(obj, TRUE);
2820 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2822 for (
long i=0; i<len; i++) {
2823 CHECK_AND_REPLACE(ptr[i]);
2829 CHECK_AND_REPLACE(RRATIONAL(obj)->num);
2830 CHECK_AND_REPLACE(RRATIONAL(obj)->den);
2833 CHECK_AND_REPLACE(RCOMPLEX(obj)->real);
2834 CHECK_AND_REPLACE(RCOMPLEX(obj)->imag);
2838 if (!data->move && obj_refer_only_shareables_p(obj)) {
2842 rb_raise(rb_eRactorError,
"can not %s %"PRIsVALUE
" object.",
2859 data->replacement = replacement;
2861 if (data->leave_func(obj, data) == traverse_stop) {
2872rb_obj_traverse_replace(VALUE obj,
2873 rb_obj_traverse_replace_enter_func enter_func,
2874 rb_obj_traverse_replace_leave_func leave_func,
2878 .enter_func = enter_func,
2879 .leave_func = leave_func,
2885 if (obj_traverse_replace_i(obj, &data)) {
2889 return data.replacement;
2908ractor_moved_bang(VALUE obj)
2911 struct RVALUE *rv = (
void *)obj;
2913 rv->klass = rb_cRactorMovedObject;
2917 rv->flags = rv->flags & ~fl_users;
2922static enum obj_traverse_iterator_result
2926 data->replacement = obj;
2927 return traverse_skip;
2931 return traverse_cont;
2935void rb_replace_generic_ivar(VALUE clone, VALUE obj);
2937static enum obj_traverse_iterator_result
2940 VALUE v = data->replacement;
2944 dst->flags = (dst->flags & ~fl_users) | (src->flags & fl_users);
2951 rb_replace_generic_ivar(v, obj);
2956 ractor_moved_bang(obj);
2957 return traverse_cont;
2961ractor_move(VALUE obj)
2963 VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave,
true);
2968 rb_raise(rb_eRactorError,
"can not move the object");
2972static enum obj_traverse_iterator_result
2976 data->replacement = obj;
2977 return traverse_skip;
2980 data->replacement = rb_obj_clone(obj);
2981 return traverse_cont;
2985static enum obj_traverse_iterator_result
2988 return traverse_cont;
2992ractor_copy(VALUE obj)
2994 VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave,
false);
2999 rb_raise(rb_eRactorError,
"can not copy the object");
3014} freed_ractor_local_keys;
3017ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
3020 if (k->type->mark) (*k->type->mark)((
void *)val);
3024static enum rb_id_table_iterator_result
3025idkey_local_storage_mark_i(ID
id, VALUE val,
void *dmy)
3028 return ID_TABLE_CONTINUE;
3034 if (r->local_storage) {
3035 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
3037 for (
int i=0; i<freed_ractor_local_keys.cnt; i++) {
3040 if (st_delete(r->local_storage, (st_data_t *)&key, &val) &&
3042 (*key->type->free)((
void *)val);
3047 if (r->idkey_local_storage) {
3048 rb_id_table_foreach(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
3053ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
3056 if (k->type->free) (*k->type->free)((
void *)val);
3063 if (r->local_storage) {
3064 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
3065 st_free_table(r->local_storage);
3068 if (r->idkey_local_storage) {
3069 rb_id_table_free(r->idkey_local_storage);
3074rb_ractor_local_storage_value_mark(
void *ptr)
3090 rb_ractor_local_storage_value_mark,
3098 key->type =
type ?
type : &ractor_local_storage_type_null;
3099 key->main_cache = (
void *)
Qundef;
3114 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
3115 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
3118 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
3126 if (rb_ractor_main_p()) {
3127 if ((VALUE)key->main_cache !=
Qundef) {
3128 *pret = key->main_cache;
3138 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
3152 if (cr->local_storage == NULL) {
3153 cr->local_storage = st_init_numtable();
3156 st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
3158 if (rb_ractor_main_p()) {
3159 key->main_cache = ptr;
3167 if (ractor_local_ref(key, (
void **)&val)) {
3178 if (ractor_local_ref(key, (
void **)val)) {
3189 ractor_local_set(key, (
void *)val);
3196 if (ractor_local_ref(key, &ret)) {
3207 ractor_local_set(key, ptr);
3210#define DEFAULT_KEYS_CAPA 0x10
3213rb_ractor_finish_marking(
void)
3215 for (
int i=0; i<freed_ractor_local_keys.cnt; i++) {
3218 freed_ractor_local_keys.cnt = 0;
3219 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
3220 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
3230 struct rb_id_table *tbl = cr->idkey_local_storage;
3233 if (
id && tbl && rb_id_table_lookup(tbl,
id, &val)) {
3246 struct rb_id_table *tbl = cr->idkey_local_storage;
3249 tbl = cr->idkey_local_storage = rb_id_table_create(2);
3251 rb_id_table_insert(tbl,
id, val);
3255#include "ractor.rbinc"
#define RUBY_ATOMIC_FETCH_ADD(var, val)
Atomically replaces the value pointed by var with the result of addition of val to the old value of v...
static VALUE RB_OBJ_FROZEN_RAW(VALUE obj)
This is an implenentation 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.
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a method.
#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.
void rb_bug(const char *fmt,...)
Interpreter panic switch.
VALUE rb_eStopIteration
StopIteration exception.
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
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_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.
static const char * rb_enc_name(rb_encoding *enc)
Queries the (canonical) name of the passed encoding.
rb_encoding * rb_enc_get(VALUE obj)
Identical to rb_enc_get_index(), except the return type.
static bool rb_enc_asciicompat(rb_encoding *enc)
Queries if the passed encoding is in some sense compatible with ASCII.
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
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.
void rb_jump_tag(int state)
This function is to re-throw global escapes.
void rb_gc_mark(VALUE obj)
Marks an object.
VALUE rb_gc_disable(void)
Disables GC.
VALUE rb_gc_start(void)
Identical to rb_gc(), except the return value.
VALUE rb_gc_enable(void)
(Re-) enables GC.
void rb_hash_foreach(VALUE hash, int(*func)(VALUE key, VALUE val, VALUE arg), VALUE arg)
Iterates over a hash.
VALUE rb_protect(VALUE(*func)(VALUE args), VALUE args, int *state)
Protects a function call from potential global escapes from the function.
VALUE rb_obj_is_proc(VALUE recv)
Queries if the given object is a proc.
#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.
ID rb_intern(const char *name)
Finds or creates a symbol of the given name.
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
#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.
int st_foreach(st_table *q, int_type *w, st_data_t e)
Iteration over the given table.
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...
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.
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)
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
#define DATA_PTR(obj)
Convenient getter macro.
#define RHASH_SET_IFNONE(h, ifnone)
Destructively updates the default value of the hash.
static uint32_t ROBJECT_NUMIV(VALUE obj)
Queries the number of instance variables.
static VALUE * ROBJECT_IVPTR(VALUE obj)
Queries the instance variables.
static char * RSTRING_PTR(VALUE str)
Queries the contents pointer of the string.
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
static long RSTRUCT_LEN(VALUE st)
Returns the number of struct members.
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.
This is the struct that holds necessary info for a struct.
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.
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
void ruby_xfree(void *ptr)
Deallocates a storage instance.