Ruby 3.5.0dev (2025-02-20 revision 34098b669c0cbc024cd08e686891f1dfe0a10aaf)
ractor.c (34098b669c0cbc024cd08e686891f1dfe0a10aaf)
1// Ractor implementation
2
3#include "ruby/ruby.h"
4#include "ruby/thread.h"
5#include "ruby/ractor.h"
7#include "vm_core.h"
8#include "eval_intern.h"
9#include "vm_sync.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/ractor.h"
16#include "internal/rational.h"
17#include "internal/struct.h"
18#include "internal/thread.h"
19#include "variable.h"
20#include "yjit.h"
21
23static VALUE rb_cRactorSelector;
24
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;
32
33static void vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line);
34
35// Ractor locking
36
37static void
38ASSERT_ractor_unlocking(rb_ractor_t *r)
39{
40#if RACTOR_CHECK_MODE > 0
41 if (rb_current_execution_context(false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) {
42 rb_bug("recursive ractor locking");
43 }
44#endif
45}
46
47static void
48ASSERT_ractor_locking(rb_ractor_t *r)
49{
50#if RACTOR_CHECK_MODE > 0
51 if (rb_current_execution_context(false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) {
52 rp(r->sync.locked_by);
53 rb_bug("ractor lock is not acquired.");
54 }
55#endif
56}
57
58static void
59ractor_lock(rb_ractor_t *r, const char *file, int line)
60{
61 RUBY_DEBUG_LOG2(file, line, "locking r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
62
63 ASSERT_ractor_unlocking(r);
64 rb_native_mutex_lock(&r->sync.lock);
65
66#if RACTOR_CHECK_MODE > 0
67 if (rb_current_execution_context(false) != NULL) {
68 rb_ractor_t *cr = rb_current_ractor_raw(false);
69 r->sync.locked_by = cr ? rb_ractor_self(cr) : Qundef;
70 }
71#endif
72
73 RUBY_DEBUG_LOG2(file, line, "locked r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
74}
75
76static void
77ractor_lock_self(rb_ractor_t *cr, const char *file, int line)
78{
79 VM_ASSERT(cr == GET_RACTOR());
80#if RACTOR_CHECK_MODE > 0
81 VM_ASSERT(cr->sync.locked_by != cr->pub.self);
82#endif
83 ractor_lock(cr, file, line);
84}
85
86static void
87ractor_unlock(rb_ractor_t *r, const char *file, int line)
88{
89 ASSERT_ractor_locking(r);
90#if RACTOR_CHECK_MODE > 0
91 r->sync.locked_by = Qnil;
92#endif
93 rb_native_mutex_unlock(&r->sync.lock);
94
95 RUBY_DEBUG_LOG2(file, line, "r:%u%s", r->pub.id, rb_current_ractor_raw(false) == r ? " (self)" : "");
96}
97
98static void
99ractor_unlock_self(rb_ractor_t *cr, const char *file, int line)
100{
101 VM_ASSERT(cr == GET_RACTOR());
102#if RACTOR_CHECK_MODE > 0
103 VM_ASSERT(cr->sync.locked_by == cr->pub.self);
104#endif
105 ractor_unlock(cr, file, line);
106}
107
108#define RACTOR_LOCK(r) ractor_lock(r, __FILE__, __LINE__)
109#define RACTOR_UNLOCK(r) ractor_unlock(r, __FILE__, __LINE__)
110#define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
111#define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
112
113void
114rb_ractor_lock_self(rb_ractor_t *r)
115{
116 RACTOR_LOCK_SELF(r);
117}
118
119void
120rb_ractor_unlock_self(rb_ractor_t *r)
121{
122 RACTOR_UNLOCK_SELF(r);
123}
124
125// Ractor status
126
127static const char *
128ractor_status_str(enum ractor_status status)
129{
130 switch (status) {
131 case ractor_created: return "created";
132 case ractor_running: return "running";
133 case ractor_blocking: return "blocking";
134 case ractor_terminated: return "terminated";
135 }
136 rb_bug("unreachable");
137}
138
139static void
140ractor_status_set(rb_ractor_t *r, enum ractor_status status)
141{
142 RUBY_DEBUG_LOG("r:%u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
143
144 // check 1
145 if (r->status_ != ractor_created) {
146 VM_ASSERT(r == GET_RACTOR()); // only self-modification is allowed.
147 ASSERT_vm_locking();
148 }
149
150 // check2: transition check. assume it will be vanished on non-debug build.
151 switch (r->status_) {
152 case ractor_created:
153 VM_ASSERT(status == ractor_blocking);
154 break;
155 case ractor_running:
156 VM_ASSERT(status == ractor_blocking||
157 status == ractor_terminated);
158 break;
159 case ractor_blocking:
160 VM_ASSERT(status == ractor_running);
161 break;
162 case ractor_terminated:
163 rb_bug("unreachable");
164 break;
165 }
166
167 r->status_ = status;
168}
169
170static bool
171ractor_status_p(rb_ractor_t *r, enum ractor_status status)
172{
173 return rb_ractor_status_p(r, status);
174}
175
176// Ractor data/mark/free
177
178static struct rb_ractor_basket *ractor_queue_at(rb_ractor_t *r, struct rb_ractor_queue *rq, int i);
179static void ractor_local_storage_mark(rb_ractor_t *r);
180static void ractor_local_storage_free(rb_ractor_t *r);
181
182static void
183ractor_queue_mark(struct rb_ractor_queue *rq)
184{
185 for (int i=0; i<rq->cnt; i++) {
186 struct rb_ractor_basket *b = ractor_queue_at(NULL, rq, i);
187 rb_gc_mark(b->sender);
188
189 switch (b->type.e) {
190 case basket_type_yielding:
191 case basket_type_take_basket:
192 case basket_type_deleted:
193 case basket_type_reserved:
194 // ignore
195 break;
196 default:
197 rb_gc_mark(b->p.send.v);
198 }
199 }
200}
201
202static void
203ractor_mark(void *ptr)
204{
205 rb_ractor_t *r = (rb_ractor_t *)ptr;
206
207 ractor_queue_mark(&r->sync.recv_queue);
208 ractor_queue_mark(&r->sync.takers_queue);
209
210 rb_gc_mark(r->receiving_mutex);
211
212 rb_gc_mark(r->loc);
213 rb_gc_mark(r->name);
214 rb_gc_mark(r->r_stdin);
215 rb_gc_mark(r->r_stdout);
216 rb_gc_mark(r->r_stderr);
217 rb_hook_list_mark(&r->pub.hooks);
218
219 if (r->threads.cnt > 0) {
220 rb_thread_t *th = 0;
221 ccan_list_for_each(&r->threads.set, th, lt_node) {
222 VM_ASSERT(th != NULL);
223 rb_gc_mark(th->self);
224 }
225 }
226
227 ractor_local_storage_mark(r);
228}
229
230static void
231ractor_queue_free(struct rb_ractor_queue *rq)
232{
233 free(rq->baskets);
234}
235
236static void
237ractor_free(void *ptr)
238{
239 rb_ractor_t *r = (rb_ractor_t *)ptr;
240 RUBY_DEBUG_LOG("free r:%d", rb_ractor_id(r));
241 rb_native_mutex_destroy(&r->sync.lock);
242#ifdef RUBY_THREAD_WIN32_H
243 rb_native_cond_destroy(&r->sync.cond);
244#endif
245 ractor_queue_free(&r->sync.recv_queue);
246 ractor_queue_free(&r->sync.takers_queue);
247 ractor_local_storage_free(r);
248 rb_hook_list_free(&r->pub.hooks);
249
250 if (r->newobj_cache) {
251 RUBY_ASSERT(r == ruby_single_main_ractor);
252
253 rb_gc_ractor_cache_free(r->newobj_cache);
254 r->newobj_cache = NULL;
255 }
256
257 ruby_xfree(r);
258}
259
260static size_t
261ractor_queue_memsize(const struct rb_ractor_queue *rq)
262{
263 return sizeof(struct rb_ractor_basket) * rq->size;
264}
265
266static size_t
267ractor_memsize(const void *ptr)
268{
269 rb_ractor_t *r = (rb_ractor_t *)ptr;
270
271 // TODO: more correct?
272 return sizeof(rb_ractor_t) +
273 ractor_queue_memsize(&r->sync.recv_queue) +
274 ractor_queue_memsize(&r->sync.takers_queue);
275}
276
277static const rb_data_type_t ractor_data_type = {
278 "ractor",
279 {
280 ractor_mark,
281 ractor_free,
282 ractor_memsize,
283 NULL, // update
284 },
285 0, 0, RUBY_TYPED_FREE_IMMEDIATELY /* | RUBY_TYPED_WB_PROTECTED */
286};
287
288bool
289rb_ractor_p(VALUE gv)
290{
291 if (rb_typeddata_is_kind_of(gv, &ractor_data_type)) {
292 return true;
293 }
294 else {
295 return false;
296 }
297}
298
299static inline rb_ractor_t *
300RACTOR_PTR(VALUE self)
301{
302 VM_ASSERT(rb_ractor_p(self));
303 rb_ractor_t *r = DATA_PTR(self);
304 return r;
305}
306
307static rb_atomic_t ractor_last_id;
308
309#if RACTOR_CHECK_MODE > 0
310uint32_t
311rb_ractor_current_id(void)
312{
313 if (GET_THREAD()->ractor == NULL) {
314 return 1; // main ractor
315 }
316 else {
317 return rb_ractor_id(GET_RACTOR());
318 }
319}
320#endif
321
322// Ractor queue
323
324static void
325ractor_queue_setup(struct rb_ractor_queue *rq)
326{
327 rq->size = 2;
328 rq->cnt = 0;
329 rq->start = 0;
330 rq->baskets = malloc(sizeof(struct rb_ractor_basket) * rq->size);
331}
332
333static struct rb_ractor_basket *
334ractor_queue_head(rb_ractor_t *r, struct rb_ractor_queue *rq)
335{
336 if (r != NULL) ASSERT_ractor_locking(r);
337 return &rq->baskets[rq->start];
338}
339
340static struct rb_ractor_basket *
341ractor_queue_at(rb_ractor_t *r, struct rb_ractor_queue *rq, int i)
342{
343 if (r != NULL) ASSERT_ractor_locking(r);
344 return &rq->baskets[(rq->start + i) % rq->size];
345}
346
347static void
348ractor_queue_advance(rb_ractor_t *r, struct rb_ractor_queue *rq)
349{
350 ASSERT_ractor_locking(r);
351
352 if (rq->reserved_cnt == 0) {
353 rq->cnt--;
354 rq->start = (rq->start + 1) % rq->size;
355 rq->serial++;
356 }
357 else {
358 ractor_queue_at(r, rq, 0)->type.e = basket_type_deleted;
359 }
360}
361
362static bool
363ractor_queue_skip_p(rb_ractor_t *r, struct rb_ractor_queue *rq, int i)
364{
365 struct rb_ractor_basket *b = ractor_queue_at(r, rq, i);
366 return basket_type_p(b, basket_type_deleted) ||
367 basket_type_p(b, basket_type_reserved);
368}
369
370static void
371ractor_queue_compact(rb_ractor_t *r, struct rb_ractor_queue *rq)
372{
373 ASSERT_ractor_locking(r);
374
375 while (rq->cnt > 0 && basket_type_p(ractor_queue_at(r, rq, 0), basket_type_deleted)) {
376 ractor_queue_advance(r, rq);
377 }
378}
379
380static bool
381ractor_queue_empty_p(rb_ractor_t *r, struct rb_ractor_queue *rq)
382{
383 ASSERT_ractor_locking(r);
384
385 if (rq->cnt == 0) {
386 return true;
387 }
388
389 ractor_queue_compact(r, rq);
390
391 for (int i=0; i<rq->cnt; i++) {
392 if (!ractor_queue_skip_p(r, rq, i)) {
393 return false;
394 }
395 }
396
397 return true;
398}
399
400static bool
401ractor_queue_deq(rb_ractor_t *r, struct rb_ractor_queue *rq, struct rb_ractor_basket *basket)
402{
403 ASSERT_ractor_locking(r);
404
405 for (int i=0; i<rq->cnt; i++) {
406 if (!ractor_queue_skip_p(r, rq, i)) {
407 struct rb_ractor_basket *b = ractor_queue_at(r, rq, i);
408 *basket = *b;
409
410 // remove from queue
411 b->type.e = basket_type_deleted;
412 ractor_queue_compact(r, rq);
413 return true;
414 }
415 }
416
417 return false;
418}
419
420static void
421ractor_queue_enq(rb_ractor_t *r, struct rb_ractor_queue *rq, struct rb_ractor_basket *basket)
422{
423 ASSERT_ractor_locking(r);
424
425 if (rq->size <= rq->cnt) {
426 rq->baskets = realloc(rq->baskets, sizeof(struct rb_ractor_basket) * rq->size * 2);
427 for (int i=rq->size - rq->start; i<rq->cnt; i++) {
428 rq->baskets[i + rq->start] = rq->baskets[i + rq->start - rq->size];
429 }
430 rq->size *= 2;
431 }
432 rq->baskets[(rq->start + rq->cnt++) % rq->size] = *basket;
433 // fprintf(stderr, "%s %p->cnt:%d\n", RUBY_FUNCTION_NAME_STRING, (void *)rq, rq->cnt);
434}
435
436static void
437ractor_queue_delete(rb_ractor_t *r, struct rb_ractor_queue *rq, struct rb_ractor_basket *basket)
438{
439 basket->type.e = basket_type_deleted;
440}
441
442// Ractor basket
443
444static VALUE ractor_reset_belonging(VALUE obj); // in this file
445
446static VALUE
447ractor_basket_value(struct rb_ractor_basket *b)
448{
449 switch (b->type.e) {
450 case basket_type_ref:
451 break;
452 case basket_type_copy:
453 case basket_type_move:
454 case basket_type_will:
455 b->type.e = basket_type_ref;
456 b->p.send.v = ractor_reset_belonging(b->p.send.v);
457 break;
458 default:
459 rb_bug("unreachable");
460 }
461
462 return b->p.send.v;
463}
464
465static VALUE
466ractor_basket_accept(struct rb_ractor_basket *b)
467{
468 VALUE v = ractor_basket_value(b);
469
470 if (b->p.send.exception) {
471 VALUE cause = v;
472 VALUE err = rb_exc_new_cstr(rb_eRactorRemoteError, "thrown by remote Ractor.");
473 rb_ivar_set(err, rb_intern("@ractor"), b->sender);
474 rb_ec_setup_exception(NULL, err, cause);
475 rb_exc_raise(err);
476 }
477
478 return v;
479}
480
481// Ractor synchronizations
482
483#if USE_RUBY_DEBUG_LOG
484static const char *
485wait_status_str(enum rb_ractor_wait_status wait_status)
486{
487 switch ((int)wait_status) {
488 case wait_none: return "none";
489 case wait_receiving: return "receiving";
490 case wait_taking: return "taking";
491 case wait_yielding: return "yielding";
492 case wait_receiving|wait_taking: return "receiving|taking";
493 case wait_receiving|wait_yielding: return "receiving|yielding";
494 case wait_taking|wait_yielding: return "taking|yielding";
495 case wait_receiving|wait_taking|wait_yielding: return "receiving|taking|yielding";
496 }
497 rb_bug("unreachable");
498}
499
500static const char *
501wakeup_status_str(enum rb_ractor_wakeup_status wakeup_status)
502{
503 switch (wakeup_status) {
504 case wakeup_none: return "none";
505 case wakeup_by_send: return "by_send";
506 case wakeup_by_yield: return "by_yield";
507 case wakeup_by_take: return "by_take";
508 case wakeup_by_close: return "by_close";
509 case wakeup_by_interrupt: return "by_interrupt";
510 case wakeup_by_retry: return "by_retry";
511 }
512 rb_bug("unreachable");
513}
514
515static const char *
516basket_type_name(enum rb_ractor_basket_type type)
517{
518 switch (type) {
519 case basket_type_none: return "none";
520 case basket_type_ref: return "ref";
521 case basket_type_copy: return "copy";
522 case basket_type_move: return "move";
523 case basket_type_will: return "will";
524 case basket_type_deleted: return "deleted";
525 case basket_type_reserved: return "reserved";
526 case basket_type_take_basket: return "take_basket";
527 case basket_type_yielding: return "yielding";
528 }
529 VM_ASSERT(0);
530 return NULL;
531}
532#endif // USE_RUBY_DEBUG_LOG
533
534static bool
535ractor_sleeping_by(const rb_ractor_t *r, enum rb_ractor_wait_status wait_status)
536{
537 return (r->sync.wait.status & wait_status) && r->sync.wait.wakeup_status == wakeup_none;
538}
539
540#ifdef RUBY_THREAD_PTHREAD_H
541// thread_*.c
542void rb_ractor_sched_wakeup(rb_ractor_t *r);
543#else
544
545static void
546rb_ractor_sched_wakeup(rb_ractor_t *r)
547{
548 rb_native_cond_broadcast(&r->sync.cond);
549}
550#endif
551
552
553static bool
554ractor_wakeup(rb_ractor_t *r, enum rb_ractor_wait_status wait_status, enum rb_ractor_wakeup_status wakeup_status)
555{
556 ASSERT_ractor_locking(r);
557
558 RUBY_DEBUG_LOG("r:%u wait_by:%s -> wait:%s wakeup:%s",
559 rb_ractor_id(r),
560 wait_status_str(r->sync.wait.status),
561 wait_status_str(wait_status),
562 wakeup_status_str(wakeup_status));
563
564 if (ractor_sleeping_by(r, wait_status)) {
565 r->sync.wait.wakeup_status = wakeup_status;
566 rb_ractor_sched_wakeup(r);
567 return true;
568 }
569 else {
570 return false;
571 }
572}
573
574static void
575ractor_sleep_interrupt(void *ptr)
576{
577 rb_ractor_t *r = ptr;
578
579 RACTOR_LOCK(r);
580 {
581 ractor_wakeup(r, wait_receiving | wait_taking | wait_yielding, wakeup_by_interrupt);
582 }
583 RACTOR_UNLOCK(r);
584}
585
586typedef void (*ractor_sleep_cleanup_function)(rb_ractor_t *cr, void *p);
587
588static void
589ractor_check_ints(rb_execution_context_t *ec, rb_ractor_t *cr, ractor_sleep_cleanup_function cf_func, void *cf_data)
590{
591 if (cr->sync.wait.status != wait_none) {
592 enum rb_ractor_wait_status prev_wait_status = cr->sync.wait.status;
593 cr->sync.wait.status = wait_none;
594 cr->sync.wait.wakeup_status = wakeup_by_interrupt;
595
596 RACTOR_UNLOCK(cr);
597 {
598 if (cf_func) {
599 enum ruby_tag_type state;
600 EC_PUSH_TAG(ec);
601 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
602 rb_ec_check_ints(ec);
603 }
604 EC_POP_TAG();
605
606 if (state) {
607 (*cf_func)(cr, cf_data);
608 EC_JUMP_TAG(ec, state);
609 }
610 }
611 else {
612 rb_ec_check_ints(ec);
613 }
614 }
615
616 // reachable?
617 RACTOR_LOCK(cr);
618 cr->sync.wait.status = prev_wait_status;
619 }
620}
621
622#ifdef RUBY_THREAD_PTHREAD_H
623void rb_ractor_sched_sleep(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_function_t *ubf);
624#else
625
626// win32
627static void
628ractor_cond_wait(rb_ractor_t *r)
629{
630#if RACTOR_CHECK_MODE > 0
631 VALUE locked_by = r->sync.locked_by;
632 r->sync.locked_by = Qnil;
633#endif
634 rb_native_cond_wait(&r->sync.cond, &r->sync.lock);
635
636#if RACTOR_CHECK_MODE > 0
637 r->sync.locked_by = locked_by;
638#endif
639}
640
641static void *
642ractor_sleep_wo_gvl(void *ptr)
643{
644 rb_ractor_t *cr = ptr;
645 RACTOR_LOCK_SELF(cr);
646 {
647 VM_ASSERT(cr->sync.wait.status != wait_none);
648 if (cr->sync.wait.wakeup_status == wakeup_none) {
649 ractor_cond_wait(cr);
650 }
651 cr->sync.wait.status = wait_none;
652 }
653 RACTOR_UNLOCK_SELF(cr);
654 return NULL;
655}
656
657static void
658rb_ractor_sched_sleep(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_function_t *ubf)
659{
660 RACTOR_UNLOCK(cr);
661 {
662 rb_nogvl(ractor_sleep_wo_gvl, cr,
663 ubf, cr,
665 }
666 RACTOR_LOCK(cr);
667}
668#endif
669
670static enum rb_ractor_wakeup_status
671ractor_sleep_with_cleanup(rb_execution_context_t *ec, rb_ractor_t *cr, enum rb_ractor_wait_status wait_status,
672 ractor_sleep_cleanup_function cf_func, void *cf_data)
673{
674 enum rb_ractor_wakeup_status wakeup_status;
675 VM_ASSERT(GET_RACTOR() == cr);
676
677 // TODO: multi-threads
678 VM_ASSERT(cr->sync.wait.status == wait_none);
679 VM_ASSERT(wait_status != wait_none);
680 cr->sync.wait.status = wait_status;
681 cr->sync.wait.wakeup_status = wakeup_none;
682
683 // fprintf(stderr, "%s r:%p status:%s, wakeup_status:%s\n", RUBY_FUNCTION_NAME_STRING, (void *)cr,
684 // wait_status_str(cr->sync.wait.status), wakeup_status_str(cr->sync.wait.wakeup_status));
685
686 RUBY_DEBUG_LOG("sleep by %s", wait_status_str(wait_status));
687
688 while (cr->sync.wait.wakeup_status == wakeup_none) {
689 rb_ractor_sched_sleep(ec, cr, ractor_sleep_interrupt);
690 ractor_check_ints(ec, cr, cf_func, cf_data);
691 }
692
693 cr->sync.wait.status = wait_none;
694
695 // TODO: multi-thread
696 wakeup_status = cr->sync.wait.wakeup_status;
697 cr->sync.wait.wakeup_status = wakeup_none;
698
699 RUBY_DEBUG_LOG("wakeup %s", wakeup_status_str(wakeup_status));
700
701 return wakeup_status;
702}
703
704static enum rb_ractor_wakeup_status
705ractor_sleep(rb_execution_context_t *ec, rb_ractor_t *cr, enum rb_ractor_wait_status wait_status)
706{
707 return ractor_sleep_with_cleanup(ec, cr, wait_status, 0, NULL);
708}
709
710// Ractor.receive
711
712static void
713ractor_recursive_receive_if(rb_ractor_t *r)
714{
715 if (r->receiving_mutex && rb_mutex_owned_p(r->receiving_mutex)) {
716 rb_raise(rb_eRactorError, "can not call receive/receive_if recursively");
717 }
718}
719
720static VALUE
721ractor_try_receive(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_queue *rq)
722{
723 struct rb_ractor_basket basket;
724 ractor_recursive_receive_if(cr);
725 bool received = false;
726
727 RACTOR_LOCK_SELF(cr);
728 {
729 RUBY_DEBUG_LOG("rq->cnt:%d", rq->cnt);
730 received = ractor_queue_deq(cr, rq, &basket);
731 }
732 RACTOR_UNLOCK_SELF(cr);
733
734 if (!received) {
735 if (cr->sync.incoming_port_closed) {
736 rb_raise(rb_eRactorClosedError, "The incoming port is already closed");
737 }
738 return Qundef;
739 }
740 else {
741 return ractor_basket_accept(&basket);
742 }
743}
744
745static void
746ractor_wait_receive(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_queue *rq)
747{
748 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
749 ractor_recursive_receive_if(cr);
750
751 RACTOR_LOCK(cr);
752 {
753 while (ractor_queue_empty_p(cr, rq) && !cr->sync.incoming_port_closed) {
754 ractor_sleep(ec, cr, wait_receiving);
755 }
756 }
757 RACTOR_UNLOCK(cr);
758}
759
760static VALUE
761ractor_receive(rb_execution_context_t *ec, rb_ractor_t *cr)
762{
763 VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
764 VALUE v;
765 struct rb_ractor_queue *rq = &cr->sync.recv_queue;
766
767 while (UNDEF_P(v = ractor_try_receive(ec, cr, rq))) {
768 ractor_wait_receive(ec, cr, rq);
769 }
770
771 return v;
772}
773
774#if 0
775static void
776rq_dump(struct rb_ractor_queue *rq)
777{
778 bool bug = false;
779 for (int i=0; i<rq->cnt; i++) {
780 struct rb_ractor_basket *b = ractor_queue_at(NULL, rq, i);
781 fprintf(stderr, "%d (start:%d) type:%s %p %s\n", i, rq->start, basket_type_name(b->type),
782 (void *)b, RSTRING_PTR(RARRAY_AREF(b->v, 1)));
783 if (basket_type_p(b, basket_type_reserved) bug = true;
784 }
785 if (bug) rb_bug("!!");
786}
787#endif
788
790 rb_ractor_t *cr;
791 struct rb_ractor_queue *rq;
792 VALUE v;
793 int index;
794 bool success;
795};
796
797static void
798ractor_receive_if_lock(rb_ractor_t *cr)
799{
800 VALUE m = cr->receiving_mutex;
801 if (m == Qfalse) {
802 m = cr->receiving_mutex = rb_mutex_new();
803 }
804 rb_mutex_lock(m);
805}
806
807static VALUE
808receive_if_body(VALUE ptr)
809{
810 struct receive_block_data *data = (struct receive_block_data *)ptr;
811
812 ractor_receive_if_lock(data->cr);
813 VALUE block_result = rb_yield(data->v);
814 rb_ractor_t *cr = data->cr;
815
816 RACTOR_LOCK_SELF(cr);
817 {
818 struct rb_ractor_basket *b = ractor_queue_at(cr, data->rq, data->index);
819 VM_ASSERT(basket_type_p(b, basket_type_reserved));
820 data->rq->reserved_cnt--;
821
822 if (RTEST(block_result)) {
823 ractor_queue_delete(cr, data->rq, b);
824 ractor_queue_compact(cr, data->rq);
825 }
826 else {
827 b->type.e = basket_type_ref;
828 }
829 }
830 RACTOR_UNLOCK_SELF(cr);
831
832 data->success = true;
833
834 if (RTEST(block_result)) {
835 return data->v;
836 }
837 else {
838 return Qundef;
839 }
840}
841
842static VALUE
843receive_if_ensure(VALUE v)
844{
845 struct receive_block_data *data = (struct receive_block_data *)v;
846 rb_ractor_t *cr = data->cr;
847
848 if (!data->success) {
849 RACTOR_LOCK_SELF(cr);
850 {
851 struct rb_ractor_basket *b = ractor_queue_at(cr, data->rq, data->index);
852 VM_ASSERT(basket_type_p(b, basket_type_reserved));
853 b->type.e = basket_type_deleted;
854 data->rq->reserved_cnt--;
855 }
856 RACTOR_UNLOCK_SELF(cr);
857 }
858
859 rb_mutex_unlock(cr->receiving_mutex);
860 return Qnil;
861}
862
863static VALUE
864ractor_receive_if(rb_execution_context_t *ec, VALUE crv, VALUE b)
865{
866 if (!RTEST(b)) rb_raise(rb_eArgError, "no block given");
867
868 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
869 unsigned int serial = (unsigned int)-1;
870 int index = 0;
871 struct rb_ractor_queue *rq = &cr->sync.recv_queue;
872
873 while (1) {
874 VALUE v = Qundef;
875
876 ractor_wait_receive(ec, cr, rq);
877
878 RACTOR_LOCK_SELF(cr);
879 {
880 if (serial != rq->serial) {
881 serial = rq->serial;
882 index = 0;
883 }
884
885 // check newer version
886 for (int i=index; i<rq->cnt; i++) {
887 if (!ractor_queue_skip_p(cr, rq, i)) {
888 struct rb_ractor_basket *b = ractor_queue_at(cr, rq, i);
889 v = ractor_basket_value(b);
890 b->type.e = basket_type_reserved;
891 rq->reserved_cnt++;
892 index = i;
893 break;
894 }
895 }
896 }
897 RACTOR_UNLOCK_SELF(cr);
898
899 if (!UNDEF_P(v)) {
900 struct receive_block_data data = {
901 .cr = cr,
902 .rq = rq,
903 .v = v,
904 .index = index,
905 .success = false,
906 };
907
908 VALUE result = rb_ensure(receive_if_body, (VALUE)&data,
909 receive_if_ensure, (VALUE)&data);
910
911 if (!UNDEF_P(result)) return result;
912 index++;
913 }
914
915 RUBY_VM_CHECK_INTS(ec);
916 }
917}
918
919static void
920ractor_send_basket(rb_execution_context_t *ec, rb_ractor_t *r, struct rb_ractor_basket *b)
921{
922 bool closed = false;
923
924 RACTOR_LOCK(r);
925 {
926 if (r->sync.incoming_port_closed) {
927 closed = true;
928 }
929 else {
930 ractor_queue_enq(r, &r->sync.recv_queue, b);
931 ractor_wakeup(r, wait_receiving, wakeup_by_send);
932 }
933 }
934 RACTOR_UNLOCK(r);
935
936 if (closed) {
937 rb_raise(rb_eRactorClosedError, "The incoming-port is already closed");
938 }
939}
940
941// Ractor#send
942
943static VALUE ractor_move(VALUE obj); // in this file
944static VALUE ractor_copy(VALUE obj); // in this file
945
946static void
947ractor_basket_prepare_contents(VALUE obj, VALUE move, volatile VALUE *pobj, enum rb_ractor_basket_type *ptype)
948{
949 VALUE v;
950 enum rb_ractor_basket_type type;
951
952 if (rb_ractor_shareable_p(obj)) {
953 type = basket_type_ref;
954 v = obj;
955 }
956 else if (!RTEST(move)) {
957 v = ractor_copy(obj);
958 type = basket_type_copy;
959 }
960 else {
961 type = basket_type_move;
962 v = ractor_move(obj);
963 }
964
965 *pobj = v;
966 *ptype = type;
967}
968
969static void
970ractor_basket_fill_(rb_ractor_t *cr, struct rb_ractor_basket *basket, VALUE obj, bool exc)
971{
972 VM_ASSERT(cr == GET_RACTOR());
973
974 basket->sender = cr->pub.self;
975 basket->p.send.exception = exc;
976 basket->p.send.v = obj;
977}
978
979static void
980ractor_basket_fill(rb_ractor_t *cr, struct rb_ractor_basket *basket, VALUE obj, VALUE move, bool exc)
981{
982 VALUE v;
983 enum rb_ractor_basket_type type;
984 ractor_basket_prepare_contents(obj, move, &v, &type);
985 ractor_basket_fill_(cr, basket, v, exc);
986 basket->type.e = type;
987}
988
989static void
990ractor_basket_fill_will(rb_ractor_t *cr, struct rb_ractor_basket *basket, VALUE obj, bool exc)
991{
992 ractor_basket_fill_(cr, basket, obj, exc);
993 basket->type.e = basket_type_will;
994}
995
996static VALUE
997ractor_send(rb_execution_context_t *ec, rb_ractor_t *r, VALUE obj, VALUE move)
998{
999 struct rb_ractor_basket basket;
1000 // TODO: Ractor local GC
1001 ractor_basket_fill(rb_ec_ractor_ptr(ec), &basket, obj, move, false);
1002 ractor_send_basket(ec, r, &basket);
1003 return r->pub.self;
1004}
1005
1006// Ractor#take
1007
1008static bool
1009ractor_take_has_will(rb_ractor_t *r)
1010{
1011 ASSERT_ractor_locking(r);
1012
1013 return basket_type_p(&r->sync.will_basket, basket_type_will);
1014}
1015
1016static bool
1017ractor_take_will(rb_ractor_t *r, struct rb_ractor_basket *b)
1018{
1019 ASSERT_ractor_locking(r);
1020
1021 if (ractor_take_has_will(r)) {
1022 *b = r->sync.will_basket;
1023 r->sync.will_basket.type.e = basket_type_none;
1024 return true;
1025 }
1026 else {
1027 VM_ASSERT(basket_type_p(&r->sync.will_basket, basket_type_none));
1028 return false;
1029 }
1030}
1031
1032static bool
1033ractor_take_will_lock(rb_ractor_t *r, struct rb_ractor_basket *b)
1034{
1035 ASSERT_ractor_unlocking(r);
1036 bool taken;
1037
1038 RACTOR_LOCK(r);
1039 {
1040 taken = ractor_take_will(r, b);
1041 }
1042 RACTOR_UNLOCK(r);
1043
1044 return taken;
1045}
1046
1047static bool
1048ractor_register_take(rb_ractor_t *cr, rb_ractor_t *r, struct rb_ractor_basket *take_basket,
1049 bool is_take, struct rb_ractor_selector_take_config *config, bool ignore_error)
1050{
1051 struct rb_ractor_basket b = {
1052 .type.e = basket_type_take_basket,
1053 .sender = cr->pub.self,
1054 .p = {
1055 .take = {
1056 .basket = take_basket,
1057 .config = config,
1058 },
1059 },
1060 };
1061 bool closed = false;
1062
1063 RACTOR_LOCK(r);
1064 {
1065 if (is_take && ractor_take_will(r, take_basket)) {
1066 RUBY_DEBUG_LOG("take over a will of r:%d", rb_ractor_id(r));
1067 }
1068 else if (!is_take && ractor_take_has_will(r)) {
1069 RUBY_DEBUG_LOG("has_will");
1070 VM_ASSERT(config != NULL);
1071 config->closed = true;
1072 }
1073 else if (r->sync.outgoing_port_closed) {
1074 closed = true;
1075 }
1076 else {
1077 RUBY_DEBUG_LOG("register in r:%d", rb_ractor_id(r));
1078 ractor_queue_enq(r, &r->sync.takers_queue, &b);
1079
1080 if (basket_none_p(take_basket)) {
1081 ractor_wakeup(r, wait_yielding, wakeup_by_take);
1082 }
1083 }
1084 }
1085 RACTOR_UNLOCK(r);
1086
1087 if (closed) {
1088 if (!ignore_error) rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
1089 return false;
1090 }
1091 else {
1092 return true;
1093 }
1094}
1095
1096static bool
1097ractor_deregister_take(rb_ractor_t *r, struct rb_ractor_basket *take_basket)
1098{
1099 struct rb_ractor_queue *ts = &r->sync.takers_queue;
1100 bool deleted = false;
1101
1102 RACTOR_LOCK(r);
1103 {
1104 if (r->sync.outgoing_port_closed) {
1105 // ok
1106 }
1107 else {
1108 for (int i=0; i<ts->cnt; i++) {
1109 struct rb_ractor_basket *b = ractor_queue_at(r, ts, i);
1110 if (basket_type_p(b, basket_type_take_basket) && b->p.take.basket == take_basket) {
1111 ractor_queue_delete(r, ts, b);
1112 deleted = true;
1113 }
1114 }
1115 if (deleted) {
1116 ractor_queue_compact(r, ts);
1117 }
1118 }
1119 }
1120 RACTOR_UNLOCK(r);
1121
1122 return deleted;
1123}
1124
1125static VALUE
1126ractor_try_take(rb_ractor_t *cr, rb_ractor_t *r, struct rb_ractor_basket *take_basket)
1127{
1128 bool taken;
1129
1130 RACTOR_LOCK_SELF(cr);
1131 {
1132 if (basket_none_p(take_basket) || basket_type_p(take_basket, basket_type_yielding)) {
1133 taken = false;
1134 }
1135 else {
1136 taken = true;
1137 }
1138 }
1139 RACTOR_UNLOCK_SELF(cr);
1140
1141 if (taken) {
1142 RUBY_DEBUG_LOG("taken");
1143 if (basket_type_p(take_basket, basket_type_deleted)) {
1144 VM_ASSERT(r->sync.outgoing_port_closed);
1145 rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
1146 }
1147 return ractor_basket_accept(take_basket);
1148 }
1149 else {
1150 RUBY_DEBUG_LOG("not taken");
1151 return Qundef;
1152 }
1153}
1154
1155
1156#if VM_CHECK_MODE > 0
1157static bool
1158ractor_check_specific_take_basket_lock(rb_ractor_t *r, struct rb_ractor_basket *tb)
1159{
1160 bool ret = false;
1161 struct rb_ractor_queue *ts = &r->sync.takers_queue;
1162
1163 RACTOR_LOCK(r);
1164 {
1165 for (int i=0; i<ts->cnt; i++) {
1166 struct rb_ractor_basket *b = ractor_queue_at(r, ts, i);
1167 if (basket_type_p(b, basket_type_take_basket) && b->p.take.basket == tb) {
1168 ret = true;
1169 break;
1170 }
1171 }
1172 }
1173 RACTOR_UNLOCK(r);
1174
1175 return ret;
1176}
1177#endif
1178
1179static void
1180ractor_take_cleanup(rb_ractor_t *cr, rb_ractor_t *r, struct rb_ractor_basket *tb)
1181{
1182 retry:
1183 if (basket_none_p(tb)) { // not yielded yet
1184 if (!ractor_deregister_take(r, tb)) {
1185 // not in r's takers queue
1186 rb_thread_sleep(0);
1187 goto retry;
1188 }
1189 }
1190 else {
1191 VM_ASSERT(!ractor_check_specific_take_basket_lock(r, tb));
1192 }
1193}
1194
1196 rb_ractor_t *r;
1197 struct rb_ractor_basket *tb;
1198};
1199
1200static void
1201ractor_wait_take_cleanup(rb_ractor_t *cr, void *ptr)
1202{
1203 struct take_wait_take_cleanup_data *data = (struct take_wait_take_cleanup_data *)ptr;
1204 ractor_take_cleanup(cr, data->r, data->tb);
1205}
1206
1207static void
1208ractor_wait_take(rb_execution_context_t *ec, rb_ractor_t *cr, rb_ractor_t *r, struct rb_ractor_basket *take_basket)
1209{
1210 struct take_wait_take_cleanup_data data = {
1211 .r = r,
1212 .tb = take_basket,
1213 };
1214
1215 RACTOR_LOCK_SELF(cr);
1216 {
1217 if (basket_none_p(take_basket) || basket_type_p(take_basket, basket_type_yielding)) {
1218 ractor_sleep_with_cleanup(ec, cr, wait_taking, ractor_wait_take_cleanup, &data);
1219 }
1220 }
1221 RACTOR_UNLOCK_SELF(cr);
1222}
1223
1224static VALUE
1225ractor_take(rb_execution_context_t *ec, rb_ractor_t *r)
1226{
1227 RUBY_DEBUG_LOG("from r:%u", rb_ractor_id(r));
1228 VALUE v;
1229 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1230
1231 struct rb_ractor_basket take_basket = {
1232 .type.e = basket_type_none,
1233 .sender = 0,
1234 };
1235
1236 ractor_register_take(cr, r, &take_basket, true, NULL, false);
1237
1238 while (UNDEF_P(v = ractor_try_take(cr, r, &take_basket))) {
1239 ractor_wait_take(ec, cr, r, &take_basket);
1240 }
1241
1242 VM_ASSERT(!basket_none_p(&take_basket));
1243 VM_ASSERT(!ractor_check_specific_take_basket_lock(r, &take_basket));
1244
1245 return v;
1246}
1247
1248// Ractor.yield
1249
1250static bool
1251ractor_check_take_basket(rb_ractor_t *cr, struct rb_ractor_queue *rs)
1252{
1253 ASSERT_ractor_locking(cr);
1254
1255 for (int i=0; i<rs->cnt; i++) {
1256 struct rb_ractor_basket *b = ractor_queue_at(cr, rs, i);
1257 if (basket_type_p(b, basket_type_take_basket) &&
1258 basket_none_p(b->p.take.basket)) {
1259 return true;
1260 }
1261 }
1262
1263 return false;
1264}
1265
1266static bool
1267ractor_deq_take_basket(rb_ractor_t *cr, struct rb_ractor_queue *rs, struct rb_ractor_basket *b)
1268{
1269 ASSERT_ractor_unlocking(cr);
1270 struct rb_ractor_basket *first_tb = NULL;
1271 bool found = false;
1272
1273 RACTOR_LOCK_SELF(cr);
1274 {
1275 while (ractor_queue_deq(cr, rs, b)) {
1276 if (basket_type_p(b, basket_type_take_basket)) {
1277 struct rb_ractor_basket *tb = b->p.take.basket;
1278
1279 if (RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_yielding) == basket_type_none) {
1280 found = true;
1281 break;
1282 }
1283 else {
1284 ractor_queue_enq(cr, rs, b);
1285 if (first_tb == NULL) first_tb = tb;
1286 struct rb_ractor_basket *head = ractor_queue_head(cr, rs);
1287 VM_ASSERT(head != NULL);
1288 if (basket_type_p(head, basket_type_take_basket) && head->p.take.basket == first_tb) {
1289 break; // loop detected
1290 }
1291 }
1292 }
1293 else {
1294 VM_ASSERT(basket_none_p(b));
1295 }
1296 }
1297
1298 if (found && b->p.take.config && !b->p.take.config->oneshot) {
1299 ractor_queue_enq(cr, rs, b);
1300 }
1301 }
1302 RACTOR_UNLOCK_SELF(cr);
1303
1304 return found;
1305}
1306
1307static bool
1308ractor_try_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_queue *ts, volatile VALUE obj, VALUE move, bool exc, bool is_will)
1309{
1310 ASSERT_ractor_unlocking(cr);
1311
1312 struct rb_ractor_basket b;
1313
1314 if (ractor_deq_take_basket(cr, ts, &b)) {
1315 VM_ASSERT(basket_type_p(&b, basket_type_take_basket));
1316 VM_ASSERT(basket_type_p(b.p.take.basket, basket_type_yielding));
1317
1318 rb_ractor_t *tr = RACTOR_PTR(b.sender);
1319 struct rb_ractor_basket *tb = b.p.take.basket;
1320 enum rb_ractor_basket_type type;
1321
1322 RUBY_DEBUG_LOG("basket from r:%u", rb_ractor_id(tr));
1323
1324 if (is_will) {
1325 type = basket_type_will;
1326 }
1327 else {
1328 enum ruby_tag_type state;
1329
1330 // begin
1331 EC_PUSH_TAG(ec);
1332 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1333 // TODO: Ractor local GC
1334 ractor_basket_prepare_contents(obj, move, &obj, &type);
1335 }
1336 EC_POP_TAG();
1337 // rescue
1338 if (state) {
1339 RACTOR_LOCK_SELF(cr);
1340 {
1341 b.p.take.basket->type.e = basket_type_none;
1342 ractor_queue_enq(cr, ts, &b);
1343 }
1344 RACTOR_UNLOCK_SELF(cr);
1345 EC_JUMP_TAG(ec, state);
1346 }
1347 }
1348
1349 RACTOR_LOCK(tr);
1350 {
1351 VM_ASSERT(basket_type_p(tb, basket_type_yielding));
1352 // fill atomic
1353 RUBY_DEBUG_LOG("fill %sbasket from r:%u", is_will ? "will " : "", rb_ractor_id(tr));
1354 ractor_basket_fill_(cr, tb, obj, exc);
1355 if (RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_yielding, type) != basket_type_yielding) {
1356 rb_bug("unreachable");
1357 }
1358 ractor_wakeup(tr, wait_taking, wakeup_by_yield);
1359 }
1360 RACTOR_UNLOCK(tr);
1361
1362 return true;
1363 }
1364 else if (cr->sync.outgoing_port_closed) {
1365 rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
1366 }
1367 else {
1368 RUBY_DEBUG_LOG("no take basket");
1369 return false;
1370 }
1371}
1372
1373static void
1374ractor_wait_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_queue *ts)
1375{
1376 RACTOR_LOCK_SELF(cr);
1377 {
1378 while (!ractor_check_take_basket(cr, ts) && !cr->sync.outgoing_port_closed) {
1379 ractor_sleep(ec, cr, wait_yielding);
1380 }
1381 }
1382 RACTOR_UNLOCK_SELF(cr);
1383}
1384
1385static VALUE
1386ractor_yield(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE obj, VALUE move)
1387{
1388 struct rb_ractor_queue *ts = &cr->sync.takers_queue;
1389
1390 while (!ractor_try_yield(ec, cr, ts, obj, move, false, false)) {
1391 ractor_wait_yield(ec, cr, ts);
1392 }
1393
1394 return Qnil;
1395}
1396
1397// Ractor::Selector
1398
1400 rb_ractor_t *r;
1401 struct rb_ractor_basket take_basket;
1402 st_table *take_ractors; // rb_ractor_t * => (struct rb_ractor_selector_take_config *)
1403};
1404
1405static int
1406ractor_selector_mark_ractors_i(st_data_t key, st_data_t value, st_data_t data)
1407{
1408 const rb_ractor_t *r = (rb_ractor_t *)key;
1409 rb_gc_mark(r->pub.self);
1410 return ST_CONTINUE;
1411}
1412
1413static void
1414ractor_selector_mark(void *ptr)
1415{
1416 struct rb_ractor_selector *s = ptr;
1417
1418 if (s->take_ractors) {
1419 st_foreach(s->take_ractors, ractor_selector_mark_ractors_i, 0);
1420 }
1421
1422 switch (s->take_basket.type.e) {
1423 case basket_type_ref:
1424 case basket_type_copy:
1425 case basket_type_move:
1426 case basket_type_will:
1427 rb_gc_mark(s->take_basket.sender);
1428 rb_gc_mark(s->take_basket.p.send.v);
1429 break;
1430 default:
1431 break;
1432 }
1433}
1434
1435static int
1436ractor_selector_release_i(st_data_t key, st_data_t val, st_data_t data)
1437{
1438 struct rb_ractor_selector *s = (struct rb_ractor_selector *)data;
1440
1441 if (!config->closed) {
1442 ractor_deregister_take((rb_ractor_t *)key, &s->take_basket);
1443 }
1444 free(config);
1445 return ST_CONTINUE;
1446}
1447
1448static void
1449ractor_selector_free(void *ptr)
1450{
1451 struct rb_ractor_selector *s = ptr;
1452 st_foreach(s->take_ractors, ractor_selector_release_i, (st_data_t)s);
1453 st_free_table(s->take_ractors);
1454 ruby_xfree(ptr);
1455}
1456
1457static size_t
1458ractor_selector_memsize(const void *ptr)
1459{
1460 const struct rb_ractor_selector *s = ptr;
1461 return sizeof(struct rb_ractor_selector) +
1462 st_memsize(s->take_ractors) +
1463 s->take_ractors->num_entries * sizeof(struct rb_ractor_selector_take_config);
1464}
1465
1466static const rb_data_type_t ractor_selector_data_type = {
1467 "ractor/selector",
1468 {
1469 ractor_selector_mark,
1470 ractor_selector_free,
1471 ractor_selector_memsize,
1472 NULL, // update
1473 },
1474 0, 0, RUBY_TYPED_FREE_IMMEDIATELY,
1475};
1476
1477static struct rb_ractor_selector *
1478RACTOR_SELECTOR_PTR(VALUE selv)
1479{
1480 VM_ASSERT(rb_typeddata_is_kind_of(selv, &ractor_selector_data_type));
1481
1482 return (struct rb_ractor_selector *)DATA_PTR(selv);
1483}
1484
1485// Ractor::Selector.new
1486
1487static VALUE
1488ractor_selector_create(VALUE klass)
1489{
1490 struct rb_ractor_selector *s;
1491 VALUE selv = TypedData_Make_Struct(klass, struct rb_ractor_selector, &ractor_selector_data_type, s);
1492 s->take_basket.type.e = basket_type_reserved;
1493 s->take_ractors = st_init_numtable(); // ractor (ptr) -> take_config
1494 return selv;
1495}
1496
1497// Ractor::Selector#add(r)
1498
1499/*
1500 * call-seq:
1501 * add(ractor) -> ractor
1502 *
1503 * Adds _ractor_ to +self+. Raises an exception if _ractor_ is already added.
1504 * Returns _ractor_.
1505 */
1506static VALUE
1507ractor_selector_add(VALUE selv, VALUE rv)
1508{
1509 if (!rb_ractor_p(rv)) {
1510 rb_raise(rb_eArgError, "Not a ractor object");
1511 }
1512
1513 rb_ractor_t *r = RACTOR_PTR(rv);
1514 struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1515
1516 if (st_lookup(s->take_ractors, (st_data_t)r, NULL)) {
1517 rb_raise(rb_eArgError, "already added");
1518 }
1519
1520 struct rb_ractor_selector_take_config *config = malloc(sizeof(struct rb_ractor_selector_take_config));
1521 VM_ASSERT(config != NULL);
1522 config->closed = false;
1523 config->oneshot = false;
1524
1525 if (ractor_register_take(GET_RACTOR(), r, &s->take_basket, false, config, true)) {
1526 st_insert(s->take_ractors, (st_data_t)r, (st_data_t)config);
1527 }
1528
1529 return rv;
1530}
1531
1532// Ractor::Selector#remove(r)
1533
1534/* call-seq:
1535 * remove(ractor) -> ractor
1536 *
1537 * Removes _ractor_ from +self+. Raises an exception if _ractor_ is not added.
1538 * Returns the removed _ractor_.
1539 */
1540static VALUE
1541ractor_selector_remove(VALUE selv, VALUE rv)
1542{
1543 if (!rb_ractor_p(rv)) {
1544 rb_raise(rb_eArgError, "Not a ractor object");
1545 }
1546
1547 rb_ractor_t *r = RACTOR_PTR(rv);
1548 struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1549
1550 RUBY_DEBUG_LOG("r:%u", rb_ractor_id(r));
1551
1552 if (!st_lookup(s->take_ractors, (st_data_t)r, NULL)) {
1553 rb_raise(rb_eArgError, "not added yet");
1554 }
1555
1556 ractor_deregister_take(r, &s->take_basket);
1557 struct rb_ractor_selector_take_config *config;
1558 st_delete(s->take_ractors, (st_data_t *)&r, (st_data_t *)&config);
1559 free(config);
1560
1561 return rv;
1562}
1563
1564// Ractor::Selector#clear
1565
1567 VALUE selv;
1569};
1570
1571static int
1572ractor_selector_clear_i(st_data_t key, st_data_t val, st_data_t data)
1573{
1574 VALUE selv = (VALUE)data;
1575 rb_ractor_t *r = (rb_ractor_t *)key;
1576 ractor_selector_remove(selv, r->pub.self);
1577 return ST_CONTINUE;
1578}
1579
1580/*
1581 * call-seq:
1582 * clear -> self
1583 *
1584 * Removes all ractors from +self+. Raises +self+.
1585 */
1586static VALUE
1587ractor_selector_clear(VALUE selv)
1588{
1589 struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1590
1591 st_foreach(s->take_ractors, ractor_selector_clear_i, (st_data_t)selv);
1592 st_clear(s->take_ractors);
1593 return selv;
1594}
1595
1596/*
1597 * call-seq:
1598 * empty? -> true or false
1599 *
1600 * Returns +true+ if no ractor is added.
1601 */
1602static VALUE
1603ractor_selector_empty_p(VALUE selv)
1604{
1605 struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1606 return s->take_ractors->num_entries == 0 ? Qtrue : Qfalse;
1607}
1608
1609static int
1610ractor_selector_wait_i(st_data_t key, st_data_t val, st_data_t dat)
1611{
1612 rb_ractor_t *r = (rb_ractor_t *)key;
1613 struct rb_ractor_basket *tb = (struct rb_ractor_basket *)dat;
1614 int ret;
1615
1616 if (!basket_none_p(tb)) {
1617 RUBY_DEBUG_LOG("already taken:%s", basket_type_name(tb->type.e));
1618 return ST_STOP;
1619 }
1620
1621 RACTOR_LOCK(r);
1622 {
1623 if (basket_type_p(&r->sync.will_basket, basket_type_will)) {
1624 RUBY_DEBUG_LOG("r:%u has will", rb_ractor_id(r));
1625
1626 if (RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_will) == basket_type_none) {
1627 ractor_take_will(r, tb);
1628 ret = ST_STOP;
1629 }
1630 else {
1631 RUBY_DEBUG_LOG("has will, but already taken (%s)", basket_type_name(tb->type.e));
1632 ret = ST_CONTINUE;
1633 }
1634 }
1635 else if (r->sync.outgoing_port_closed) {
1636 RUBY_DEBUG_LOG("r:%u is closed", rb_ractor_id(r));
1637
1638 if (RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_deleted) == basket_type_none) {
1639 tb->sender = r->pub.self;
1640 ret = ST_STOP;
1641 }
1642 else {
1643 RUBY_DEBUG_LOG("closed, but already taken (%s)", basket_type_name(tb->type.e));
1644 ret = ST_CONTINUE;
1645 }
1646 }
1647 else {
1648 RUBY_DEBUG_LOG("wakeup r:%u", rb_ractor_id(r));
1649 ractor_wakeup(r, wait_yielding, wakeup_by_take);
1650 ret = ST_CONTINUE;
1651 }
1652 }
1653 RACTOR_UNLOCK(r);
1654
1655 return ret;
1656}
1657
1658// Ractor::Selector#wait
1659
1660static void
1661ractor_selector_wait_cleaup(rb_ractor_t *cr, void *ptr)
1662{
1663 struct rb_ractor_basket *tb = (struct rb_ractor_basket *)ptr;
1664
1665 RACTOR_LOCK_SELF(cr);
1666 {
1667 while (basket_type_p(tb, basket_type_yielding)) rb_thread_sleep(0);
1668 // if tb->type is not none, taking is succeeded, but interruption ignore it unfortunately.
1669 tb->type.e = basket_type_reserved;
1670 }
1671 RACTOR_UNLOCK_SELF(cr);
1672}
1673
1674/* :nodoc: */
1675static VALUE
1676ractor_selector__wait(VALUE selv, VALUE do_receivev, VALUE do_yieldv, VALUE yield_value, VALUE move)
1677{
1678 rb_execution_context_t *ec = GET_EC();
1679 struct rb_ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1680 struct rb_ractor_basket *tb = &s->take_basket;
1681 struct rb_ractor_basket taken_basket;
1682 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1683 bool do_receive = !!RTEST(do_receivev);
1684 bool do_yield = !!RTEST(do_yieldv);
1685 VALUE ret_v, ret_r;
1686 enum rb_ractor_wait_status wait_status;
1687 struct rb_ractor_queue *rq = &cr->sync.recv_queue;
1688 struct rb_ractor_queue *ts = &cr->sync.takers_queue;
1689
1690 RUBY_DEBUG_LOG("start");
1691
1692 retry:
1693 RUBY_DEBUG_LOG("takers:%ld", s->take_ractors->num_entries);
1694
1695 // setup wait_status
1696 wait_status = wait_none;
1697 if (s->take_ractors->num_entries > 0) wait_status |= wait_taking;
1698 if (do_receive) wait_status |= wait_receiving;
1699 if (do_yield) wait_status |= wait_yielding;
1700
1701 RUBY_DEBUG_LOG("wait:%s", wait_status_str(wait_status));
1702
1703 if (wait_status == wait_none) {
1704 rb_raise(rb_eRactorError, "no taking ractors");
1705 }
1706
1707 // check recv_queue
1708 if (do_receive && !UNDEF_P(ret_v = ractor_try_receive(ec, cr, rq))) {
1709 ret_r = ID2SYM(rb_intern("receive"));
1710 goto success;
1711 }
1712
1713 // check takers
1714 if (do_yield && ractor_try_yield(ec, cr, ts, yield_value, move, false, false)) {
1715 ret_v = Qnil;
1716 ret_r = ID2SYM(rb_intern("yield"));
1717 goto success;
1718 }
1719
1720 // check take_basket
1721 VM_ASSERT(basket_type_p(&s->take_basket, basket_type_reserved));
1722 s->take_basket.type.e = basket_type_none;
1723 // kick all take target ractors
1724 st_foreach(s->take_ractors, ractor_selector_wait_i, (st_data_t)tb);
1725
1726 RACTOR_LOCK_SELF(cr);
1727 {
1728 retry_waiting:
1729 while (1) {
1730 if (!basket_none_p(tb)) {
1731 RUBY_DEBUG_LOG("taken:%s from r:%u", basket_type_name(tb->type.e),
1732 tb->sender ? rb_ractor_id(RACTOR_PTR(tb->sender)) : 0);
1733 break;
1734 }
1735 if (do_receive && !ractor_queue_empty_p(cr, rq)) {
1736 RUBY_DEBUG_LOG("can receive (%d)", rq->cnt);
1737 break;
1738 }
1739 if (do_yield && ractor_check_take_basket(cr, ts)) {
1740 RUBY_DEBUG_LOG("can yield");
1741 break;
1742 }
1743
1744 ractor_sleep_with_cleanup(ec, cr, wait_status, ractor_selector_wait_cleaup, tb);
1745 }
1746
1747 taken_basket = *tb;
1748
1749 // ensure
1750 // tb->type.e = basket_type_reserved # do it atomic in the following code
1751 if (taken_basket.type.e == basket_type_yielding ||
1752 RUBY_ATOMIC_CAS(tb->type.atomic, taken_basket.type.e, basket_type_reserved) != taken_basket.type.e) {
1753
1754 if (basket_type_p(tb, basket_type_yielding)) {
1755 RACTOR_UNLOCK_SELF(cr);
1756 {
1757 rb_thread_sleep(0);
1758 }
1759 RACTOR_LOCK_SELF(cr);
1760 }
1761 goto retry_waiting;
1762 }
1763 }
1764 RACTOR_UNLOCK_SELF(cr);
1765
1766 // check the taken result
1767 switch (taken_basket.type.e) {
1768 case basket_type_none:
1769 VM_ASSERT(do_receive || do_yield);
1770 goto retry;
1771 case basket_type_yielding:
1772 rb_bug("unreachable");
1773 case basket_type_deleted: {
1774 ractor_selector_remove(selv, taken_basket.sender);
1775
1776 rb_ractor_t *r = RACTOR_PTR(taken_basket.sender);
1777 if (ractor_take_will_lock(r, &taken_basket)) {
1778 RUBY_DEBUG_LOG("has_will");
1779 }
1780 else {
1781 RUBY_DEBUG_LOG("no will");
1782 // rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
1783 // remove and retry wait
1784 goto retry;
1785 }
1786 break;
1787 }
1788 case basket_type_will:
1789 // no more messages
1790 ractor_selector_remove(selv, taken_basket.sender);
1791 break;
1792 default:
1793 break;
1794 }
1795
1796 RUBY_DEBUG_LOG("taken_basket:%s", basket_type_name(taken_basket.type.e));
1797
1798 ret_v = ractor_basket_accept(&taken_basket);
1799 ret_r = taken_basket.sender;
1800 success:
1801 return rb_ary_new_from_args(2, ret_r, ret_v);
1802}
1803
1804/*
1805 * call-seq:
1806 * wait(receive: false, yield_value: undef, move: false) -> [ractor, value]
1807 *
1808 * Waits until any ractor in _selector_ can be active.
1809 */
1810static VALUE
1811ractor_selector_wait(int argc, VALUE *argv, VALUE selector)
1812{
1813 VALUE options;
1814 ID keywords[3];
1815 VALUE values[3];
1816
1817 keywords[0] = rb_intern("receive");
1818 keywords[1] = rb_intern("yield_value");
1819 keywords[2] = rb_intern("move");
1820
1821 rb_scan_args(argc, argv, "0:", &options);
1822 rb_get_kwargs(options, keywords, 0, numberof(values), values);
1823 return ractor_selector__wait(selector,
1824 values[0] == Qundef ? Qfalse : RTEST(values[0]),
1825 values[1] != Qundef, values[1], values[2]);
1826}
1827
1828static VALUE
1829ractor_selector_new(int argc, VALUE *ractors, VALUE klass)
1830{
1831 VALUE selector = ractor_selector_create(klass);
1832
1833 for (int i=0; i<argc; i++) {
1834 ractor_selector_add(selector, ractors[i]);
1835 }
1836
1837 return selector;
1838}
1839
1840static VALUE
1841ractor_select_internal(rb_execution_context_t *ec, VALUE self, VALUE ractors, VALUE do_receive, VALUE do_yield, VALUE yield_value, VALUE move)
1842{
1843 VALUE selector = ractor_selector_new(RARRAY_LENINT(ractors), (VALUE *)RARRAY_CONST_PTR(ractors), rb_cRactorSelector);
1844 VALUE result;
1845 int state;
1846
1847 EC_PUSH_TAG(ec);
1848 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1849 result = ractor_selector__wait(selector, do_receive, do_yield, yield_value, move);
1850 }
1851 EC_POP_TAG();
1852 if (state != TAG_NONE) {
1853 // ensure
1854 ractor_selector_clear(selector);
1855
1856 // jump
1857 EC_JUMP_TAG(ec, state);
1858 }
1859
1860 RB_GC_GUARD(ractors);
1861 return result;
1862}
1863
1864// Ractor#close_incoming
1865
1866static VALUE
1867ractor_close_incoming(rb_execution_context_t *ec, rb_ractor_t *r)
1868{
1869 VALUE prev;
1870
1871 RACTOR_LOCK(r);
1872 {
1873 if (!r->sync.incoming_port_closed) {
1874 prev = Qfalse;
1875 r->sync.incoming_port_closed = true;
1876 if (ractor_wakeup(r, wait_receiving, wakeup_by_close)) {
1877 VM_ASSERT(ractor_queue_empty_p(r, &r->sync.recv_queue));
1878 RUBY_DEBUG_LOG("cancel receiving");
1879 }
1880 }
1881 else {
1882 prev = Qtrue;
1883 }
1884 }
1885 RACTOR_UNLOCK(r);
1886 return prev;
1887}
1888
1889// Ractor#close_outgoing
1890
1891static VALUE
1892ractor_close_outgoing(rb_execution_context_t *ec, rb_ractor_t *r)
1893{
1894 VALUE prev;
1895
1896 RACTOR_LOCK(r);
1897 {
1898 struct rb_ractor_queue *ts = &r->sync.takers_queue;
1899 rb_ractor_t *tr;
1900 struct rb_ractor_basket b;
1901
1902 if (!r->sync.outgoing_port_closed) {
1903 prev = Qfalse;
1904 r->sync.outgoing_port_closed = true;
1905 }
1906 else {
1907 VM_ASSERT(ractor_queue_empty_p(r, ts));
1908 prev = Qtrue;
1909 }
1910
1911 // wakeup all taking ractors
1912 while (ractor_queue_deq(r, ts, &b)) {
1913 if (basket_type_p(&b, basket_type_take_basket)) {
1914 tr = RACTOR_PTR(b.sender);
1915 struct rb_ractor_basket *tb = b.p.take.basket;
1916
1917 if (RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_none, basket_type_yielding) == basket_type_none) {
1918 b.p.take.basket->sender = r->pub.self;
1919 if (RUBY_ATOMIC_CAS(tb->type.atomic, basket_type_yielding, basket_type_deleted) != basket_type_yielding) {
1920 rb_bug("unreachable");
1921 }
1922 RUBY_DEBUG_LOG("set delete for r:%u", rb_ractor_id(RACTOR_PTR(b.sender)));
1923 }
1924
1925 if (b.p.take.config) {
1926 b.p.take.config->closed = true;
1927 }
1928
1929 // TODO: deadlock-able?
1930 RACTOR_LOCK(tr);
1931 {
1932 ractor_wakeup(tr, wait_taking, wakeup_by_close);
1933 }
1934 RACTOR_UNLOCK(tr);
1935 }
1936 }
1937
1938 // raising yielding Ractor
1939 ractor_wakeup(r, wait_yielding, wakeup_by_close);
1940
1941 VM_ASSERT(ractor_queue_empty_p(r, ts));
1942 }
1943 RACTOR_UNLOCK(r);
1944 return prev;
1945}
1946
1947// creation/termination
1948
1949static uint32_t
1950ractor_next_id(void)
1951{
1952 uint32_t id;
1953
1954 id = (uint32_t)(RUBY_ATOMIC_FETCH_ADD(ractor_last_id, 1) + 1);
1955
1956 return id;
1957}
1958
1959static void
1960vm_insert_ractor0(rb_vm_t *vm, rb_ractor_t *r, bool single_ractor_mode)
1961{
1962 RUBY_DEBUG_LOG("r:%u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
1963 VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
1964
1965 ccan_list_add_tail(&vm->ractor.set, &r->vmlr_node);
1966 vm->ractor.cnt++;
1967
1968 if (r->newobj_cache) {
1969 VM_ASSERT(r == ruby_single_main_ractor);
1970 }
1971 else {
1972 r->newobj_cache = rb_gc_ractor_cache_alloc(r);
1973 }
1974}
1975
1976static void
1977cancel_single_ractor_mode(void)
1978{
1979 // enable multi-ractor mode
1980 RUBY_DEBUG_LOG("enable multi-ractor mode");
1981
1982 VALUE was_disabled = rb_gc_enable();
1983
1984 rb_gc_start();
1985
1986 if (was_disabled) {
1987 rb_gc_disable();
1988 }
1989
1990 ruby_single_main_ractor = NULL;
1991 rb_funcall(rb_cRactor, rb_intern("_activated"), 0);
1992}
1993
1994static void
1995vm_insert_ractor(rb_vm_t *vm, rb_ractor_t *r)
1996{
1997 VM_ASSERT(ractor_status_p(r, ractor_created));
1998
1999 if (rb_multi_ractor_p()) {
2000 RB_VM_LOCK();
2001 {
2002 vm_insert_ractor0(vm, r, false);
2003 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
2004 }
2005 RB_VM_UNLOCK();
2006 }
2007 else {
2008 if (vm->ractor.cnt == 0) {
2009 // main ractor
2010 vm_insert_ractor0(vm, r, true);
2011 ractor_status_set(r, ractor_blocking);
2012 ractor_status_set(r, ractor_running);
2013 }
2014 else {
2015 cancel_single_ractor_mode();
2016 vm_insert_ractor0(vm, r, true);
2017 vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
2018 }
2019 }
2020}
2021
2022static void
2023vm_remove_ractor(rb_vm_t *vm, rb_ractor_t *cr)
2024{
2025 VM_ASSERT(ractor_status_p(cr, ractor_running));
2026 VM_ASSERT(vm->ractor.cnt > 1);
2027 VM_ASSERT(cr->threads.cnt == 1);
2028
2029 RB_VM_LOCK();
2030 {
2031 RUBY_DEBUG_LOG("ractor.cnt:%u-- terminate_waiting:%d",
2032 vm->ractor.cnt, vm->ractor.sync.terminate_waiting);
2033
2034 VM_ASSERT(vm->ractor.cnt > 0);
2035 ccan_list_del(&cr->vmlr_node);
2036
2037 if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
2038 rb_native_cond_signal(&vm->ractor.sync.terminate_cond);
2039 }
2040 vm->ractor.cnt--;
2041
2042 rb_gc_ractor_cache_free(cr->newobj_cache);
2043 cr->newobj_cache = NULL;
2044
2045 ractor_status_set(cr, ractor_terminated);
2046 }
2047 RB_VM_UNLOCK();
2048}
2049
2050static VALUE
2051ractor_alloc(VALUE klass)
2052{
2053 rb_ractor_t *r;
2054 VALUE rv = TypedData_Make_Struct(klass, rb_ractor_t, &ractor_data_type, r);
2056 r->pub.self = rv;
2057 VM_ASSERT(ractor_status_p(r, ractor_created));
2058 return rv;
2059}
2060
2062rb_ractor_main_alloc(void)
2063{
2064 rb_ractor_t *r = ruby_mimcalloc(1, sizeof(rb_ractor_t));
2065 if (r == NULL) {
2066 fprintf(stderr, "[FATAL] failed to allocate memory for main ractor\n");
2067 exit(EXIT_FAILURE);
2068 }
2069 r->pub.id = ++ractor_last_id;
2070 r->loc = Qnil;
2071 r->name = Qnil;
2072 r->pub.self = Qnil;
2073 r->newobj_cache = rb_gc_ractor_cache_alloc(r);
2074 ruby_single_main_ractor = r;
2075
2076 return r;
2077}
2078
2079#if defined(HAVE_WORKING_FORK)
2080void
2081rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th)
2082{
2083 // initialize as a main ractor
2084 vm->ractor.cnt = 0;
2085 vm->ractor.blocking_cnt = 0;
2086 ruby_single_main_ractor = th->ractor;
2087 th->ractor->status_ = ractor_created;
2088
2089 rb_ractor_living_threads_init(th->ractor);
2090 rb_ractor_living_threads_insert(th->ractor, th);
2091
2092 VM_ASSERT(vm->ractor.blocking_cnt == 0);
2093 VM_ASSERT(vm->ractor.cnt == 1);
2094}
2095#endif
2096
2097void rb_thread_sched_init(struct rb_thread_sched *, bool atfork);
2098
2099void
2100rb_ractor_living_threads_init(rb_ractor_t *r)
2101{
2102 ccan_list_head_init(&r->threads.set);
2103 r->threads.cnt = 0;
2104 r->threads.blocking_cnt = 0;
2105}
2106
2107static void
2108ractor_init(rb_ractor_t *r, VALUE name, VALUE loc)
2109{
2110 ractor_queue_setup(&r->sync.recv_queue);
2111 ractor_queue_setup(&r->sync.takers_queue);
2112 rb_native_mutex_initialize(&r->sync.lock);
2113 rb_native_cond_initialize(&r->barrier_wait_cond);
2114
2115#ifdef RUBY_THREAD_WIN32_H
2116 rb_native_cond_initialize(&r->sync.cond);
2117 rb_native_cond_initialize(&r->barrier_wait_cond);
2118#endif
2119
2120 // thread management
2121 rb_thread_sched_init(&r->threads.sched, false);
2122 rb_ractor_living_threads_init(r);
2123
2124 // naming
2125 if (!NIL_P(name)) {
2126 rb_encoding *enc;
2127 StringValueCStr(name);
2128 enc = rb_enc_get(name);
2129 if (!rb_enc_asciicompat(enc)) {
2130 rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
2131 rb_enc_name(enc));
2132 }
2133 name = rb_str_new_frozen(name);
2134 }
2135 r->name = name;
2136 r->loc = loc;
2137}
2138
2139void
2140rb_ractor_main_setup(rb_vm_t *vm, rb_ractor_t *r, rb_thread_t *th)
2141{
2142 r->pub.self = TypedData_Wrap_Struct(rb_cRactor, &ractor_data_type, r);
2143 FL_SET_RAW(r->pub.self, RUBY_FL_SHAREABLE);
2144 ractor_init(r, Qnil, Qnil);
2145 r->threads.main = th;
2146 rb_ractor_living_threads_insert(r, th);
2147}
2148
2149static VALUE
2150ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VALUE args, VALUE block)
2151{
2152 VALUE rv = ractor_alloc(self);
2153 rb_ractor_t *r = RACTOR_PTR(rv);
2154 ractor_init(r, name, loc);
2155
2156 // can block here
2157 r->pub.id = ractor_next_id();
2158 RUBY_DEBUG_LOG("r:%u", r->pub.id);
2159
2160 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2161 r->verbose = cr->verbose;
2162 r->debug = cr->debug;
2163
2164 rb_yjit_before_ractor_spawn();
2165 rb_thread_create_ractor(r, args, block);
2166
2167 RB_GC_GUARD(rv);
2168 return rv;
2169}
2170
2171static VALUE
2172ractor_create_func(VALUE klass, VALUE loc, VALUE name, VALUE args, rb_block_call_func_t func)
2173{
2174 VALUE block = rb_proc_new(func, Qnil);
2175 return ractor_create(rb_current_ec_noinline(), klass, loc, name, args, block);
2176}
2177
2178static void
2179ractor_yield_atexit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE v, bool exc)
2180{
2181 if (cr->sync.outgoing_port_closed) {
2182 return;
2183 }
2184
2185 ASSERT_ractor_unlocking(cr);
2186
2187 struct rb_ractor_queue *ts = &cr->sync.takers_queue;
2188
2189 retry:
2190 if (ractor_try_yield(ec, cr, ts, v, Qfalse, exc, true)) {
2191 // OK.
2192 }
2193 else {
2194 bool retry = false;
2195 RACTOR_LOCK(cr);
2196 {
2197 if (!ractor_check_take_basket(cr, ts)) {
2198 VM_ASSERT(cr->sync.wait.status == wait_none);
2199 RUBY_DEBUG_LOG("leave a will");
2200 ractor_basket_fill_will(cr, &cr->sync.will_basket, v, exc);
2201 }
2202 else {
2203 RUBY_DEBUG_LOG("rare timing!");
2204 retry = true; // another ractor is waiting for the yield.
2205 }
2206 }
2207 RACTOR_UNLOCK(cr);
2208
2209 if (retry) goto retry;
2210 }
2211}
2212
2213void
2214rb_ractor_atexit(rb_execution_context_t *ec, VALUE result)
2215{
2216 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2217 ractor_yield_atexit(ec, cr, result, false);
2218}
2219
2220void
2221rb_ractor_atexit_exception(rb_execution_context_t *ec)
2222{
2223 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2224 ractor_yield_atexit(ec, cr, ec->errinfo, true);
2225}
2226
2227void
2228rb_ractor_teardown(rb_execution_context_t *ec)
2229{
2230 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
2231 ractor_close_incoming(ec, cr);
2232 ractor_close_outgoing(ec, cr);
2233
2234 // sync with rb_ractor_terminate_interrupt_main_thread()
2235 RB_VM_LOCK_ENTER();
2236 {
2237 VM_ASSERT(cr->threads.main != NULL);
2238 cr->threads.main = NULL;
2239 }
2240 RB_VM_LOCK_LEAVE();
2241}
2242
2243void
2244rb_ractor_receive_parameters(rb_execution_context_t *ec, rb_ractor_t *r, int len, VALUE *ptr)
2245{
2246 for (int i=0; i<len; i++) {
2247 ptr[i] = ractor_receive(ec, r);
2248 }
2249}
2250
2251void
2252rb_ractor_send_parameters(rb_execution_context_t *ec, rb_ractor_t *r, VALUE args)
2253{
2254 int len = RARRAY_LENINT(args);
2255 for (int i=0; i<len; i++) {
2256 ractor_send(ec, r, RARRAY_AREF(args, i), false);
2257 }
2258}
2259
2260bool
2261rb_ractor_main_p_(void)
2262{
2263 VM_ASSERT(rb_multi_ractor_p());
2264 rb_execution_context_t *ec = GET_EC();
2265 return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
2266}
2267
2268bool
2269rb_obj_is_main_ractor(VALUE gv)
2270{
2271 if (!rb_ractor_p(gv)) return false;
2272 rb_ractor_t *r = DATA_PTR(gv);
2273 return r == GET_VM()->ractor.main_ractor;
2274}
2275
2276int
2277rb_ractor_living_thread_num(const rb_ractor_t *r)
2278{
2279 return r->threads.cnt;
2280}
2281
2282// only for current ractor
2283VALUE
2284rb_ractor_thread_list(void)
2285{
2286 rb_ractor_t *r = GET_RACTOR();
2287 rb_thread_t *th = 0;
2288 VALUE ary = rb_ary_new();
2289
2290 ccan_list_for_each(&r->threads.set, th, lt_node) {
2291 switch (th->status) {
2292 case THREAD_RUNNABLE:
2293 case THREAD_STOPPED:
2294 case THREAD_STOPPED_FOREVER:
2295 rb_ary_push(ary, th->self);
2296 default:
2297 break;
2298 }
2299 }
2300
2301 return ary;
2302}
2303
2304void
2305rb_ractor_living_threads_insert(rb_ractor_t *r, rb_thread_t *th)
2306{
2307 VM_ASSERT(th != NULL);
2308
2309 RACTOR_LOCK(r);
2310 {
2311 RUBY_DEBUG_LOG("r(%d)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
2312 ccan_list_add_tail(&r->threads.set, &th->lt_node);
2313 r->threads.cnt++;
2314 }
2315 RACTOR_UNLOCK(r);
2316
2317 // first thread for a ractor
2318 if (r->threads.cnt == 1) {
2319 VM_ASSERT(ractor_status_p(r, ractor_created));
2320 vm_insert_ractor(th->vm, r);
2321 }
2322}
2323
2324static void
2325vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line)
2326{
2327 ractor_status_set(r, ractor_blocking);
2328
2329 RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d++", vm->ractor.blocking_cnt);
2330 vm->ractor.blocking_cnt++;
2331 VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
2332}
2333
2334void
2335rb_vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
2336{
2337 ASSERT_vm_locking();
2338 VM_ASSERT(GET_RACTOR() == cr);
2339 vm_ractor_blocking_cnt_inc(vm, cr, file, line);
2340}
2341
2342void
2343rb_vm_ractor_blocking_cnt_dec(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
2344{
2345 ASSERT_vm_locking();
2346 VM_ASSERT(GET_RACTOR() == cr);
2347
2348 RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d--", vm->ractor.blocking_cnt);
2349 VM_ASSERT(vm->ractor.blocking_cnt > 0);
2350 vm->ractor.blocking_cnt--;
2351
2352 ractor_status_set(cr, ractor_running);
2353}
2354
2355static void
2356ractor_check_blocking(rb_ractor_t *cr, unsigned int remained_thread_cnt, const char *file, int line)
2357{
2358 VM_ASSERT(cr == GET_RACTOR());
2359
2360 RUBY_DEBUG_LOG2(file, line,
2361 "cr->threads.cnt:%u cr->threads.blocking_cnt:%u vm->ractor.cnt:%u vm->ractor.blocking_cnt:%u",
2362 cr->threads.cnt, cr->threads.blocking_cnt,
2363 GET_VM()->ractor.cnt, GET_VM()->ractor.blocking_cnt);
2364
2365 VM_ASSERT(cr->threads.cnt >= cr->threads.blocking_cnt + 1);
2366
2367 if (remained_thread_cnt > 0 &&
2368 // will be block
2369 cr->threads.cnt == cr->threads.blocking_cnt + 1) {
2370 // change ractor status: running -> blocking
2371 rb_vm_t *vm = GET_VM();
2372
2373 RB_VM_LOCK_ENTER();
2374 {
2375 rb_vm_ractor_blocking_cnt_inc(vm, cr, file, line);
2376 }
2377 RB_VM_LOCK_LEAVE();
2378 }
2379}
2380
2381void rb_threadptr_remove(rb_thread_t *th);
2382
2383void
2384rb_ractor_living_threads_remove(rb_ractor_t *cr, rb_thread_t *th)
2385{
2386 VM_ASSERT(cr == GET_RACTOR());
2387 RUBY_DEBUG_LOG("r->threads.cnt:%d--", cr->threads.cnt);
2388 ractor_check_blocking(cr, cr->threads.cnt - 1, __FILE__, __LINE__);
2389
2390 rb_threadptr_remove(th);
2391
2392 if (cr->threads.cnt == 1) {
2393 vm_remove_ractor(th->vm, cr);
2394 }
2395 else {
2396 RACTOR_LOCK(cr);
2397 {
2398 ccan_list_del(&th->lt_node);
2399 cr->threads.cnt--;
2400 }
2401 RACTOR_UNLOCK(cr);
2402 }
2403}
2404
2405void
2406rb_ractor_blocking_threads_inc(rb_ractor_t *cr, const char *file, int line)
2407{
2408 RUBY_DEBUG_LOG2(file, line, "cr->threads.blocking_cnt:%d++", cr->threads.blocking_cnt);
2409
2410 VM_ASSERT(cr->threads.cnt > 0);
2411 VM_ASSERT(cr == GET_RACTOR());
2412
2413 ractor_check_blocking(cr, cr->threads.cnt, __FILE__, __LINE__);
2414 cr->threads.blocking_cnt++;
2415}
2416
2417void
2418rb_ractor_blocking_threads_dec(rb_ractor_t *cr, const char *file, int line)
2419{
2420 RUBY_DEBUG_LOG2(file, line,
2421 "r->threads.blocking_cnt:%d--, r->threads.cnt:%u",
2422 cr->threads.blocking_cnt, cr->threads.cnt);
2423
2424 VM_ASSERT(cr == GET_RACTOR());
2425
2426 if (cr->threads.cnt == cr->threads.blocking_cnt) {
2427 rb_vm_t *vm = GET_VM();
2428
2429 RB_VM_LOCK_ENTER();
2430 {
2431 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
2432 }
2433 RB_VM_LOCK_LEAVE();
2434 }
2435
2436 cr->threads.blocking_cnt--;
2437}
2438
2439void
2440rb_ractor_vm_barrier_interrupt_running_thread(rb_ractor_t *r)
2441{
2442 VM_ASSERT(r != GET_RACTOR());
2443 ASSERT_ractor_unlocking(r);
2444 ASSERT_vm_locking();
2445
2446 RACTOR_LOCK(r);
2447 {
2448 if (ractor_status_p(r, ractor_running)) {
2449 rb_execution_context_t *ec = r->threads.running_ec;
2450 if (ec) {
2451 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec);
2452 }
2453 }
2454 }
2455 RACTOR_UNLOCK(r);
2456}
2457
2458void
2459rb_ractor_terminate_interrupt_main_thread(rb_ractor_t *r)
2460{
2461 VM_ASSERT(r != GET_RACTOR());
2462 ASSERT_ractor_unlocking(r);
2463 ASSERT_vm_locking();
2464
2465 rb_thread_t *main_th = r->threads.main;
2466 if (main_th) {
2467 if (main_th->status != THREAD_KILLED) {
2468 RUBY_VM_SET_TERMINATE_INTERRUPT(main_th->ec);
2469 rb_threadptr_interrupt(main_th);
2470 }
2471 else {
2472 RUBY_DEBUG_LOG("killed (%p)", (void *)main_th);
2473 }
2474 }
2475}
2476
2477void rb_thread_terminate_all(rb_thread_t *th); // thread.c
2478
2479static void
2480ractor_terminal_interrupt_all(rb_vm_t *vm)
2481{
2482 if (vm->ractor.cnt > 1) {
2483 // send terminate notification to all ractors
2484 rb_ractor_t *r = 0;
2485 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
2486 if (r != vm->ractor.main_ractor) {
2487 RUBY_DEBUG_LOG("r:%d", rb_ractor_id(r));
2488 rb_ractor_terminate_interrupt_main_thread(r);
2489 }
2490 }
2491 }
2492}
2493
2494void rb_add_running_thread(rb_thread_t *th);
2495void rb_del_running_thread(rb_thread_t *th);
2496
2497void
2498rb_ractor_terminate_all(void)
2499{
2500 rb_vm_t *vm = GET_VM();
2501 rb_ractor_t *cr = vm->ractor.main_ractor;
2502
2503 RUBY_DEBUG_LOG("ractor.cnt:%d", (int)vm->ractor.cnt);
2504
2505 VM_ASSERT(cr == GET_RACTOR()); // only main-ractor's main-thread should kick it.
2506
2507 if (vm->ractor.cnt > 1) {
2508 RB_VM_LOCK();
2509 {
2510 ractor_terminal_interrupt_all(vm); // kill all ractors
2511 }
2512 RB_VM_UNLOCK();
2513 }
2514 rb_thread_terminate_all(GET_THREAD()); // kill other threads in main-ractor and wait
2515
2516 RB_VM_LOCK();
2517 {
2518 while (vm->ractor.cnt > 1) {
2519 RUBY_DEBUG_LOG("terminate_waiting:%d", vm->ractor.sync.terminate_waiting);
2520 vm->ractor.sync.terminate_waiting = true;
2521
2522 // wait for 1sec
2523 rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
2524 rb_del_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
2525 rb_vm_cond_timedwait(vm, &vm->ractor.sync.terminate_cond, 1000 /* ms */);
2526 rb_add_running_thread(rb_ec_thread_ptr(cr->threads.running_ec));
2527 rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
2528
2529 ractor_terminal_interrupt_all(vm);
2530 }
2531 }
2532 RB_VM_UNLOCK();
2533}
2534
2536rb_vm_main_ractor_ec(rb_vm_t *vm)
2537{
2538 /* This code needs to carefully work around two bugs:
2539 * - Bug #20016: When M:N threading is enabled, running_ec is NULL if no thread is
2540 * actually currently running (as opposed to without M:N threading, when
2541 * running_ec will still point to the _last_ thread which ran)
2542 * - Bug #20197: If the main thread is sleeping, setting its postponed job
2543 * interrupt flag is pointless; it won't look at the flag until it stops sleeping
2544 * for some reason. It would be better to set the flag on the running ec, which
2545 * will presumably look at it soon.
2546 *
2547 * Solution: use running_ec if it's set, otherwise fall back to the main thread ec.
2548 * This is still susceptible to some rare race conditions (what if the last thread
2549 * to run just entered a long-running sleep?), but seems like the best balance of
2550 * robustness and complexity.
2551 */
2552 rb_execution_context_t *running_ec = vm->ractor.main_ractor->threads.running_ec;
2553 if (running_ec) { return running_ec; }
2554 return vm->ractor.main_thread->ec;
2555}
2556
2557static VALUE
2558ractor_moved_missing(int argc, VALUE *argv, VALUE self)
2559{
2560 rb_raise(rb_eRactorMovedError, "can not send any methods to a moved object");
2561}
2562
2563#ifndef USE_RACTOR_SELECTOR
2564#define USE_RACTOR_SELECTOR 0
2565#endif
2566
2567RUBY_SYMBOL_EXPORT_BEGIN
2568void rb_init_ractor_selector(void);
2569RUBY_SYMBOL_EXPORT_END
2570
2571/*
2572 * Document-class: Ractor::Selector
2573 * :nodoc: currently
2574 *
2575 * Selects multiple Ractors to be activated.
2576 */
2577void
2578rb_init_ractor_selector(void)
2579{
2580 rb_cRactorSelector = rb_define_class_under(rb_cRactor, "Selector", rb_cObject);
2581 rb_undef_alloc_func(rb_cRactorSelector);
2582
2583 rb_define_singleton_method(rb_cRactorSelector, "new", ractor_selector_new , -1);
2584 rb_define_method(rb_cRactorSelector, "add", ractor_selector_add, 1);
2585 rb_define_method(rb_cRactorSelector, "remove", ractor_selector_remove, 1);
2586 rb_define_method(rb_cRactorSelector, "clear", ractor_selector_clear, 0);
2587 rb_define_method(rb_cRactorSelector, "empty?", ractor_selector_empty_p, 0);
2588 rb_define_method(rb_cRactorSelector, "wait", ractor_selector_wait, -1);
2589 rb_define_method(rb_cRactorSelector, "_wait", ractor_selector__wait, 4);
2590}
2591
2592/*
2593 * Document-class: Ractor::ClosedError
2594 *
2595 * Raised when an attempt is made to send a message to a closed port,
2596 * or to retrieve a message from a closed and empty port.
2597 * Ports may be closed explicitly with Ractor#close_outgoing/close_incoming
2598 * and are closed implicitly when a Ractor terminates.
2599 *
2600 * r = Ractor.new { sleep(500) }
2601 * r.close_outgoing
2602 * r.take # Ractor::ClosedError
2603 *
2604 * ClosedError is a descendant of StopIteration, so the closing of the ractor will break
2605 * the loops without propagating the error:
2606 *
2607 * r = Ractor.new do
2608 * loop do
2609 * msg = receive # raises ClosedError and loop traps it
2610 * puts "Received: #{msg}"
2611 * end
2612 * puts "loop exited"
2613 * end
2614 *
2615 * 3.times{|i| r << i}
2616 * r.close_incoming
2617 * r.take
2618 * puts "Continue successfully"
2619 *
2620 * This will print:
2621 *
2622 * Received: 0
2623 * Received: 1
2624 * Received: 2
2625 * loop exited
2626 * Continue successfully
2627 */
2628
2629/*
2630 * Document-class: Ractor::RemoteError
2631 *
2632 * Raised on attempt to Ractor#take if there was an uncaught exception in the Ractor.
2633 * Its +cause+ will contain the original exception, and +ractor+ is the original ractor
2634 * it was raised in.
2635 *
2636 * r = Ractor.new { raise "Something weird happened" }
2637 *
2638 * begin
2639 * r.take
2640 * rescue => e
2641 * p e # => #<Ractor::RemoteError: thrown by remote Ractor.>
2642 * p e.ractor == r # => true
2643 * p e.cause # => #<RuntimeError: Something weird happened>
2644 * end
2645 *
2646 */
2647
2648/*
2649 * Document-class: Ractor::MovedError
2650 *
2651 * Raised on an attempt to access an object which was moved in Ractor#send or Ractor.yield.
2652 *
2653 * r = Ractor.new { sleep }
2654 *
2655 * ary = [1, 2, 3]
2656 * r.send(ary, move: true)
2657 * ary.inspect
2658 * # Ractor::MovedError (can not send any methods to a moved object)
2659 *
2660 */
2661
2662/*
2663 * Document-class: Ractor::MovedObject
2664 *
2665 * A special object which replaces any value that was moved to another ractor in Ractor#send
2666 * or Ractor.yield. Any attempt to access the object results in Ractor::MovedError.
2667 *
2668 * r = Ractor.new { receive }
2669 *
2670 * ary = [1, 2, 3]
2671 * r.send(ary, move: true)
2672 * p Ractor::MovedObject === ary
2673 * # => true
2674 * ary.inspect
2675 * # Ractor::MovedError (can not send any methods to a moved object)
2676 */
2677
2678// Main docs are in ractor.rb, but without this clause there are weird artifacts
2679// in their rendering.
2680/*
2681 * Document-class: Ractor
2682 *
2683 */
2684
2685void
2686Init_Ractor(void)
2687{
2688 rb_cRactor = rb_define_class("Ractor", rb_cObject);
2690
2691 rb_eRactorError = rb_define_class_under(rb_cRactor, "Error", rb_eRuntimeError);
2692 rb_eRactorIsolationError = rb_define_class_under(rb_cRactor, "IsolationError", rb_eRactorError);
2693 rb_eRactorRemoteError = rb_define_class_under(rb_cRactor, "RemoteError", rb_eRactorError);
2694 rb_eRactorMovedError = rb_define_class_under(rb_cRactor, "MovedError", rb_eRactorError);
2695 rb_eRactorClosedError = rb_define_class_under(rb_cRactor, "ClosedError", rb_eStopIteration);
2696 rb_eRactorUnsafeError = rb_define_class_under(rb_cRactor, "UnsafeError", rb_eRactorError);
2697
2698 rb_cRactorMovedObject = rb_define_class_under(rb_cRactor, "MovedObject", rb_cBasicObject);
2699 rb_undef_alloc_func(rb_cRactorMovedObject);
2700 rb_define_method(rb_cRactorMovedObject, "method_missing", ractor_moved_missing, -1);
2701
2702 // override methods defined in BasicObject
2703 rb_define_method(rb_cRactorMovedObject, "__send__", ractor_moved_missing, -1);
2704 rb_define_method(rb_cRactorMovedObject, "!", ractor_moved_missing, -1);
2705 rb_define_method(rb_cRactorMovedObject, "==", ractor_moved_missing, -1);
2706 rb_define_method(rb_cRactorMovedObject, "!=", ractor_moved_missing, -1);
2707 rb_define_method(rb_cRactorMovedObject, "__id__", ractor_moved_missing, -1);
2708 rb_define_method(rb_cRactorMovedObject, "equal?", ractor_moved_missing, -1);
2709 rb_define_method(rb_cRactorMovedObject, "instance_eval", ractor_moved_missing, -1);
2710 rb_define_method(rb_cRactorMovedObject, "instance_exec", ractor_moved_missing, -1);
2711
2712 // internal
2713
2714#if USE_RACTOR_SELECTOR
2715 rb_init_ractor_selector();
2716#endif
2717}
2718
2719void
2720rb_ractor_dump(void)
2721{
2722 rb_vm_t *vm = GET_VM();
2723 rb_ractor_t *r = 0;
2724
2725 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
2726 if (r != vm->ractor.main_ractor) {
2727 fprintf(stderr, "r:%u (%s)\n", r->pub.id, ractor_status_str(r->status_));
2728 }
2729 }
2730}
2731
2732VALUE
2734{
2735 if (rb_ractor_main_p()) {
2736 return rb_stdin;
2737 }
2738 else {
2739 rb_ractor_t *cr = GET_RACTOR();
2740 return cr->r_stdin;
2741 }
2742}
2743
2744VALUE
2745rb_ractor_stdout(void)
2746{
2747 if (rb_ractor_main_p()) {
2748 return rb_stdout;
2749 }
2750 else {
2751 rb_ractor_t *cr = GET_RACTOR();
2752 return cr->r_stdout;
2753 }
2754}
2755
2756VALUE
2757rb_ractor_stderr(void)
2758{
2759 if (rb_ractor_main_p()) {
2760 return rb_stderr;
2761 }
2762 else {
2763 rb_ractor_t *cr = GET_RACTOR();
2764 return cr->r_stderr;
2765 }
2766}
2767
2768void
2770{
2771 if (rb_ractor_main_p()) {
2772 rb_stdin = in;
2773 }
2774 else {
2775 rb_ractor_t *cr = GET_RACTOR();
2776 RB_OBJ_WRITE(cr->pub.self, &cr->r_stdin, in);
2777 }
2778}
2779
2780void
2782{
2783 if (rb_ractor_main_p()) {
2784 rb_stdout = out;
2785 }
2786 else {
2787 rb_ractor_t *cr = GET_RACTOR();
2788 RB_OBJ_WRITE(cr->pub.self, &cr->r_stdout, out);
2789 }
2790}
2791
2792void
2794{
2795 if (rb_ractor_main_p()) {
2796 rb_stderr = err;
2797 }
2798 else {
2799 rb_ractor_t *cr = GET_RACTOR();
2800 RB_OBJ_WRITE(cr->pub.self, &cr->r_stderr, err);
2801 }
2802}
2803
2805rb_ractor_hooks(rb_ractor_t *cr)
2806{
2807 return &cr->pub.hooks;
2808}
2809
2811
2812// 2: stop search
2813// 1: skip child
2814// 0: continue
2815
2816enum obj_traverse_iterator_result {
2817 traverse_cont,
2818 traverse_skip,
2819 traverse_stop,
2820};
2821
2822typedef enum obj_traverse_iterator_result (*rb_obj_traverse_enter_func)(VALUE obj);
2823typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(VALUE obj);
2824typedef enum obj_traverse_iterator_result (*rb_obj_traverse_final_func)(VALUE obj);
2825
2826static enum obj_traverse_iterator_result null_leave(VALUE obj);
2827
2829 rb_obj_traverse_enter_func enter_func;
2830 rb_obj_traverse_leave_func leave_func;
2831
2832 st_table *rec;
2833 VALUE rec_hash;
2834};
2835
2836
2838 bool stop;
2839 struct obj_traverse_data *data;
2840};
2841
2842static int obj_traverse_i(VALUE obj, struct obj_traverse_data *data);
2843
2844static int
2845obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
2846{
2848
2849 if (obj_traverse_i(key, d->data)) {
2850 d->stop = true;
2851 return ST_STOP;
2852 }
2853
2854 if (obj_traverse_i(val, d->data)) {
2855 d->stop = true;
2856 return ST_STOP;
2857 }
2858
2859 return ST_CONTINUE;
2860}
2861
2862static void
2863obj_traverse_reachable_i(VALUE obj, void *ptr)
2864{
2866
2867 if (obj_traverse_i(obj, d->data)) {
2868 d->stop = true;
2869 }
2870}
2871
2872static struct st_table *
2873obj_traverse_rec(struct obj_traverse_data *data)
2874{
2875 if (UNLIKELY(!data->rec)) {
2876 data->rec_hash = rb_ident_hash_new();
2877 data->rec = RHASH_ST_TABLE(data->rec_hash);
2878 }
2879 return data->rec;
2880}
2881
2882static int
2883obj_traverse_ivar_foreach_i(ID key, VALUE val, st_data_t ptr)
2884{
2886
2887 if (obj_traverse_i(val, d->data)) {
2888 d->stop = true;
2889 return ST_STOP;
2890 }
2891
2892 return ST_CONTINUE;
2893}
2894
2895static int
2896obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
2897{
2898 if (RB_SPECIAL_CONST_P(obj)) return 0;
2899
2900 switch (data->enter_func(obj)) {
2901 case traverse_cont: break;
2902 case traverse_skip: return 0; // skip children
2903 case traverse_stop: return 1; // stop search
2904 }
2905
2906 if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
2907 // already traversed
2908 return 0;
2909 }
2910
2911 struct obj_traverse_callback_data d = {
2912 .stop = false,
2913 .data = data,
2914 };
2915 rb_ivar_foreach(obj, obj_traverse_ivar_foreach_i, (st_data_t)&d);
2916 if (d.stop) return 1;
2917
2918 switch (BUILTIN_TYPE(obj)) {
2919 // no child node
2920 case T_STRING:
2921 case T_FLOAT:
2922 case T_BIGNUM:
2923 case T_REGEXP:
2924 case T_FILE:
2925 case T_SYMBOL:
2926 case T_MATCH:
2927 break;
2928
2929 case T_OBJECT:
2930 /* Instance variables already traversed. */
2931 break;
2932
2933 case T_ARRAY:
2934 {
2935 for (int i = 0; i < RARRAY_LENINT(obj); i++) {
2936 VALUE e = rb_ary_entry(obj, i);
2937 if (obj_traverse_i(e, data)) return 1;
2938 }
2939 }
2940 break;
2941
2942 case T_HASH:
2943 {
2944 if (obj_traverse_i(RHASH_IFNONE(obj), data)) return 1;
2945
2946 struct obj_traverse_callback_data d = {
2947 .stop = false,
2948 .data = data,
2949 };
2950 rb_hash_foreach(obj, obj_hash_traverse_i, (VALUE)&d);
2951 if (d.stop) return 1;
2952 }
2953 break;
2954
2955 case T_STRUCT:
2956 {
2957 long len = RSTRUCT_LEN(obj);
2958 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2959
2960 for (long i=0; i<len; i++) {
2961 if (obj_traverse_i(ptr[i], data)) return 1;
2962 }
2963 }
2964 break;
2965
2966 case T_RATIONAL:
2967 if (obj_traverse_i(RRATIONAL(obj)->num, data)) return 1;
2968 if (obj_traverse_i(RRATIONAL(obj)->den, data)) return 1;
2969 break;
2970 case T_COMPLEX:
2971 if (obj_traverse_i(RCOMPLEX(obj)->real, data)) return 1;
2972 if (obj_traverse_i(RCOMPLEX(obj)->imag, data)) return 1;
2973 break;
2974
2975 case T_DATA:
2976 case T_IMEMO:
2977 {
2978 struct obj_traverse_callback_data d = {
2979 .stop = false,
2980 .data = data,
2981 };
2982 RB_VM_LOCK_ENTER_NO_BARRIER();
2983 {
2984 rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
2985 }
2986 RB_VM_LOCK_LEAVE_NO_BARRIER();
2987 if (d.stop) return 1;
2988 }
2989 break;
2990
2991 // unreachable
2992 case T_CLASS:
2993 case T_MODULE:
2994 case T_ICLASS:
2995 default:
2996 rp(obj);
2997 rb_bug("unreachable");
2998 }
2999
3000 if (data->leave_func(obj) == traverse_stop) {
3001 return 1;
3002 }
3003 else {
3004 return 0;
3005 }
3006}
3007
3009 rb_obj_traverse_final_func final_func;
3010 int stopped;
3011};
3012
3013static int
3014obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
3015{
3016 struct rb_obj_traverse_final_data *data = (void *)arg;
3017 if (data->final_func(key)) {
3018 data->stopped = 1;
3019 return ST_STOP;
3020 }
3021 return ST_CONTINUE;
3022}
3023
3024// 0: traverse all
3025// 1: stopped
3026static int
3027rb_obj_traverse(VALUE obj,
3028 rb_obj_traverse_enter_func enter_func,
3029 rb_obj_traverse_leave_func leave_func,
3030 rb_obj_traverse_final_func final_func)
3031{
3032 struct obj_traverse_data data = {
3033 .enter_func = enter_func,
3034 .leave_func = leave_func,
3035 .rec = NULL,
3036 };
3037
3038 if (obj_traverse_i(obj, &data)) return 1;
3039 if (final_func && data.rec) {
3040 struct rb_obj_traverse_final_data f = {final_func, 0};
3041 st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
3042 return f.stopped;
3043 }
3044 return 0;
3045}
3046
3047static int
3048frozen_shareable_p(VALUE obj, bool *made_shareable)
3049{
3050 if (!RB_TYPE_P(obj, T_DATA)) {
3051 return true;
3052 }
3053 else if (RTYPEDDATA_P(obj)) {
3054 const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
3055 if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
3056 return true;
3057 }
3058 else if (made_shareable && rb_obj_is_proc(obj)) {
3059 // special path to make shareable Proc.
3060 rb_proc_ractor_make_shareable(obj);
3061 *made_shareable = true;
3062 VM_ASSERT(RB_OBJ_SHAREABLE_P(obj));
3063 return false;
3064 }
3065 }
3066
3067 return false;
3068}
3069
3070static enum obj_traverse_iterator_result
3071make_shareable_check_shareable(VALUE obj)
3072{
3073 VM_ASSERT(!SPECIAL_CONST_P(obj));
3074 bool made_shareable = false;
3075
3076 if (rb_ractor_shareable_p(obj)) {
3077 return traverse_skip;
3078 }
3079 if (!frozen_shareable_p(obj, &made_shareable)) {
3080 if (made_shareable) {
3081 return traverse_skip;
3082 }
3083 else {
3084 rb_raise(rb_eRactorError, "can not make shareable object for %"PRIsVALUE, obj);
3085 }
3086 }
3087
3088 if (!RB_OBJ_FROZEN_RAW(obj)) {
3089 rb_funcall(obj, idFreeze, 0);
3090
3091 if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
3092 rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
3093 }
3094
3095 if (RB_OBJ_SHAREABLE_P(obj)) {
3096 return traverse_skip;
3097 }
3098 }
3099
3100 return traverse_cont;
3101}
3102
3103static enum obj_traverse_iterator_result
3104mark_shareable(VALUE obj)
3105{
3107 return traverse_cont;
3108}
3109
3110VALUE
3112{
3113 rb_obj_traverse(obj,
3114 make_shareable_check_shareable,
3115 null_leave, mark_shareable);
3116 return obj;
3117}
3118
3119VALUE
3121{
3122 VALUE copy = ractor_copy(obj);
3123 return rb_ractor_make_shareable(copy);
3124}
3125
3126VALUE
3127rb_ractor_ensure_shareable(VALUE obj, VALUE name)
3128{
3129 if (!rb_ractor_shareable_p(obj)) {
3130 VALUE message = rb_sprintf("cannot assign unshareable object to %"PRIsVALUE,
3131 name);
3132 rb_exc_raise(rb_exc_new_str(rb_eRactorIsolationError, message));
3133 }
3134 return obj;
3135}
3136
3137void
3138rb_ractor_ensure_main_ractor(const char *msg)
3139{
3140 if (!rb_ractor_main_p()) {
3141 rb_raise(rb_eRactorIsolationError, "%s", msg);
3142 }
3143}
3144
3145static enum obj_traverse_iterator_result
3146shareable_p_enter(VALUE obj)
3147{
3148 if (RB_OBJ_SHAREABLE_P(obj)) {
3149 return traverse_skip;
3150 }
3151 else if (RB_TYPE_P(obj, T_CLASS) ||
3152 RB_TYPE_P(obj, T_MODULE) ||
3153 RB_TYPE_P(obj, T_ICLASS)) {
3154 // TODO: remove it
3155 mark_shareable(obj);
3156 return traverse_skip;
3157 }
3158 else if (RB_OBJ_FROZEN_RAW(obj) &&
3159 frozen_shareable_p(obj, NULL)) {
3160 return traverse_cont;
3161 }
3162
3163 return traverse_stop; // fail
3164}
3165
3166bool
3167rb_ractor_shareable_p_continue(VALUE obj)
3168{
3169 if (rb_obj_traverse(obj,
3170 shareable_p_enter, null_leave,
3171 mark_shareable)) {
3172 return false;
3173 }
3174 else {
3175 return true;
3176 }
3177}
3178
3179#if RACTOR_CHECK_MODE > 0
3180void
3181rb_ractor_setup_belonging(VALUE obj)
3182{
3183 rb_ractor_setup_belonging_to(obj, rb_ractor_current_id());
3184}
3185
3186static enum obj_traverse_iterator_result
3187reset_belonging_enter(VALUE obj)
3188{
3189 if (rb_ractor_shareable_p(obj)) {
3190 return traverse_skip;
3191 }
3192 else {
3193 rb_ractor_setup_belonging(obj);
3194 return traverse_cont;
3195 }
3196}
3197#endif
3198
3199static enum obj_traverse_iterator_result
3200null_leave(VALUE obj)
3201{
3202 return traverse_cont;
3203}
3204
3205static VALUE
3206ractor_reset_belonging(VALUE obj)
3207{
3208#if RACTOR_CHECK_MODE > 0
3209 rb_obj_traverse(obj, reset_belonging_enter, null_leave, NULL);
3210#endif
3211 return obj;
3212}
3213
3214
3216
3217// 2: stop search
3218// 1: skip child
3219// 0: continue
3220
3222static int obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data);
3223typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_enter_func)(VALUE obj, struct obj_traverse_replace_data *data);
3224typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_leave_func)(VALUE obj, struct obj_traverse_replace_data *data);
3225
3227 rb_obj_traverse_replace_enter_func enter_func;
3228 rb_obj_traverse_replace_leave_func leave_func;
3229
3230 st_table *rec;
3231 VALUE rec_hash;
3232
3233 VALUE replacement;
3234 bool move;
3235};
3236
3238 bool stop;
3239 VALUE src;
3240 struct obj_traverse_replace_data *data;
3241};
3242
3243static int
3244obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp, int error)
3245{
3246 return ST_REPLACE;
3247}
3248
3249static int
3250obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr, int exists)
3251{
3253 struct obj_traverse_replace_data *data = d->data;
3254
3255 if (obj_traverse_replace_i(*key, data)) {
3256 d->stop = true;
3257 return ST_STOP;
3258 }
3259 else if (*key != data->replacement) {
3260 VALUE v = *key = data->replacement;
3261 RB_OBJ_WRITTEN(d->src, Qundef, v);
3262 }
3263
3264 if (obj_traverse_replace_i(*val, data)) {
3265 d->stop = true;
3266 return ST_STOP;
3267 }
3268 else if (*val != data->replacement) {
3269 VALUE v = *val = data->replacement;
3270 RB_OBJ_WRITTEN(d->src, Qundef, v);
3271 }
3272
3273 return ST_CONTINUE;
3274}
3275
3276static int
3277obj_iv_hash_traverse_replace_foreach_i(st_data_t _key, st_data_t _val, st_data_t _data, int _x)
3278{
3279 return ST_REPLACE;
3280}
3281
3282static int
3283obj_iv_hash_traverse_replace_i(st_data_t * _key, st_data_t * val, st_data_t ptr, int exists)
3284{
3286 struct obj_traverse_replace_data *data = d->data;
3287
3288 if (obj_traverse_replace_i(*(VALUE *)val, data)) {
3289 d->stop = true;
3290 return ST_STOP;
3291 }
3292 else if (*(VALUE *)val != data->replacement) {
3293 VALUE v = *(VALUE *)val = data->replacement;
3294 RB_OBJ_WRITTEN(d->src, Qundef, v);
3295 }
3296
3297 return ST_CONTINUE;
3298}
3299
3300static struct st_table *
3301obj_traverse_replace_rec(struct obj_traverse_replace_data *data)
3302{
3303 if (UNLIKELY(!data->rec)) {
3304 data->rec_hash = rb_ident_hash_new();
3305 data->rec = RHASH_ST_TABLE(data->rec_hash);
3306 }
3307 return data->rec;
3308}
3309
3310static void
3311obj_refer_only_shareables_p_i(VALUE obj, void *ptr)
3312{
3313 int *pcnt = (int *)ptr;
3314
3315 if (!rb_ractor_shareable_p(obj)) {
3316 ++*pcnt;
3317 }
3318}
3319
3320static int
3321obj_refer_only_shareables_p(VALUE obj)
3322{
3323 int cnt = 0;
3324 RB_VM_LOCK_ENTER_NO_BARRIER();
3325 {
3326 rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
3327 }
3328 RB_VM_LOCK_LEAVE_NO_BARRIER();
3329 return cnt == 0;
3330}
3331
3332static int
3333obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
3334{
3335 st_data_t replacement;
3336
3337 if (RB_SPECIAL_CONST_P(obj)) {
3338 data->replacement = obj;
3339 return 0;
3340 }
3341
3342 switch (data->enter_func(obj, data)) {
3343 case traverse_cont: break;
3344 case traverse_skip: return 0; // skip children
3345 case traverse_stop: return 1; // stop search
3346 }
3347
3348 replacement = (st_data_t)data->replacement;
3349
3350 if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, &replacement))) {
3351 data->replacement = (VALUE)replacement;
3352 return 0;
3353 }
3354 else {
3355 st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, replacement);
3356 }
3357
3358 if (!data->move) {
3359 obj = replacement;
3360 }
3361
3362#define CHECK_AND_REPLACE(v) do { \
3363 VALUE _val = (v); \
3364 if (obj_traverse_replace_i(_val, data)) { return 1; } \
3365 else if (data->replacement != _val) { RB_OBJ_WRITE(obj, &v, data->replacement); } \
3366} while (0)
3367
3368 if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
3369 struct gen_ivtbl *ivtbl;
3370 rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
3371
3372 if (UNLIKELY(rb_shape_obj_too_complex(obj))) {
3374 .stop = false,
3375 .data = data,
3376 .src = obj,
3377 };
3378 rb_st_foreach_with_replace(
3379 ivtbl->as.complex.table,
3380 obj_iv_hash_traverse_replace_foreach_i,
3381 obj_iv_hash_traverse_replace_i,
3382 (st_data_t)&d
3383 );
3384 if (d.stop) return 1;
3385 }
3386 else {
3387 for (uint32_t i = 0; i < ivtbl->as.shape.numiv; i++) {
3388 if (!UNDEF_P(ivtbl->as.shape.ivptr[i])) {
3389 CHECK_AND_REPLACE(ivtbl->as.shape.ivptr[i]);
3390 }
3391 }
3392 }
3393 }
3394
3395 switch (BUILTIN_TYPE(obj)) {
3396 // no child node
3397 case T_FLOAT:
3398 case T_BIGNUM:
3399 case T_REGEXP:
3400 case T_FILE:
3401 case T_SYMBOL:
3402 case T_MATCH:
3403 break;
3404 case T_STRING:
3405 rb_str_make_independent(obj);
3406 break;
3407
3408 case T_OBJECT:
3409 {
3410 if (rb_shape_obj_too_complex(obj)) {
3412 .stop = false,
3413 .data = data,
3414 .src = obj,
3415 };
3416 rb_st_foreach_with_replace(
3417 ROBJECT_IV_HASH(obj),
3418 obj_iv_hash_traverse_replace_foreach_i,
3419 obj_iv_hash_traverse_replace_i,
3420 (st_data_t)&d
3421 );
3422 if (d.stop) return 1;
3423 }
3424 else {
3425 uint32_t len = ROBJECT_IV_COUNT(obj);
3426 VALUE *ptr = ROBJECT_IVPTR(obj);
3427
3428 for (uint32_t i = 0; i < len; i++) {
3429 CHECK_AND_REPLACE(ptr[i]);
3430 }
3431 }
3432 }
3433 break;
3434
3435 case T_ARRAY:
3436 {
3437 rb_ary_cancel_sharing(obj);
3438
3439 for (int i = 0; i < RARRAY_LENINT(obj); i++) {
3440 VALUE e = rb_ary_entry(obj, i);
3441
3442 if (obj_traverse_replace_i(e, data)) {
3443 return 1;
3444 }
3445 else if (e != data->replacement) {
3446 RARRAY_ASET(obj, i, data->replacement);
3447 }
3448 }
3449 RB_GC_GUARD(obj);
3450 }
3451 break;
3452 case T_HASH:
3453 {
3455 .stop = false,
3456 .data = data,
3457 .src = obj,
3458 };
3459 rb_hash_stlike_foreach_with_replace(obj,
3460 obj_hash_traverse_replace_foreach_i,
3461 obj_hash_traverse_replace_i,
3462 (VALUE)&d);
3463 if (d.stop) return 1;
3464 // TODO: rehash here?
3465
3466 VALUE ifnone = RHASH_IFNONE(obj);
3467 if (obj_traverse_replace_i(ifnone, data)) {
3468 return 1;
3469 }
3470 else if (ifnone != data->replacement) {
3471 RHASH_SET_IFNONE(obj, data->replacement);
3472 }
3473 }
3474 break;
3475
3476 case T_STRUCT:
3477 {
3478 long len = RSTRUCT_LEN(obj);
3479 const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
3480
3481 for (long i=0; i<len; i++) {
3482 CHECK_AND_REPLACE(ptr[i]);
3483 }
3484 }
3485 break;
3486
3487 case T_RATIONAL:
3488 CHECK_AND_REPLACE(RRATIONAL(obj)->num);
3489 CHECK_AND_REPLACE(RRATIONAL(obj)->den);
3490 break;
3491 case T_COMPLEX:
3492 CHECK_AND_REPLACE(RCOMPLEX(obj)->real);
3493 CHECK_AND_REPLACE(RCOMPLEX(obj)->imag);
3494 break;
3495
3496 case T_DATA:
3497 if (!data->move && obj_refer_only_shareables_p(obj)) {
3498 break;
3499 }
3500 else {
3501 rb_raise(rb_eRactorError, "can not %s %"PRIsVALUE" object.",
3502 data->move ? "move" : "copy", rb_class_of(obj));
3503 }
3504
3505 case T_IMEMO:
3506 // not supported yet
3507 return 1;
3508
3509 // unreachable
3510 case T_CLASS:
3511 case T_MODULE:
3512 case T_ICLASS:
3513 default:
3514 rp(obj);
3515 rb_bug("unreachable");
3516 }
3517
3518 data->replacement = (VALUE)replacement;
3519
3520 if (data->leave_func(obj, data) == traverse_stop) {
3521 return 1;
3522 }
3523 else {
3524 return 0;
3525 }
3526}
3527
3528// 0: traverse all
3529// 1: stopped
3530static VALUE
3531rb_obj_traverse_replace(VALUE obj,
3532 rb_obj_traverse_replace_enter_func enter_func,
3533 rb_obj_traverse_replace_leave_func leave_func,
3534 bool move)
3535{
3536 struct obj_traverse_replace_data data = {
3537 .enter_func = enter_func,
3538 .leave_func = leave_func,
3539 .rec = NULL,
3540 .replacement = Qundef,
3541 .move = move,
3542 };
3543
3544 if (obj_traverse_replace_i(obj, &data)) {
3545 return Qundef;
3546 }
3547 else {
3548 return data.replacement;
3549 }
3550}
3551
3552struct RVALUE {
3553 VALUE flags;
3554 VALUE klass;
3555 VALUE v1;
3556 VALUE v2;
3557 VALUE v3;
3558};
3559
3560static const VALUE fl_users = FL_USER1 | FL_USER2 | FL_USER3 |
3565
3566static void
3567ractor_moved_bang(VALUE obj)
3568{
3569 // invalidate src object
3570 struct RVALUE *rv = (void *)obj;
3571
3572 rv->klass = rb_cRactorMovedObject;
3573 rv->v1 = 0;
3574 rv->v2 = 0;
3575 rv->v3 = 0;
3576 rv->flags = rv->flags & ~fl_users;
3577
3578 if (BUILTIN_TYPE(obj) == T_OBJECT) ROBJECT_SET_SHAPE_ID(obj, ROOT_SHAPE_ID);
3579
3580 // TODO: record moved location
3581}
3582
3583static enum obj_traverse_iterator_result
3584move_enter(VALUE obj, struct obj_traverse_replace_data *data)
3585{
3586 if (rb_ractor_shareable_p(obj)) {
3587 data->replacement = obj;
3588 return traverse_skip;
3589 }
3590 else {
3591 VALUE moved = rb_obj_alloc(RBASIC_CLASS(obj));
3592 rb_shape_set_shape(moved, rb_shape_get_shape(obj));
3593 data->replacement = moved;
3594 return traverse_cont;
3595 }
3596}
3597
3598void rb_replace_generic_ivar(VALUE clone, VALUE obj); // variable.c
3599
3600static enum obj_traverse_iterator_result
3601move_leave(VALUE obj, struct obj_traverse_replace_data *data)
3602{
3603 VALUE v = data->replacement;
3604 struct RVALUE *dst = (struct RVALUE *)v;
3605 struct RVALUE *src = (struct RVALUE *)obj;
3606
3607 dst->flags = (dst->flags & ~fl_users) | (src->flags & fl_users);
3608
3609 dst->v1 = src->v1;
3610 dst->v2 = src->v2;
3611 dst->v3 = src->v3;
3612
3613 if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
3614 rb_replace_generic_ivar(v, obj);
3615 }
3616
3617 if (OBJ_FROZEN(obj)) {
3618 OBJ_FREEZE(v);
3619 }
3620
3621 // TODO: generic_ivar
3622
3623 ractor_moved_bang(obj);
3624 return traverse_cont;
3625}
3626
3627static VALUE
3628ractor_move(VALUE obj)
3629{
3630 VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave, true);
3631 if (!UNDEF_P(val)) {
3632 return val;
3633 }
3634 else {
3635 rb_raise(rb_eRactorError, "can not move the object");
3636 }
3637}
3638
3639static enum obj_traverse_iterator_result
3640copy_enter(VALUE obj, struct obj_traverse_replace_data *data)
3641{
3642 if (rb_ractor_shareable_p(obj)) {
3643 data->replacement = obj;
3644 return traverse_skip;
3645 }
3646 else {
3647 data->replacement = rb_obj_clone(obj);
3648 return traverse_cont;
3649 }
3650}
3651
3652static enum obj_traverse_iterator_result
3653copy_leave(VALUE obj, struct obj_traverse_replace_data *data)
3654{
3655 return traverse_cont;
3656}
3657
3658static VALUE
3659ractor_copy(VALUE obj)
3660{
3661 VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave, false);
3662 if (!UNDEF_P(val)) {
3663 return val;
3664 }
3665 else {
3666 rb_raise(rb_eRactorError, "can not copy the object");
3667 }
3668}
3669
3670// Ractor local storage
3671
3673 const struct rb_ractor_local_storage_type *type;
3674 void *main_cache;
3675};
3676
3678 int cnt;
3679 int capa;
3681} freed_ractor_local_keys;
3682
3683static int
3684ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
3685{
3687 if (k->type->mark) (*k->type->mark)((void *)val);
3688 return ST_CONTINUE;
3689}
3690
3691static enum rb_id_table_iterator_result
3692idkey_local_storage_mark_i(VALUE val, void *dmy)
3693{
3694 rb_gc_mark(val);
3695 return ID_TABLE_CONTINUE;
3696}
3697
3698static void
3699ractor_local_storage_mark(rb_ractor_t *r)
3700{
3701 if (r->local_storage) {
3702 st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
3703
3704 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
3705 rb_ractor_local_key_t key = freed_ractor_local_keys.keys[i];
3706 st_data_t val, k = (st_data_t)key;
3707 if (st_delete(r->local_storage, &k, &val) &&
3708 (key = (rb_ractor_local_key_t)k)->type->free) {
3709 (*key->type->free)((void *)val);
3710 }
3711 }
3712 }
3713
3714 if (r->idkey_local_storage) {
3715 rb_id_table_foreach_values(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
3716 }
3717
3718 rb_gc_mark(r->local_storage_store_lock);
3719}
3720
3721static int
3722ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
3723{
3725 if (k->type->free) (*k->type->free)((void *)val);
3726 return ST_CONTINUE;
3727}
3728
3729static void
3730ractor_local_storage_free(rb_ractor_t *r)
3731{
3732 if (r->local_storage) {
3733 st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
3734 st_free_table(r->local_storage);
3735 }
3736
3737 if (r->idkey_local_storage) {
3738 rb_id_table_free(r->idkey_local_storage);
3739 }
3740}
3741
3742static void
3743rb_ractor_local_storage_value_mark(void *ptr)
3744{
3745 rb_gc_mark((VALUE)ptr);
3746}
3747
3748static const struct rb_ractor_local_storage_type ractor_local_storage_type_null = {
3749 NULL,
3750 NULL,
3751};
3752
3754 NULL,
3755 ruby_xfree,
3756};
3757
3758static const struct rb_ractor_local_storage_type ractor_local_storage_type_value = {
3759 rb_ractor_local_storage_value_mark,
3760 NULL,
3761};
3762
3765{
3767 key->type = type ? type : &ractor_local_storage_type_null;
3768 key->main_cache = (void *)Qundef;
3769 return key;
3770}
3771
3774{
3775 return rb_ractor_local_storage_ptr_newkey(&ractor_local_storage_type_value);
3776}
3777
3778void
3779rb_ractor_local_storage_delkey(rb_ractor_local_key_t key)
3780{
3781 RB_VM_LOCK_ENTER();
3782 {
3783 if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
3784 freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
3785 REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, freed_ractor_local_keys.capa);
3786 }
3787 freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
3788 }
3789 RB_VM_LOCK_LEAVE();
3790}
3791
3792static bool
3793ractor_local_ref(rb_ractor_local_key_t key, void **pret)
3794{
3795 if (rb_ractor_main_p()) {
3796 if (!UNDEF_P((VALUE)key->main_cache)) {
3797 *pret = key->main_cache;
3798 return true;
3799 }
3800 else {
3801 return false;
3802 }
3803 }
3804 else {
3805 rb_ractor_t *cr = GET_RACTOR();
3806
3807 if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
3808 return true;
3809 }
3810 else {
3811 return false;
3812 }
3813 }
3814}
3815
3816static void
3817ractor_local_set(rb_ractor_local_key_t key, void *ptr)
3818{
3819 rb_ractor_t *cr = GET_RACTOR();
3820
3821 if (cr->local_storage == NULL) {
3822 cr->local_storage = st_init_numtable();
3823 }
3824
3825 st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
3826
3827 if (rb_ractor_main_p()) {
3828 key->main_cache = ptr;
3829 }
3830}
3831
3832VALUE
3834{
3835 void *val;
3836 if (ractor_local_ref(key, &val)) {
3837 return (VALUE)val;
3838 }
3839 else {
3840 return Qnil;
3841 }
3842}
3843
3844bool
3846{
3847 if (ractor_local_ref(key, (void **)val)) {
3848 return true;
3849 }
3850 else {
3851 return false;
3852 }
3853}
3854
3855void
3857{
3858 ractor_local_set(key, (void *)val);
3859}
3860
3861void *
3863{
3864 void *ret;
3865 if (ractor_local_ref(key, &ret)) {
3866 return ret;
3867 }
3868 else {
3869 return NULL;
3870 }
3871}
3872
3873void
3875{
3876 ractor_local_set(key, ptr);
3877}
3878
3879#define DEFAULT_KEYS_CAPA 0x10
3880
3881void
3882rb_ractor_finish_marking(void)
3883{
3884 for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
3885 ruby_xfree(freed_ractor_local_keys.keys[i]);
3886 }
3887 freed_ractor_local_keys.cnt = 0;
3888 if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
3889 freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
3890 REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, DEFAULT_KEYS_CAPA);
3891 }
3892}
3893
3894static VALUE
3895ractor_local_value(rb_execution_context_t *ec, VALUE self, VALUE sym)
3896{
3897 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3898 ID id = rb_check_id(&sym);
3899 struct rb_id_table *tbl = cr->idkey_local_storage;
3900 VALUE val;
3901
3902 if (id && tbl && rb_id_table_lookup(tbl, id, &val)) {
3903 return val;
3904 }
3905 else {
3906 return Qnil;
3907 }
3908}
3909
3910static VALUE
3911ractor_local_value_set(rb_execution_context_t *ec, VALUE self, VALUE sym, VALUE val)
3912{
3913 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3914 ID id = SYM2ID(rb_to_symbol(sym));
3915 struct rb_id_table *tbl = cr->idkey_local_storage;
3916
3917 if (tbl == NULL) {
3918 tbl = cr->idkey_local_storage = rb_id_table_create(2);
3919 }
3920 rb_id_table_insert(tbl, id, val);
3921 return val;
3922}
3923
3926 struct rb_id_table *tbl;
3927 ID id;
3928 VALUE sym;
3929};
3930
3931static VALUE
3932ractor_local_value_store_i(VALUE ptr)
3933{
3934 VALUE val;
3936
3937 if (rb_id_table_lookup(data->tbl, data->id, &val)) {
3938 // after synchronization, we found already registered entry
3939 }
3940 else {
3941 val = rb_yield(Qnil);
3942 ractor_local_value_set(data->ec, Qnil, data->sym, val);
3943 }
3944 return val;
3945}
3946
3947static VALUE
3948ractor_local_value_store_if_absent(rb_execution_context_t *ec, VALUE self, VALUE sym)
3949{
3950 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3951 struct ractor_local_storage_store_data data = {
3952 .ec = ec,
3953 .sym = sym,
3954 .id = SYM2ID(rb_to_symbol(sym)),
3955 .tbl = cr->idkey_local_storage,
3956 };
3957 VALUE val;
3958
3959 if (data.tbl == NULL) {
3960 data.tbl = cr->idkey_local_storage = rb_id_table_create(2);
3961 }
3962 else if (rb_id_table_lookup(data.tbl, data.id, &val)) {
3963 // already set
3964 return val;
3965 }
3966
3967 if (!cr->local_storage_store_lock) {
3968 cr->local_storage_store_lock = rb_mutex_new();
3969 }
3970
3971 return rb_mutex_synchronize(cr->local_storage_store_lock, ractor_local_value_store_i, (VALUE)&data);
3972}
3973
3974// Ractor::Channel (emulate with Ractor)
3975
3977
3978static VALUE
3979ractor_channel_func(RB_BLOCK_CALL_FUNC_ARGLIST(y, c))
3980{
3981 rb_execution_context_t *ec = GET_EC();
3982 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3983
3984 while (1) {
3985 int state;
3986
3987 EC_PUSH_TAG(ec);
3988 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
3989 VALUE obj = ractor_receive(ec, cr);
3990 ractor_yield(ec, cr, obj, Qfalse);
3991 }
3992 EC_POP_TAG();
3993
3994 if (state) {
3995 // ignore the error
3996 break;
3997 }
3998 }
3999
4000 return Qnil;
4001}
4002
4003static VALUE
4004rb_ractor_channel_new(void)
4005{
4006#if 0
4007 return rb_funcall(rb_const_get(rb_cRactor, rb_intern("Channel")), rb_intern("new"), 0);
4008#else
4009 // class Channel
4010 // def self.new
4011 // Ractor.new do # func body
4012 // while true
4013 // obj = Ractor.receive
4014 // Ractor.yield obj
4015 // end
4016 // rescue Ractor::ClosedError
4017 // nil
4018 // end
4019 // end
4020 // end
4021
4022 return ractor_create_func(rb_cRactor, Qnil, rb_str_new2("Ractor/channel"), rb_ary_new(), ractor_channel_func);
4023#endif
4024}
4025
4026static VALUE
4027rb_ractor_channel_yield(rb_execution_context_t *ec, VALUE vch, VALUE obj)
4028{
4029 VM_ASSERT(ec == rb_current_ec_noinline());
4030 rb_ractor_channel_t *ch = RACTOR_PTR(vch);
4031
4032 ractor_send(ec, (rb_ractor_t *)ch, obj, Qfalse);
4033 return Qnil;
4034}
4035
4036static VALUE
4037rb_ractor_channel_take(rb_execution_context_t *ec, VALUE vch)
4038{
4039 VM_ASSERT(ec == rb_current_ec_noinline());
4040 rb_ractor_channel_t *ch = RACTOR_PTR(vch);
4041
4042 return ractor_take(ec, (rb_ractor_t *)ch);
4043}
4044
4045static VALUE
4046rb_ractor_channel_close(rb_execution_context_t *ec, VALUE vch)
4047{
4048 VM_ASSERT(ec == rb_current_ec_noinline());
4049 rb_ractor_channel_t *ch = RACTOR_PTR(vch);
4050
4051 ractor_close_incoming(ec, (rb_ractor_t *)ch);
4052 return ractor_close_outgoing(ec, (rb_ractor_t *)ch);
4053}
4054
4055// Ractor#require
4056
4058 VALUE ch;
4059 VALUE result;
4060 VALUE exception;
4061
4062 // require
4063 VALUE feature;
4064
4065 // autoload
4066 VALUE module;
4067 ID name;
4068};
4069
4070static VALUE
4071require_body(VALUE data)
4072{
4073 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4074
4075 ID require;
4076 CONST_ID(require, "require");
4077 crr->result = rb_funcallv(Qnil, require, 1, &crr->feature);
4078
4079 return Qnil;
4080}
4081
4082static VALUE
4083require_rescue(VALUE data, VALUE errinfo)
4084{
4085 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4086 crr->exception = errinfo;
4087 return Qundef;
4088}
4089
4090static VALUE
4091require_result_copy_body(VALUE data)
4092{
4093 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4094
4095 if (crr->exception != Qundef) {
4096 VM_ASSERT(crr->result == Qundef);
4097 crr->exception = ractor_copy(crr->exception);
4098 }
4099 else{
4100 VM_ASSERT(crr->result != Qundef);
4101 crr->result = ractor_copy(crr->result);
4102 }
4103
4104 return Qnil;
4105}
4106
4107static VALUE
4108require_result_copy_resuce(VALUE data, VALUE errinfo)
4109{
4110 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4111 crr->exception = errinfo; // ractor_move(crr->exception);
4112 return Qnil;
4113}
4114
4115static VALUE
4116ractor_require_protect(struct cross_ractor_require *crr, VALUE (*func)(VALUE))
4117{
4118 // catch any error
4119 rb_rescue2(func, (VALUE)crr,
4120 require_rescue, (VALUE)crr, rb_eException, 0);
4121
4122 rb_rescue2(require_result_copy_body, (VALUE)crr,
4123 require_result_copy_resuce, (VALUE)crr, rb_eException, 0);
4124
4125 rb_ractor_channel_yield(GET_EC(), crr->ch, Qtrue);
4126 return Qnil;
4127
4128}
4129
4130static VALUE
4131ractore_require_func(void *data)
4132{
4133 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4134 return ractor_require_protect(crr, require_body);
4135}
4136
4137VALUE
4138rb_ractor_require(VALUE feature)
4139{
4140 // TODO: make feature shareable
4141 struct cross_ractor_require crr = {
4142 .feature = feature, // TODO: ractor
4143 .ch = rb_ractor_channel_new(),
4144 .result = Qundef,
4145 .exception = Qundef,
4146 };
4147
4148 rb_execution_context_t *ec = GET_EC();
4149 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
4150 rb_ractor_interrupt_exec(main_r, ractore_require_func, &crr, 0);
4151
4152 // wait for require done
4153 rb_ractor_channel_take(ec, crr.ch);
4154 rb_ractor_channel_close(ec, crr.ch);
4155
4156 if (crr.exception != Qundef) {
4157 rb_exc_raise(crr.exception);
4158 }
4159 else {
4160 return crr.result;
4161 }
4162}
4163
4164static VALUE
4165ractor_require(rb_execution_context_t *ec, VALUE self, VALUE feature)
4166{
4167 return rb_ractor_require(feature);
4168}
4169
4170static VALUE
4171autoload_load_body(VALUE data)
4172{
4173 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4174 crr->result = rb_autoload_load(crr->module, crr->name);
4175 return Qnil;
4176}
4177
4178static VALUE
4179ractor_autoload_load_func(void *data)
4180{
4181 struct cross_ractor_require *crr = (struct cross_ractor_require *)data;
4182 return ractor_require_protect(crr, autoload_load_body);
4183}
4184
4185VALUE
4186rb_ractor_autoload_load(VALUE module, ID name)
4187{
4188 struct cross_ractor_require crr = {
4189 .module = module,
4190 .name = name,
4191 .ch = rb_ractor_channel_new(),
4192 .result = Qundef,
4193 .exception = Qundef,
4194 };
4195
4196 rb_execution_context_t *ec = GET_EC();
4197 rb_ractor_t *main_r = GET_VM()->ractor.main_ractor;
4198 rb_ractor_interrupt_exec(main_r, ractor_autoload_load_func, &crr, 0);
4199
4200 // wait for require done
4201 rb_ractor_channel_take(ec, crr.ch);
4202 rb_ractor_channel_close(ec, crr.ch);
4203
4204 if (crr.exception != Qundef) {
4205 rb_exc_raise(crr.exception);
4206 }
4207 else {
4208 return crr.result;
4209 }
4210}
4211
4212#include "ractor.rbinc"
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
#define RUBY_ATOMIC_CAS(var, oldval, newval)
Atomic compare-and-swap.
Definition atomic.h:140
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
Definition atomic.h:69
#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...
Definition atomic.h:93
#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().
Definition fl_type.h:883
@ RUBY_FL_SHAREABLE
This flag has something to do with Ractor.
Definition fl_type.h:266
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition class.c:980
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1012
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.
Definition class.c:2635
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)
Keyword argument deconstructor.
Definition class.c:2424
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1675
#define T_COMPLEX
Old name of RUBY_T_COMPLEX.
Definition value_type.h:59
#define T_FILE
Old name of RUBY_T_FILE.
Definition value_type.h:62
#define FL_EXIVAR
Old name of RUBY_FL_EXIVAR.
Definition fl_type.h:66
#define FL_USER3
Old name of RUBY_FL_USER3.
Definition fl_type.h:74
#define REALLOC_N
Old name of RB_REALLOC_N.
Definition memory.h:403
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define FL_USER7
Old name of RUBY_FL_USER7.
Definition fl_type.h:78
#define FL_USER10
Old name of RUBY_FL_USER10.
Definition fl_type.h:81
#define Qundef
Old name of RUBY_Qundef.
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
Definition fl_type.h:137
#define FL_USER6
Old name of RUBY_FL_USER6.
Definition fl_type.h:77
#define T_FLOAT
Old name of RUBY_T_FLOAT.
Definition value_type.h:64
#define T_IMEMO
Old name of RUBY_T_IMEMO.
Definition value_type.h:67
#define FL_USER1
Old name of RUBY_FL_USER1.
Definition fl_type.h:72
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define T_BIGNUM
Old name of RUBY_T_BIGNUM.
Definition value_type.h:57
#define SPECIAL_CONST_P
Old name of RB_SPECIAL_CONST_P.
#define T_STRUCT
Old name of RUBY_T_STRUCT.
Definition value_type.h:79
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:135
#define FL_USER19
Old name of RUBY_FL_USER19.
Definition fl_type.h:90
#define SYM2ID
Old name of RB_SYM2ID.
Definition symbol.h:45
#define FL_USER12
Old name of RUBY_FL_USER12.
Definition fl_type.h:83
#define T_DATA
Old name of RUBY_T_DATA.
Definition value_type.h:60
#define FL_USER11
Old name of RUBY_FL_USER11.
Definition fl_type.h:82
#define FL_USER15
Old name of RUBY_FL_USER15.
Definition fl_type.h:86
#define T_MODULE
Old name of RUBY_T_MODULE.
Definition value_type.h:70
#define FL_USER8
Old name of RUBY_FL_USER8.
Definition fl_type.h:79
#define FL_USER14
Old name of RUBY_FL_USER14.
Definition fl_type.h:85
#define FL_USER17
Old name of RUBY_FL_USER17.
Definition fl_type.h:88
#define T_RATIONAL
Old name of RUBY_T_RATIONAL.
Definition value_type.h:76
#define T_ICLASS
Old name of RUBY_T_ICLASS.
Definition value_type.h:66
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
Definition fl_type.h:132
#define FL_USER18
Old name of RUBY_FL_USER18.
Definition fl_type.h:89
#define FL_USER2
Old name of RUBY_FL_USER2.
Definition fl_type.h:73
#define FL_USER9
Old name of RUBY_FL_USER9.
Definition fl_type.h:80
#define Qtrue
Old name of RUBY_Qtrue.
#define FL_USER13
Old name of RUBY_FL_USER13.
Definition fl_type.h:84
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define T_OBJECT
Old name of RUBY_T_OBJECT.
Definition value_type.h:75
#define NIL_P
Old name of RB_NIL_P.
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
Definition value_type.h:80
#define T_MATCH
Old name of RUBY_T_MATCH.
Definition value_type.h:69
#define FL_USER16
Old name of RUBY_FL_USER16.
Definition fl_type.h:87
#define T_CLASS
Old name of RUBY_T_CLASS.
Definition value_type.h:58
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition value_type.h:85
#define FL_USER5
Old name of RUBY_FL_USER5.
Definition fl_type.h:76
#define CONST_ID
Old name of RUBY_CONST_ID.
Definition symbol.h:47
#define FL_USER4
Old name of RUBY_FL_USER4.
Definition fl_type.h:75
#define FL_SET_RAW
Old name of RB_FL_SET_RAW.
Definition fl_type.h:130
#define T_REGEXP
Old name of RUBY_T_REGEXP.
Definition value_type.h:77
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:675
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Checks if the given object is of given kind.
Definition error.c:1380
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1428
VALUE rb_eStopIteration
StopIteration exception.
Definition enumerator.c:181
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.
Definition error.c:1481
VALUE rb_eException
Mother of all exceptions.
Definition error.c:1422
VALUE rb_obj_alloc(VALUE klass)
Allocates an instance of the given class.
Definition object.c:2097
VALUE rb_cRactor
Ractor class.
Definition ractor.c:22
VALUE rb_stdin
STDIN constant.
Definition io.c:201
VALUE rb_stderr
STDERR constant.
Definition io.c:201
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
Definition globals.h:172
VALUE rb_cBasicObject
BasicObject class.
Definition object.c:64
VALUE rb_obj_clone(VALUE obj)
Produces a shallow copy of the given object.
Definition object.c:521
VALUE rb_stdout
STDOUT constant.
Definition io.c:201
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:615
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition gc.h:603
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition vm_eval.c:1099
VALUE rb_obj_is_proc(VALUE recv)
Queries if the given object is a proc.
Definition proc.c:119
#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.
Definition string.h:1670
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition string.c:1462
VALUE rb_mutex_new(void)
Creates a mutex.
VALUE rb_mutex_synchronize(VALUE mutex, VALUE(*func)(VALUE arg), VALUE arg)
Obtains the lock, runs the passed function, and releases the lock when it completes.
void rb_unblock_function_t(void *)
This is the type of UBFs.
Definition thread.h:336
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.
Definition thread.c:1452
VALUE rb_const_get(VALUE space, ID name)
Identical to rb_const_defined(), except it returns the actual defined value.
Definition variable.c:3135
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.
Definition variable.c:1844
VALUE rb_autoload_load(VALUE space, ID name)
Kicks the autoload procedure as if it was "touched".
Definition variable.c:2970
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1284
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition symbol.c:1133
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
Definition string.c:12489
int len
Length of the buffer.
Definition io.h:8
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.
Definition ractor.c:3753
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...
Definition ractor.c:3120
struct rb_ractor_local_key_struct * rb_ractor_local_key_t
(Opaque) struct that holds a ractor-local storage key.
Definition ractor.h:42
void * rb_ractor_local_storage_ptr(rb_ractor_local_key_t key)
Identical to rb_ractor_local_storage_value() except the return type.
Definition ractor.c:3862
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.
Definition ractor.c:3874
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().
Definition ractor.c:3764
VALUE rb_ractor_stdin(void)
Queries the standard input of the current Ractor that is calling this function.
Definition ractor.c:2733
static bool rb_ractor_shareable_p(VALUE obj)
Queries if multiple Ractors can share the passed object or not.
Definition ractor.h:249
void rb_ractor_stderr_set(VALUE io)
Assigns an IO to the standard error of the Ractor that is calling this function.
Definition ractor.c:2793
void rb_ractor_local_storage_value_set(rb_ractor_local_key_t key, VALUE val)
Associates the passed value to the passed key.
Definition ractor.c:3856
bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
Queries the key.
Definition ractor.c:3845
#define RB_OBJ_SHAREABLE_P(obj)
Queries if the passed object has previously classified as shareable or not.
Definition ractor.h:235
VALUE rb_ractor_make_shareable(VALUE obj)
Destructively transforms the passed object so that multiple Ractors can share it.
Definition ractor.c:3111
rb_ractor_local_key_t rb_ractor_local_storage_value_newkey(void)
Issues a new key.
Definition ractor.c:3773
void rb_ractor_stdout_set(VALUE io)
Assigns an IO to the standard output of the Ractor that is calling this function.
Definition ractor.c:2781
void rb_ractor_stdin_set(VALUE io)
Assigns an IO to the standard input of the Ractor that is calling this function.
Definition ractor.c:2769
VALUE rb_ractor_local_storage_value(rb_ractor_local_key_t key)
Queries the key.
Definition ractor.c:3833
#define RB_NOGVL_UBF_ASYNC_SAFE
Passing this flag to rb_nogvl() indicates that the passed UBF is async-signal-safe.
Definition thread.h:60
#define RB_NOGVL_INTR_FAIL
Passing this flag to rb_nogvl() prevents it from checking interrupts.
Definition thread.h:48
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...
Definition thread.c:1534
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
Shim for block function parameters.
Definition iterator.h:58
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1354
rb_block_call_func * rb_block_call_func_t
Shorthand type that represents an iterator-written-in-C function pointer.
Definition iterator.h:88
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
VALUE rb_proc_new(type *q, VALUE w)
Creates a rb_cProc instance.
VALUE type(ANYARGS)
ANYARGS-ed function type.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
void rb_ivar_foreach(VALUE q, int_type *w, VALUE e)
Iteration over each instance variable of the object.
VALUE rb_rescue2(type *q, VALUE w, type *e, VALUE r,...)
An equivalent of rescue clause.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:281
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:386
#define RARRAY_AREF(a, i)
Definition rarray.h:403
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
Definition rarray.h:52
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
Definition rbasic.h:150
#define DATA_PTR(obj)
Convenient getter macro.
Definition rdata.h:67
#define RHASH_SET_IFNONE(h, ifnone)
Destructively updates the default value of the hash.
Definition rhash.h:92
#define RHASH_IFNONE(h)
Definition rhash.h:59
static VALUE * ROBJECT_IVPTR(VALUE obj)
Queries the instance variables.
Definition robject.h:126
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
static bool RTYPEDDATA_P(VALUE obj)
Checks whether the passed object is RTypedData or RData.
Definition rtypeddata.h:579
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:449
#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...
Definition rtypeddata.h:497
static const struct rb_data_type_struct * RTYPEDDATA_TYPE(VALUE obj)
Queries for the type of given object.
Definition rtypeddata.h:602
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.
Definition rtypeddata.h:200
Type that defines a ractor-local storage.
Definition ractor.h:21
void(* free)(void *ptr)
A function to destruct a ractor-local storage.
Definition ractor.h:37
void(* mark)(void *ptr)
A function to mark a ractor-local storage.
Definition ractor.h:29
Definition st.h:79
Definition string.c:8284
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.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376