8#include "eval_intern.h"
10#include "ractor_core.h"
11#include "internal/complex.h"
12#include "internal/error.h"
13#include "internal/gc.h"
14#include "internal/hash.h"
15#include "internal/rational.h"
16#include "internal/struct.h"
17#include "internal/thread.h"
23static VALUE rb_cRactorSelector;
25VALUE rb_eRactorUnsafeError;
26VALUE rb_eRactorIsolationError;
27static VALUE rb_eRactorError;
28static VALUE rb_eRactorRemoteError;
29static VALUE rb_eRactorMovedError;
30static VALUE rb_eRactorClosedError;
31static VALUE rb_cRactorMovedObject;
33static void vm_ractor_blocking_cnt_inc(
rb_vm_t *vm,
rb_ractor_t *r,
const char *file,
int line);
40#if RACTOR_CHECK_MODE > 0
42 if (rb_current_execution_context(
false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) {
43 rb_bug(
"recursive ractor locking");
51#if RACTOR_CHECK_MODE > 0
53 if (rb_current_execution_context(
false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) {
54 rp(r->sync.locked_by);
55 rb_bug(
"ractor lock is not acquired.");
61ractor_lock(
rb_ractor_t *r,
const char *file,
int line)
63 RUBY_DEBUG_LOG2(file, line,
"locking r:%u%s", r->pub.id, rb_current_ractor_raw(
false) == r ?
" (self)" :
"");
65 ASSERT_ractor_unlocking(r);
68#if RACTOR_CHECK_MODE > 0
69 if (rb_current_execution_context(
false) != NULL) {
71 r->sync.locked_by = cr ? rb_ractor_self(cr) :
Qundef;
75 RUBY_DEBUG_LOG2(file, line,
"locked r:%u%s", r->pub.id, rb_current_ractor_raw(
false) == r ?
" (self)" :
"");
79ractor_lock_self(
rb_ractor_t *cr,
const char *file,
int line)
81 VM_ASSERT(cr == GET_RACTOR());
82#if RACTOR_CHECK_MODE > 0
83 VM_ASSERT(cr->sync.locked_by != cr->pub.self);
85 ractor_lock(cr, file, line);
89ractor_unlock(
rb_ractor_t *r,
const char *file,
int line)
91 ASSERT_ractor_locking(r);
92#if RACTOR_CHECK_MODE > 0
93 r->sync.locked_by =
Qnil;
97 RUBY_DEBUG_LOG2(file, line,
"r:%u%s", r->pub.id, rb_current_ractor_raw(
false) == r ?
" (self)" :
"");
101ractor_unlock_self(
rb_ractor_t *cr,
const char *file,
int line)
103 VM_ASSERT(cr == GET_RACTOR());
104#if RACTOR_CHECK_MODE > 0
105 VM_ASSERT(cr->sync.locked_by == cr->pub.self);
107 ractor_unlock(cr, file, line);
110#define RACTOR_LOCK(r) ractor_lock(r, __FILE__, __LINE__)
111#define RACTOR_UNLOCK(r) ractor_unlock(r, __FILE__, __LINE__)
112#define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
113#define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
124 RACTOR_UNLOCK_SELF(r);
130ractor_status_str(
enum ractor_status status)
133 case ractor_created:
return "created";
134 case ractor_running:
return "running";
135 case ractor_blocking:
return "blocking";
136 case ractor_terminated:
return "terminated";
138 rb_bug(
"unreachable");
142ractor_status_set(
rb_ractor_t *r,
enum ractor_status status)
144 RUBY_DEBUG_LOG(
"r:%u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
147 if (r->status_ != ractor_created) {
148 VM_ASSERT(r == GET_RACTOR());
153 switch (r->status_) {
155 VM_ASSERT(status == ractor_blocking);
158 VM_ASSERT(status == ractor_blocking||
159 status == ractor_terminated);
161 case ractor_blocking:
162 VM_ASSERT(status == ractor_running);
164 case ractor_terminated:
165 rb_bug(
"unreachable");
173ractor_status_p(
rb_ractor_t *r,
enum ractor_status status)
175 return rb_ractor_status_p(r, status);
181static void ractor_local_storage_mark(
rb_ractor_t *r);
182static void ractor_local_storage_free(
rb_ractor_t *r);
187 for (
int i=0; i<rq->cnt; i++) {
189 rb_gc_mark(b->sender);
192 case basket_type_yielding:
193 case basket_type_take_basket:
194 case basket_type_deleted:
195 case basket_type_reserved:
199 rb_gc_mark(b->p.send.v);
205ractor_mark(
void *ptr)
209 ractor_queue_mark(&r->sync.recv_queue);
210 ractor_queue_mark(&r->sync.takers_queue);
212 rb_gc_mark(r->receiving_mutex);
216 rb_gc_mark(r->r_stdin);
217 rb_gc_mark(r->r_stdout);
218 rb_gc_mark(r->r_stderr);
219 rb_hook_list_mark(&r->pub.hooks);
221 if (r->threads.cnt > 0) {
223 ccan_list_for_each(&r->threads.set, th, lt_node) {
224 VM_ASSERT(th != NULL);
225 rb_gc_mark(th->self);
229 ractor_local_storage_mark(r);
239ractor_free(
void *ptr)
242 RUBY_DEBUG_LOG(
"free r:%d", rb_ractor_id(r));
244#ifdef RUBY_THREAD_WIN32_H
247 ractor_queue_free(&r->sync.recv_queue);
248 ractor_queue_free(&r->sync.takers_queue);
249 ractor_local_storage_free(r);
250 rb_hook_list_free(&r->pub.hooks);
261ractor_memsize(
const void *ptr)
267 ractor_queue_memsize(&r->sync.recv_queue) +
268 ractor_queue_memsize(&r->sync.takers_queue);
279 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
294RACTOR_PTR(
VALUE self)
296 VM_ASSERT(rb_ractor_p(self));
303#if RACTOR_CHECK_MODE > 0
305rb_ractor_current_id(
void)
307 if (GET_THREAD()->ractor == NULL) {
311 return rb_ractor_id(GET_RACTOR());
330 if (r != NULL) ASSERT_ractor_locking(r);
331 return &rq->baskets[rq->start];
337 if (r != NULL) ASSERT_ractor_locking(r);
338 return &rq->baskets[(rq->start + i) % rq->size];
344 ASSERT_ractor_locking(r);
346 if (rq->reserved_cnt == 0) {
348 rq->start = (rq->start + 1) % rq->size;
352 ractor_queue_at(r, rq, 0)->type.e = basket_type_deleted;
360 return basket_type_p(b, basket_type_deleted) ||
361 basket_type_p(b, basket_type_reserved);
367 ASSERT_ractor_locking(r);
369 while (rq->cnt > 0 && basket_type_p(ractor_queue_at(r, rq, 0), basket_type_deleted)) {
370 ractor_queue_advance(r, rq);
377 ASSERT_ractor_locking(r);
383 ractor_queue_compact(r, rq);
385 for (
int i=0; i<rq->cnt; i++) {
386 if (!ractor_queue_skip_p(r, rq, i)) {
397 ASSERT_ractor_locking(r);
399 for (
int i=0; i<rq->cnt; i++) {
400 if (!ractor_queue_skip_p(r, rq, i)) {
405 b->type.e = basket_type_deleted;
406 ractor_queue_compact(r, rq);
417 ASSERT_ractor_locking(r);
419 if (rq->size <= rq->cnt) {
420 rq->baskets = realloc(rq->baskets,
sizeof(
struct rb_ractor_basket) * rq->size * 2);
421 for (
int i=rq->size - rq->start; i<rq->cnt; i++) {
422 rq->baskets[i + rq->start] = rq->baskets[i + rq->start - rq->size];
426 rq->baskets[(rq->start + rq->cnt++) % rq->size] = *basket;
433 basket->type.e = basket_type_deleted;
438static VALUE ractor_reset_belonging(
VALUE obj);
444 case basket_type_ref:
446 case basket_type_copy:
447 case basket_type_move:
448 case basket_type_will:
449 b->type.e = basket_type_ref;
450 b->p.send.v = ractor_reset_belonging(b->p.send.v);
453 rb_bug(
"unreachable");
462 VALUE v = ractor_basket_value(b);
464 if (b->p.send.exception) {
468 rb_ec_setup_exception(NULL, err, cause);
477#if USE_RUBY_DEBUG_LOG
479wait_status_str(
enum rb_ractor_wait_status wait_status)
481 switch ((
int)wait_status) {
482 case wait_none:
return "none";
483 case wait_receiving:
return "receiving";
484 case wait_taking:
return "taking";
485 case wait_yielding:
return "yielding";
486 case wait_receiving|wait_taking:
return "receiving|taking";
487 case wait_receiving|wait_yielding:
return "receiving|yielding";
488 case wait_taking|wait_yielding:
return "taking|yielding";
489 case wait_receiving|wait_taking|wait_yielding:
return "receiving|taking|yielding";
491 rb_bug(
"unreachable");
495wakeup_status_str(
enum rb_ractor_wakeup_status wakeup_status)
497 switch (wakeup_status) {
498 case wakeup_none:
return "none";
499 case wakeup_by_send:
return "by_send";
500 case wakeup_by_yield:
return "by_yield";
501 case wakeup_by_take:
return "by_take";
502 case wakeup_by_close:
return "by_close";
503 case wakeup_by_interrupt:
return "by_interrupt";
504 case wakeup_by_retry:
return "by_retry";
506 rb_bug(
"unreachable");
510basket_type_name(
enum rb_ractor_basket_type
type)
513 case basket_type_none:
return "none";
514 case basket_type_ref:
return "ref";
515 case basket_type_copy:
return "copy";
516 case basket_type_move:
return "move";
517 case basket_type_will:
return "will";
518 case basket_type_deleted:
return "deleted";
519 case basket_type_reserved:
return "reserved";
520 case basket_type_take_basket:
return "take_basket";
521 case basket_type_yielding:
return "yielding";
529ractor_sleeping_by(
const rb_ractor_t *r,
enum rb_ractor_wait_status wait_status)
531 return (r->sync.wait.status & wait_status) && r->sync.wait.wakeup_status == wakeup_none;
534#ifdef RUBY_THREAD_PTHREAD_H
548ractor_wakeup(
rb_ractor_t *r,
enum rb_ractor_wait_status wait_status,
enum rb_ractor_wakeup_status wakeup_status)
550 ASSERT_ractor_locking(r);
552 RUBY_DEBUG_LOG(
"r:%u wait_by:%s -> wait:%s wakeup:%s",
554 wait_status_str(r->sync.wait.status),
555 wait_status_str(wait_status),
556 wakeup_status_str(wakeup_status));
558 if (ractor_sleeping_by(r, wait_status)) {
559 r->sync.wait.wakeup_status = wakeup_status;
560 rb_ractor_sched_wakeup(r);
569ractor_sleep_interrupt(
void *ptr)
575 ractor_wakeup(r, wait_receiving | wait_taking | wait_yielding, wakeup_by_interrupt);
580typedef void (*ractor_sleep_cleanup_function)(
rb_ractor_t *cr,
void *p);
585 if (cr->sync.wait.status != wait_none) {
586 enum rb_ractor_wait_status prev_wait_status = cr->sync.wait.status;
587 cr->sync.wait.status = wait_none;
588 cr->sync.wait.wakeup_status = wakeup_by_interrupt;
595 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
601 (*cf_func)(cr, cf_data);
602 EC_JUMP_TAG(ec, state);
612 cr->sync.wait.status = prev_wait_status;
616#ifdef RUBY_THREAD_PTHREAD_H
624#if RACTOR_CHECK_MODE > 0
625 VALUE locked_by = r->sync.locked_by;
626 r->sync.locked_by =
Qnil;
630#if RACTOR_CHECK_MODE > 0
631 r->sync.locked_by = locked_by;
636ractor_sleep_wo_gvl(
void *ptr)
639 RACTOR_LOCK_SELF(cr);
641 VM_ASSERT(cr->sync.wait.status != wait_none);
642 if (cr->sync.wait.wakeup_status == wakeup_none) {
643 ractor_cond_wait(cr);
645 cr->sync.wait.status = wait_none;
647 RACTOR_UNLOCK_SELF(cr);
664static enum rb_ractor_wakeup_status
666 ractor_sleep_cleanup_function cf_func,
void *cf_data)
668 enum rb_ractor_wakeup_status wakeup_status;
669 VM_ASSERT(GET_RACTOR() == cr);
672 VM_ASSERT(cr->sync.wait.status == wait_none);
673 VM_ASSERT(wait_status != wait_none);
674 cr->sync.wait.status = wait_status;
675 cr->sync.wait.wakeup_status = wakeup_none;
680 RUBY_DEBUG_LOG(
"sleep by %s", wait_status_str(wait_status));
682 while (cr->sync.wait.wakeup_status == wakeup_none) {
683 rb_ractor_sched_sleep(ec, cr, ractor_sleep_interrupt);
684 ractor_check_ints(ec, cr, cf_func, cf_data);
687 cr->sync.wait.status = wait_none;
690 wakeup_status = cr->sync.wait.wakeup_status;
691 cr->sync.wait.wakeup_status = wakeup_none;
693 RUBY_DEBUG_LOG(
"wakeup %s", wakeup_status_str(wakeup_status));
695 return wakeup_status;
698static enum rb_ractor_wakeup_status
701 return ractor_sleep_with_cleanup(ec, cr, wait_status, 0, NULL);
709 if (r->receiving_mutex && rb_mutex_owned_p(r->receiving_mutex)) {
710 rb_raise(rb_eRactorError,
"can not call receive/receive_if recursively");
718 ractor_recursive_receive_if(cr);
719 bool received =
false;
721 RACTOR_LOCK_SELF(cr);
723 RUBY_DEBUG_LOG(
"rq->cnt:%d", rq->cnt);
724 received = ractor_queue_deq(cr, rq, &basket);
726 RACTOR_UNLOCK_SELF(cr);
729 if (cr->sync.incoming_port_closed) {
730 rb_raise(rb_eRactorClosedError,
"The incoming port is already closed");
735 return ractor_basket_accept(&basket);
742 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
743 ractor_recursive_receive_if(cr);
747 while (ractor_queue_empty_p(cr, rq)) {
748 ractor_sleep(ec, cr, wait_receiving);
757 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
761 while (UNDEF_P(v = ractor_try_receive(ec, cr, rq))) {
762 ractor_wait_receive(ec, cr, rq);
773 for (
int i=0; i<rq->cnt; i++) {
775 fprintf(stderr,
"%d (start:%d) type:%s %p %s\n", i, rq->start, basket_type_name(b->type),
777 if (basket_type_p(b, basket_type_reserved) bug =
true;
779 if (bug) rb_bug(
"!!");
794 VALUE m = cr->receiving_mutex;
802receive_if_body(
VALUE ptr)
806 ractor_receive_if_lock(data->cr);
810 RACTOR_LOCK_SELF(cr);
813 VM_ASSERT(basket_type_p(b, basket_type_reserved));
814 data->rq->reserved_cnt--;
816 if (
RTEST(block_result)) {
817 ractor_queue_delete(cr, data->rq, b);
818 ractor_queue_compact(cr, data->rq);
821 b->type.e = basket_type_ref;
824 RACTOR_UNLOCK_SELF(cr);
826 data->success =
true;
828 if (
RTEST(block_result)) {
837receive_if_ensure(
VALUE v)
842 if (!data->success) {
843 RACTOR_LOCK_SELF(cr);
846 VM_ASSERT(basket_type_p(b, basket_type_reserved));
847 b->type.e = basket_type_deleted;
848 data->rq->reserved_cnt--;
850 RACTOR_UNLOCK_SELF(cr);
860 if (!
RTEST(b)) rb_raise(rb_eArgError,
"no block given");
863 unsigned int serial = (
unsigned int)-1;
870 ractor_wait_receive(ec, cr, rq);
872 RACTOR_LOCK_SELF(cr);
874 if (serial != rq->serial) {
880 for (
int i=index; i<rq->cnt; i++) {
881 if (!ractor_queue_skip_p(cr, rq, i)) {
883 v = ractor_basket_value(b);
884 b->type.e = basket_type_reserved;
891 RACTOR_UNLOCK_SELF(cr);
903 receive_if_ensure, (
VALUE)&data);
905 if (!UNDEF_P(result))
return result;
909 RUBY_VM_CHECK_INTS(ec);
920 if (r->sync.incoming_port_closed) {
924 ractor_queue_enq(r, &r->sync.recv_queue, b);
925 ractor_wakeup(r, wait_receiving, wakeup_by_send);
931 rb_raise(rb_eRactorClosedError,
"The incoming-port is already closed");
941ractor_basket_prepare_contents(
VALUE obj,
VALUE move,
volatile VALUE *pobj,
enum rb_ractor_basket_type *ptype)
944 enum rb_ractor_basket_type
type;
947 type = basket_type_ref;
950 else if (!
RTEST(move)) {
951 v = ractor_copy(obj);
952 type = basket_type_copy;
955 type = basket_type_move;
956 v = ractor_move(obj);
966 VM_ASSERT(cr == GET_RACTOR());
968 basket->sender = cr->pub.self;
969 basket->p.send.exception = exc;
970 basket->p.send.v = obj;
977 enum rb_ractor_basket_type
type;
978 ractor_basket_prepare_contents(obj, move, &v, &
type);
979 ractor_basket_fill_(cr, basket, v, exc);
980 basket->type.e =
type;
986 ractor_basket_fill_(cr, basket, obj, exc);
987 basket->type.e = basket_type_will;
995 ractor_basket_fill(rb_ec_ractor_ptr(ec), &basket, obj, move,
false);
996 ractor_send_basket(ec, r, &basket);
1005 ASSERT_ractor_locking(r);
1007 return basket_type_p(&r->sync.will_basket, basket_type_will);
1013 ASSERT_ractor_locking(r);
1015 if (ractor_take_has_will(r)) {
1016 *b = r->sync.will_basket;
1017 r->sync.will_basket.type.e = basket_type_none;
1021 VM_ASSERT(basket_type_p(&r->sync.will_basket, basket_type_none));
1029 ASSERT_ractor_unlocking(r);
1034 taken = ractor_take_will(r, b);
1046 .type.e = basket_type_take_basket,
1047 .sender = cr->pub.self,
1050 .basket = take_basket,
1055 bool closed =
false;
1059 if (is_take && ractor_take_will(r, take_basket)) {
1060 RUBY_DEBUG_LOG(
"take over a will of r:%d", rb_ractor_id(r));
1062 else if (!is_take && ractor_take_has_will(r)) {
1063 RUBY_DEBUG_LOG(
"has_will");
1064 VM_ASSERT(config != NULL);
1065 config->closed =
true;
1067 else if (r->sync.outgoing_port_closed) {
1071 RUBY_DEBUG_LOG(
"register in r:%d", rb_ractor_id(r));
1072 ractor_queue_enq(r, &r->sync.takers_queue, &b);
1074 if (basket_none_p(take_basket)) {
1075 ractor_wakeup(r, wait_yielding, wakeup_by_take);
1082 if (!ignore_error) rb_raise(rb_eRactorClosedError,
"The outgoing-port is already closed");
1094 bool deleted =
false;
1098 if (r->sync.outgoing_port_closed) {
1102 for (
int i=0; i<ts->cnt; i++) {
1104 if (basket_type_p(b, basket_type_take_basket) && b->p.take.basket == take_basket) {
1105 ractor_queue_delete(r, ts, b);
1110 ractor_queue_compact(r, ts);
1124 RACTOR_LOCK_SELF(cr);
1126 if (basket_none_p(take_basket) || basket_type_p(take_basket, basket_type_yielding)) {
1133 RACTOR_UNLOCK_SELF(cr);
1136 RUBY_DEBUG_LOG(
"taken");
1137 if (basket_type_p(take_basket, basket_type_deleted)) {
1138 VM_ASSERT(r->sync.outgoing_port_closed);
1139 rb_raise(rb_eRactorClosedError,
"The outgoing-port is already closed");
1141 return ractor_basket_accept(take_basket);
1144 RUBY_DEBUG_LOG(
"not taken");
1150#if VM_CHECK_MODE > 0
1159 for (
int i=0; i<ts->cnt; i++) {
1161 if (basket_type_p(b, basket_type_take_basket) && b->p.take.basket == tb) {
1177 if (basket_none_p(tb)) {
1178 if (!ractor_deregister_take(r, tb)) {
1185 VM_ASSERT(!ractor_check_specific_take_basket_lock(r, tb));
1195ractor_wait_take_cleanup(
rb_ractor_t *cr,
void *ptr)
1198 ractor_take_cleanup(cr, data->r, data->tb);
1209 RACTOR_LOCK_SELF(cr);
1211 if (basket_none_p(take_basket) || basket_type_p(take_basket, basket_type_yielding)) {
1212 ractor_sleep_with_cleanup(ec, cr, wait_taking, ractor_wait_take_cleanup, &data);
1215 RACTOR_UNLOCK_SELF(cr);
1221 RUBY_DEBUG_LOG(
"from r:%u", rb_ractor_id(r));
1226 .type.e = basket_type_none,
1230 ractor_register_take(cr, r, &take_basket,
true, NULL,
false);
1232 while (UNDEF_P(v = ractor_try_take(cr, r, &take_basket))) {
1233 ractor_wait_take(ec, cr, r, &take_basket);
1236 VM_ASSERT(!basket_none_p(&take_basket));
1237 VM_ASSERT(!ractor_check_specific_take_basket_lock(r, &take_basket));
1247 ASSERT_ractor_locking(cr);
1249 for (
int i=0; i<rs->cnt; i++) {
1251 if (basket_type_p(b, basket_type_take_basket) &&
1252 basket_none_p(b->p.take.basket)) {
1263 ASSERT_ractor_unlocking(cr);
1267 RACTOR_LOCK_SELF(cr);
1269 while (ractor_queue_deq(cr, rs, b)) {
1270 if (basket_type_p(b, basket_type_take_basket)) {
1273 if (
RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_yielding) == basket_type_none) {
1278 ractor_queue_enq(cr, rs, b);
1279 if (first_tb == NULL) first_tb = tb;
1281 VM_ASSERT(head != NULL);
1282 if (basket_type_p(head, basket_type_take_basket) && head->p.take.basket == first_tb) {
1288 VM_ASSERT(basket_none_p(b));
1292 if (found && b->p.take.config && !b->p.take.config->oneshot) {
1293 ractor_queue_enq(cr, rs, b);
1296 RACTOR_UNLOCK_SELF(cr);
1304 ASSERT_ractor_unlocking(cr);
1308 if (ractor_deq_take_basket(cr, ts, &b)) {
1309 VM_ASSERT(basket_type_p(&b, basket_type_take_basket));
1310 VM_ASSERT(basket_type_p(b.p.take.basket, basket_type_yielding));
1314 enum rb_ractor_basket_type
type;
1316 RUBY_DEBUG_LOG(
"basket from r:%u", rb_ractor_id(
tr));
1319 type = basket_type_will;
1326 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1328 ractor_basket_prepare_contents(obj, move, &obj, &
type);
1333 RACTOR_LOCK_SELF(cr);
1335 b.p.take.basket->type.e = basket_type_none;
1336 ractor_queue_enq(cr, ts, &b);
1338 RACTOR_UNLOCK_SELF(cr);
1339 EC_JUMP_TAG(ec, state);
1345 VM_ASSERT(basket_type_p(tb, basket_type_yielding));
1347 RUBY_DEBUG_LOG(
"fill %sbasket from r:%u", is_will ?
"will " :
"", rb_ractor_id(
tr));
1348 ractor_basket_fill_(cr, tb, obj, exc);
1349 if (
RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_yielding,
type) != basket_type_yielding) {
1350 rb_bug(
"unreachable");
1352 ractor_wakeup(
tr, wait_taking, wakeup_by_yield);
1359 RUBY_DEBUG_LOG(
"no take basket");
1367 RACTOR_LOCK_SELF(cr);
1369 while (!ractor_check_take_basket(cr, ts)) {
1370 ractor_sleep(ec, cr, wait_yielding);
1373 RACTOR_UNLOCK_SELF(cr);
1381 while (!ractor_try_yield(ec, cr, ts, obj, move,
false,
false)) {
1382 ractor_wait_yield(ec, cr, ts);
1397ractor_selector_mark_ractors_i(st_data_t key, st_data_t value, st_data_t data)
1400 rb_gc_mark(r->pub.self);
1405ractor_selector_mark(
void *ptr)
1409 if (s->take_ractors) {
1410 st_foreach(s->take_ractors, ractor_selector_mark_ractors_i, 0);
1413 switch (s->take_basket.type.e) {
1414 case basket_type_ref:
1415 case basket_type_copy:
1416 case basket_type_move:
1417 case basket_type_will:
1418 rb_gc_mark(s->take_basket.sender);
1419 rb_gc_mark(s->take_basket.p.send.v);
1427ractor_selector_release_i(st_data_t key, st_data_t val, st_data_t data)
1432 if (!config->closed) {
1433 ractor_deregister_take((
rb_ractor_t *)key, &s->take_basket);
1440ractor_selector_free(
void *ptr)
1443 st_foreach(s->take_ractors, ractor_selector_release_i, (st_data_t)s);
1444 st_free_table(s->take_ractors);
1449ractor_selector_memsize(
const void *ptr)
1453 st_memsize(s->take_ractors) +
1460 ractor_selector_mark,
1461 ractor_selector_free,
1462 ractor_selector_memsize,
1465 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
1469RACTOR_SELECTOR_PTR(
VALUE selv)
1479ractor_selector_create(
VALUE klass)
1483 s->take_basket.type.e = basket_type_reserved;
1484 s->take_ractors = st_init_numtable();
1493 if (!rb_ractor_p(rv)) {
1494 rb_raise(rb_eArgError,
"Not a ractor object");
1500 if (st_lookup(s->take_ractors, (st_data_t)r, NULL)) {
1501 rb_raise(rb_eArgError,
"already added");
1505 VM_ASSERT(config != NULL);
1506 config->closed =
false;
1507 config->oneshot =
false;
1509 if (ractor_register_take(GET_RACTOR(), r, &s->take_basket,
false, config,
true)) {
1510 st_insert(s->take_ractors, (st_data_t)r, (st_data_t)config);
1521 if (!rb_ractor_p(rv)) {
1522 rb_raise(rb_eArgError,
"Not a ractor object");
1528 RUBY_DEBUG_LOG(
"r:%u", rb_ractor_id(r));
1530 if (!st_lookup(s->take_ractors, (st_data_t)r, NULL)) {
1531 rb_raise(rb_eArgError,
"not added yet");
1534 ractor_deregister_take(r, &s->take_basket);
1536 st_delete(s->take_ractors, (st_data_t *)&r, (st_data_t *)&config);
1550ractor_selector_clear_i(st_data_t key, st_data_t val, st_data_t data)
1554 ractor_selector_remove(selv, r->pub.self);
1559ractor_selector_clear(
VALUE selv)
1563 st_foreach(s->take_ractors, ractor_selector_clear_i, (st_data_t)selv);
1564 st_clear(s->take_ractors);
1569ractor_selector_empty_p(
VALUE selv)
1572 return s->take_ractors->num_entries == 0 ?
Qtrue :
Qfalse;
1576ractor_selector_wait_i(st_data_t key, st_data_t val, st_data_t dat)
1582 if (!basket_none_p(tb)) {
1583 RUBY_DEBUG_LOG(
"already taken:%s", basket_type_name(tb->type.e));
1589 if (basket_type_p(&r->sync.will_basket, basket_type_will)) {
1590 RUBY_DEBUG_LOG(
"r:%u has will", rb_ractor_id(r));
1592 if (
RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_will) == basket_type_none) {
1593 ractor_take_will(r, tb);
1597 RUBY_DEBUG_LOG(
"has will, but already taken (%s)", basket_type_name(tb->type.e));
1601 else if (r->sync.outgoing_port_closed) {
1602 RUBY_DEBUG_LOG(
"r:%u is closed", rb_ractor_id(r));
1604 if (
RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_deleted) == basket_type_none) {
1605 tb->sender = r->pub.self;
1609 RUBY_DEBUG_LOG(
"closed, but already taken (%s)", basket_type_name(tb->type.e));
1614 RUBY_DEBUG_LOG(
"wakeup r:%u", rb_ractor_id(r));
1615 ractor_wakeup(r, wait_yielding, wakeup_by_take);
1627ractor_selector_wait_cleaup(
rb_ractor_t *cr,
void *ptr)
1631 RACTOR_LOCK_SELF(cr);
1635 tb->type.e = basket_type_reserved;
1637 RACTOR_UNLOCK_SELF(cr);
1648 bool do_receive = !!
RTEST(do_receivev);
1649 bool do_yield = !!
RTEST(do_yieldv);
1651 enum rb_ractor_wait_status wait_status;
1655 RUBY_DEBUG_LOG(
"start");
1658 RUBY_DEBUG_LOG(
"takers:%ld", s->take_ractors->num_entries);
1661 wait_status = wait_none;
1662 if (s->take_ractors->num_entries > 0) wait_status |= wait_taking;
1663 if (do_receive) wait_status |= wait_receiving;
1664 if (do_yield) wait_status |= wait_yielding;
1666 RUBY_DEBUG_LOG(
"wait:%s", wait_status_str(wait_status));
1668 if (wait_status == wait_none) {
1669 rb_raise(rb_eRactorError,
"no taking ractors");
1673 if (do_receive && (ret_v = ractor_try_receive(ec, cr, rq)) !=
Qundef) {
1674 ret_r =
ID2SYM(rb_intern(
"receive"));
1679 if (do_yield && ractor_try_yield(ec, cr, ts, yield_value, move,
false,
false)) {
1681 ret_r =
ID2SYM(rb_intern(
"yield"));
1686 VM_ASSERT(basket_type_p(&s->take_basket, basket_type_reserved));
1687 s->take_basket.type.e = basket_type_none;
1689 st_foreach(s->take_ractors, ractor_selector_wait_i, (st_data_t)tb);
1691 RACTOR_LOCK_SELF(cr);
1695 if (!basket_none_p(tb)) {
1696 RUBY_DEBUG_LOG(
"taken:%s from r:%u", basket_type_name(tb->type.e),
1697 tb->sender ? rb_ractor_id(RACTOR_PTR(tb->sender)) : 0);
1700 if (do_receive && !ractor_queue_empty_p(cr, rq)) {
1701 RUBY_DEBUG_LOG(
"can receive (%d)", rq->cnt);
1704 if (do_yield && ractor_check_take_basket(cr, ts)) {
1705 RUBY_DEBUG_LOG(
"can yield");
1709 ractor_sleep_with_cleanup(ec, cr, wait_status, ractor_selector_wait_cleaup, tb);
1716 if (taken_basket.type.e == basket_type_yielding ||
1717 RUBY_ATOMIC_CAS(tb->type.atomic, taken_basket.type.e, basket_type_reserved) != taken_basket.type.e) {
1719 if (basket_type_p(tb, basket_type_yielding)) {
1720 RACTOR_UNLOCK_SELF(cr);
1724 RACTOR_LOCK_SELF(cr);
1729 RACTOR_UNLOCK_SELF(cr);
1732 switch (taken_basket.type.e) {
1733 case basket_type_none:
1734 VM_ASSERT(do_receive || do_yield);
1736 case basket_type_yielding:
1737 rb_bug(
"unreachable");
1738 case basket_type_deleted: {
1739 ractor_selector_remove(selv, taken_basket.sender);
1742 if (ractor_take_will_lock(r, &taken_basket)) {
1743 RUBY_DEBUG_LOG(
"has_will");
1746 RUBY_DEBUG_LOG(
"no will");
1753 case basket_type_will:
1755 ractor_selector_remove(selv, taken_basket.sender);
1761 RUBY_DEBUG_LOG(
"taken_basket:%s", basket_type_name(taken_basket.type.e));
1763 ret_v = ractor_basket_accept(&taken_basket);
1764 ret_r = taken_basket.sender;
1766 return rb_ary_new_from_args(2, ret_r, ret_v);
1770ractor_selector_wait(
int argc,
VALUE *argv,
VALUE selector)
1776 keywords[0] = rb_intern(
"receive");
1777 keywords[1] = rb_intern(
"yield_value");
1778 keywords[2] = rb_intern(
"move");
1781 rb_get_kwargs(options, keywords, 0, numberof(values), values);
1782 return ractor_selector__wait(selector,
1784 values[1] !=
Qundef, values[1], values[2]);
1788ractor_selector_new(
int argc,
VALUE *ractors,
VALUE klass)
1790 VALUE selector = ractor_selector_create(klass);
1792 for (
int i=0; i<argc; i++) {
1793 ractor_selector_add(selector, ractors[i]);
1807 if ((state = EC_EXEC_TAG() == TAG_NONE)) {
1808 result = ractor_selector__wait(selector, do_receive, do_yield, yield_value, move);
1812 ractor_selector_clear(selector);
1815 EC_JUMP_TAG(ec, state);
1832 if (!r->sync.incoming_port_closed) {
1834 r->sync.incoming_port_closed =
true;
1835 if (ractor_wakeup(r, wait_receiving, wakeup_by_close)) {
1836 VM_ASSERT(ractor_queue_empty_p(r, &r->sync.recv_queue));
1837 RUBY_DEBUG_LOG(
"cancel receiving");
1861 if (!r->sync.outgoing_port_closed) {
1863 r->sync.outgoing_port_closed =
true;
1866 VM_ASSERT(ractor_queue_empty_p(r, ts));
1871 while (ractor_queue_deq(r, ts, &b)) {
1872 if (basket_type_p(&b, basket_type_take_basket)) {
1873 tr = RACTOR_PTR(b.sender);
1876 if (
RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_yielding) == basket_type_none) {
1877 b.p.take.basket->sender = r->pub.self;
1878 if (
RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_yielding, basket_type_deleted) != basket_type_yielding) {
1879 rb_bug(
"unreachable");
1881 RUBY_DEBUG_LOG(
"set delete for r:%u", rb_ractor_id(RACTOR_PTR(b.sender)));
1884 if (b.p.take.config) {
1885 b.p.take.config->closed =
true;
1891 ractor_wakeup(
tr, wait_taking, wakeup_by_close);
1898 ractor_wakeup(r, wait_yielding, wakeup_by_close);
1900 VM_ASSERT(ractor_queue_empty_p(r, ts));
1921 RUBY_DEBUG_LOG(
"r:%u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
1922 VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
1924 ccan_list_add_tail(&vm->ractor.set, &r->vmlr_node);
1929cancel_single_ractor_mode(
void)
1932 RUBY_DEBUG_LOG(
"enable multi-ractor mode");
1934 VALUE was_disabled = rb_gc_enable();
1942 ruby_single_main_ractor = NULL;
1948 VM_ASSERT(ractor_status_p(r, ractor_created));
1950 if (rb_multi_ractor_p()) {
1953 vm_insert_ractor0(vm, r,
false);
1954 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
1959 if (vm->ractor.cnt == 0) {
1961 vm_insert_ractor0(vm, r,
true);
1962 ractor_status_set(r, ractor_blocking);
1963 ractor_status_set(r, ractor_running);
1966 cancel_single_ractor_mode();
1967 vm_insert_ractor0(vm, r,
true);
1968 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
1976 VM_ASSERT(ractor_status_p(cr, ractor_running));
1977 VM_ASSERT(vm->ractor.cnt > 1);
1978 VM_ASSERT(cr->threads.cnt == 1);
1982 RUBY_DEBUG_LOG(
"ractor.cnt:%u-- terminate_waiting:%d",
1983 vm->ractor.cnt, vm->ractor.sync.terminate_waiting);
1985 VM_ASSERT(vm->ractor.cnt > 0);
1986 ccan_list_del(&cr->vmlr_node);
1988 if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
1994 rb_gc_ractor_newobj_cache_clear(&cr->newobj_cache);
1996 ractor_status_set(cr, ractor_terminated);
2002ractor_alloc(
VALUE klass)
2008 VM_ASSERT(ractor_status_p(r, ractor_created));
2013rb_ractor_main_alloc(
void)
2017 fprintf(stderr,
"[FATAL] failed to allocate memory for main ractor\n");
2021 r->pub.id = ++ractor_last_id;
2025 ruby_single_main_ractor = r;
2030#if defined(HAVE_WORKING_FORK)
2036 vm->ractor.blocking_cnt = 0;
2037 ruby_single_main_ractor = th->ractor;
2038 th->ractor->status_ = ractor_created;
2040 rb_ractor_living_threads_init(th->ractor);
2041 rb_ractor_living_threads_insert(th->ractor, th);
2043 VM_ASSERT(vm->ractor.blocking_cnt == 0);
2044 VM_ASSERT(vm->ractor.cnt == 1);
2053 ccan_list_head_init(&r->threads.set);
2055 r->threads.blocking_cnt = 0;
2061 ractor_queue_setup(&r->sync.recv_queue);
2062 ractor_queue_setup(&r->sync.takers_queue);
2066#ifdef RUBY_THREAD_WIN32_H
2072 rb_thread_sched_init(&r->threads.sched,
false);
2073 rb_ractor_living_threads_init(r);
2079 enc = rb_enc_get(name);
2080 if (!rb_enc_asciicompat(enc)) {
2081 rb_raise(rb_eArgError,
"ASCII incompatible encoding (%s)",
2084 name = rb_str_new_frozen(name);
2096 r->threads.main = th;
2097 rb_ractor_living_threads_insert(r, th);
2103 VALUE rv = ractor_alloc(self);
2105 ractor_init(r, name, loc);
2108 r->pub.id = ractor_next_id();
2109 RUBY_DEBUG_LOG(
"r:%u", r->pub.id);
2112 r->verbose = cr->verbose;
2113 r->debug = cr->debug;
2115 rb_yjit_before_ractor_spawn();
2116 rb_rjit_before_ractor_spawn();
2117 rb_thread_create_ractor(r, args, block);
2126 if (cr->sync.outgoing_port_closed) {
2130 ASSERT_ractor_unlocking(cr);
2135 if (ractor_try_yield(ec, cr, ts, v,
Qfalse, exc,
true)) {
2142 if (!ractor_check_take_basket(cr, ts)) {
2143 VM_ASSERT(cr->sync.wait.status == wait_none);
2144 RUBY_DEBUG_LOG(
"leave a will");
2145 ractor_basket_fill_will(cr, &cr->sync.will_basket, v, exc);
2148 RUBY_DEBUG_LOG(
"rare timing!");
2154 if (retry)
goto retry;
2162 ractor_yield_atexit(ec, cr, result,
false);
2169 ractor_yield_atexit(ec, cr, ec->errinfo,
true);
2176 ractor_close_incoming(ec, cr);
2177 ractor_close_outgoing(ec, cr);
2182 VM_ASSERT(cr->threads.main != NULL);
2183 cr->threads.main = NULL;
2191 for (
int i=0; i<
len; i++) {
2192 ptr[i] = ractor_receive(ec, r);
2200 for (
int i=0; i<
len; i++) {
2206rb_ractor_main_p_(
void)
2208 VM_ASSERT(rb_multi_ractor_p());
2210 return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
2214rb_obj_is_main_ractor(
VALUE gv)
2216 if (!rb_ractor_p(gv))
return false;
2218 return r == GET_VM()->ractor.main_ractor;
2224 return r->threads.cnt;
2229rb_ractor_thread_list(
void)
2233 VALUE ary = rb_ary_new();
2235 ccan_list_for_each(&r->threads.set, th, lt_node) {
2236 switch (th->status) {
2237 case THREAD_RUNNABLE:
2238 case THREAD_STOPPED:
2239 case THREAD_STOPPED_FOREVER:
2240 rb_ary_push(ary, th->self);
2252 VM_ASSERT(th != NULL);
2256 RUBY_DEBUG_LOG(
"r(%d)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
2257 ccan_list_add_tail(&r->threads.set, &th->lt_node);
2263 if (r->threads.cnt == 1) {
2264 VM_ASSERT(ractor_status_p(r, ractor_created));
2265 vm_insert_ractor(th->vm, r);
2272 ractor_status_set(r, ractor_blocking);
2274 RUBY_DEBUG_LOG2(file, line,
"vm->ractor.blocking_cnt:%d++", vm->ractor.blocking_cnt);
2275 vm->ractor.blocking_cnt++;
2276 VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
2280rb_vm_ractor_blocking_cnt_inc(
rb_vm_t *vm,
rb_ractor_t *cr,
const char *file,
int line)
2282 ASSERT_vm_locking();
2283 VM_ASSERT(GET_RACTOR() == cr);
2284 vm_ractor_blocking_cnt_inc(vm, cr, file, line);
2288rb_vm_ractor_blocking_cnt_dec(
rb_vm_t *vm,
rb_ractor_t *cr,
const char *file,
int line)
2290 ASSERT_vm_locking();
2291 VM_ASSERT(GET_RACTOR() == cr);
2293 RUBY_DEBUG_LOG2(file, line,
"vm->ractor.blocking_cnt:%d--", vm->ractor.blocking_cnt);
2294 VM_ASSERT(vm->ractor.blocking_cnt > 0);
2295 vm->ractor.blocking_cnt--;
2297 ractor_status_set(cr, ractor_running);
2301ractor_check_blocking(
rb_ractor_t *cr,
unsigned int remained_thread_cnt,
const char *file,
int line)
2303 VM_ASSERT(cr == GET_RACTOR());
2305 RUBY_DEBUG_LOG2(file, line,
2306 "cr->threads.cnt:%u cr->threads.blocking_cnt:%u vm->ractor.cnt:%u vm->ractor.blocking_cnt:%u",
2307 cr->threads.cnt, cr->threads.blocking_cnt,
2308 GET_VM()->ractor.cnt, GET_VM()->ractor.blocking_cnt);
2310 VM_ASSERT(cr->threads.cnt >= cr->threads.blocking_cnt + 1);
2312 if (remained_thread_cnt > 0 &&
2314 cr->threads.cnt == cr->threads.blocking_cnt + 1) {
2317 ASSERT_vm_unlocking();
2321 rb_vm_ractor_blocking_cnt_inc(vm, cr, file, line);
2332 VM_ASSERT(cr == GET_RACTOR());
2333 RUBY_DEBUG_LOG(
"r->threads.cnt:%d--", cr->threads.cnt);
2334 ractor_check_blocking(cr, cr->threads.cnt - 1, __FILE__, __LINE__);
2336 rb_threadptr_remove(th);
2338 if (cr->threads.cnt == 1) {
2339 vm_remove_ractor(th->vm, cr);
2344 ccan_list_del(&th->lt_node);
2352rb_ractor_blocking_threads_inc(
rb_ractor_t *cr,
const char *file,
int line)
2354 RUBY_DEBUG_LOG2(file, line,
"cr->threads.blocking_cnt:%d++", cr->threads.blocking_cnt);
2356 VM_ASSERT(cr->threads.cnt > 0);
2357 VM_ASSERT(cr == GET_RACTOR());
2359 ractor_check_blocking(cr, cr->threads.cnt, __FILE__, __LINE__);
2360 cr->threads.blocking_cnt++;
2364rb_ractor_blocking_threads_dec(
rb_ractor_t *cr,
const char *file,
int line)
2366 RUBY_DEBUG_LOG2(file, line,
2367 "r->threads.blocking_cnt:%d--, r->threads.cnt:%u",
2368 cr->threads.blocking_cnt, cr->threads.cnt);
2370 VM_ASSERT(cr == GET_RACTOR());
2372 if (cr->threads.cnt == cr->threads.blocking_cnt) {
2377 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
2382 cr->threads.blocking_cnt--;
2386rb_ractor_vm_barrier_interrupt_running_thread(
rb_ractor_t *r)
2388 VM_ASSERT(r != GET_RACTOR());
2389 ASSERT_ractor_unlocking(r);
2390 ASSERT_vm_locking();
2394 if (ractor_status_p(r, ractor_running)) {
2397 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec);
2405rb_ractor_terminate_interrupt_main_thread(
rb_ractor_t *r)
2407 VM_ASSERT(r != GET_RACTOR());
2408 ASSERT_ractor_unlocking(r);
2409 ASSERT_vm_locking();
2413 if (main_th->status != THREAD_KILLED) {
2414 RUBY_VM_SET_TERMINATE_INTERRUPT(main_th->ec);
2415 rb_threadptr_interrupt(main_th);
2418 RUBY_DEBUG_LOG(
"killed (%p)", (
void *)main_th);
2426ractor_terminal_interrupt_all(
rb_vm_t *vm)
2428 if (vm->ractor.cnt > 1) {
2431 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
2432 if (r != vm->ractor.main_ractor) {
2433 RUBY_DEBUG_LOG(
"r:%d", rb_ractor_id(r));
2434 rb_ractor_terminate_interrupt_main_thread(r);
2444rb_ractor_terminate_all(
void)
2449 RUBY_DEBUG_LOG(
"ractor.cnt:%d", (
int)vm->ractor.cnt);
2451 VM_ASSERT(cr == GET_RACTOR());
2453 if (vm->ractor.cnt > 1) {
2456 ractor_terminal_interrupt_all(vm);
2460 rb_thread_terminate_all(GET_THREAD());
2464 while (vm->ractor.cnt > 1) {
2465 RUBY_DEBUG_LOG(
"terminate_waiting:%d", vm->ractor.sync.terminate_waiting);
2466 vm->ractor.sync.terminate_waiting =
true;
2469 rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
2470 rb_del_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
2471 rb_vm_cond_timedwait(vm, &vm->ractor.sync.terminate_cond, 1000 );
2472 rb_add_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
2473 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
2475 ractor_terminal_interrupt_all(vm);
2482rb_vm_main_ractor_ec(
rb_vm_t *vm)
2499 if (running_ec) {
return running_ec; }
2500 return vm->ractor.main_thread->ec;
2504ractor_moved_missing(
int argc,
VALUE *argv,
VALUE self)
2506 rb_raise(rb_eRactorMovedError,
"can not send any methods to a moved object");
2509#ifndef USE_RACTOR_SELECTOR
2510#define USE_RACTOR_SELECTOR 0
2513RUBY_SYMBOL_EXPORT_BEGIN
2514void rb_init_ractor_selector(
void);
2515RUBY_SYMBOL_EXPORT_END
2518rb_init_ractor_selector(
void)
2527 rb_define_method(rb_cRactorSelector,
"empty?", ractor_selector_empty_p, 0);
2640 rb_define_method(rb_cRactorMovedObject,
"method_missing", ractor_moved_missing, -1);
2643 rb_define_method(rb_cRactorMovedObject,
"__send__", ractor_moved_missing, -1);
2647 rb_define_method(rb_cRactorMovedObject,
"__id__", ractor_moved_missing, -1);
2648 rb_define_method(rb_cRactorMovedObject,
"equal?", ractor_moved_missing, -1);
2649 rb_define_method(rb_cRactorMovedObject,
"instance_eval", ractor_moved_missing, -1);
2650 rb_define_method(rb_cRactorMovedObject,
"instance_exec", ractor_moved_missing, -1);
2652#if USE_RACTOR_SELECTOR
2653 rb_init_ractor_selector();
2663 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
2664 if (r != vm->ractor.main_ractor) {
2665 fprintf(stderr,
"r:%u (%s)\n", r->pub.id, ractor_status_str(r->status_));
2673 if (rb_ractor_main_p()) {
2683rb_ractor_stdout(
void)
2685 if (rb_ractor_main_p()) {
2690 return cr->r_stdout;
2695rb_ractor_stderr(
void)
2697 if (rb_ractor_main_p()) {
2702 return cr->r_stderr;
2709 if (rb_ractor_main_p()) {
2721 if (rb_ractor_main_p()) {
2733 if (rb_ractor_main_p()) {
2745 return &cr->pub.hooks;
2754enum obj_traverse_iterator_result {
2760typedef enum obj_traverse_iterator_result (*rb_obj_traverse_enter_func)(
VALUE obj);
2761typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(
VALUE obj);
2762typedef enum obj_traverse_iterator_result (*rb_obj_traverse_final_func)(
VALUE obj);
2764static enum obj_traverse_iterator_result null_leave(
VALUE obj);
2767 rb_obj_traverse_enter_func enter_func;
2768 rb_obj_traverse_leave_func leave_func;
2787 if (obj_traverse_i(key, d->data)) {
2792 if (obj_traverse_i(val, d->data)) {
2801obj_traverse_reachable_i(
VALUE obj,
void *ptr)
2805 if (obj_traverse_i(obj, d->data)) {
2813 if (UNLIKELY(!data->rec)) {
2814 data->rec_hash = rb_ident_hash_new();
2815 data->rec = RHASH_ST_TABLE(data->rec_hash);
2821obj_traverse_ivar_foreach_i(
ID key,
VALUE val, st_data_t ptr)
2825 if (obj_traverse_i(val, d->data)) {
2838 switch (data->enter_func(obj)) {
2839 case traverse_cont:
break;
2840 case traverse_skip:
return 0;
2841 case traverse_stop:
return 1;
2844 if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
2854 if (d.stop)
return 1;
2874 VALUE e = rb_ary_entry(obj, i);
2875 if (obj_traverse_i(e, data))
return 1;
2889 if (d.stop)
return 1;
2895 long len = RSTRUCT_LEN(obj);
2896 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2898 for (
long i=0; i<
len; i++) {
2899 if (obj_traverse_i(ptr[i], data))
return 1;
2905 if (obj_traverse_i(RRATIONAL(obj)->num, data))
return 1;
2906 if (obj_traverse_i(RRATIONAL(obj)->den, data))
return 1;
2909 if (obj_traverse_i(RCOMPLEX(obj)->real, data))
return 1;
2910 if (obj_traverse_i(RCOMPLEX(obj)->imag, data))
return 1;
2920 RB_VM_LOCK_ENTER_NO_BARRIER();
2922 rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
2924 RB_VM_LOCK_LEAVE_NO_BARRIER();
2925 if (d.stop)
return 1;
2935 rb_bug(
"unreachable");
2938 if (data->leave_func(obj) == traverse_stop) {
2947 rb_obj_traverse_final_func final_func;
2952obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
2955 if (data->final_func(key)) {
2965rb_obj_traverse(
VALUE obj,
2966 rb_obj_traverse_enter_func enter_func,
2967 rb_obj_traverse_leave_func leave_func,
2968 rb_obj_traverse_final_func final_func)
2971 .enter_func = enter_func,
2972 .leave_func = leave_func,
2976 if (obj_traverse_i(obj, &data))
return 1;
2977 if (final_func && data.rec) {
2979 st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
2986frozen_shareable_p(
VALUE obj,
bool *made_shareable)
2988 if (!RB_TYPE_P(obj,
T_DATA)) {
2993 if (
type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
2998 rb_proc_ractor_make_shareable(obj);
2999 *made_shareable =
true;
3008static enum obj_traverse_iterator_result
3009make_shareable_check_shareable(
VALUE obj)
3012 bool made_shareable =
false;
3015 return traverse_skip;
3017 else if (!frozen_shareable_p(obj, &made_shareable)) {
3018 if (made_shareable) {
3019 return traverse_skip;
3022 rb_raise(rb_eRactorError,
"can not make shareable object for %"PRIsVALUE, obj);
3030 rb_raise(rb_eRactorError,
"#freeze does not freeze object correctly");
3034 return traverse_skip;
3038 return traverse_cont;
3041static enum obj_traverse_iterator_result
3042mark_shareable(
VALUE obj)
3045 return traverse_cont;
3051 rb_obj_traverse(obj,
3052 make_shareable_check_shareable,
3053 null_leave, mark_shareable);
3060 VALUE copy = ractor_copy(obj);
3061 return rb_ractor_make_shareable(copy);
3065rb_ractor_ensure_shareable(
VALUE obj,
VALUE name)
3068 VALUE message = rb_sprintf(
"cannot assign unshareable object to %"PRIsVALUE,
3076rb_ractor_ensure_main_ractor(
const char *msg)
3078 if (!rb_ractor_main_p()) {
3079 rb_raise(rb_eRactorIsolationError,
"%s", msg);
3083static enum obj_traverse_iterator_result
3084shareable_p_enter(
VALUE obj)
3087 return traverse_skip;
3089 else if (RB_TYPE_P(obj,
T_CLASS) ||
3093 mark_shareable(obj);
3094 return traverse_skip;
3097 frozen_shareable_p(obj, NULL)) {
3098 return traverse_cont;
3101 return traverse_stop;
3105rb_ractor_shareable_p_continue(
VALUE obj)
3107 if (rb_obj_traverse(obj,
3108 shareable_p_enter, null_leave,
3117#if RACTOR_CHECK_MODE > 0
3118static enum obj_traverse_iterator_result
3119reset_belonging_enter(
VALUE obj)
3122 return traverse_skip;
3125 rb_ractor_setup_belonging(obj);
3126 return traverse_cont;
3131static enum obj_traverse_iterator_result
3132null_leave(
VALUE obj)
3134 return traverse_cont;
3138ractor_reset_belonging(
VALUE obj)
3140#if RACTOR_CHECK_MODE > 0
3141 rb_obj_traverse(obj, reset_belonging_enter, null_leave, NULL);
3159 rb_obj_traverse_replace_enter_func enter_func;
3160 rb_obj_traverse_replace_leave_func leave_func;
3176obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp,
int error)
3182obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr,
int exists)
3187 if (obj_traverse_replace_i(*key, data)) {
3191 else if (*key != data->replacement) {
3192 VALUE v = *key = data->replacement;
3196 if (obj_traverse_replace_i(*val, data)) {
3200 else if (*val != data->replacement) {
3201 VALUE v = *val = data->replacement;
3209obj_iv_hash_traverse_replace_foreach_i(st_data_t _key, st_data_t _val, st_data_t _data,
int _x)
3215obj_iv_hash_traverse_replace_i(st_data_t * _key, st_data_t * val, st_data_t ptr,
int exists)
3220 if (obj_traverse_replace_i(*(
VALUE *)val, data)) {
3224 else if (*(
VALUE *)val != data->replacement) {
3235 if (UNLIKELY(!data->rec)) {
3236 data->rec_hash = rb_ident_hash_new();
3237 data->rec = RHASH_ST_TABLE(data->rec_hash);
3243obj_refer_only_shareables_p_i(
VALUE obj,
void *ptr)
3245 int *pcnt = (
int *)ptr;
3253obj_refer_only_shareables_p(
VALUE obj)
3256 RB_VM_LOCK_ENTER_NO_BARRIER();
3258 rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
3260 RB_VM_LOCK_LEAVE_NO_BARRIER();
3267 st_data_t replacement;
3270 data->replacement = obj;
3274 switch (data->enter_func(obj, data)) {
3275 case traverse_cont:
break;
3276 case traverse_skip:
return 0;
3277 case traverse_stop:
return 1;
3280 replacement = (st_data_t)data->replacement;
3282 if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, &replacement))) {
3283 data->replacement = (
VALUE)replacement;
3287 st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, replacement);
3294#define CHECK_AND_REPLACE(v) do { \
3296 if (obj_traverse_replace_i(_val, data)) { return 1; } \
3297 else if (data->replacement != _val) { RB_OBJ_WRITE(obj, &v, data->replacement); } \
3302 rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
3304 if (UNLIKELY(rb_shape_obj_too_complex(obj))) {
3310 rb_st_foreach_with_replace(
3311 ivtbl->as.complex.table,
3312 obj_iv_hash_traverse_replace_foreach_i,
3313 obj_iv_hash_traverse_replace_i,
3316 if (d.stop)
return 1;
3319 for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
3320 if (!UNDEF_P(ivtbl->as.shape.ivptr[i])) {
3321 CHECK_AND_REPLACE(ivtbl->as.shape.ivptr[i]);
3337 rb_str_make_independent(obj);
3342 if (rb_shape_obj_too_complex(obj)) {
3348 rb_st_foreach_with_replace(
3349 ROBJECT_IV_HASH(obj),
3350 obj_iv_hash_traverse_replace_foreach_i,
3351 obj_iv_hash_traverse_replace_i,
3354 if (d.stop)
return 1;
3357 uint32_t
len = ROBJECT_IV_COUNT(obj);
3360 for (uint32_t i = 0; i <
len; i++) {
3361 CHECK_AND_REPLACE(ptr[i]);
3369 rb_ary_cancel_sharing(obj);
3372 VALUE e = rb_ary_entry(obj, i);
3374 if (obj_traverse_replace_i(e, data)) {
3377 else if (e != data->replacement) {
3391 rb_hash_stlike_foreach_with_replace(obj,
3392 obj_hash_traverse_replace_foreach_i,
3393 obj_hash_traverse_replace_i,
3395 if (d.stop)
return 1;
3399 if (obj_traverse_replace_i(ifnone, data)) {
3402 else if (ifnone != data->replacement) {
3410 long len = RSTRUCT_LEN(obj);
3411 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
3413 for (
long i=0; i<
len; i++) {
3414 CHECK_AND_REPLACE(ptr[i]);
3420 CHECK_AND_REPLACE(RRATIONAL(obj)->num);
3421 CHECK_AND_REPLACE(RRATIONAL(obj)->den);
3424 CHECK_AND_REPLACE(RCOMPLEX(obj)->real);
3425 CHECK_AND_REPLACE(RCOMPLEX(obj)->imag);
3429 if (!data->move && obj_refer_only_shareables_p(obj)) {
3433 rb_raise(rb_eRactorError,
"can not %s %"PRIsVALUE
" object.",
3447 rb_bug(
"unreachable");
3450 data->replacement = (
VALUE)replacement;
3452 if (data->leave_func(obj, data) == traverse_stop) {
3463rb_obj_traverse_replace(
VALUE obj,
3464 rb_obj_traverse_replace_enter_func enter_func,
3465 rb_obj_traverse_replace_leave_func leave_func,
3469 .enter_func = enter_func,
3470 .leave_func = leave_func,
3476 if (obj_traverse_replace_i(obj, &data)) {
3480 return data.replacement;
3499ractor_moved_bang(
VALUE obj)
3502 struct RVALUE *rv = (
void *)obj;
3504 rv->klass = rb_cRactorMovedObject;
3508 rv->flags = rv->flags & ~fl_users;
3515static enum obj_traverse_iterator_result
3519 data->replacement = obj;
3520 return traverse_skip;
3524 return traverse_cont;
3528void rb_replace_generic_ivar(
VALUE clone,
VALUE obj);
3530static enum obj_traverse_iterator_result
3533 VALUE v = data->replacement;
3537 dst->flags = (dst->flags & ~fl_users) | (src->flags & fl_users);
3544 rb_replace_generic_ivar(v, obj);
3549 ractor_moved_bang(obj);
3550 return traverse_cont;
3554ractor_move(
VALUE obj)
3556 VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave,
true);
3557 if (!UNDEF_P(val)) {
3561 rb_raise(rb_eRactorError,
"can not move the object");
3565static enum obj_traverse_iterator_result
3569 data->replacement = obj;
3570 return traverse_skip;
3574 return traverse_cont;
3578static enum obj_traverse_iterator_result
3581 return traverse_cont;
3585ractor_copy(
VALUE obj)
3587 VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave,
false);
3588 if (!UNDEF_P(val)) {
3592 rb_raise(rb_eRactorError,
"can not copy the object");
3607} freed_ractor_local_keys;
3610ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
3613 if (k->type->
mark) (*k->type->
mark)((
void *)val);
3617static enum rb_id_table_iterator_result
3618idkey_local_storage_mark_i(
ID id,
VALUE val,
void *dmy)
3621 return ID_TABLE_CONTINUE;
3627 if (r->local_storage) {
3628 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
3630 for (
int i=0; i<freed_ractor_local_keys.cnt; i++) {
3632 st_data_t val, k = (st_data_t)key;
3633 if (st_delete(r->local_storage, &k, &val) &&
3635 (*key->type->free)((
void *)val);
3640 if (r->idkey_local_storage) {
3641 rb_id_table_foreach(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
3646ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
3649 if (k->type->
free) (*k->type->
free)((
void *)val);
3656 if (r->local_storage) {
3657 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
3658 st_free_table(r->local_storage);
3661 if (r->idkey_local_storage) {
3662 rb_id_table_free(r->idkey_local_storage);
3667rb_ractor_local_storage_value_mark(
void *ptr)
3669 rb_gc_mark((
VALUE)ptr);
3683 rb_ractor_local_storage_value_mark,
3691 key->type =
type ?
type : &ractor_local_storage_type_null;
3692 key->main_cache = (
void *)
Qundef;
3707 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
3708 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
3711 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
3719 if (rb_ractor_main_p()) {
3720 if (!UNDEF_P((
VALUE)key->main_cache)) {
3721 *pret = key->main_cache;
3731 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
3745 if (cr->local_storage == NULL) {
3746 cr->local_storage = st_init_numtable();
3749 st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
3751 if (rb_ractor_main_p()) {
3752 key->main_cache = ptr;
3760 if (ractor_local_ref(key, &val)) {
3771 if (ractor_local_ref(key, (
void **)val)) {
3782 ractor_local_set(key, (
void *)val);
3789 if (ractor_local_ref(key, &ret)) {
3800 ractor_local_set(key, ptr);
3803#define DEFAULT_KEYS_CAPA 0x10
3806rb_ractor_finish_marking(
void)
3808 for (
int i=0; i<freed_ractor_local_keys.cnt; i++) {
3809 ruby_xfree(freed_ractor_local_keys.keys[i]);
3811 freed_ractor_local_keys.cnt = 0;
3812 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
3813 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
3823 struct rb_id_table *tbl = cr->idkey_local_storage;
3826 if (
id && tbl && rb_id_table_lookup(tbl,
id, &val)) {
3839 struct rb_id_table *tbl = cr->idkey_local_storage;
3842 tbl = cr->idkey_local_storage = rb_id_table_create(2);
3844 rb_id_table_insert(tbl,
id, val);
3848#include "ractor.rbinc"
#define RUBY_ATOMIC_CAS(var, oldval, newval)
Atomic compare-and-swap.
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
#define RUBY_ATOMIC_FETCH_ADD(var, val)
Atomically replaces the value pointed by var with the result of addition of val to the old value of v...
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
static VALUE RB_OBJ_FROZEN_RAW(VALUE obj)
This is an implementation detail of RB_OBJ_FROZEN().
@ RUBY_FL_SHAREABLE
This flag has something to do with Ractor.
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)
Keyword argument deconstructor.
#define 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.
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Checks if the given object is of given kind.
VALUE rb_eRuntimeError
RuntimeError exception.
VALUE rb_eStopIteration
StopIteration exception.
VALUE rb_exc_new_str(VALUE etype, VALUE str)
Identical to rb_exc_new_cstr(), except it takes a Ruby's string instead of C's.
VALUE rb_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_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
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_mutex_new(void)
Creates a mutex.
void rb_unblock_function_t(void *)
This is the type of UBFs.
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.
void rb_thread_sleep(int sec)
Blocks for the given period of time.
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.
int len
Length of the buffer.
const struct rb_ractor_local_storage_type rb_ractor_local_storage_type_free
A type of ractor-local storage that destructs itself using ruby_xfree.
VALUE rb_ractor_make_shareable_copy(VALUE obj)
Identical to rb_ractor_make_shareable(), except it returns a (deep) copy of the passed one instead of...
struct rb_ractor_local_key_struct * rb_ractor_local_key_t
(Opaque) struct that holds a ractor-local storage key.
void * rb_ractor_local_storage_ptr(rb_ractor_local_key_t key)
Identical to rb_ractor_local_storage_value() except the return type.
void rb_ractor_local_storage_ptr_set(rb_ractor_local_key_t key, void *ptr)
Identical to rb_ractor_local_storage_value_set() except the parameter type.
rb_ractor_local_key_t rb_ractor_local_storage_ptr_newkey(const struct rb_ractor_local_storage_type *type)
Extended version of rb_ractor_local_storage_value_newkey().
VALUE rb_ractor_stdin(void)
Queries the standard input of the current Ractor that is calling this function.
static bool rb_ractor_shareable_p(VALUE obj)
Queries if multiple Ractors can share the passed object or not.
void rb_ractor_stderr_set(VALUE io)
Assigns an IO to the standard error of the Ractor that is calling this function.
void rb_ractor_local_storage_value_set(rb_ractor_local_key_t key, VALUE val)
Associates the passed value to the passed key.
bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
Queries the key.
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
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_yield(VALUE val)
Yields the block.
#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.
void rb_ivar_foreach(VALUE q, int_type *w, VALUE e)
Iteration over each instance variable of the object.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
#define RARRAY_AREF(a, i)
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr
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 VALUE * ROBJECT_IVPTR(VALUE obj)
Queries the instance variables.
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
static bool RTYPEDDATA_P(VALUE obj)
Checks whether the passed object is RTypedData or RData.
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
static const struct rb_data_type_struct * RTYPEDDATA_TYPE(VALUE obj)
Queries for the type of given object.
static bool RB_SPECIAL_CONST_P(VALUE obj)
Checks if the given object is of enum ruby_special_consts.
#define RTEST
This is an old name of RB_TEST.
This is the struct that holds necessary info for a struct.
Type that defines a ractor-local storage.
void(* free)(void *ptr)
A function to destruct a ractor-local storage.
void(* mark)(void *ptr)
A function to mark a ractor-local storage.
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_lock
void rb_native_cond_initialize(rb_nativethread_cond_t *cond)
Fills the passed condition variable with an initial value.
void rb_native_cond_broadcast(rb_nativethread_cond_t *cond)
Signals a condition variable.
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_initialize
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_unlock
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_destroy
void rb_native_cond_destroy(rb_nativethread_cond_t *cond)
Destroys the passed condition variable.
void rb_native_cond_signal(rb_nativethread_cond_t *cond)
Signals a condition variable.
void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
Waits for the passed condition variable to be signalled.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.