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