Ruby  3.1.4p223 (2023-03-30 revision HEAD)
ractor.c
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 "vm_sync.h"
9 #include "ractor_core.h"
10 #include "internal/complex.h"
11 #include "internal/error.h"
12 #include "internal/hash.h"
13 #include "internal/rational.h"
14 #include "internal/struct.h"
15 #include "internal/thread.h"
16 #include "variable.h"
17 #include "gc.h"
18 #include "transient_heap.h"
19 #include "yjit.h"
20 
22 
23 VALUE rb_eRactorUnsafeError;
24 VALUE rb_eRactorIsolationError;
25 static VALUE rb_eRactorError;
26 static VALUE rb_eRactorRemoteError;
27 static VALUE rb_eRactorMovedError;
28 static VALUE rb_eRactorClosedError;
29 static VALUE rb_cRactorMovedObject;
30 
31 static void vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line);
32 
33 static void
34 ASSERT_ractor_unlocking(rb_ractor_t *r)
35 {
36 #if RACTOR_CHECK_MODE > 0
37  // GET_EC is NULL in an MJIT worker
38  if (rb_current_execution_context(false) != NULL && r->sync.locked_by == rb_ractor_self(GET_RACTOR())) {
39  rb_bug("recursive ractor locking");
40  }
41 #endif
42 }
43 
44 static void
45 ASSERT_ractor_locking(rb_ractor_t *r)
46 {
47 #if RACTOR_CHECK_MODE > 0
48  // GET_EC is NULL in an MJIT worker
49  if (rb_current_execution_context(false) != NULL && r->sync.locked_by != rb_ractor_self(GET_RACTOR())) {
50  rp(r->sync.locked_by);
51  rb_bug("ractor lock is not acquired.");
52  }
53 #endif
54 }
55 
56 static void
57 ractor_lock(rb_ractor_t *r, const char *file, int line)
58 {
59  RUBY_DEBUG_LOG2(file, line, "locking r:%u%s", r->pub.id, GET_RACTOR() == r ? " (self)" : "");
60 
61  ASSERT_ractor_unlocking(r);
62  rb_native_mutex_lock(&r->sync.lock);
63 
64 #if RACTOR_CHECK_MODE > 0
65  if (rb_current_execution_context(false) != NULL) { // GET_EC is NULL in an MJIT worker
66  r->sync.locked_by = rb_ractor_self(GET_RACTOR());
67  }
68 #endif
69 
70  RUBY_DEBUG_LOG2(file, line, "locked r:%u%s", r->pub.id, GET_RACTOR() == r ? " (self)" : "");
71 }
72 
73 static void
74 ractor_lock_self(rb_ractor_t *cr, const char *file, int line)
75 {
76  VM_ASSERT(cr == GET_RACTOR());
77  VM_ASSERT(cr->sync.locked_by != cr->pub.self);
78  ractor_lock(cr, file, line);
79 }
80 
81 static void
82 ractor_unlock(rb_ractor_t *r, const char *file, int line)
83 {
84  ASSERT_ractor_locking(r);
85 #if RACTOR_CHECK_MODE > 0
86  r->sync.locked_by = Qnil;
87 #endif
88  rb_native_mutex_unlock(&r->sync.lock);
89 
90  RUBY_DEBUG_LOG2(file, line, "r:%u%s", r->pub.id, GET_RACTOR() == r ? " (self)" : "");
91 }
92 
93 static void
94 ractor_unlock_self(rb_ractor_t *cr, const char *file, int line)
95 {
96  VM_ASSERT(cr == GET_RACTOR());
97  VM_ASSERT(cr->sync.locked_by == cr->pub.self);
98  ractor_unlock(cr, file, line);
99 }
100 
101 #define RACTOR_LOCK(r) ractor_lock(r, __FILE__, __LINE__)
102 #define RACTOR_UNLOCK(r) ractor_unlock(r, __FILE__, __LINE__)
103 #define RACTOR_LOCK_SELF(r) ractor_lock_self(r, __FILE__, __LINE__)
104 #define RACTOR_UNLOCK_SELF(r) ractor_unlock_self(r, __FILE__, __LINE__)
105 
106 static void
107 ractor_cond_wait(rb_ractor_t *r)
108 {
109 #if RACTOR_CHECK_MODE > 0
110  VALUE locked_by = r->sync.locked_by;
111  r->sync.locked_by = Qnil;
112 #endif
113  rb_native_cond_wait(&r->sync.cond, &r->sync.lock);
114 
115 #if RACTOR_CHECK_MODE > 0
116  r->sync.locked_by = locked_by;
117 #endif
118 }
119 
120 static const char *
121 ractor_status_str(enum ractor_status status)
122 {
123  switch (status) {
124  case ractor_created: return "created";
125  case ractor_running: return "running";
126  case ractor_blocking: return "blocking";
127  case ractor_terminated: return "terminated";
128  }
129  rb_bug("unreachable");
130 }
131 
132 static void
133 ractor_status_set(rb_ractor_t *r, enum ractor_status status)
134 {
135  RUBY_DEBUG_LOG("r:%u [%s]->[%s]", r->pub.id, ractor_status_str(r->status_), ractor_status_str(status));
136 
137  // check 1
138  if (r->status_ != ractor_created) {
139  VM_ASSERT(r == GET_RACTOR()); // only self-modification is allowed.
140  ASSERT_vm_locking();
141  }
142 
143  // check2: transition check. assume it will be vanished on non-debug build.
144  switch (r->status_) {
145  case ractor_created:
146  VM_ASSERT(status == ractor_blocking);
147  break;
148  case ractor_running:
149  VM_ASSERT(status == ractor_blocking||
150  status == ractor_terminated);
151  break;
152  case ractor_blocking:
153  VM_ASSERT(status == ractor_running);
154  break;
155  case ractor_terminated:
156  VM_ASSERT(0); // unreachable
157  break;
158  }
159 
160  r->status_ = status;
161 }
162 
163 static bool
164 ractor_status_p(rb_ractor_t *r, enum ractor_status status)
165 {
166  return rb_ractor_status_p(r, status);
167 }
168 
169 static struct rb_ractor_basket *ractor_queue_at(struct rb_ractor_queue *rq, int i);
170 
171 static void
172 ractor_queue_mark(struct rb_ractor_queue *rq)
173 {
174  for (int i=0; i<rq->cnt; i++) {
175  struct rb_ractor_basket *b = ractor_queue_at(rq, i);
176  rb_gc_mark(b->v);
177  rb_gc_mark(b->sender);
178  }
179 }
180 
181 static void ractor_local_storage_mark(rb_ractor_t *r);
182 static void ractor_local_storage_free(rb_ractor_t *r);
183 
184 static void
185 ractor_mark(void *ptr)
186 {
187  rb_ractor_t *r = (rb_ractor_t *)ptr;
188 
189  ractor_queue_mark(&r->sync.incoming_queue);
190  rb_gc_mark(r->sync.wait.taken_basket.v);
191  rb_gc_mark(r->sync.wait.taken_basket.sender);
192  rb_gc_mark(r->sync.wait.yielded_basket.v);
193  rb_gc_mark(r->sync.wait.yielded_basket.sender);
194  rb_gc_mark(r->receiving_mutex);
195 
196  rb_gc_mark(r->loc);
197  rb_gc_mark(r->name);
198  rb_gc_mark(r->r_stdin);
199  rb_gc_mark(r->r_stdout);
200  rb_gc_mark(r->r_stderr);
201  rb_hook_list_mark(&r->pub.hooks);
202 
203  if (r->threads.cnt > 0) {
204  rb_thread_t *th = 0;
205  list_for_each(&r->threads.set, th, lt_node) {
206  VM_ASSERT(th != NULL);
207  rb_gc_mark(th->self);
208  }
209  }
210 
211  ractor_local_storage_mark(r);
212 }
213 
214 static void
215 ractor_queue_free(struct rb_ractor_queue *rq)
216 {
217  free(rq->baskets);
218 }
219 
220 static void
221 ractor_waiting_list_free(struct rb_ractor_waiting_list *wl)
222 {
223  free(wl->ractors);
224 }
225 
226 static void
227 ractor_free(void *ptr)
228 {
229  rb_ractor_t *r = (rb_ractor_t *)ptr;
230  rb_native_mutex_destroy(&r->sync.lock);
231  rb_native_cond_destroy(&r->sync.cond);
232  ractor_queue_free(&r->sync.incoming_queue);
233  ractor_waiting_list_free(&r->sync.taking_ractors);
234  ractor_local_storage_free(r);
235  rb_hook_list_free(&r->pub.hooks);
236  ruby_xfree(r);
237 }
238 
239 static size_t
240 ractor_queue_memsize(const struct rb_ractor_queue *rq)
241 {
242  return sizeof(struct rb_ractor_basket) * rq->size;
243 }
244 
245 static size_t
246 ractor_waiting_list_memsize(const struct rb_ractor_waiting_list *wl)
247 {
248  return sizeof(rb_ractor_t *) * wl->size;
249 }
250 
251 static size_t
252 ractor_memsize(const void *ptr)
253 {
254  rb_ractor_t *r = (rb_ractor_t *)ptr;
255 
256  // TODO
257  return sizeof(rb_ractor_t) +
258  ractor_queue_memsize(&r->sync.incoming_queue) +
259  ractor_waiting_list_memsize(&r->sync.taking_ractors);
260 }
261 
262 static const rb_data_type_t ractor_data_type = {
263  "ractor",
264  {
265  ractor_mark,
266  ractor_free,
267  ractor_memsize,
268  NULL, // update
269  },
270  0, 0, RUBY_TYPED_FREE_IMMEDIATELY /* | RUBY_TYPED_WB_PROTECTED */
271 };
272 
273 bool
274 rb_ractor_p(VALUE gv)
275 {
276  if (rb_typeddata_is_kind_of(gv, &ractor_data_type)) {
277  return true;
278  }
279  else {
280  return false;
281  }
282 }
283 
284 static inline rb_ractor_t *
285 RACTOR_PTR(VALUE self)
286 {
287  VM_ASSERT(rb_ractor_p(self));
288 
289  rb_ractor_t *r = DATA_PTR(self);
290  // TODO: check
291  return r;
292 }
293 
294 static rb_atomic_t ractor_last_id;
295 
296 #if RACTOR_CHECK_MODE > 0
297 MJIT_FUNC_EXPORTED uint32_t
298 rb_ractor_current_id(void)
299 {
300  if (GET_THREAD()->ractor == NULL) {
301  return 1; // main ractor
302  }
303  else {
304  return rb_ractor_id(GET_RACTOR());
305  }
306 }
307 #endif
308 
309 static void
310 ractor_queue_setup(struct rb_ractor_queue *rq)
311 {
312  rq->size = 2;
313  rq->cnt = 0;
314  rq->start = 0;
315  rq->baskets = malloc(sizeof(struct rb_ractor_basket) * rq->size);
316 }
317 
318 static struct rb_ractor_basket *
319 ractor_queue_at(struct rb_ractor_queue *rq, int i)
320 {
321  return &rq->baskets[(rq->start + i) % rq->size];
322 }
323 
324 static void
325 ractor_queue_advance(struct rb_ractor_queue *rq)
326 {
327  ASSERT_ractor_locking(GET_RACTOR());
328 
329  if (rq->reserved_cnt == 0) {
330  rq->cnt--;
331  rq->start = (rq->start + 1) % rq->size;
332  rq->serial++;
333  }
334  else {
335  ractor_queue_at(rq, 0)->type = basket_type_deleted;
336  }
337 }
338 
339 static bool
340 ractor_queue_skip_p(struct rb_ractor_queue *rq, int i)
341 {
342  struct rb_ractor_basket *b = ractor_queue_at(rq, i);
343  return b->type == basket_type_deleted ||
344  b->type == basket_type_reserved;
345 }
346 
347 static void
348 ractor_queue_compact(rb_ractor_t *r, struct rb_ractor_queue *rq)
349 {
350  ASSERT_ractor_locking(r);
351 
352  while (rq->cnt > 0 && ractor_queue_at(rq, 0)->type == basket_type_deleted) {
353  ractor_queue_advance(rq);
354  }
355 }
356 
357 static bool
358 ractor_queue_empty_p(rb_ractor_t *r, struct rb_ractor_queue *rq)
359 {
360  ASSERT_ractor_locking(r);
361 
362  if (rq->cnt == 0) {
363  return true;
364  }
365 
366  ractor_queue_compact(r, rq);
367 
368  for (int i=0; i<rq->cnt; i++) {
369  if (!ractor_queue_skip_p(rq, i)) {
370  return false;
371  }
372  }
373 
374  return true;
375 }
376 
377 static bool
378 ractor_queue_deq(rb_ractor_t *r, struct rb_ractor_queue *rq, struct rb_ractor_basket *basket)
379 {
380  bool found = false;
381 
382  RACTOR_LOCK(r);
383  {
384  if (!ractor_queue_empty_p(r, rq)) {
385  for (int i=0; i<rq->cnt; i++) {
386  if (!ractor_queue_skip_p(rq, i)) {
387  struct rb_ractor_basket *b = ractor_queue_at(rq, i);
388  *basket = *b;
389 
390  // remove from queue
391  b->type = basket_type_deleted;
392  ractor_queue_compact(r, rq);
393  found = true;
394  break;
395  }
396  }
397  }
398  }
399  RACTOR_UNLOCK(r);
400 
401  return found;
402 }
403 
404 static void
405 ractor_queue_enq(rb_ractor_t *r, struct rb_ractor_queue *rq, struct rb_ractor_basket *basket)
406 {
407  ASSERT_ractor_locking(r);
408 
409  if (rq->size <= rq->cnt) {
410  rq->baskets = realloc(rq->baskets, sizeof(struct rb_ractor_basket) * rq->size * 2);
411  for (int i=rq->size - rq->start; i<rq->cnt; i++) {
412  rq->baskets[i + rq->start] = rq->baskets[i + rq->start - rq->size];
413  }
414  rq->size *= 2;
415  }
416  rq->baskets[(rq->start + rq->cnt++) % rq->size] = *basket;
417  // fprintf(stderr, "%s %p->cnt:%d\n", RUBY_FUNCTION_NAME_STRING, (void *)rq, rq->cnt);
418 }
419 
420 static void
421 ractor_basket_clear(struct rb_ractor_basket *b)
422 {
423  b->type = basket_type_none;
424  b->v = Qfalse;
425  b->sender = Qfalse;
426 }
427 
428 static VALUE ractor_reset_belonging(VALUE obj); // in this file
429 
430 static VALUE
431 ractor_basket_value(struct rb_ractor_basket *b)
432 {
433  switch (b->type) {
434  case basket_type_ref:
435  break;
436  case basket_type_copy:
437  case basket_type_move:
438  case basket_type_will:
439  b->type = basket_type_ref;
440  b->v = ractor_reset_belonging(b->v);
441  break;
442  default:
443  rb_bug("unreachable");
444  }
445 
446  return b->v;
447 }
448 
449 static VALUE
450 ractor_basket_accept(struct rb_ractor_basket *b)
451 {
452  VALUE v = ractor_basket_value(b);
453 
454  if (b->exception) {
455  VALUE cause = v;
456  VALUE err = rb_exc_new_cstr(rb_eRactorRemoteError, "thrown by remote Ractor.");
457  rb_ivar_set(err, rb_intern("@ractor"), b->sender);
458  ractor_basket_clear(b);
459  rb_ec_setup_exception(NULL, err, cause);
460  rb_exc_raise(err);
461  }
462 
463  ractor_basket_clear(b);
464  return v;
465 }
466 
467 static void
468 ractor_recursive_receive_if(rb_ractor_t *r)
469 {
470  if (r->receiving_mutex && rb_mutex_owned_p(r->receiving_mutex)) {
471  rb_raise(rb_eRactorError, "can not call receive/receive_if recursively");
472  }
473 }
474 
475 static VALUE
476 ractor_try_receive(rb_execution_context_t *ec, rb_ractor_t *r)
477 {
478  struct rb_ractor_queue *rq = &r->sync.incoming_queue;
479  struct rb_ractor_basket basket;
480 
481  ractor_recursive_receive_if(r);
482 
483  if (ractor_queue_deq(r, rq, &basket) == false) {
484  if (r->sync.incoming_port_closed) {
485  rb_raise(rb_eRactorClosedError, "The incoming port is already closed");
486  }
487  else {
488  return Qundef;
489  }
490  }
491 
492  return ractor_basket_accept(&basket);
493 }
494 
495 static bool
496 ractor_sleeping_by(const rb_ractor_t *r, enum ractor_wait_status wait_status)
497 {
498  return (r->sync.wait.status & wait_status) && r->sync.wait.wakeup_status == wakeup_none;
499 }
500 
501 static bool
502 ractor_wakeup(rb_ractor_t *r, enum ractor_wait_status wait_status, enum ractor_wakeup_status wakeup_status)
503 {
504  ASSERT_ractor_locking(r);
505 
506  // fprintf(stderr, "%s r:%p status:%s/%s wakeup_status:%s/%s\n", RUBY_FUNCTION_NAME_STRING, (void *)r,
507  // wait_status_str(r->sync.wait.status), wait_status_str(wait_status),
508  // wakeup_status_str(r->sync.wait.wakeup_status), wakeup_status_str(wakeup_status));
509 
510  if (ractor_sleeping_by(r, wait_status)) {
511  r->sync.wait.wakeup_status = wakeup_status;
512  rb_native_cond_signal(&r->sync.cond);
513  return true;
514  }
515  else {
516  return false;
517  }
518 }
519 
520 static void *
521 ractor_sleep_wo_gvl(void *ptr)
522 {
523  rb_ractor_t *cr = ptr;
524  RACTOR_LOCK_SELF(cr);
525  {
526  VM_ASSERT(cr->sync.wait.status != wait_none);
527  if (cr->sync.wait.wakeup_status == wakeup_none) {
528  ractor_cond_wait(cr);
529  }
530  cr->sync.wait.status = wait_none;
531  }
532  RACTOR_UNLOCK_SELF(cr);
533  return NULL;
534 }
535 
536 static void
537 ractor_sleep_interrupt(void *ptr)
538 {
539  rb_ractor_t *r = ptr;
540 
541  RACTOR_LOCK(r);
542  {
543  ractor_wakeup(r, wait_receiving | wait_taking | wait_yielding, wakeup_by_interrupt);
544  }
545  RACTOR_UNLOCK(r);
546 }
547 
548 #if defined(USE_RUBY_DEBUG_LOG) && USE_RUBY_DEBUG_LOG
549 static const char *
550 wait_status_str(enum ractor_wait_status wait_status)
551 {
552  switch ((int)wait_status) {
553  case wait_none: return "none";
554  case wait_receiving: return "receiving";
555  case wait_taking: return "taking";
556  case wait_yielding: return "yielding";
557  case wait_receiving|wait_taking: return "receiving|taking";
558  case wait_receiving|wait_yielding: return "receiving|yielding";
559  case wait_taking|wait_yielding: return "taking|yielding";
560  case wait_receiving|wait_taking|wait_yielding: return "receiving|taking|yielding";
561  }
562  rb_bug("unreachable");
563 }
564 
565 static const char *
566 wakeup_status_str(enum ractor_wakeup_status wakeup_status)
567 {
568  switch (wakeup_status) {
569  case wakeup_none: return "none";
570  case wakeup_by_send: return "by_send";
571  case wakeup_by_yield: return "by_yield";
572  case wakeup_by_take: return "by_take";
573  case wakeup_by_close: return "by_close";
574  case wakeup_by_interrupt: return "by_interrupt";
575  case wakeup_by_retry: return "by_retry";
576  }
577  rb_bug("unreachable");
578 }
579 #endif // USE_RUBY_DEBUG_LOG
580 
581 static void
582 ractor_sleep(rb_execution_context_t *ec, rb_ractor_t *cr)
583 {
584  VM_ASSERT(GET_RACTOR() == cr);
585  VM_ASSERT(cr->sync.wait.status != wait_none);
586  // fprintf(stderr, "%s r:%p status:%s, wakeup_status:%s\n", RUBY_FUNCTION_NAME_STRING, (void *)cr,
587  // wait_status_str(cr->sync.wait.status), wakeup_status_str(cr->sync.wait.wakeup_status));
588 
589  RACTOR_UNLOCK(cr);
590  {
591  rb_nogvl(ractor_sleep_wo_gvl, cr,
592  ractor_sleep_interrupt, cr,
594  }
595  RACTOR_LOCK(cr);
596 
597  // rb_nogvl() can be canceled by interrupts
598  if (cr->sync.wait.status != wait_none) {
599  cr->sync.wait.status = wait_none;
600  cr->sync.wait.wakeup_status = wakeup_by_interrupt;
601 
602  RACTOR_UNLOCK(cr);
604  RACTOR_LOCK(cr); // reachable?
605  }
606 }
607 
608 static void
609 ractor_register_taking(rb_ractor_t *r, rb_ractor_t *cr)
610 {
611  VM_ASSERT(cr == GET_RACTOR());
612  bool retry_try = false;
613 
614  RACTOR_LOCK(r);
615  {
616  if (ractor_sleeping_by(r, wait_yielding)) {
617  // already waiting for yielding. retry try_take.
618  retry_try = true;
619  }
620  else {
621  // insert cr into taking list
622  struct rb_ractor_waiting_list *wl = &r->sync.taking_ractors;
623 
624  for (int i=0; i<wl->cnt; i++) {
625  if (wl->ractors[i] == cr) {
626  // TODO: make it clean code.
627  rb_native_mutex_unlock(&r->sync.lock);
628  rb_raise(rb_eRuntimeError, "Already another thread of same ractor is waiting.");
629  }
630  }
631 
632  if (wl->size == 0) {
633  wl->size = 1;
634  wl->ractors = malloc(sizeof(rb_ractor_t *) * wl->size);
635  if (wl->ractors == NULL) rb_bug("can't allocate buffer");
636  }
637  else if (wl->size <= wl->cnt + 1) {
638  wl->size *= 2;
639  wl->ractors = realloc(wl->ractors, sizeof(rb_ractor_t *) * wl->size);
640  if (wl->ractors == NULL) rb_bug("can't re-allocate buffer");
641  }
642  wl->ractors[wl->cnt++] = cr;
643  }
644  }
645  RACTOR_UNLOCK(r);
646 
647  if (retry_try) {
648  RACTOR_LOCK(cr);
649  {
650  if (cr->sync.wait.wakeup_status == wakeup_none) {
651  VM_ASSERT(cr->sync.wait.status != wait_none);
652 
653  cr->sync.wait.wakeup_status = wakeup_by_retry;
654  cr->sync.wait.status = wait_none;
655  }
656  }
657  RACTOR_UNLOCK(cr);
658  }
659 }
660 
661 static void
662 ractor_waiting_list_del(rb_ractor_t *r, struct rb_ractor_waiting_list *wl, rb_ractor_t *wr)
663 {
664  RACTOR_LOCK(r);
665  {
666  int pos = -1;
667  for (int i=0; i<wl->cnt; i++) {
668  if (wl->ractors[i] == wr) {
669  pos = i;
670  break;
671  }
672  }
673  if (pos >= 0) { // found
674  wl->cnt--;
675  for (int i=pos; i<wl->cnt; i++) {
676  wl->ractors[i] = wl->ractors[i+1];
677  }
678  }
679  }
680  RACTOR_UNLOCK(r);
681 }
682 
683 static rb_ractor_t *
684 ractor_waiting_list_shift(rb_ractor_t *r, struct rb_ractor_waiting_list *wl)
685 {
686  ASSERT_ractor_locking(r);
687  VM_ASSERT(&r->sync.taking_ractors == wl);
688 
689  if (wl->cnt > 0) {
690  rb_ractor_t *tr = wl->ractors[0];
691  for (int i=1; i<wl->cnt; i++) {
692  wl->ractors[i-1] = wl->ractors[i];
693  }
694  wl->cnt--;
695  return tr;
696  }
697  else {
698  return NULL;
699  }
700 }
701 
702 static void
703 ractor_receive_wait(rb_execution_context_t *ec, rb_ractor_t *cr)
704 {
705  VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
706  ractor_recursive_receive_if(cr);
707 
708  RACTOR_LOCK(cr);
709  {
710  if (ractor_queue_empty_p(cr, &cr->sync.incoming_queue)) {
711  VM_ASSERT(cr->sync.wait.status == wait_none);
712  cr->sync.wait.status = wait_receiving;
713  cr->sync.wait.wakeup_status = wakeup_none;
714  ractor_sleep(ec, cr);
715  cr->sync.wait.wakeup_status = wakeup_none;
716  }
717  }
718  RACTOR_UNLOCK(cr);
719 }
720 
721 static VALUE
722 ractor_receive(rb_execution_context_t *ec, rb_ractor_t *cr)
723 {
724  VM_ASSERT(cr == rb_ec_ractor_ptr(ec));
725  VALUE v;
726 
727  while ((v = ractor_try_receive(ec, cr)) == Qundef) {
728  ractor_receive_wait(ec, cr);
729  }
730 
731  return v;
732 }
733 
734 #if 0
735 // for debug
736 static const char *
737 basket_type_name(enum rb_ractor_basket_type type)
738 {
739  switch (type) {
740 #define T(t) case basket_type_##t: return #t
741  T(none);
742  T(ref);
743  T(copy);
744  T(move);
745  T(will);
746  T(deleted);
747  T(reserved);
748  default: rb_bug("unreachable");
749  }
750 }
751 
752 static void
753 rq_dump(struct rb_ractor_queue *rq)
754 {
755  bool bug = false;
756  for (int i=0; i<rq->cnt; i++) {
757  struct rb_ractor_basket *b = ractor_queue_at(rq, i);
758  fprintf(stderr, "%d (start:%d) type:%s %p %s\n", i, rq->start, basket_type_name(b->type),
759  (void *)b, RSTRING_PTR(RARRAY_AREF(b->v, 1)));
760  if (b->type == basket_type_reserved) bug = true;
761  }
762  if (bug) rb_bug("!!");
763 }
764 #endif
765 
767  rb_ractor_t *cr;
768  struct rb_ractor_queue *rq;
769  VALUE v;
770  int index;
771  bool success;
772 };
773 
774 static void
775 ractor_receive_if_lock(rb_ractor_t *cr)
776 {
777  VALUE m = cr->receiving_mutex;
778  if (m == Qfalse) {
779  m = cr->receiving_mutex = rb_mutex_new();
780  }
781  rb_mutex_lock(m);
782 }
783 
784 static VALUE
785 receive_if_body(VALUE ptr)
786 {
787  struct receive_block_data *data = (struct receive_block_data *)ptr;
788 
789  ractor_receive_if_lock(data->cr);
790  VALUE block_result = rb_yield(data->v);
791 
792  RACTOR_LOCK_SELF(data->cr);
793  {
794  struct rb_ractor_basket *b = ractor_queue_at(data->rq, data->index);
795  VM_ASSERT(b->type == basket_type_reserved);
796  data->rq->reserved_cnt--;
797 
798  if (RTEST(block_result)) {
799  b->type = basket_type_deleted;
800  ractor_queue_compact(data->cr, data->rq);
801  }
802  else {
803  b->type = basket_type_ref;
804  }
805  }
806  RACTOR_UNLOCK_SELF(data->cr);
807 
808  data->success = true;
809 
810  if (RTEST(block_result)) {
811  return data->v;
812  }
813  else {
814  return Qundef;
815  }
816 }
817 
818 static VALUE
819 receive_if_ensure(VALUE v)
820 {
821  struct receive_block_data *data = (struct receive_block_data *)v;
822 
823  if (!data->success) {
824  RACTOR_LOCK_SELF(data->cr);
825  {
826  struct rb_ractor_basket *b = ractor_queue_at(data->rq, data->index);
827  VM_ASSERT(b->type == basket_type_reserved);
828  b->type = basket_type_deleted;
829  data->rq->reserved_cnt--;
830  }
831  RACTOR_UNLOCK_SELF(data->cr);
832  }
833 
834  rb_mutex_unlock(data->cr->receiving_mutex);
835  return Qnil;
836 }
837 
838 static VALUE
839 ractor_receive_if(rb_execution_context_t *ec, VALUE crv, VALUE b)
840 {
841  if (!RTEST(b)) rb_raise(rb_eArgError, "no block given");
842 
843  rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
844  unsigned int serial = (unsigned int)-1;
845  int index = 0;
846  struct rb_ractor_queue *rq = &cr->sync.incoming_queue;
847 
848  while (1) {
849  VALUE v = Qundef;
850 
851  ractor_receive_wait(ec, cr);
852 
853  RACTOR_LOCK_SELF(cr);
854  {
855  if (serial != rq->serial) {
856  serial = rq->serial;
857  index = 0;
858  }
859 
860  // check newer version
861  for (int i=index; i<rq->cnt; i++) {
862  if (!ractor_queue_skip_p(rq, i)) {
863  struct rb_ractor_basket *b = ractor_queue_at(rq, i);
864  v = ractor_basket_value(b);
865  b->type = basket_type_reserved;
866  rq->reserved_cnt++;
867  index = i;
868  break;
869  }
870  }
871  }
872  RACTOR_UNLOCK_SELF(cr);
873 
874  if (v != Qundef) {
875  struct receive_block_data data = {
876  .cr = cr,
877  .rq = rq,
878  .v = v,
879  .index = index,
880  .success = false,
881  };
882 
883  VALUE result = rb_ensure(receive_if_body, (VALUE)&data,
884  receive_if_ensure, (VALUE)&data);
885 
886  if (result != Qundef) return result;
887  index++;
888  }
889  }
890 }
891 
892 static void
893 ractor_send_basket(rb_execution_context_t *ec, rb_ractor_t *r, struct rb_ractor_basket *b)
894 {
895  bool closed = false;
896  struct rb_ractor_queue *rq = &r->sync.incoming_queue;
897 
898  RACTOR_LOCK(r);
899  {
900  if (r->sync.incoming_port_closed) {
901  closed = true;
902  }
903  else {
904  ractor_queue_enq(r, rq, b);
905  if (ractor_wakeup(r, wait_receiving, wakeup_by_send)) {
906  RUBY_DEBUG_LOG("wakeup");
907  }
908  }
909  }
910  RACTOR_UNLOCK(r);
911 
912  if (closed) {
913  rb_raise(rb_eRactorClosedError, "The incoming-port is already closed");
914  }
915 }
916 
917 static VALUE ractor_move(VALUE obj); // in this file
918 static VALUE ractor_copy(VALUE obj); // in this file
919 
920 static void
921 ractor_basket_setup(rb_execution_context_t *ec, struct rb_ractor_basket *basket, VALUE obj, VALUE move, bool exc, bool is_will, bool is_yield)
922 {
923  basket->sender = rb_ec_ractor_ptr(ec)->pub.self;
924  basket->exception = exc;
925 
926  if (is_will) {
927  basket->type = basket_type_will;
928  basket->v = obj;
929  }
930  else if (rb_ractor_shareable_p(obj)) {
931  basket->type = basket_type_ref;
932  basket->v = obj;
933  }
934  else if (!RTEST(move)) {
935  basket->v = ractor_copy(obj);
936  basket->type = basket_type_copy;
937  }
938  else {
939  basket->type = basket_type_move;
940 
941  if (is_yield) {
942  basket->v = obj; // call ractor_move() when yielding timing.
943  }
944  else {
945  basket->v = ractor_move(obj);
946  }
947  }
948 }
949 
950 static VALUE
951 ractor_send(rb_execution_context_t *ec, rb_ractor_t *r, VALUE obj, VALUE move)
952 {
953  struct rb_ractor_basket basket;
954  ractor_basket_setup(ec, &basket, obj, move, false, false, false);
955  ractor_send_basket(ec, r, &basket);
956  return r->pub.self;
957 }
958 
959 static VALUE
960 ractor_try_take(rb_execution_context_t *ec, rb_ractor_t *r)
961 {
962  struct rb_ractor_basket basket = {
963  .type = basket_type_none,
964  };
965  bool closed = false;
966 
967  RACTOR_LOCK(r);
968  {
969  if (ractor_sleeping_by(r, wait_yielding)) {
970  MAYBE_UNUSED(bool) wakeup_result;
971  VM_ASSERT(r->sync.wait.yielded_basket.type != basket_type_none);
972 
973  if (r->sync.wait.yielded_basket.type == basket_type_move) {
974  wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_retry);
975  }
976  else {
977  wakeup_result = ractor_wakeup(r, wait_yielding, wakeup_by_take);
978  basket = r->sync.wait.yielded_basket;
979  ractor_basket_clear(&r->sync.wait.yielded_basket);
980  }
981  VM_ASSERT(wakeup_result);
982  }
983  else if (r->sync.outgoing_port_closed) {
984  closed = true;
985  }
986  }
987  RACTOR_UNLOCK(r);
988 
989  if (basket.type == basket_type_none) {
990  if (closed) {
991  rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
992  }
993  else {
994  return Qundef;
995  }
996  }
997  else {
998  return ractor_basket_accept(&basket);
999  }
1000 }
1001 
1002 static VALUE
1003 ractor_yield_move_body(VALUE v)
1004 {
1005  return ractor_move(v);
1006 }
1007 
1008 static bool
1009 ractor_try_yield(rb_execution_context_t *ec, rb_ractor_t *cr, struct rb_ractor_basket *basket)
1010 {
1011  ASSERT_ractor_unlocking(cr);
1012  VM_ASSERT(basket->type != basket_type_none);
1013 
1014  if (cr->sync.outgoing_port_closed) {
1015  rb_raise(rb_eRactorClosedError, "The outgoing-port is already closed");
1016  }
1017 
1018  rb_ractor_t *r;
1019 
1020  retry_shift:
1021  RACTOR_LOCK(cr);
1022  {
1023  r = ractor_waiting_list_shift(cr, &cr->sync.taking_ractors);
1024  }
1025  RACTOR_UNLOCK(cr);
1026 
1027  if (r) {
1028  bool retry_shift = false;
1029 
1030  RACTOR_LOCK(r);
1031  {
1032  if (ractor_sleeping_by(r, wait_taking)) {
1033  VM_ASSERT(r->sync.wait.taken_basket.type == basket_type_none);
1034 
1035  if (basket->type == basket_type_move) {
1036  enum ractor_wait_status prev_wait_status = r->sync.wait.status;
1037  r->sync.wait.status = wait_moving;
1038 
1039  RACTOR_UNLOCK(r);
1040  {
1041  int state;
1042  VALUE moved_value = rb_protect(ractor_yield_move_body, basket->v, &state);
1043  if (state) {
1044  r->sync.wait.status = prev_wait_status;
1045  rb_jump_tag(state);
1046  }
1047  else {
1048  basket->v = moved_value;
1049  }
1050  }
1051  RACTOR_LOCK(r);
1052 
1053  if (!ractor_wakeup(r, wait_moving, wakeup_by_yield)) {
1054  // terminating?
1055  }
1056  }
1057  else {
1058  ractor_wakeup(r, wait_taking, wakeup_by_yield);
1059  }
1060  r->sync.wait.taken_basket = *basket;
1061  }
1062  else {
1063  retry_shift = true;
1064  }
1065  }
1066  RACTOR_UNLOCK(r);
1067 
1068  if (retry_shift) {
1069  // get candidate take-waiting ractor, but already woke up by another reason.
1070  // retry to check another ractor.
1071  goto retry_shift;
1072  }
1073  else {
1074  return true;
1075  }
1076  }
1077  else {
1078  return false;
1079  }
1080 }
1081 
1082 // select(r1, r2, r3, receive: true, yield: obj)
1083 static VALUE
1084 ractor_select(rb_execution_context_t *ec, const VALUE *rs, const int rs_len, VALUE yielded_value, bool move, VALUE *ret_r)
1085 {
1086  rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1087  VALUE crv = cr->pub.self;
1088  VALUE ret = Qundef;
1089  int i;
1090  bool interrupted = false;
1091  enum ractor_wait_status wait_status = 0;
1092  bool yield_p = (yielded_value != Qundef) ? true : false;
1093  const int alen = rs_len + (yield_p ? 1 : 0);
1094 
1095  struct ractor_select_action {
1096  enum ractor_select_action_type {
1097  ractor_select_action_take,
1098  ractor_select_action_receive,
1099  ractor_select_action_yield,
1100  } type;
1101  VALUE v;
1102  } *actions = ALLOCA_N(struct ractor_select_action, alen);
1103 
1104  VM_ASSERT(cr->sync.wait.status == wait_none);
1105  VM_ASSERT(cr->sync.wait.wakeup_status == wakeup_none);
1106  VM_ASSERT(cr->sync.wait.taken_basket.type == basket_type_none);
1107  VM_ASSERT(cr->sync.wait.yielded_basket.type == basket_type_none);
1108 
1109  // setup actions
1110  for (i=0; i<rs_len; i++) {
1111  VALUE v = rs[i];
1112 
1113  if (v == crv) {
1114  actions[i].type = ractor_select_action_receive;
1115  actions[i].v = Qnil;
1116  wait_status |= wait_receiving;
1117  }
1118  else if (rb_ractor_p(v)) {
1119  actions[i].type = ractor_select_action_take;
1120  actions[i].v = v;
1121  wait_status |= wait_taking;
1122  }
1123  else {
1124  rb_raise(rb_eArgError, "should be a ractor object, but %"PRIsVALUE, v);
1125  }
1126  }
1127  rs = NULL;
1128 
1129  restart:
1130 
1131  if (yield_p) {
1132  actions[rs_len].type = ractor_select_action_yield;
1133  actions[rs_len].v = Qundef;
1134  wait_status |= wait_yielding;
1135  ractor_basket_setup(ec, &cr->sync.wait.yielded_basket, yielded_value, move, false, false, true);
1136  }
1137 
1138  // TODO: shuffle actions
1139 
1140  while (1) {
1141  RUBY_DEBUG_LOG("try actions (%s)", wait_status_str(wait_status));
1142 
1143  for (i=0; i<alen; i++) {
1144  VALUE v, rv;
1145  switch (actions[i].type) {
1146  case ractor_select_action_take:
1147  rv = actions[i].v;
1148  v = ractor_try_take(ec, RACTOR_PTR(rv));
1149  if (v != Qundef) {
1150  *ret_r = rv;
1151  ret = v;
1152  goto cleanup;
1153  }
1154  break;
1155  case ractor_select_action_receive:
1156  v = ractor_try_receive(ec, cr);
1157  if (v != Qundef) {
1158  *ret_r = ID2SYM(rb_intern("receive"));
1159  ret = v;
1160  goto cleanup;
1161  }
1162  break;
1163  case ractor_select_action_yield:
1164  {
1165  if (ractor_try_yield(ec, cr, &cr->sync.wait.yielded_basket)) {
1166  *ret_r = ID2SYM(rb_intern("yield"));
1167  ret = Qnil;
1168  goto cleanup;
1169  }
1170  }
1171  break;
1172  }
1173  }
1174 
1175  RUBY_DEBUG_LOG("wait actions (%s)", wait_status_str(wait_status));
1176 
1177  RACTOR_LOCK(cr);
1178  {
1179  VM_ASSERT(cr->sync.wait.status == wait_none);
1180  cr->sync.wait.status = wait_status;
1181  cr->sync.wait.wakeup_status = wakeup_none;
1182  }
1183  RACTOR_UNLOCK(cr);
1184 
1185  // prepare waiting
1186  for (i=0; i<alen; i++) {
1187  rb_ractor_t *r;
1188  switch (actions[i].type) {
1189  case ractor_select_action_take:
1190  r = RACTOR_PTR(actions[i].v);
1191  ractor_register_taking(r, cr);
1192  break;
1193  case ractor_select_action_yield:
1194  case ractor_select_action_receive:
1195  break;
1196  }
1197  }
1198 
1199  // wait
1200  RACTOR_LOCK(cr);
1201  {
1202  if (cr->sync.wait.wakeup_status == wakeup_none) {
1203  for (i=0; i<alen; i++) {
1204  rb_ractor_t *r;
1205 
1206  switch (actions[i].type) {
1207  case ractor_select_action_take:
1208  r = RACTOR_PTR(actions[i].v);
1209  if (ractor_sleeping_by(r, wait_yielding)) {
1210  RUBY_DEBUG_LOG("wakeup_none, but r:%u is waiting for yielding", r->pub.id);
1211  cr->sync.wait.wakeup_status = wakeup_by_retry;
1212  goto skip_sleep;
1213  }
1214  break;
1215  case ractor_select_action_receive:
1216  if (cr->sync.incoming_queue.cnt > 0) {
1217  RUBY_DEBUG_LOG("wakeup_none, but incoming_queue has %u messages", cr->sync.incoming_queue.cnt);
1218  cr->sync.wait.wakeup_status = wakeup_by_retry;
1219  goto skip_sleep;
1220  }
1221  break;
1222  case ractor_select_action_yield:
1223  if (cr->sync.taking_ractors.cnt > 0) {
1224  RUBY_DEBUG_LOG("wakeup_none, but %u taking_ractors are waiting", cr->sync.taking_ractors.cnt);
1225  cr->sync.wait.wakeup_status = wakeup_by_retry;
1226  goto skip_sleep;
1227  }
1228  else if (cr->sync.outgoing_port_closed) {
1229  cr->sync.wait.wakeup_status = wakeup_by_close;
1230  goto skip_sleep;
1231  }
1232  break;
1233  }
1234  }
1235 
1236  RUBY_DEBUG_LOG("sleep %s", wait_status_str(cr->sync.wait.status));
1237  ractor_sleep(ec, cr);
1238  RUBY_DEBUG_LOG("awaken %s", wakeup_status_str(cr->sync.wait.wakeup_status));
1239  }
1240  else {
1241  skip_sleep:
1242  RUBY_DEBUG_LOG("no need to sleep %s->%s",
1243  wait_status_str(cr->sync.wait.status),
1244  wakeup_status_str(cr->sync.wait.wakeup_status));
1245  cr->sync.wait.status = wait_none;
1246  }
1247  }
1248  RACTOR_UNLOCK(cr);
1249 
1250  // cleanup waiting
1251  for (i=0; i<alen; i++) {
1252  rb_ractor_t *r;
1253  switch (actions[i].type) {
1254  case ractor_select_action_take:
1255  r = RACTOR_PTR(actions[i].v);
1256  ractor_waiting_list_del(r, &r->sync.taking_ractors, cr);
1257  break;
1258  case ractor_select_action_receive:
1259  case ractor_select_action_yield:
1260  break;
1261  }
1262  }
1263 
1264  // check results
1265  enum ractor_wakeup_status wakeup_status = cr->sync.wait.wakeup_status;
1266  cr->sync.wait.wakeup_status = wakeup_none;
1267 
1268  switch (wakeup_status) {
1269  case wakeup_none:
1270  // OK. something happens.
1271  // retry loop.
1272  break;
1273  case wakeup_by_retry:
1274  // Retry request.
1275  break;
1276  case wakeup_by_send:
1277  // OK.
1278  // retry loop and try_receive will succss.
1279  break;
1280  case wakeup_by_yield:
1281  // take was succeeded!
1282  // cr.wait.taken_basket contains passed block
1283  VM_ASSERT(cr->sync.wait.taken_basket.type != basket_type_none);
1284  *ret_r = cr->sync.wait.taken_basket.sender;
1285  VM_ASSERT(rb_ractor_p(*ret_r));
1286  ret = ractor_basket_accept(&cr->sync.wait.taken_basket);
1287  goto cleanup;
1288  case wakeup_by_take:
1289  *ret_r = ID2SYM(rb_intern("yield"));
1290  ret = Qnil;
1291  goto cleanup;
1292  case wakeup_by_close:
1293  // OK.
1294  // retry loop and will get CloseError.
1295  break;
1296  case wakeup_by_interrupt:
1297  ret = Qundef;
1298  interrupted = true;
1299  goto cleanup;
1300  }
1301  }
1302 
1303  cleanup:
1304  RUBY_DEBUG_LOG("cleanup actions (%s)", wait_status_str(wait_status));
1305 
1306  if (cr->sync.wait.yielded_basket.type != basket_type_none) {
1307  ractor_basket_clear(&cr->sync.wait.yielded_basket);
1308  }
1309 
1310  VM_ASSERT(cr->sync.wait.status == wait_none);
1311  VM_ASSERT(cr->sync.wait.wakeup_status == wakeup_none);
1312  VM_ASSERT(cr->sync.wait.taken_basket.type == basket_type_none);
1313  VM_ASSERT(cr->sync.wait.yielded_basket.type == basket_type_none);
1314 
1315  if (interrupted) {
1316  rb_vm_check_ints_blocking(ec);
1317  interrupted = false;
1318  goto restart;
1319  }
1320 
1321  VM_ASSERT(ret != Qundef);
1322  return ret;
1323 }
1324 
1325 static VALUE
1326 ractor_yield(rb_execution_context_t *ec, rb_ractor_t *r, VALUE obj, VALUE move)
1327 {
1328  VALUE ret_r;
1329  ractor_select(ec, NULL, 0, obj, RTEST(move) ? true : false, &ret_r);
1330  return Qnil;
1331 }
1332 
1333 static VALUE
1334 ractor_take(rb_execution_context_t *ec, rb_ractor_t *r)
1335 {
1336  VALUE ret_r;
1337  VALUE v = ractor_select(ec, &r->pub.self, 1, Qundef, false, &ret_r);
1338  return v;
1339 }
1340 
1341 static VALUE
1342 ractor_close_incoming(rb_execution_context_t *ec, rb_ractor_t *r)
1343 {
1344  VALUE prev;
1345 
1346  RACTOR_LOCK(r);
1347  {
1348  if (!r->sync.incoming_port_closed) {
1349  prev = Qfalse;
1350  r->sync.incoming_port_closed = true;
1351  if (ractor_wakeup(r, wait_receiving, wakeup_by_close)) {
1352  VM_ASSERT(r->sync.incoming_queue.cnt == 0);
1353  RUBY_DEBUG_LOG("cancel receiving");
1354  }
1355  }
1356  else {
1357  prev = Qtrue;
1358  }
1359  }
1360  RACTOR_UNLOCK(r);
1361  return prev;
1362 }
1363 
1364 static VALUE
1365 ractor_close_outgoing(rb_execution_context_t *ec, rb_ractor_t *r)
1366 {
1367  VALUE prev;
1368 
1369  RACTOR_LOCK(r);
1370  {
1371  if (!r->sync.outgoing_port_closed) {
1372  prev = Qfalse;
1373  r->sync.outgoing_port_closed = true;
1374  }
1375  else {
1376  prev = Qtrue;
1377  }
1378 
1379  // wakeup all taking ractors
1380  rb_ractor_t *taking_ractor;
1381  while ((taking_ractor = ractor_waiting_list_shift(r, &r->sync.taking_ractors)) != NULL) {
1382  RACTOR_LOCK(taking_ractor);
1383  ractor_wakeup(taking_ractor, wait_taking, wakeup_by_close);
1384  RACTOR_UNLOCK(taking_ractor);
1385  }
1386 
1387  // raising yielding Ractor
1388  if (!r->yield_atexit &&
1389  ractor_wakeup(r, wait_yielding, wakeup_by_close)) {
1390  RUBY_DEBUG_LOG("cancel yielding");
1391  }
1392  }
1393  RACTOR_UNLOCK(r);
1394  return prev;
1395 }
1396 
1397 // creation/termination
1398 
1399 static uint32_t
1400 ractor_next_id(void)
1401 {
1402  uint32_t id;
1403 
1404  id = (uint32_t)(RUBY_ATOMIC_FETCH_ADD(ractor_last_id, 1) + 1);
1405 
1406  return id;
1407 }
1408 
1409 static void
1410 vm_insert_ractor0(rb_vm_t *vm, rb_ractor_t *r, bool single_ractor_mode)
1411 {
1412  RUBY_DEBUG_LOG("r:%u ractor.cnt:%u++", r->pub.id, vm->ractor.cnt);
1413  VM_ASSERT(single_ractor_mode || RB_VM_LOCKED_P());
1414 
1415  list_add_tail(&vm->ractor.set, &r->vmlr_node);
1416  vm->ractor.cnt++;
1417 }
1418 
1419 static void
1420 cancel_single_ractor_mode(void)
1421 {
1422  // enable multi-ractor mode
1423  RUBY_DEBUG_LOG("enable multi-ractor mode");
1424 
1425  VALUE was_disabled = rb_gc_enable();
1426 
1427  rb_gc_start();
1428  rb_transient_heap_evacuate();
1429 
1430  if (was_disabled) {
1431  rb_gc_disable();
1432  }
1433 
1434  ruby_single_main_ractor = NULL;
1435 }
1436 
1437 static void
1438 vm_insert_ractor(rb_vm_t *vm, rb_ractor_t *r)
1439 {
1440  VM_ASSERT(ractor_status_p(r, ractor_created));
1441 
1442  if (rb_multi_ractor_p()) {
1443  RB_VM_LOCK();
1444  {
1445  vm_insert_ractor0(vm, r, false);
1446  vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
1447  }
1448  RB_VM_UNLOCK();
1449  }
1450  else {
1451  if (vm->ractor.cnt == 0) {
1452  // main ractor
1453  vm_insert_ractor0(vm, r, true);
1454  ractor_status_set(r, ractor_blocking);
1455  ractor_status_set(r, ractor_running);
1456  }
1457  else {
1458  cancel_single_ractor_mode();
1459  vm_insert_ractor0(vm, r, true);
1460  vm_ractor_blocking_cnt_inc(vm, r, __FILE__, __LINE__);
1461  }
1462  }
1463 }
1464 
1465 static void
1466 vm_remove_ractor(rb_vm_t *vm, rb_ractor_t *cr)
1467 {
1468  VM_ASSERT(ractor_status_p(cr, ractor_running));
1469  VM_ASSERT(vm->ractor.cnt > 1);
1470  VM_ASSERT(cr->threads.cnt == 1);
1471 
1472  RB_VM_LOCK();
1473  {
1474  RUBY_DEBUG_LOG("ractor.cnt:%u-- terminate_waiting:%d",
1475  vm->ractor.cnt, vm->ractor.sync.terminate_waiting);
1476 
1477  VM_ASSERT(vm->ractor.cnt > 0);
1478  list_del(&cr->vmlr_node);
1479 
1480  if (vm->ractor.cnt <= 2 && vm->ractor.sync.terminate_waiting) {
1481  rb_native_cond_signal(&vm->ractor.sync.terminate_cond);
1482  }
1483  vm->ractor.cnt--;
1484 
1485  /* Clear the cached freelist to prevent a memory leak. */
1486  rb_gc_ractor_newobj_cache_clear(&cr->newobj_cache);
1487 
1488  ractor_status_set(cr, ractor_terminated);
1489  }
1490  RB_VM_UNLOCK();
1491 }
1492 
1493 static VALUE
1494 ractor_alloc(VALUE klass)
1495 {
1496  rb_ractor_t *r;
1497  VALUE rv = TypedData_Make_Struct(klass, rb_ractor_t, &ractor_data_type, r);
1499  r->pub.self = rv;
1500  VM_ASSERT(ractor_status_p(r, ractor_created));
1501  return rv;
1502 }
1503 
1504 rb_ractor_t *
1505 rb_ractor_main_alloc(void)
1506 {
1507  rb_ractor_t *r = ruby_mimmalloc(sizeof(rb_ractor_t));
1508  if (r == NULL) {
1509  fprintf(stderr, "[FATAL] failed to allocate memory for main ractor\n");
1510  exit(EXIT_FAILURE);
1511  }
1512  MEMZERO(r, rb_ractor_t, 1);
1513  r->pub.id = ++ractor_last_id;
1514  r->loc = Qnil;
1515  r->name = Qnil;
1516  r->pub.self = Qnil;
1517  ruby_single_main_ractor = r;
1518 
1519  return r;
1520 }
1521 
1522 #if defined(HAVE_WORKING_FORK)
1523 void
1524 rb_ractor_atfork(rb_vm_t *vm, rb_thread_t *th)
1525 {
1526  // initialize as a main ractor
1527  vm->ractor.cnt = 0;
1528  vm->ractor.blocking_cnt = 0;
1529  ruby_single_main_ractor = th->ractor;
1530  th->ractor->status_ = ractor_created;
1531 
1532  rb_ractor_living_threads_init(th->ractor);
1533  rb_ractor_living_threads_insert(th->ractor, th);
1534 
1535  VM_ASSERT(vm->ractor.blocking_cnt == 0);
1536  VM_ASSERT(vm->ractor.cnt == 1);
1537 }
1538 #endif
1539 
1540 void rb_gvl_init(rb_global_vm_lock_t *gvl);
1541 
1542 void
1543 rb_ractor_living_threads_init(rb_ractor_t *r)
1544 {
1545  list_head_init(&r->threads.set);
1546  r->threads.cnt = 0;
1547  r->threads.blocking_cnt = 0;
1548 }
1549 
1550 static void
1551 ractor_init(rb_ractor_t *r, VALUE name, VALUE loc)
1552 {
1553  ractor_queue_setup(&r->sync.incoming_queue);
1554  rb_native_mutex_initialize(&r->sync.lock);
1555  rb_native_cond_initialize(&r->sync.cond);
1556  rb_native_cond_initialize(&r->barrier_wait_cond);
1557 
1558  // thread management
1559  rb_gvl_init(&r->threads.gvl);
1560  rb_ractor_living_threads_init(r);
1561 
1562  // naming
1563  if (!NIL_P(name)) {
1564  rb_encoding *enc;
1565  StringValueCStr(name);
1566  enc = rb_enc_get(name);
1567  if (!rb_enc_asciicompat(enc)) {
1568  rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
1569  rb_enc_name(enc));
1570  }
1571  name = rb_str_new_frozen(name);
1572  }
1573  r->name = name;
1574  r->loc = loc;
1575 }
1576 
1577 void
1578 rb_ractor_main_setup(rb_vm_t *vm, rb_ractor_t *r, rb_thread_t *th)
1579 {
1580  r->pub.self = TypedData_Wrap_Struct(rb_cRactor, &ractor_data_type, r);
1581  FL_SET_RAW(r->pub.self, RUBY_FL_SHAREABLE);
1582  ractor_init(r, Qnil, Qnil);
1583  r->threads.main = th;
1584  rb_ractor_living_threads_insert(r, th);
1585 }
1586 
1587 static VALUE
1588 ractor_create(rb_execution_context_t *ec, VALUE self, VALUE loc, VALUE name, VALUE args, VALUE block)
1589 {
1590  VALUE rv = ractor_alloc(self);
1591  rb_ractor_t *r = RACTOR_PTR(rv);
1592  ractor_init(r, name, loc);
1593 
1594  // can block here
1595  r->pub.id = ractor_next_id();
1596  RUBY_DEBUG_LOG("r:%u", r->pub.id);
1597 
1598  rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1599  r->verbose = cr->verbose;
1600  r->debug = cr->debug;
1601 
1602  rb_yjit_before_ractor_spawn();
1603  rb_thread_create_ractor(r, args, block);
1604 
1605  RB_GC_GUARD(rv);
1606  return rv;
1607 }
1608 
1609 static void
1610 ractor_yield_atexit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE v, bool exc)
1611 {
1612  if (cr->sync.outgoing_port_closed) {
1613  return;
1614  }
1615 
1616  ASSERT_ractor_unlocking(cr);
1617 
1618  struct rb_ractor_basket basket;
1619  ractor_basket_setup(ec, &basket, v, Qfalse, exc, true, true /* this flag is ignored because move is Qfalse */);
1620 
1621  retry:
1622  if (ractor_try_yield(ec, cr, &basket)) {
1623  // OK.
1624  }
1625  else {
1626  bool retry = false;
1627  RACTOR_LOCK(cr);
1628  {
1629  if (cr->sync.taking_ractors.cnt == 0) {
1630  cr->sync.wait.yielded_basket = basket;
1631 
1632  VM_ASSERT(cr->sync.wait.status == wait_none);
1633  cr->sync.wait.status = wait_yielding;
1634  cr->sync.wait.wakeup_status = wakeup_none;
1635 
1636  VM_ASSERT(cr->yield_atexit == false);
1637  cr->yield_atexit = true;
1638  }
1639  else {
1640  retry = true; // another ractor is waiting for the yield.
1641  }
1642  }
1643  RACTOR_UNLOCK(cr);
1644 
1645  if (retry) goto retry;
1646  }
1647 }
1648 
1649 void
1650 rb_ractor_teardown(rb_execution_context_t *ec)
1651 {
1652  rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1653  ractor_close_incoming(ec, cr);
1654  ractor_close_outgoing(ec, cr);
1655 
1656  // sync with rb_ractor_terminate_interrupt_main_thread()
1657  RB_VM_LOCK_ENTER();
1658  {
1659  VM_ASSERT(cr->threads.main != NULL);
1660  cr->threads.main = NULL;
1661  }
1662  RB_VM_LOCK_LEAVE();
1663 }
1664 
1665 void
1666 rb_ractor_atexit(rb_execution_context_t *ec, VALUE result)
1667 {
1668  rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1669  ractor_yield_atexit(ec, cr, result, false);
1670 }
1671 
1672 void
1673 rb_ractor_atexit_exception(rb_execution_context_t *ec)
1674 {
1675  rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1676  ractor_yield_atexit(ec, cr, ec->errinfo, true);
1677 }
1678 
1679 void
1680 rb_ractor_receive_parameters(rb_execution_context_t *ec, rb_ractor_t *r, int len, VALUE *ptr)
1681 {
1682  for (int i=0; i<len; i++) {
1683  ptr[i] = ractor_receive(ec, r);
1684  }
1685 }
1686 
1687 void
1688 rb_ractor_send_parameters(rb_execution_context_t *ec, rb_ractor_t *r, VALUE args)
1689 {
1690  int len = RARRAY_LENINT(args);
1691  for (int i=0; i<len; i++) {
1692  ractor_send(ec, r, RARRAY_AREF(args, i), false);
1693  }
1694 }
1695 
1696 MJIT_FUNC_EXPORTED bool
1697 rb_ractor_main_p_(void)
1698 {
1699  VM_ASSERT(rb_multi_ractor_p());
1700  rb_execution_context_t *ec = GET_EC();
1701  return rb_ec_ractor_ptr(ec) == rb_ec_vm_ptr(ec)->ractor.main_ractor;
1702 }
1703 
1704 bool
1705 rb_obj_is_main_ractor(VALUE gv)
1706 {
1707  if (!rb_ractor_p(gv)) return false;
1708  rb_ractor_t *r = DATA_PTR(gv);
1709  return r == GET_VM()->ractor.main_ractor;
1710 }
1711 
1713 rb_ractor_gvl(rb_ractor_t *r)
1714 {
1715  return &r->threads.gvl;
1716 }
1717 
1718 int
1719 rb_ractor_living_thread_num(const rb_ractor_t *r)
1720 {
1721  return r->threads.cnt;
1722 }
1723 
1724 VALUE
1725 rb_ractor_thread_list(rb_ractor_t *r)
1726 {
1727  rb_thread_t *th = 0;
1728  VALUE *ts;
1729  int ts_cnt;
1730 
1731  RACTOR_LOCK(r);
1732  {
1733  ts = ALLOCA_N(VALUE, r->threads.cnt);
1734  ts_cnt = 0;
1735 
1736  list_for_each(&r->threads.set, th, lt_node) {
1737  switch (th->status) {
1738  case THREAD_RUNNABLE:
1739  case THREAD_STOPPED:
1740  case THREAD_STOPPED_FOREVER:
1741  ts[ts_cnt++] = th->self;
1742  default:
1743  break;
1744  }
1745  }
1746  }
1747  RACTOR_UNLOCK(r);
1748 
1749  VALUE ary = rb_ary_new();
1750  for (int i=0; i<ts_cnt; i++) {
1751  rb_ary_push(ary, ts[i]);
1752  }
1753 
1754  return ary;
1755 }
1756 
1757 void
1758 rb_ractor_living_threads_insert(rb_ractor_t *r, rb_thread_t *th)
1759 {
1760  VM_ASSERT(th != NULL);
1761 
1762  RACTOR_LOCK(r);
1763  {
1764  RUBY_DEBUG_LOG("r(%d)->threads.cnt:%d++", r->pub.id, r->threads.cnt);
1765  list_add_tail(&r->threads.set, &th->lt_node);
1766  r->threads.cnt++;
1767  }
1768  RACTOR_UNLOCK(r);
1769 
1770  // first thread for a ractor
1771  if (r->threads.cnt == 1) {
1772  VM_ASSERT(ractor_status_p(r, ractor_created));
1773  vm_insert_ractor(th->vm, r);
1774  }
1775 }
1776 
1777 static void
1778 vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *r, const char *file, int line)
1779 {
1780  ractor_status_set(r, ractor_blocking);
1781 
1782  RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d++", vm->ractor.blocking_cnt);
1783  vm->ractor.blocking_cnt++;
1784  VM_ASSERT(vm->ractor.blocking_cnt <= vm->ractor.cnt);
1785 }
1786 
1787 void
1788 rb_vm_ractor_blocking_cnt_inc(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
1789 {
1790  ASSERT_vm_locking();
1791  VM_ASSERT(GET_RACTOR() == cr);
1792  vm_ractor_blocking_cnt_inc(vm, cr, file, line);
1793 }
1794 
1795 void
1796 rb_vm_ractor_blocking_cnt_dec(rb_vm_t *vm, rb_ractor_t *cr, const char *file, int line)
1797 {
1798  ASSERT_vm_locking();
1799  VM_ASSERT(GET_RACTOR() == cr);
1800 
1801  RUBY_DEBUG_LOG2(file, line, "vm->ractor.blocking_cnt:%d--", vm->ractor.blocking_cnt);
1802  VM_ASSERT(vm->ractor.blocking_cnt > 0);
1803  vm->ractor.blocking_cnt--;
1804 
1805  ractor_status_set(cr, ractor_running);
1806 }
1807 
1808 static void
1809 ractor_check_blocking(rb_ractor_t *cr, unsigned int remained_thread_cnt, const char *file, int line)
1810 {
1811  VM_ASSERT(cr == GET_RACTOR());
1812 
1813  RUBY_DEBUG_LOG2(file, line,
1814  "cr->threads.cnt:%u cr->threads.blocking_cnt:%u vm->ractor.cnt:%u vm->ractor.blocking_cnt:%u",
1815  cr->threads.cnt, cr->threads.blocking_cnt,
1816  GET_VM()->ractor.cnt, GET_VM()->ractor.blocking_cnt);
1817 
1818  VM_ASSERT(cr->threads.cnt >= cr->threads.blocking_cnt + 1);
1819 
1820  if (remained_thread_cnt > 0 &&
1821  // will be block
1822  cr->threads.cnt == cr->threads.blocking_cnt + 1) {
1823  // change ractor status: running -> blocking
1824  rb_vm_t *vm = GET_VM();
1825  ASSERT_vm_unlocking();
1826 
1827  RB_VM_LOCK();
1828  {
1829  rb_vm_ractor_blocking_cnt_inc(vm, cr, file, line);
1830  }
1831  RB_VM_UNLOCK();
1832  }
1833 }
1834 
1835 void
1836 rb_ractor_living_threads_remove(rb_ractor_t *cr, rb_thread_t *th)
1837 {
1838  VM_ASSERT(cr == GET_RACTOR());
1839  RUBY_DEBUG_LOG("r->threads.cnt:%d--", cr->threads.cnt);
1840  ractor_check_blocking(cr, cr->threads.cnt - 1, __FILE__, __LINE__);
1841 
1842  if (cr->threads.cnt == 1) {
1843  vm_remove_ractor(th->vm, cr);
1844  }
1845  else {
1846  RACTOR_LOCK(cr);
1847  {
1848  list_del(&th->lt_node);
1849  cr->threads.cnt--;
1850  }
1851  RACTOR_UNLOCK(cr);
1852  }
1853 }
1854 
1855 void
1856 rb_ractor_blocking_threads_inc(rb_ractor_t *cr, const char *file, int line)
1857 {
1858  RUBY_DEBUG_LOG2(file, line, "cr->threads.blocking_cnt:%d++", cr->threads.blocking_cnt);
1859 
1860  VM_ASSERT(cr->threads.cnt > 0);
1861  VM_ASSERT(cr == GET_RACTOR());
1862 
1863  ractor_check_blocking(cr, cr->threads.cnt, __FILE__, __LINE__);
1864  cr->threads.blocking_cnt++;
1865 }
1866 
1867 void
1868 rb_ractor_blocking_threads_dec(rb_ractor_t *cr, const char *file, int line)
1869 {
1870  RUBY_DEBUG_LOG2(file, line,
1871  "r->threads.blocking_cnt:%d--, r->threads.cnt:%u",
1872  cr->threads.blocking_cnt, cr->threads.cnt);
1873 
1874  VM_ASSERT(cr == GET_RACTOR());
1875 
1876  if (cr->threads.cnt == cr->threads.blocking_cnt) {
1877  rb_vm_t *vm = GET_VM();
1878 
1879  RB_VM_LOCK_ENTER();
1880  {
1881  rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
1882  }
1883  RB_VM_LOCK_LEAVE();
1884  }
1885 
1886  cr->threads.blocking_cnt--;
1887 }
1888 
1889 void
1890 rb_ractor_vm_barrier_interrupt_running_thread(rb_ractor_t *r)
1891 {
1892  VM_ASSERT(r != GET_RACTOR());
1893  ASSERT_ractor_unlocking(r);
1894  ASSERT_vm_locking();
1895 
1896  RACTOR_LOCK(r);
1897  {
1898  if (ractor_status_p(r, ractor_running)) {
1899  rb_execution_context_t *ec = r->threads.running_ec;
1900  if (ec) {
1901  RUBY_VM_SET_VM_BARRIER_INTERRUPT(ec);
1902  }
1903  }
1904  }
1905  RACTOR_UNLOCK(r);
1906 }
1907 
1908 void
1909 rb_ractor_terminate_interrupt_main_thread(rb_ractor_t *r)
1910 {
1911  VM_ASSERT(r != GET_RACTOR());
1912  ASSERT_ractor_unlocking(r);
1913  ASSERT_vm_locking();
1914 
1915  rb_thread_t *main_th = r->threads.main;
1916  if (main_th) {
1917  if (main_th->status != THREAD_KILLED) {
1918  RUBY_VM_SET_TERMINATE_INTERRUPT(main_th->ec);
1919  rb_threadptr_interrupt(main_th);
1920  }
1921  else {
1922  RUBY_DEBUG_LOG("killed (%p)", (void *)main_th);
1923  }
1924  }
1925 }
1926 
1927 void rb_thread_terminate_all(rb_thread_t *th); // thread.c
1928 
1929 static void
1930 ractor_terminal_interrupt_all(rb_vm_t *vm)
1931 {
1932  if (vm->ractor.cnt > 1) {
1933  // send terminate notification to all ractors
1934  rb_ractor_t *r = 0;
1935  list_for_each(&vm->ractor.set, r, vmlr_node) {
1936  if (r != vm->ractor.main_ractor) {
1937  rb_ractor_terminate_interrupt_main_thread(r);
1938  }
1939  }
1940  }
1941 }
1942 
1943 void
1944 rb_ractor_terminate_all(void)
1945 {
1946  rb_vm_t *vm = GET_VM();
1947  rb_ractor_t *cr = vm->ractor.main_ractor;
1948 
1949  VM_ASSERT(cr == GET_RACTOR()); // only main-ractor's main-thread should kick it.
1950 
1951  if (vm->ractor.cnt > 1) {
1952  RB_VM_LOCK();
1953  ractor_terminal_interrupt_all(vm); // kill all ractors
1954  RB_VM_UNLOCK();
1955  }
1956  rb_thread_terminate_all(GET_THREAD()); // kill other threads in main-ractor and wait
1957 
1958  RB_VM_LOCK();
1959  {
1960  while (vm->ractor.cnt > 1) {
1961  RUBY_DEBUG_LOG("terminate_waiting:%d", vm->ractor.sync.terminate_waiting);
1962  vm->ractor.sync.terminate_waiting = true;
1963 
1964  // wait for 1sec
1965  rb_vm_ractor_blocking_cnt_inc(vm, cr, __FILE__, __LINE__);
1966  rb_vm_cond_timedwait(vm, &vm->ractor.sync.terminate_cond, 1000 /* ms */);
1967  rb_vm_ractor_blocking_cnt_dec(vm, cr, __FILE__, __LINE__);
1968 
1969  ractor_terminal_interrupt_all(vm);
1970  }
1971  }
1972  RB_VM_UNLOCK();
1973 }
1974 
1976 rb_vm_main_ractor_ec(rb_vm_t *vm)
1977 {
1978  return vm->ractor.main_ractor->threads.running_ec;
1979 }
1980 
1981 static VALUE
1982 ractor_moved_missing(int argc, VALUE *argv, VALUE self)
1983 {
1984  rb_raise(rb_eRactorMovedError, "can not send any methods to a moved object");
1985 }
1986 
1987 /*
1988  * Document-class: Ractor::ClosedError
1989  *
1990  * Raised when an attempt is made to send a message to a closed port,
1991  * or to retrieve a message from a closed and empty port.
1992  * Ports may be closed explicitly with Ractor#close_outgoing/close_incoming
1993  * and are closed implicitly when a Ractor terminates.
1994  *
1995  * r = Ractor.new { sleep(500) }
1996  * r.close_outgoing
1997  * r.take # Ractor::ClosedError
1998  *
1999  * ClosedError is a descendant of StopIteration, so the closing of the ractor will break
2000  * the loops without propagating the error:
2001  *
2002  * r = Ractor.new do
2003  * loop do
2004  * msg = receive # raises ClosedError and loop traps it
2005  * puts "Received: #{msg}"
2006  * end
2007  * puts "loop exited"
2008  * end
2009  *
2010  * 3.times{|i| r << i}
2011  * r.close_incoming
2012  * r.take
2013  * puts "Continue successfully"
2014  *
2015  * This will print:
2016  *
2017  * Received: 0
2018  * Received: 1
2019  * Received: 2
2020  * loop exited
2021  * Continue successfully
2022  */
2023 
2024 /*
2025  * Document-class: Ractor::RemoteError
2026  *
2027  * Raised on attempt to Ractor#take if there was an uncaught exception in the Ractor.
2028  * Its +cause+ will contain the original exception, and +ractor+ is the original ractor
2029  * it was raised in.
2030  *
2031  * r = Ractor.new { raise "Something weird happened" }
2032  *
2033  * begin
2034  * r.take
2035  * rescue => e
2036  * p e # => #<Ractor::RemoteError: thrown by remote Ractor.>
2037  * p e.ractor == r # => true
2038  * p e.cause # => #<RuntimeError: Something weird happened>
2039  * end
2040  *
2041  */
2042 
2043 /*
2044  * Document-class: Ractor::MovedError
2045  *
2046  * Raised on an attempt to access an object which was moved in Ractor#send or Ractor.yield.
2047  *
2048  * r = Ractor.new { sleep }
2049  *
2050  * ary = [1, 2, 3]
2051  * r.send(ary, move: true)
2052  * ary.inspect
2053  * # Ractor::MovedError (can not send any methods to a moved object)
2054  *
2055  */
2056 
2057 /*
2058  * Document-class: Ractor::MovedObject
2059  *
2060  * A special object which replaces any value that was moved to another ractor in Ractor#send
2061  * or Ractor.yield. Any attempt to access the object results in Ractor::MovedError.
2062  *
2063  * r = Ractor.new { receive }
2064  *
2065  * ary = [1, 2, 3]
2066  * r.send(ary, move: true)
2067  * p Ractor::MovedObject === ary
2068  * # => true
2069  * ary.inspect
2070  * # Ractor::MovedError (can not send any methods to a moved object)
2071  */
2072 
2073 // Main docs are in ractor.rb, but without this clause there are weird artifacts
2074 // in their rendering.
2075 /*
2076  * Document-class: Ractor
2077  *
2078  */
2079 
2080 void
2081 Init_Ractor(void)
2082 {
2083  rb_cRactor = rb_define_class("Ractor", rb_cObject);
2085 
2086  rb_eRactorError = rb_define_class_under(rb_cRactor, "Error", rb_eRuntimeError);
2087  rb_eRactorIsolationError = rb_define_class_under(rb_cRactor, "IsolationError", rb_eRactorError);
2088  rb_eRactorRemoteError = rb_define_class_under(rb_cRactor, "RemoteError", rb_eRactorError);
2089  rb_eRactorMovedError = rb_define_class_under(rb_cRactor, "MovedError", rb_eRactorError);
2090  rb_eRactorClosedError = rb_define_class_under(rb_cRactor, "ClosedError", rb_eStopIteration);
2091  rb_eRactorUnsafeError = rb_define_class_under(rb_cRactor, "UnsafeError", rb_eRactorError);
2092 
2093  rb_cRactorMovedObject = rb_define_class_under(rb_cRactor, "MovedObject", rb_cBasicObject);
2094  rb_undef_alloc_func(rb_cRactorMovedObject);
2095  rb_define_method(rb_cRactorMovedObject, "method_missing", ractor_moved_missing, -1);
2096 
2097  // override methods defined in BasicObject
2098  rb_define_method(rb_cRactorMovedObject, "__send__", ractor_moved_missing, -1);
2099  rb_define_method(rb_cRactorMovedObject, "!", ractor_moved_missing, -1);
2100  rb_define_method(rb_cRactorMovedObject, "==", ractor_moved_missing, -1);
2101  rb_define_method(rb_cRactorMovedObject, "!=", ractor_moved_missing, -1);
2102  rb_define_method(rb_cRactorMovedObject, "__id__", ractor_moved_missing, -1);
2103  rb_define_method(rb_cRactorMovedObject, "equal?", ractor_moved_missing, -1);
2104  rb_define_method(rb_cRactorMovedObject, "instance_eval", ractor_moved_missing, -1);
2105  rb_define_method(rb_cRactorMovedObject, "instance_exec", ractor_moved_missing, -1);
2106 }
2107 
2108 void
2109 rb_ractor_dump(void)
2110 {
2111  rb_vm_t *vm = GET_VM();
2112  rb_ractor_t *r = 0;
2113 
2114  list_for_each(&vm->ractor.set, r, vmlr_node) {
2115  if (r != vm->ractor.main_ractor) {
2116  fprintf(stderr, "r:%u (%s)\n", r->pub.id, ractor_status_str(r->status_));
2117  }
2118  }
2119 }
2120 
2121 VALUE
2123 {
2124  if (rb_ractor_main_p()) {
2125  return rb_stdin;
2126  }
2127  else {
2128  rb_ractor_t *cr = GET_RACTOR();
2129  return cr->r_stdin;
2130  }
2131 }
2132 
2133 VALUE
2135 {
2136  if (rb_ractor_main_p()) {
2137  return rb_stdout;
2138  }
2139  else {
2140  rb_ractor_t *cr = GET_RACTOR();
2141  return cr->r_stdout;
2142  }
2143 }
2144 
2145 VALUE
2147 {
2148  if (rb_ractor_main_p()) {
2149  return rb_stderr;
2150  }
2151  else {
2152  rb_ractor_t *cr = GET_RACTOR();
2153  return cr->r_stderr;
2154  }
2155 }
2156 
2157 void
2159 {
2160  if (rb_ractor_main_p()) {
2161  rb_stdin = in;
2162  }
2163  else {
2164  rb_ractor_t *cr = GET_RACTOR();
2165  RB_OBJ_WRITE(cr->pub.self, &cr->r_stdin, in);
2166  }
2167 }
2168 
2169 void
2171 {
2172  if (rb_ractor_main_p()) {
2173  rb_stdout = out;
2174  }
2175  else {
2176  rb_ractor_t *cr = GET_RACTOR();
2177  RB_OBJ_WRITE(cr->pub.self, &cr->r_stdout, out);
2178  }
2179 }
2180 
2181 void
2183 {
2184  if (rb_ractor_main_p()) {
2185  rb_stderr = err;
2186  }
2187  else {
2188  rb_ractor_t *cr = GET_RACTOR();
2189  RB_OBJ_WRITE(cr->pub.self, &cr->r_stderr, err);
2190  }
2191 }
2192 
2194 rb_ractor_hooks(rb_ractor_t *cr)
2195 {
2196  return &cr->pub.hooks;
2197 }
2198 
2200 
2201 // 2: stop search
2202 // 1: skip child
2203 // 0: continue
2204 
2205 enum obj_traverse_iterator_result {
2206  traverse_cont,
2207  traverse_skip,
2208  traverse_stop,
2209 };
2210 
2211 typedef enum obj_traverse_iterator_result (*rb_obj_traverse_enter_func)(VALUE obj);
2212 typedef enum obj_traverse_iterator_result (*rb_obj_traverse_leave_func)(VALUE obj);
2213 typedef enum obj_traverse_iterator_result (*rb_obj_traverse_final_func)(VALUE obj);
2214 
2215 static enum obj_traverse_iterator_result null_leave(VALUE obj);
2216 
2218  rb_obj_traverse_enter_func enter_func;
2219  rb_obj_traverse_leave_func leave_func;
2220 
2221  st_table *rec;
2222  VALUE rec_hash;
2223 };
2224 
2225 
2227  bool stop;
2228  struct obj_traverse_data *data;
2229 };
2230 
2231 static int obj_traverse_i(VALUE obj, struct obj_traverse_data *data);
2232 
2233 static int
2234 obj_hash_traverse_i(VALUE key, VALUE val, VALUE ptr)
2235 {
2236  struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
2237 
2238  if (obj_traverse_i(key, d->data)) {
2239  d->stop = true;
2240  return ST_STOP;
2241  }
2242 
2243  if (obj_traverse_i(val, d->data)) {
2244  d->stop = true;
2245  return ST_STOP;
2246  }
2247 
2248  return ST_CONTINUE;
2249 }
2250 
2251 static void
2252 obj_traverse_reachable_i(VALUE obj, void *ptr)
2253 {
2254  struct obj_traverse_callback_data *d = (struct obj_traverse_callback_data *)ptr;
2255 
2256  if (obj_traverse_i(obj, d->data)) {
2257  d->stop = true;
2258  }
2259 }
2260 
2261 static struct st_table *
2262 obj_traverse_rec(struct obj_traverse_data *data)
2263 {
2264  if (UNLIKELY(!data->rec)) {
2265  data->rec_hash = rb_ident_hash_new();
2266  data->rec = rb_hash_st_table(data->rec_hash);
2267  }
2268  return data->rec;
2269 }
2270 
2271 static int
2272 obj_traverse_i(VALUE obj, struct obj_traverse_data *data)
2273 {
2274  if (RB_SPECIAL_CONST_P(obj)) return 0;
2275 
2276  switch (data->enter_func(obj)) {
2277  case traverse_cont: break;
2278  case traverse_skip: return 0; // skip children
2279  case traverse_stop: return 1; // stop search
2280  }
2281 
2282  if (UNLIKELY(st_insert(obj_traverse_rec(data), obj, 1))) {
2283  // already traversed
2284  return 0;
2285  }
2286 
2287  if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
2288  struct gen_ivtbl *ivtbl;
2289  rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
2290  for (uint32_t i = 0; i < ivtbl->numiv; i++) {
2291  VALUE val = ivtbl->ivptr[i];
2292  if (val != Qundef && obj_traverse_i(val, data)) return 1;
2293  }
2294  }
2295 
2296  switch (BUILTIN_TYPE(obj)) {
2297  // no child node
2298  case T_STRING:
2299  case T_FLOAT:
2300  case T_BIGNUM:
2301  case T_REGEXP:
2302  case T_FILE:
2303  case T_SYMBOL:
2304  case T_MATCH:
2305  break;
2306 
2307  case T_OBJECT:
2308  {
2309  uint32_t len = ROBJECT_NUMIV(obj);
2310  VALUE *ptr = ROBJECT_IVPTR(obj);
2311 
2312  for (uint32_t i=0; i<len; i++) {
2313  VALUE val = ptr[i];
2314  if (val != Qundef && obj_traverse_i(val, data)) return 1;
2315  }
2316  }
2317  break;
2318 
2319  case T_ARRAY:
2320  {
2321  for (int i = 0; i < RARRAY_LENINT(obj); i++) {
2322  VALUE e = rb_ary_entry(obj, i);
2323  if (obj_traverse_i(e, data)) return 1;
2324  }
2325  }
2326  break;
2327 
2328  case T_HASH:
2329  {
2330  if (obj_traverse_i(RHASH_IFNONE(obj), data)) return 1;
2331 
2332  struct obj_traverse_callback_data d = {
2333  .stop = false,
2334  .data = data,
2335  };
2336  rb_hash_foreach(obj, obj_hash_traverse_i, (VALUE)&d);
2337  if (d.stop) return 1;
2338  }
2339  break;
2340 
2341  case T_STRUCT:
2342  {
2343  long len = RSTRUCT_LEN(obj);
2344  const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2345 
2346  for (long i=0; i<len; i++) {
2347  if (obj_traverse_i(ptr[i], data)) return 1;
2348  }
2349  }
2350  break;
2351 
2352  case T_RATIONAL:
2353  if (obj_traverse_i(RRATIONAL(obj)->num, data)) return 1;
2354  if (obj_traverse_i(RRATIONAL(obj)->den, data)) return 1;
2355  break;
2356  case T_COMPLEX:
2357  if (obj_traverse_i(RCOMPLEX(obj)->real, data)) return 1;
2358  if (obj_traverse_i(RCOMPLEX(obj)->imag, data)) return 1;
2359  break;
2360 
2361  case T_DATA:
2362  case T_IMEMO:
2363  {
2364  struct obj_traverse_callback_data d = {
2365  .stop = false,
2366  .data = data,
2367  };
2368  RB_VM_LOCK_ENTER_NO_BARRIER();
2369  {
2370  rb_objspace_reachable_objects_from(obj, obj_traverse_reachable_i, &d);
2371  }
2372  RB_VM_LOCK_LEAVE_NO_BARRIER();
2373  if (d.stop) return 1;
2374  }
2375  break;
2376 
2377  // unreachable
2378  case T_CLASS:
2379  case T_MODULE:
2380  case T_ICLASS:
2381  default:
2382  rp(obj);
2383  rb_bug("unreachable");
2384  }
2385 
2386  if (data->leave_func(obj) == traverse_stop) {
2387  return 1;
2388  }
2389  else {
2390  return 0;
2391  }
2392 }
2393 
2395  rb_obj_traverse_final_func final_func;
2396  int stopped;
2397 };
2398 
2399 static int
2400 obj_traverse_final_i(st_data_t key, st_data_t val, st_data_t arg)
2401 {
2402  struct rb_obj_traverse_final_data *data = (void *)arg;
2403  if (data->final_func(key)) {
2404  data->stopped = 1;
2405  return ST_STOP;
2406  }
2407  return ST_CONTINUE;
2408 }
2409 
2410 // 0: traverse all
2411 // 1: stopped
2412 static int
2413 rb_obj_traverse(VALUE obj,
2414  rb_obj_traverse_enter_func enter_func,
2415  rb_obj_traverse_leave_func leave_func,
2416  rb_obj_traverse_final_func final_func)
2417 {
2418  struct obj_traverse_data data = {
2419  .enter_func = enter_func,
2420  .leave_func = leave_func,
2421  .rec = NULL,
2422  };
2423 
2424  if (obj_traverse_i(obj, &data)) return 1;
2425  if (final_func && data.rec) {
2426  struct rb_obj_traverse_final_data f = {final_func, 0};
2427  st_foreach(data.rec, obj_traverse_final_i, (st_data_t)&f);
2428  return f.stopped;
2429  }
2430  return 0;
2431 }
2432 
2433 static int
2434 frozen_shareable_p(VALUE obj, bool *made_shareable)
2435 {
2436  if (!RB_TYPE_P(obj, T_DATA)) {
2437  return true;
2438  }
2439  else if (RTYPEDDATA_P(obj)) {
2440  const rb_data_type_t *type = RTYPEDDATA_TYPE(obj);
2441  if (type->flags & RUBY_TYPED_FROZEN_SHAREABLE) {
2442  return true;
2443  }
2444  else if (made_shareable && rb_obj_is_proc(obj)) {
2445  // special path to make shareable Proc.
2446  rb_proc_ractor_make_shareable(obj);
2447  *made_shareable = true;
2448  VM_ASSERT(RB_OBJ_SHAREABLE_P(obj));
2449  return false;
2450  }
2451  }
2452 
2453  return false;
2454 }
2455 
2456 static enum obj_traverse_iterator_result
2457 make_shareable_check_shareable(VALUE obj)
2458 {
2459  VM_ASSERT(!SPECIAL_CONST_P(obj));
2460  bool made_shareable = false;
2461 
2462  if (rb_ractor_shareable_p(obj)) {
2463  return traverse_skip;
2464  }
2465  else if (!frozen_shareable_p(obj, &made_shareable)) {
2466  if (made_shareable) {
2467  return traverse_skip;
2468  }
2469  else {
2470  rb_raise(rb_eRactorError, "can not make shareable object for %"PRIsVALUE, obj);
2471  }
2472  }
2473 
2474  if (!RB_OBJ_FROZEN_RAW(obj)) {
2475  rb_funcall(obj, idFreeze, 0);
2476 
2477  if (UNLIKELY(!RB_OBJ_FROZEN_RAW(obj))) {
2478  rb_raise(rb_eRactorError, "#freeze does not freeze object correctly");
2479  }
2480 
2481  if (RB_OBJ_SHAREABLE_P(obj)) {
2482  return traverse_skip;
2483  }
2484  }
2485 
2486  return traverse_cont;
2487 }
2488 
2489 static enum obj_traverse_iterator_result
2490 mark_shareable(VALUE obj)
2491 {
2493  return traverse_cont;
2494 }
2495 
2496 VALUE
2498 {
2499  rb_obj_traverse(obj,
2500  make_shareable_check_shareable,
2501  null_leave, mark_shareable);
2502  return obj;
2503 }
2504 
2505 VALUE
2507 {
2508  VALUE copy = ractor_copy(obj);
2509  rb_obj_traverse(copy,
2510  make_shareable_check_shareable,
2511  null_leave, mark_shareable);
2512  return copy;
2513 }
2514 
2515 VALUE
2516 rb_ractor_ensure_shareable(VALUE obj, VALUE name)
2517 {
2518  if (!rb_ractor_shareable_p(obj)) {
2519  VALUE message = rb_sprintf("cannot assign unshareable object to %"PRIsVALUE,
2520  name);
2521  rb_exc_raise(rb_exc_new_str(rb_eRactorIsolationError, message));
2522  }
2523  return obj;
2524 }
2525 
2526 static enum obj_traverse_iterator_result
2527 shareable_p_enter(VALUE obj)
2528 {
2529  if (RB_OBJ_SHAREABLE_P(obj)) {
2530  return traverse_skip;
2531  }
2532  else if (RB_TYPE_P(obj, T_CLASS) ||
2533  RB_TYPE_P(obj, T_MODULE) ||
2534  RB_TYPE_P(obj, T_ICLASS)) {
2535  // TODO: remove it
2536  mark_shareable(obj);
2537  return traverse_skip;
2538  }
2539  else if (RB_OBJ_FROZEN_RAW(obj) &&
2540  frozen_shareable_p(obj, NULL)) {
2541  return traverse_cont;
2542  }
2543 
2544  return traverse_stop; // fail
2545 }
2546 
2547 MJIT_FUNC_EXPORTED bool
2548 rb_ractor_shareable_p_continue(VALUE obj)
2549 {
2550  if (rb_obj_traverse(obj,
2551  shareable_p_enter, null_leave,
2552  mark_shareable)) {
2553  return false;
2554  }
2555  else {
2556  return true;
2557  }
2558 }
2559 
2560 #if RACTOR_CHECK_MODE > 0
2561 static enum obj_traverse_iterator_result
2562 reset_belonging_enter(VALUE obj)
2563 {
2564  if (rb_ractor_shareable_p(obj)) {
2565  return traverse_skip;
2566  }
2567  else {
2568  rb_ractor_setup_belonging(obj);
2569  return traverse_cont;
2570  }
2571 }
2572 #endif
2573 
2574 static enum obj_traverse_iterator_result
2575 null_leave(VALUE obj)
2576 {
2577  return traverse_cont;
2578 }
2579 
2580 static VALUE
2581 ractor_reset_belonging(VALUE obj)
2582 {
2583 #if RACTOR_CHECK_MODE > 0
2584  rb_obj_traverse(obj, reset_belonging_enter, null_leave, NULL);
2585 #endif
2586  return obj;
2587 }
2588 
2589 
2591 
2592 // 2: stop search
2593 // 1: skip child
2594 // 0: continue
2595 
2597 static int obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data);
2598 typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_enter_func)(VALUE obj, struct obj_traverse_replace_data *data);
2599 typedef enum obj_traverse_iterator_result (*rb_obj_traverse_replace_leave_func)(VALUE obj, struct obj_traverse_replace_data *data);
2600 
2602  rb_obj_traverse_replace_enter_func enter_func;
2603  rb_obj_traverse_replace_leave_func leave_func;
2604 
2605  st_table *rec;
2606  VALUE rec_hash;
2607 
2608  VALUE replacement;
2609  bool move;
2610 };
2611 
2613  bool stop;
2614  VALUE src;
2615  struct obj_traverse_replace_data *data;
2616 };
2617 
2618 static int
2619 obj_hash_traverse_replace_foreach_i(st_data_t key, st_data_t value, st_data_t argp, int error)
2620 {
2621  return ST_REPLACE;
2622 }
2623 
2624 static int
2625 obj_hash_traverse_replace_i(st_data_t *key, st_data_t *val, st_data_t ptr, int exists)
2626 {
2628  struct obj_traverse_replace_data *data = d->data;
2629 
2630  if (obj_traverse_replace_i(*key, data)) {
2631  d->stop = true;
2632  return ST_STOP;
2633  }
2634  else if (*key != data->replacement) {
2635  VALUE v = *key = data->replacement;
2636  RB_OBJ_WRITTEN(d->src, Qundef, v);
2637  }
2638 
2639  if (obj_traverse_replace_i(*val, data)) {
2640  d->stop = true;
2641  return ST_STOP;
2642  }
2643  else if (*val != data->replacement) {
2644  VALUE v = *val = data->replacement;
2645  RB_OBJ_WRITTEN(d->src, Qundef, v);
2646  }
2647 
2648  return ST_CONTINUE;
2649 }
2650 
2651 static struct st_table *
2652 obj_traverse_replace_rec(struct obj_traverse_replace_data *data)
2653 {
2654  if (UNLIKELY(!data->rec)) {
2655  data->rec_hash = rb_ident_hash_new();
2656  data->rec = rb_hash_st_table(data->rec_hash);
2657  }
2658  return data->rec;
2659 }
2660 
2661 #if USE_TRANSIENT_HEAP
2662 void rb_ary_transient_heap_evacuate(VALUE ary, int promote);
2663 void rb_obj_transient_heap_evacuate(VALUE obj, int promote);
2664 void rb_hash_transient_heap_evacuate(VALUE hash, int promote);
2665 void rb_struct_transient_heap_evacuate(VALUE st, int promote);
2666 #endif
2667 
2668 static void
2669 obj_refer_only_shareables_p_i(VALUE obj, void *ptr)
2670 {
2671  int *pcnt = (int *)ptr;
2672 
2673  if (!rb_ractor_shareable_p(obj)) {
2674  pcnt++;
2675  }
2676 }
2677 
2678 static int
2679 obj_refer_only_shareables_p(VALUE obj)
2680 {
2681  int cnt = 0;
2682  RB_VM_LOCK_ENTER_NO_BARRIER();
2683  {
2684  rb_objspace_reachable_objects_from(obj, obj_refer_only_shareables_p_i, &cnt);
2685  }
2686  RB_VM_LOCK_LEAVE_NO_BARRIER();
2687  return cnt == 0;
2688 }
2689 
2690 static int
2691 obj_traverse_replace_i(VALUE obj, struct obj_traverse_replace_data *data)
2692 {
2693  VALUE replacement;
2694 
2695  if (RB_SPECIAL_CONST_P(obj)) {
2696  data->replacement = obj;
2697  return 0;
2698  }
2699 
2700  switch (data->enter_func(obj, data)) {
2701  case traverse_cont: break;
2702  case traverse_skip: return 0; // skip children
2703  case traverse_stop: return 1; // stop search
2704  }
2705 
2706  replacement = data->replacement;
2707 
2708  if (UNLIKELY(st_lookup(obj_traverse_replace_rec(data), (st_data_t)obj, (st_data_t *)&replacement))) {
2709  data->replacement = replacement;
2710  return 0;
2711  }
2712  else {
2713  st_insert(obj_traverse_replace_rec(data), (st_data_t)obj, (st_data_t)replacement);
2714  }
2715 
2716  if (!data->move) {
2717  obj = replacement;
2718  }
2719 
2720 #define CHECK_AND_REPLACE(v) do { \
2721  VALUE _val = (v); \
2722  if (obj_traverse_replace_i(_val, data)) { return 1; } \
2723  else if (data->replacement != _val) { RB_OBJ_WRITE(obj, &v, data->replacement); } \
2724 } while (0)
2725 
2726  if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
2727  struct gen_ivtbl *ivtbl;
2728  rb_ivar_generic_ivtbl_lookup(obj, &ivtbl);
2729  for (uint32_t i = 0; i < ivtbl->numiv; i++) {
2730  if (ivtbl->ivptr[i] != Qundef) {
2731  CHECK_AND_REPLACE(ivtbl->ivptr[i]);
2732  }
2733  }
2734  }
2735 
2736  switch (BUILTIN_TYPE(obj)) {
2737  // no child node
2738  case T_FLOAT:
2739  case T_BIGNUM:
2740  case T_REGEXP:
2741  case T_FILE:
2742  case T_SYMBOL:
2743  case T_MATCH:
2744  break;
2745  case T_STRING:
2746  rb_str_make_independent(obj);
2747  break;
2748 
2749  case T_OBJECT:
2750  {
2751 #if USE_TRANSIENT_HEAP
2752  if (data->move) rb_obj_transient_heap_evacuate(obj, TRUE);
2753 #endif
2754 
2755  uint32_t len = ROBJECT_NUMIV(obj);
2756  VALUE *ptr = ROBJECT_IVPTR(obj);
2757 
2758  for (uint32_t i=0; i<len; i++) {
2759  if (ptr[i] != Qundef) {
2760  CHECK_AND_REPLACE(ptr[i]);
2761  }
2762  }
2763  }
2764  break;
2765 
2766  case T_ARRAY:
2767  {
2768  rb_ary_cancel_sharing(obj);
2769 #if USE_TRANSIENT_HEAP
2770  if (data->move) rb_ary_transient_heap_evacuate(obj, TRUE);
2771 #endif
2772 
2773  for (int i = 0; i < RARRAY_LENINT(obj); i++) {
2774  VALUE e = rb_ary_entry(obj, i);
2775 
2776  if (obj_traverse_replace_i(e, data)) {
2777  return 1;
2778  }
2779  else if (e != data->replacement) {
2780  RARRAY_ASET(obj, i, data->replacement);
2781  }
2782  }
2783  RB_GC_GUARD(obj);
2784  }
2785  break;
2786 
2787  case T_HASH:
2788  {
2789 #if USE_TRANSIENT_HEAP
2790  if (data->move) rb_hash_transient_heap_evacuate(obj, TRUE);
2791 #endif
2793  .stop = false,
2794  .data = data,
2795  .src = obj,
2796  };
2797  rb_hash_stlike_foreach_with_replace(obj,
2798  obj_hash_traverse_replace_foreach_i,
2799  obj_hash_traverse_replace_i,
2800  (VALUE)&d);
2801  if (d.stop) return 1;
2802  // TODO: rehash here?
2803 
2804  VALUE ifnone = RHASH_IFNONE(obj);
2805  if (obj_traverse_replace_i(ifnone, data)) {
2806  return 1;
2807  }
2808  else if (ifnone != data->replacement) {
2809  RHASH_SET_IFNONE(obj, data->replacement);
2810  }
2811  }
2812  break;
2813 
2814  case T_STRUCT:
2815  {
2816 #if USE_TRANSIENT_HEAP
2817  if (data->move) rb_struct_transient_heap_evacuate(obj, TRUE);
2818 #endif
2819  long len = RSTRUCT_LEN(obj);
2820  const VALUE *ptr = RSTRUCT_CONST_PTR(obj);
2821 
2822  for (long i=0; i<len; i++) {
2823  CHECK_AND_REPLACE(ptr[i]);
2824  }
2825  }
2826  break;
2827 
2828  case T_RATIONAL:
2829  CHECK_AND_REPLACE(RRATIONAL(obj)->num);
2830  CHECK_AND_REPLACE(RRATIONAL(obj)->den);
2831  break;
2832  case T_COMPLEX:
2833  CHECK_AND_REPLACE(RCOMPLEX(obj)->real);
2834  CHECK_AND_REPLACE(RCOMPLEX(obj)->imag);
2835  break;
2836 
2837  case T_DATA:
2838  if (!data->move && obj_refer_only_shareables_p(obj)) {
2839  break;
2840  }
2841  else {
2842  rb_raise(rb_eRactorError, "can not %s %"PRIsVALUE" object.",
2843  data->move ? "move" : "copy", rb_class_of(obj));
2844  }
2845 
2846  case T_IMEMO:
2847  // not supported yet
2848  return 1;
2849 
2850  // unreachable
2851  case T_CLASS:
2852  case T_MODULE:
2853  case T_ICLASS:
2854  default:
2855  rp(obj);
2856  rb_bug("unreachable");
2857  }
2858 
2859  data->replacement = replacement;
2860 
2861  if (data->leave_func(obj, data) == traverse_stop) {
2862  return 1;
2863  }
2864  else {
2865  return 0;
2866  }
2867 }
2868 
2869 // 0: traverse all
2870 // 1: stopped
2871 static VALUE
2872 rb_obj_traverse_replace(VALUE obj,
2873  rb_obj_traverse_replace_enter_func enter_func,
2874  rb_obj_traverse_replace_leave_func leave_func,
2875  bool move)
2876 {
2877  struct obj_traverse_replace_data data = {
2878  .enter_func = enter_func,
2879  .leave_func = leave_func,
2880  .rec = NULL,
2881  .replacement = Qundef,
2882  .move = move,
2883  };
2884 
2885  if (obj_traverse_replace_i(obj, &data)) {
2886  return Qundef;
2887  }
2888  else {
2889  return data.replacement;
2890  }
2891 }
2892 
2893 struct RVALUE {
2894  VALUE flags;
2895  VALUE klass;
2896  VALUE v1;
2897  VALUE v2;
2898  VALUE v3;
2899 };
2900 
2901 static const VALUE fl_users = FL_USER1 | FL_USER2 | FL_USER3 |
2906 
2907 static void
2908 ractor_moved_bang(VALUE obj)
2909 {
2910  // invalidate src object
2911  struct RVALUE *rv = (void *)obj;
2912 
2913  rv->klass = rb_cRactorMovedObject;
2914  rv->v1 = 0;
2915  rv->v2 = 0;
2916  rv->v3 = 0;
2917  rv->flags = rv->flags & ~fl_users;
2918 
2919  // TODO: record moved location
2920 }
2921 
2922 static enum obj_traverse_iterator_result
2923 move_enter(VALUE obj, struct obj_traverse_replace_data *data)
2924 {
2925  if (rb_ractor_shareable_p(obj)) {
2926  data->replacement = obj;
2927  return traverse_skip;
2928  }
2929  else {
2930  data->replacement = rb_obj_alloc(RBASIC_CLASS(obj));
2931  return traverse_cont;
2932  }
2933 }
2934 
2935 void rb_replace_generic_ivar(VALUE clone, VALUE obj); // variable.c
2936 
2937 static enum obj_traverse_iterator_result
2938 move_leave(VALUE obj, struct obj_traverse_replace_data *data)
2939 {
2940  VALUE v = data->replacement;
2941  struct RVALUE *dst = (struct RVALUE *)v;
2942  struct RVALUE *src = (struct RVALUE *)obj;
2943 
2944  dst->flags = (dst->flags & ~fl_users) | (src->flags & fl_users);
2945 
2946  dst->v1 = src->v1;
2947  dst->v2 = src->v2;
2948  dst->v3 = src->v3;
2949 
2950  if (UNLIKELY(FL_TEST_RAW(obj, FL_EXIVAR))) {
2951  rb_replace_generic_ivar(v, obj);
2952  }
2953 
2954  // TODO: generic_ivar
2955 
2956  ractor_moved_bang(obj);
2957  return traverse_cont;
2958 }
2959 
2960 static VALUE
2961 ractor_move(VALUE obj)
2962 {
2963  VALUE val = rb_obj_traverse_replace(obj, move_enter, move_leave, true);
2964  if (val != Qundef) {
2965  return val;
2966  }
2967  else {
2968  rb_raise(rb_eRactorError, "can not move the object");
2969  }
2970 }
2971 
2972 static enum obj_traverse_iterator_result
2973 copy_enter(VALUE obj, struct obj_traverse_replace_data *data)
2974 {
2975  if (rb_ractor_shareable_p(obj)) {
2976  data->replacement = obj;
2977  return traverse_skip;
2978  }
2979  else {
2980  data->replacement = rb_obj_clone(obj);
2981  return traverse_cont;
2982  }
2983 }
2984 
2985 static enum obj_traverse_iterator_result
2986 copy_leave(VALUE obj, struct obj_traverse_replace_data *data)
2987 {
2988  return traverse_cont;
2989 }
2990 
2991 static VALUE
2992 ractor_copy(VALUE obj)
2993 {
2994  VALUE val = rb_obj_traverse_replace(obj, copy_enter, copy_leave, false);
2995  if (val != Qundef) {
2996  return val;
2997  }
2998  else {
2999  rb_raise(rb_eRactorError, "can not copy the object");
3000  }
3001 }
3002 
3003 // Ractor local storage
3004 
3006  const struct rb_ractor_local_storage_type *type;
3007  void *main_cache;
3008 };
3009 
3011  int cnt;
3012  int capa;
3013  rb_ractor_local_key_t *keys;
3014 } freed_ractor_local_keys;
3015 
3016 static int
3017 ractor_local_storage_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
3018 {
3019  struct rb_ractor_local_key_struct *k = (struct rb_ractor_local_key_struct *)key;
3020  if (k->type->mark) (*k->type->mark)((void *)val);
3021  return ST_CONTINUE;
3022 }
3023 
3024 static enum rb_id_table_iterator_result
3025 idkey_local_storage_mark_i(ID id, VALUE val, void *dmy)
3026 {
3027  rb_gc_mark(val);
3028  return ID_TABLE_CONTINUE;
3029 }
3030 
3031 static void
3032 ractor_local_storage_mark(rb_ractor_t *r)
3033 {
3034  if (r->local_storage) {
3035  st_foreach(r->local_storage, ractor_local_storage_mark_i, 0);
3036 
3037  for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
3038  rb_ractor_local_key_t key = freed_ractor_local_keys.keys[i];
3039  st_data_t val;
3040  if (st_delete(r->local_storage, (st_data_t *)&key, &val) &&
3041  key->type->free) {
3042  (*key->type->free)((void *)val);
3043  }
3044  }
3045  }
3046 
3047  if (r->idkey_local_storage) {
3048  rb_id_table_foreach(r->idkey_local_storage, idkey_local_storage_mark_i, NULL);
3049  }
3050 }
3051 
3052 static int
3053 ractor_local_storage_free_i(st_data_t key, st_data_t val, st_data_t dmy)
3054 {
3055  struct rb_ractor_local_key_struct *k = (struct rb_ractor_local_key_struct *)key;
3056  if (k->type->free) (*k->type->free)((void *)val);
3057  return ST_CONTINUE;
3058 }
3059 
3060 static void
3061 ractor_local_storage_free(rb_ractor_t *r)
3062 {
3063  if (r->local_storage) {
3064  st_foreach(r->local_storage, ractor_local_storage_free_i, 0);
3065  st_free_table(r->local_storage);
3066  }
3067 
3068  if (r->idkey_local_storage) {
3069  rb_id_table_free(r->idkey_local_storage);
3070  }
3071 }
3072 
3073 static void
3074 rb_ractor_local_storage_value_mark(void *ptr)
3075 {
3076  rb_gc_mark((VALUE)ptr);
3077 }
3078 
3079 static const struct rb_ractor_local_storage_type ractor_local_storage_type_null = {
3080  NULL,
3081  NULL,
3082 };
3083 
3085  NULL,
3086  ruby_xfree,
3087 };
3088 
3089 static const struct rb_ractor_local_storage_type ractor_local_storage_type_value = {
3090  rb_ractor_local_storage_value_mark,
3091  NULL,
3092 };
3093 
3096 {
3098  key->type = type ? type : &ractor_local_storage_type_null;
3099  key->main_cache = (void *)Qundef;
3100  return key;
3101 }
3102 
3105 {
3106  return rb_ractor_local_storage_ptr_newkey(&ractor_local_storage_type_value);
3107 }
3108 
3109 void
3110 rb_ractor_local_storage_delkey(rb_ractor_local_key_t key)
3111 {
3112  RB_VM_LOCK_ENTER();
3113  {
3114  if (freed_ractor_local_keys.cnt == freed_ractor_local_keys.capa) {
3115  freed_ractor_local_keys.capa = freed_ractor_local_keys.capa ? freed_ractor_local_keys.capa * 2 : 4;
3116  REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, freed_ractor_local_keys.capa);
3117  }
3118  freed_ractor_local_keys.keys[freed_ractor_local_keys.cnt++] = key;
3119  }
3120  RB_VM_LOCK_LEAVE();
3121 }
3122 
3123 static bool
3124 ractor_local_ref(rb_ractor_local_key_t key, void **pret)
3125 {
3126  if (rb_ractor_main_p()) {
3127  if ((VALUE)key->main_cache != Qundef) {
3128  *pret = key->main_cache;
3129  return true;
3130  }
3131  else {
3132  return false;
3133  }
3134  }
3135  else {
3136  rb_ractor_t *cr = GET_RACTOR();
3137 
3138  if (cr->local_storage && st_lookup(cr->local_storage, (st_data_t)key, (st_data_t *)pret)) {
3139  return true;
3140  }
3141  else {
3142  return false;
3143  }
3144  }
3145 }
3146 
3147 static void
3148 ractor_local_set(rb_ractor_local_key_t key, void *ptr)
3149 {
3150  rb_ractor_t *cr = GET_RACTOR();
3151 
3152  if (cr->local_storage == NULL) {
3153  cr->local_storage = st_init_numtable();
3154  }
3155 
3156  st_insert(cr->local_storage, (st_data_t)key, (st_data_t)ptr);
3157 
3158  if (rb_ractor_main_p()) {
3159  key->main_cache = ptr;
3160  }
3161 }
3162 
3163 VALUE
3165 {
3166  VALUE val;
3167  if (ractor_local_ref(key, (void **)&val)) {
3168  return val;
3169  }
3170  else {
3171  return Qnil;
3172  }
3173 }
3174 
3175 bool
3177 {
3178  if (ractor_local_ref(key, (void **)val)) {
3179  return true;
3180  }
3181  else {
3182  return false;
3183  }
3184 }
3185 
3186 void
3188 {
3189  ractor_local_set(key, (void *)val);
3190 }
3191 
3192 void *
3194 {
3195  void *ret;
3196  if (ractor_local_ref(key, &ret)) {
3197  return ret;
3198  }
3199  else {
3200  return NULL;
3201  }
3202 }
3203 
3204 void
3206 {
3207  ractor_local_set(key, ptr);
3208 }
3209 
3210 #define DEFAULT_KEYS_CAPA 0x10
3211 
3212 void
3213 rb_ractor_finish_marking(void)
3214 {
3215  for (int i=0; i<freed_ractor_local_keys.cnt; i++) {
3216  ruby_xfree(freed_ractor_local_keys.keys[i]);
3217  }
3218  freed_ractor_local_keys.cnt = 0;
3219  if (freed_ractor_local_keys.capa > DEFAULT_KEYS_CAPA) {
3220  freed_ractor_local_keys.capa = DEFAULT_KEYS_CAPA;
3221  REALLOC_N(freed_ractor_local_keys.keys, rb_ractor_local_key_t, DEFAULT_KEYS_CAPA);
3222  }
3223 }
3224 
3225 static VALUE
3226 ractor_local_value(rb_execution_context_t *ec, VALUE self, VALUE sym)
3227 {
3228  rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3229  ID id = rb_check_id(&sym);
3230  struct rb_id_table *tbl = cr->idkey_local_storage;
3231  VALUE val;
3232 
3233  if (id && tbl && rb_id_table_lookup(tbl, id, &val)) {
3234  return val;
3235  }
3236  else {
3237  return Qnil;
3238  }
3239 }
3240 
3241 static VALUE
3242 ractor_local_value_set(rb_execution_context_t *ec, VALUE self, VALUE sym, VALUE val)
3243 {
3244  rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
3245  ID id = SYM2ID(rb_to_symbol(sym));
3246  struct rb_id_table *tbl = cr->idkey_local_storage;
3247 
3248  if (tbl == NULL) {
3249  tbl = cr->idkey_local_storage = rb_id_table_create(2);
3250  }
3251  rb_id_table_insert(tbl, id, val);
3252  return val;
3253 }
3254 
3255 #include "ractor.rbinc"
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:91
static VALUE RB_OBJ_FROZEN_RAW(VALUE obj)
This is an implenentation detail of RB_OBJ_FROZEN().
Definition: fl_type.h:912
@ RUBY_FL_SHAREABLE
This flag has something to do with Ractor.
Definition: fl_type.h:298
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition: class.c:837
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition: class.c:869
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a method.
Definition: class.c:1914
#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:67
#define FL_USER3
Old name of RUBY_FL_USER3.
Definition: fl_type.h:75
#define REALLOC_N
Old name of RB_REALLOC_N.
Definition: memory.h:397
#define ALLOC
Old name of RB_ALLOC.
Definition: memory.h:394
#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:79
#define FL_USER10
Old name of RUBY_FL_USER10.
Definition: fl_type.h:82
#define Qundef
Old name of RUBY_Qundef.
#define FL_USER6
Old name of RUBY_FL_USER6.
Definition: fl_type.h:78
#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:73
#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:91
#define SYM2ID
Old name of RB_SYM2ID.
Definition: symbol.h:45
#define FL_USER12
Old name of RUBY_FL_USER12.
Definition: fl_type.h:84
#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:83
#define FL_USER15
Old name of RUBY_FL_USER15.
Definition: fl_type.h:87
#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:80
#define FL_USER14
Old name of RUBY_FL_USER14.
Definition: fl_type.h:86
#define FL_USER17
Old name of RUBY_FL_USER17.
Definition: fl_type.h:89
#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:140
#define FL_USER18
Old name of RUBY_FL_USER18.
Definition: fl_type.h:90
#define FL_USER2
Old name of RUBY_FL_USER2.
Definition: fl_type.h:74
#define FL_USER9
Old name of RUBY_FL_USER9.
Definition: fl_type.h:81
#define Qtrue
Old name of RUBY_Qtrue.
#define FL_USER13
Old name of RUBY_FL_USER13.
Definition: fl_type.h:85
#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:88
#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:77
#define FL_USER4
Old name of RUBY_FL_USER4.
Definition: fl_type.h:76
#define FL_SET_RAW
Old name of RB_FL_SET_RAW.
Definition: fl_type.h:138
#define T_REGEXP
Old name of RUBY_T_REGEXP.
Definition: value_type.h:77
void rb_raise(VALUE exc, const char *fmt,...)
Exception entry point.
Definition: error.c:3025
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition: eval.c:675
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Checks if the given object is of given kind.
Definition: error.c:1049
void rb_bug(const char *fmt,...)
Interpreter panic switch.
Definition: error.c:802
VALUE rb_eRuntimeError
RuntimeError exception.
Definition: error.c:1097
VALUE rb_eStopIteration
StopIteration exception.
Definition: enumerator.c:141
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:1150
VALUE rb_eArgError
ArgumentError exception.
Definition: error.c:1100
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
Definition: eval.c:983
VALUE rb_obj_alloc(VALUE klass)
Allocates an instance of the given class.
Definition: object.c:1909
VALUE rb_cRactor
Ractor class.
Definition: ractor.c:21
VALUE rb_stdin
STDIN constant.
Definition: io.c:198
VALUE rb_stderr
STDERR constant.
Definition: io.c:198
static VALUE rb_class_of(VALUE obj)
Object to class mapping function.
Definition: globals.h:172
VALUE rb_cBasicObject
BasicObject class.
Definition: object.c:48
VALUE rb_obj_clone(VALUE obj)
Produces a shallow copy of the given object.
Definition: object.c:405
VALUE rb_stdout
STDOUT constant.
Definition: io.c:198
#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: rgengc.h:232
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
Definition: rgengc.h:220
static bool rb_enc_asciicompat(rb_encoding *enc)
Queries if the passed encoding is in some sense compatible with ASCII.
Definition: encoding.h:782
rb_encoding * rb_enc_get(VALUE obj)
Identical to rb_enc_get_index(), except the return type.
Definition: encoding.c:1072
static const char * rb_enc_name(rb_encoding *enc)
Queries the (canonical) name of the passed encoding.
Definition: encoding.h:433
VALUE rb_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
Definition: vm_eval.c:1102
VALUE rb_ary_new(void)
Allocates a new, empty array.
Definition: array.c:750
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
Definition: array.c:1308
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
Definition: array.c:1679
void rb_jump_tag(int state)
This function is to re-throw global escapes.
Definition: eval.c:845
void rb_gc_mark(VALUE obj)
Marks an object.
Definition: gc.c:6775
VALUE rb_gc_disable(void)
Disables GC.
Definition: gc.c:10724
VALUE rb_gc_start(void)
Identical to rb_gc(), except the return value.
Definition: gc.c:10286
VALUE rb_gc_enable(void)
(Re-) enables GC.
Definition: gc.c:10687
void rb_hash_foreach(VALUE hash, int(*func)(VALUE key, VALUE val, VALUE arg), VALUE arg)
Iterates over a hash.
VALUE rb_protect(VALUE(*func)(VALUE args), VALUE args, int *state)
Protects a function call from potential global escapes from the function.
VALUE rb_obj_is_proc(VALUE recv)
Queries if the given object is a proc.
Definition: proc.c:175
#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:1733
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition: string.c:1356
VALUE rb_mutex_new(void)
Creates a mutex.
Definition: thread_sync.c:172
void rb_thread_check_ints(void)
Checks for interrupts.
Definition: thread.c:1573
VALUE rb_mutex_unlock(VALUE mutex)
Releases the mutex.
Definition: thread_sync.c:472
VALUE rb_mutex_lock(VALUE mutex)
Attempts to lock the mutex.
Definition: thread_sync.c:399
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:1575
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition: vm_method.c:1117
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition: symbol.c:1066
ID rb_intern(const char *name)
Finds or creates a symbol of the given name.
Definition: symbol.c:782
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
Definition: string.c:11904
#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:1666
VALUE rb_sprintf(const char *fmt,...)
Ruby's extended sprintf(3).
Definition: sprintf.c:1201
VALUE rb_yield(VALUE val)
Yields the block.
Definition: vm_eval.c:1357
#define ALLOCA_N(type, n)
Definition: memory.h:286
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition: memory.h:354
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition: memory.h:161
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
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:3084
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:2506
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:3205
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:3095
VALUE rb_ractor_stderr(void)
Queries the standard error of the current Ractor that is calling this function.
Definition: ractor.c:2146
VALUE rb_ractor_stdin(void)
Queries the standard input of the current Ractor that is calling this function.
Definition: ractor.c:2122
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:2182
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:3187
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:3193
bool rb_ractor_local_storage_value_lookup(rb_ractor_local_key_t key, VALUE *val)
Queries the key.
Definition: ractor.c:3176
VALUE rb_ractor_stdout(void)
Queries the standard output of the current Ractor that is calling this function.
Definition: ractor.c:2134
#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:2497
rb_ractor_local_key_t rb_ractor_local_storage_value_newkey(void)
Issues a new key.
Definition: ractor.c:3104
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:2170
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:2158
VALUE rb_ractor_local_storage_value(rb_ractor_local_key_t key)
Queries the key.
Definition: ractor.c:3164
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition: rarray.h:324
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition: rarray.h:571
#define RARRAY_AREF(a, i)
Definition: rarray.h:588
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
Definition: rbasic.h:152
#define DATA_PTR(obj)
Convenient getter macro.
Definition: rdata.h:71
#define RHASH_SET_IFNONE(h, ifnone)
Destructively updates the default value of the hash.
Definition: rhash.h:105
#define RHASH_IFNONE(h)
Definition: rhash.h:72
static VALUE * ROBJECT_IVPTR(VALUE obj)
Queries the instance variables.
Definition: robject.h:171
static uint32_t ROBJECT_NUMIV(VALUE obj)
Queries the number of instance variables.
Definition: robject.h:145
static char * RSTRING_PTR(VALUE str)
Queries the contents pointer of the string.
Definition: rstring.h:497
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition: rstring.h:95
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:540
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition: rtypeddata.h:441
static const struct rb_data_type_struct * RTYPEDDATA_TYPE(VALUE obj)
Queries for the type of given object.
Definition: rtypeddata.h:563
#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:489
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.
Definition: gc.c:572
This is the struct that holds necessary info for a struct.
Definition: rtypeddata.h:190
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:7522
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_lock.
void rb_native_cond_initialize(rb_nativethread_cond_t *cond)
Fills the passed condition variable with an initial value.
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_initialize.
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_unlock.
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_destroy.
void rb_native_cond_destroy(rb_nativethread_cond_t *cond)
Destroys the passed condition variable.
void rb_native_cond_signal(rb_nativethread_cond_t *cond)
Signals a condition variable.
void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
Waits for the passed condition variable to be signalled.
uintptr_t 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:375
void ruby_xfree(void *ptr)
Deallocates a storage instance.
Definition: gc.c:11775