Ruby 4.0.5p0 (2026-05-20 revision 64336ffd0ee9e1f4c05891695a3d7b49cb709721)
ractor_sync.c
1
2// this file is included by ractor.c
3
4struct ractor_port {
5 rb_ractor_t *r;
6 st_data_t id_;
7};
8
9static st_data_t
10ractor_port_id(const struct ractor_port *rp)
11{
12 return rp->id_;
13}
14
15static VALUE rb_cRactorPort;
16
17static VALUE ractor_receive(rb_execution_context_t *ec, const struct ractor_port *rp);
18static VALUE ractor_send(rb_execution_context_t *ec, const struct ractor_port *rp, VALUE obj, VALUE move);
19static VALUE ractor_try_send(rb_execution_context_t *ec, const struct ractor_port *rp, VALUE obj, VALUE move);
20static void ractor_add_port(rb_ractor_t *r, st_data_t id);
21
22static void
23ractor_port_mark(void *ptr)
24{
25 const struct ractor_port *rp = (struct ractor_port *)ptr;
26
27 if (rp->r) {
28 rb_gc_mark(rp->r->pub.self);
29 }
30}
31
32static void
33ractor_port_free(void *ptr)
34{
35 xfree(ptr);
36}
37
38static size_t
39ractor_port_memsize(const void *ptr)
40{
41 return sizeof(struct ractor_port);
42}
43
44static const rb_data_type_t ractor_port_data_type = {
45 "ractor/port",
46 {
47 ractor_port_mark,
48 ractor_port_free,
49 ractor_port_memsize,
50 NULL, // update
51 },
52 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_FROZEN_SHAREABLE,
53};
54
55static st_data_t
56ractor_genid_for_port(rb_ractor_t *cr)
57{
58 // TODO: enough?
59 return cr->sync.next_port_id++;
60}
61
62static struct ractor_port *
63RACTOR_PORT_PTR(VALUE self)
64{
65 VM_ASSERT(rb_typeddata_is_kind_of(self, &ractor_port_data_type));
66 struct ractor_port *rp = DATA_PTR(self);
67 return rp;
68}
69
70static VALUE
71ractor_port_alloc(VALUE klass)
72{
73 struct ractor_port *rp;
74 VALUE rpv = TypedData_Make_Struct(klass, struct ractor_port, &ractor_port_data_type, rp);
75 rb_obj_freeze(rpv);
76 return rpv;
77}
78
79static VALUE
80ractor_port_init(VALUE rpv, rb_ractor_t *r)
81{
82 struct ractor_port *rp = RACTOR_PORT_PTR(rpv);
83
84 rp->r = r;
85 RB_OBJ_WRITTEN(rpv, Qundef, r->pub.self);
86 rp->id_ = ractor_genid_for_port(r);
87
88 ractor_add_port(r, ractor_port_id(rp));
89
90 rb_obj_freeze(rpv);
91
92 return rpv;
93}
94
95/*
96 * call-seq:
97 * Ractor::Port.new -> new_port
98 *
99 * Returns a new Ractor::Port object.
100 */
101static VALUE
102ractor_port_initialize(VALUE self)
103{
104 return ractor_port_init(self, GET_RACTOR());
105}
106
107/* :nodoc: */
108static VALUE
109ractor_port_initialize_copy(VALUE self, VALUE orig)
110{
111 struct ractor_port *dst = RACTOR_PORT_PTR(self);
112 struct ractor_port *src = RACTOR_PORT_PTR(orig);
113 dst->r = src->r;
114 RB_OBJ_WRITTEN(self, Qundef, dst->r->pub.self);
115 dst->id_ = ractor_port_id(src);
116
117 return self;
118}
119
120static VALUE
121ractor_port_new(rb_ractor_t *r)
122{
123 VALUE rpv = ractor_port_alloc(rb_cRactorPort);
124 ractor_port_init(rpv, r);
125 return rpv;
126}
127
128static bool
129ractor_port_p(VALUE self)
130{
131 return rb_typeddata_is_kind_of(self, &ractor_port_data_type);
132}
133
134static VALUE
135ractor_port_receive(rb_execution_context_t *ec, VALUE self)
136{
137 const struct ractor_port *rp = RACTOR_PORT_PTR(self);
138
139 if (rp->r != rb_ec_ractor_ptr(ec)) {
140 rb_raise(rb_eRactorError, "only allowed from the creator Ractor of this port");
141 }
142
143 return ractor_receive(ec, rp);
144}
145
146static VALUE
147ractor_port_send(rb_execution_context_t *ec, VALUE self, VALUE obj, VALUE move)
148{
149 const struct ractor_port *rp = RACTOR_PORT_PTR(self);
150 ractor_send(ec, rp, obj, RTEST(move));
151 return self;
152}
153
154static bool ractor_closed_port_p(rb_execution_context_t *ec, rb_ractor_t *r, const struct ractor_port *rp);
155static bool ractor_close_port(rb_execution_context_t *ec, rb_ractor_t *r, const struct ractor_port *rp);
156
157static VALUE
158ractor_port_closed_p(rb_execution_context_t *ec, VALUE self)
159{
160 const struct ractor_port *rp = RACTOR_PORT_PTR(self);
161
162 if (ractor_closed_port_p(ec, rp->r, rp)) {
163 return Qtrue;
164 }
165 else {
166 return Qfalse;
167 }
168}
169
170static VALUE
171ractor_port_close(rb_execution_context_t *ec, VALUE self)
172{
173 const struct ractor_port *rp = RACTOR_PORT_PTR(self);
174 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
175
176 if (cr != rp->r) {
177 rb_raise(rb_eRactorError, "closing port by other ractors is not allowed");
178 }
179
180 ractor_close_port(ec, cr, rp);
181 return self;
182}
183
184// ractor-internal
185
186// ractor-internal - ractor_basket
187
188enum ractor_basket_type {
189 // basket is empty
190 basket_type_none,
191
192 // value is available
193 basket_type_ref,
194 basket_type_copy,
195 basket_type_move,
196};
197
199 enum ractor_basket_type type;
200 VALUE sender;
201 st_data_t port_id;
202
203 struct {
204 VALUE v;
205 bool exception;
206 } p; // payload
207
208 struct ccan_list_node node;
209};
210
211#if 0
212static inline bool
213ractor_basket_type_p(const struct ractor_basket *b, enum ractor_basket_type type)
214{
215 return b->type == type;
216}
217
218static inline bool
219ractor_basket_none_p(const struct ractor_basket *b)
220{
221 return ractor_basket_type_p(b, basket_type_none);
222}
223#endif
224
225static void
226ractor_basket_mark(const struct ractor_basket *b)
227{
228 rb_gc_mark(b->p.v);
229}
230
231static void
232ractor_basket_free(struct ractor_basket *b)
233{
234 xfree(b);
235}
236
237static struct ractor_basket *
238ractor_basket_alloc(void)
239{
240 struct ractor_basket *b = ALLOC(struct ractor_basket);
241 return b;
242}
243
244// ractor-internal - ractor_queue
245
247 struct ccan_list_head set;
248 bool closed;
249};
250
251static void
252ractor_queue_init(struct ractor_queue *rq)
253{
254 ccan_list_head_init(&rq->set);
255 rq->closed = false;
256}
257
258static struct ractor_queue *
259ractor_queue_new(void)
260{
261 struct ractor_queue *rq = ALLOC(struct ractor_queue);
262 ractor_queue_init(rq);
263 return rq;
264}
265
266static void
267ractor_queue_mark(const struct ractor_queue *rq)
268{
269 const struct ractor_basket *b;
270
271 ccan_list_for_each(&rq->set, b, node) {
272 ractor_basket_mark(b);
273 }
274}
275
276static void
277ractor_queue_free(struct ractor_queue *rq)
278{
279 struct ractor_basket *b, *nxt;
280
281 ccan_list_for_each_safe(&rq->set, b, nxt, node) {
282 ccan_list_del_init(&b->node);
283 ractor_basket_free(b);
284 }
285
286 VM_ASSERT(ccan_list_empty(&rq->set));
287
288 xfree(rq);
289}
290
292static size_t
293ractor_queue_size(const struct ractor_queue *rq)
294{
295 size_t size = 0;
296 const struct ractor_basket *b;
297
298 ccan_list_for_each(&rq->set, b, node) {
299 size++;
300 }
301 return size;
302}
303
304static void
305ractor_queue_close(struct ractor_queue *rq)
306{
307 rq->closed = true;
308}
309
310static void
311ractor_queue_move(struct ractor_queue *dst_rq, struct ractor_queue *src_rq)
312{
313 struct ccan_list_head *src = &src_rq->set;
314 struct ccan_list_head *dst = &dst_rq->set;
315
316 dst->n.next = src->n.next;
317 dst->n.prev = src->n.prev;
318 dst->n.next->prev = &dst->n;
319 dst->n.prev->next = &dst->n;
320 ccan_list_head_init(src);
321}
322
323#if 0
324static struct ractor_basket *
325ractor_queue_head(rb_ractor_t *r, struct ractor_queue *rq)
326{
327 return ccan_list_top(&rq->set, struct ractor_basket, node);
328}
329#endif
330
331static bool
332ractor_queue_empty_p(rb_ractor_t *r, const struct ractor_queue *rq)
333{
334 return ccan_list_empty(&rq->set);
335}
336
337static struct ractor_basket *
338ractor_queue_deq(rb_ractor_t *r, struct ractor_queue *rq)
339{
340 VM_ASSERT(GET_RACTOR() == r);
341
342 return ccan_list_pop(&rq->set, struct ractor_basket, node);
343}
344
345static void
346ractor_queue_enq(rb_ractor_t *r, struct ractor_queue *rq, struct ractor_basket *basket)
347{
348 ccan_list_add_tail(&rq->set, &basket->node);
349}
350
351#if 0
352static void
353rq_dump(const struct ractor_queue *rq)
354{
355 int i=0;
356 struct ractor_basket *b;
357 ccan_list_for_each(&rq->set, b, node) {
358 fprintf(stderr, "%d type:%s %p\n", i, basket_type_name(b->type), (void *)b);
359 i++;
360 }
361}
362#endif
363
364static void ractor_delete_port(rb_ractor_t *cr, st_data_t id, bool locked);
365
366static struct ractor_queue *
367ractor_get_queue(rb_ractor_t *cr, st_data_t id, bool locked)
368{
369 VM_ASSERT(cr == GET_RACTOR());
370
371 struct ractor_queue *rq;
372
373 if (cr->sync.ports && st_lookup(cr->sync.ports, id, (st_data_t *)&rq)) {
374 if (rq->closed && ractor_queue_empty_p(cr, rq)) {
375 ractor_delete_port(cr, id, locked);
376 return NULL;
377 }
378 else {
379 return rq;
380 }
381 }
382 else {
383 return NULL;
384 }
385}
386
387// ractor-internal - ports
388
389static void
390ractor_add_port(rb_ractor_t *r, st_data_t id)
391{
392 struct ractor_queue *rq = ractor_queue_new();
393 ASSERT_ractor_unlocking(r);
394
395 RUBY_DEBUG_LOG("id:%u", (unsigned int)id);
396
397 RACTOR_LOCK(r);
398 {
399 st_insert(r->sync.ports, id, (st_data_t)rq);
400 }
401 RACTOR_UNLOCK(r);
402}
403
404static void
405ractor_delete_port_locked(rb_ractor_t *cr, st_data_t id)
406{
407 ASSERT_ractor_locking(cr);
408
409 RUBY_DEBUG_LOG("id:%u", (unsigned int)id);
410
411 struct ractor_queue *rq;
412
413 if (st_delete(cr->sync.ports, &id, (st_data_t *)&rq)) {
414 ractor_queue_free(rq);
415 }
416 else {
417 VM_ASSERT(0);
418 }
419}
420
421static void
422ractor_delete_port(rb_ractor_t *cr, st_data_t id, bool locked)
423{
424 if (locked) {
425 ractor_delete_port_locked(cr, id);
426 }
427 else {
428 RACTOR_LOCK_SELF(cr);
429 {
430 ractor_delete_port_locked(cr, id);
431 }
432 RACTOR_UNLOCK_SELF(cr);
433 }
434}
435
436static const struct ractor_port *
437ractor_default_port(rb_ractor_t *r)
438{
439 return RACTOR_PORT_PTR(r->sync.default_port_value);
440}
441
442static VALUE
443ractor_default_port_value(rb_ractor_t *r)
444{
445 return r->sync.default_port_value;
446}
447
448static bool
449ractor_closed_port_p(rb_execution_context_t *ec, rb_ractor_t *r, const struct ractor_port *rp)
450{
451 VM_ASSERT(rb_ec_ractor_ptr(ec) == rp->r ? 1 : (ASSERT_ractor_locking(rp->r), 1));
452
453 const struct ractor_queue *rq;
454
455 if (rp->r->sync.ports && st_lookup(rp->r->sync.ports, ractor_port_id(rp), (st_data_t *)&rq)) {
456 return rq->closed;
457 }
458 else {
459 return true;
460 }
461}
462
463static void ractor_deliver_incoming_messages(rb_execution_context_t *ec, rb_ractor_t *cr);
464static bool ractor_queue_empty_p(rb_ractor_t *r, const struct ractor_queue *rq);
465
466static bool
467ractor_close_port(rb_execution_context_t *ec, rb_ractor_t *cr, const struct ractor_port *rp)
468{
469 VM_ASSERT(cr == rp->r);
470 struct ractor_queue *rq = NULL;
471
472 RACTOR_LOCK_SELF(cr);
473 {
474 ractor_deliver_incoming_messages(ec, cr); // check incoming messages
475
476 if (st_lookup(rp->r->sync.ports, ractor_port_id(rp), (st_data_t *)&rq)) {
477 ractor_queue_close(rq);
478
479 if (ractor_queue_empty_p(cr, rq)) {
480 // delete from the table
481 ractor_delete_port(cr, ractor_port_id(rp), true);
482 }
483
484 // TODO: free rq
485 }
486 }
487 RACTOR_UNLOCK_SELF(cr);
488
489 return rq != NULL;
490}
491
492static int
493ractor_free_all_ports_i(st_data_t port_id, st_data_t val, st_data_t dat)
494{
495 struct ractor_queue *rq = (struct ractor_queue *)val;
496 // rb_ractor_t *cr = (rb_ractor_t *)dat;
497
498 ractor_queue_free(rq);
499 return ST_CONTINUE;
500}
501
502static void
503ractor_free_all_ports(rb_ractor_t *cr)
504{
505 if (cr->sync.ports) {
506 st_foreach(cr->sync.ports, ractor_free_all_ports_i, (st_data_t)cr);
507 st_free_table(cr->sync.ports);
508 cr->sync.ports = NULL;
509 }
510
511 if (cr->sync.recv_queue) {
512 ractor_queue_free(cr->sync.recv_queue);
513 cr->sync.recv_queue = NULL;
514 }
515}
516
517#if defined(HAVE_WORKING_FORK)
518static void
519ractor_sync_terminate_atfork(rb_vm_t *vm, rb_ractor_t *r)
520{
521 ractor_free_all_ports(r);
522 r->sync.legacy = Qnil;
523}
524#endif
525
526// Ractor#monitor
527
529 struct ractor_port port;
530 struct ccan_list_node node;
531};
532
533static void
534ractor_mark_monitors(rb_ractor_t *r)
535{
536 const struct ractor_monitor *rm;
537 ccan_list_for_each(&r->sync.monitors, rm, node) {
538 rb_gc_mark(rm->port.r->pub.self);
539 }
540}
541
542static VALUE
543ractor_exit_token(bool exc)
544{
545 if (exc) {
546 RUBY_DEBUG_LOG("aborted");
547 return ID2SYM(idAborted);
548 }
549 else {
550 RUBY_DEBUG_LOG("exited");
551 return ID2SYM(idExited);
552 }
553}
554
555static VALUE
556ractor_monitor(rb_execution_context_t *ec, VALUE self, VALUE port)
557{
558 rb_ractor_t *r = RACTOR_PTR(self);
559 bool terminated = false;
560 const struct ractor_port *rp = RACTOR_PORT_PTR(port);
561 struct ractor_monitor *rm = ALLOC(struct ractor_monitor);
562 rm->port = *rp; // copy port information
563
564 RACTOR_LOCK(r);
565 {
566 if (UNDEF_P(r->sync.legacy)) { // not terminated
567 RUBY_DEBUG_LOG("OK/r:%u -> port:%u@r%u", (unsigned int)rb_ractor_id(r), (unsigned int)ractor_port_id(&rm->port), (unsigned int)rb_ractor_id(rm->port.r));
568 ccan_list_add_tail(&r->sync.monitors, &rm->node);
569 }
570 else {
571 RUBY_DEBUG_LOG("NG/r:%u -> port:%u@r%u", (unsigned int)rb_ractor_id(r), (unsigned int)ractor_port_id(&rm->port), (unsigned int)rb_ractor_id(rm->port.r));
572 terminated = true;
573 }
574 }
575 RACTOR_UNLOCK(r);
576
577 if (terminated) {
578 xfree(rm);
579 ractor_port_send(ec, port, ractor_exit_token(r->sync.legacy_exc), Qfalse);
580
581 return Qfalse;
582 }
583 else {
584 return Qtrue;
585 }
586}
587
588static VALUE
589ractor_unmonitor(rb_execution_context_t *ec, VALUE self, VALUE port)
590{
591 rb_ractor_t *r = RACTOR_PTR(self);
592 const struct ractor_port *rp = RACTOR_PORT_PTR(port);
593
594 RACTOR_LOCK(r);
595 {
596 if (UNDEF_P(r->sync.legacy)) { // not terminated
597 struct ractor_monitor *rm, *nxt;
598
599 ccan_list_for_each_safe(&r->sync.monitors, rm, nxt, node) {
600 if (ractor_port_id(&rm->port) == ractor_port_id(rp)) {
601 RUBY_DEBUG_LOG("r:%u -> port:%u@r%u",
602 (unsigned int)rb_ractor_id(r),
603 (unsigned int)ractor_port_id(&rm->port),
604 (unsigned int)rb_ractor_id(rm->port.r));
605 ccan_list_del(&rm->node);
606 xfree(rm);
607 }
608 }
609 }
610 }
611 RACTOR_UNLOCK(r);
612
613 return self;
614}
615
616static void
617ractor_notify_exit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE legacy, bool exc)
618{
619 RUBY_DEBUG_LOG("exc:%d", exc);
620 VM_ASSERT(!UNDEF_P(legacy));
621 VM_ASSERT(cr->sync.legacy == Qundef);
622
623 RACTOR_LOCK_SELF(cr);
624 {
625 ractor_free_all_ports(cr);
626
627 cr->sync.legacy = legacy;
628 cr->sync.legacy_exc = exc;
629 }
630 RACTOR_UNLOCK_SELF(cr);
631
632 // send token
633
634 VALUE token = ractor_exit_token(exc);
635 struct ractor_monitor *rm, *nxt;
636
637 ccan_list_for_each_safe(&cr->sync.monitors, rm, nxt, node)
638 {
639 RUBY_DEBUG_LOG("port:%u@r%u", (unsigned int)ractor_port_id(&rm->port), (unsigned int)rb_ractor_id(rm->port.r));
640
641 ractor_try_send(ec, &rm->port, token, false);
642
643 ccan_list_del(&rm->node);
644 xfree(rm);
645 }
646
647 VM_ASSERT(ccan_list_empty(&cr->sync.monitors));
648}
649
650// ractor-internal - initialize, mark, free, memsize
651
652static int
653ractor_mark_ports_i(st_data_t key, st_data_t val, st_data_t data)
654{
655 // id -> ractor_queue
656 const struct ractor_queue *rq = (struct ractor_queue *)val;
657 ractor_queue_mark(rq);
658 return ST_CONTINUE;
659}
660
661static void
662ractor_sync_mark(rb_ractor_t *r)
663{
664 rb_gc_mark(r->sync.default_port_value);
665
666 if (r->sync.ports) {
667 ractor_queue_mark(r->sync.recv_queue);
668 st_foreach(r->sync.ports, ractor_mark_ports_i, 0);
669 }
670
671 ractor_mark_monitors(r);
672}
673
674static int
675ractor_sync_free_ports_i(st_data_t _key, st_data_t val, st_data_t _args)
676{
677 struct ractor_queue *queue = (struct ractor_queue *)val;
678
679 ractor_queue_free(queue);
680
681 return ST_CONTINUE;
682}
683
684static void
685ractor_sync_free(rb_ractor_t *r)
686{
687 if (r->sync.recv_queue) {
688 ractor_queue_free(r->sync.recv_queue);
689 }
690
691 // maybe NULL
692 if (r->sync.ports) {
693 st_foreach(r->sync.ports, ractor_sync_free_ports_i, 0);
694 st_free_table(r->sync.ports);
695 r->sync.ports = NULL;
696 }
697}
698
699static size_t
700ractor_sync_memsize(const rb_ractor_t *r)
701{
702 if (r->sync.ports) {
703 return st_table_size(r->sync.ports);
704 }
705 else {
706 return 0;
707 }
708}
709
710static void
711ractor_sync_init(rb_ractor_t *r)
712{
713 // lock
714 rb_native_mutex_initialize(&r->sync.lock);
715
716 // monitors
717 ccan_list_head_init(&r->sync.monitors);
718
719 // waiters
720 ccan_list_head_init(&r->sync.waiters);
721
722 // receiving queue
723 r->sync.recv_queue = ractor_queue_new();
724
725 // ports
726 r->sync.ports = st_init_numtable();
727 r->sync.default_port_value = ractor_port_new(r);
728 FL_SET_RAW(r->sync.default_port_value, RUBY_FL_SHAREABLE); // only default ports are shareable
729
730 // legacy
731 r->sync.legacy = Qundef;
732
733#ifndef RUBY_THREAD_PTHREAD_H
734 rb_native_cond_initialize(&r->sync.wakeup_cond);
735#endif
736}
737
738// Ractor#value
739
740static rb_ractor_t *
741ractor_set_successor_once(rb_ractor_t *r, rb_ractor_t *cr)
742{
743 if (r->sync.successor == NULL) {
744 rb_ractor_t *successor = ATOMIC_PTR_CAS(r->sync.successor, NULL, cr);
745 return successor == NULL ? cr : successor;
746 }
747
748 return r->sync.successor;
749}
750
751static VALUE ractor_reset_belonging(VALUE obj);
752
753static VALUE
754ractor_make_remote_exception(VALUE cause, VALUE sender)
755{
756 VALUE err = rb_exc_new_cstr(rb_eRactorRemoteError, "thrown by remote Ractor.");
757 rb_ivar_set(err, rb_intern("@ractor"), sender);
758 rb_ec_setup_exception(NULL, err, cause);
759 return err;
760}
761
762static VALUE
763ractor_value(rb_execution_context_t *ec, VALUE self)
764{
765 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
766 rb_ractor_t *r = RACTOR_PTR(self);
767 rb_ractor_t *sr = ractor_set_successor_once(r, cr);
768
769 if (sr == cr) {
770 ractor_reset_belonging(r->sync.legacy);
771
772 if (r->sync.legacy_exc) {
773 rb_exc_raise(ractor_make_remote_exception(r->sync.legacy, self));
774 }
775 return r->sync.legacy;
776 }
777 else {
778 rb_raise(rb_eRactorError, "Only the successor ractor can take a value");
779 }
780}
781
782static VALUE ractor_move(VALUE obj); // in this file
783static VALUE ractor_copy(VALUE obj); // in this file
784
785static VALUE
786ractor_prepare_payload(rb_execution_context_t *ec, VALUE obj, enum ractor_basket_type *ptype)
787{
788 switch (*ptype) {
789 case basket_type_ref:
790 return obj;
791 case basket_type_move:
792 return ractor_move(obj);
793 default:
794 if (rb_ractor_shareable_p(obj)) {
795 *ptype = basket_type_ref;
796 return obj;
797 }
798 else {
799 *ptype = basket_type_copy;
800 return ractor_copy(obj);
801 }
802 }
803}
804
805static struct ractor_basket *
806ractor_basket_new(rb_execution_context_t *ec, VALUE obj, enum ractor_basket_type type, bool exc)
807{
808 VALUE v = ractor_prepare_payload(ec, obj, &type);
809
810 struct ractor_basket *b = ractor_basket_alloc();
811 b->type = type;
812 b->p.v = v;
813 b->p.exception = exc;
814 return b;
815}
816
817static VALUE
818ractor_basket_value(struct ractor_basket *b)
819{
820 switch (b->type) {
821 case basket_type_ref:
822 break;
823 case basket_type_copy:
824 case basket_type_move:
825 ractor_reset_belonging(b->p.v);
826 break;
827 default:
828 VM_ASSERT(0); // unreachable
829 }
830
831 VM_ASSERT(!RB_TYPE_P(b->p.v, T_NONE));
832 return b->p.v;
833}
834
835static VALUE
836ractor_basket_accept(struct ractor_basket *b)
837{
838 VALUE v = ractor_basket_value(b);
839
840 if (b->p.exception) {
841 VALUE err = ractor_make_remote_exception(v, b->sender);
842 ractor_basket_free(b);
843 rb_exc_raise(err);
844 }
845
846 ractor_basket_free(b);
847 return v;
848}
849
850// Ractor blocking by receive
851
852enum ractor_wakeup_status {
853 wakeup_none,
854 wakeup_by_send,
855 wakeup_by_interrupt,
856
857 // wakeup_by_close,
858};
859
861 enum ractor_wakeup_status wakeup_status;
862 rb_thread_t *th;
863 struct ccan_list_node node;
864};
865
866#if VM_CHECK_MODE > 0
867static bool
868ractor_waiter_included(rb_ractor_t *cr, rb_thread_t *th)
869{
870 ASSERT_ractor_locking(cr);
871
872 struct ractor_waiter *w;
873
874 ccan_list_for_each(&cr->sync.waiters, w, node) {
875 if (w->th == th) {
876 return true;
877 }
878 }
879
880 return false;
881}
882#endif
883
884#if USE_RUBY_DEBUG_LOG
885
886static const char *
887wakeup_status_str(enum ractor_wakeup_status wakeup_status)
888{
889 switch (wakeup_status) {
890 case wakeup_none: return "none";
891 case wakeup_by_send: return "by_send";
892 case wakeup_by_interrupt: return "by_interrupt";
893 // case wakeup_by_close: return "by_close";
894 }
895 rb_bug("unreachable");
896}
897
898static const char *
899basket_type_name(enum ractor_basket_type type)
900{
901 switch (type) {
902 case basket_type_none: return "none";
903 case basket_type_ref: return "ref";
904 case basket_type_copy: return "copy";
905 case basket_type_move: return "move";
906 }
907 VM_ASSERT(0);
908 return NULL;
909}
910
911#endif // USE_RUBY_DEBUG_LOG
912
913#ifdef RUBY_THREAD_PTHREAD_H
914
915//
916
917#else // win32
918
919static void
920ractor_cond_wait(rb_ractor_t *r)
921{
922#if RACTOR_CHECK_MODE > 0
923 VALUE locked_by = r->sync.locked_by;
924 r->sync.locked_by = Qnil;
925#endif
926 rb_native_cond_wait(&r->sync.wakeup_cond, &r->sync.lock);
927
928#if RACTOR_CHECK_MODE > 0
929 r->sync.locked_by = locked_by;
930#endif
931}
932
933static void *
934ractor_wait_no_gvl(void *ptr)
935{
936 struct ractor_waiter *waiter = (struct ractor_waiter *)ptr;
937 rb_ractor_t *cr = waiter->th->ractor;
938
939 RACTOR_LOCK_SELF(cr);
940 {
941 if (waiter->wakeup_status == wakeup_none) {
942 ractor_cond_wait(cr);
943 }
944 }
945 RACTOR_UNLOCK_SELF(cr);
946 return NULL;
947}
948
949static void
950rb_ractor_sched_wait(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_function_t *ubf, void *ptr)
951{
952 struct ractor_waiter *waiter = (struct ractor_waiter *)ptr;
953
954 RACTOR_UNLOCK(cr);
955 {
956 rb_nogvl(ractor_wait_no_gvl, waiter,
957 ubf, waiter,
959 }
960 RACTOR_LOCK(cr);
961}
962
963static void
964rb_ractor_sched_wakeup(rb_ractor_t *r, rb_thread_t *th)
965{
966 // ractor lock is acquired
967 rb_native_cond_broadcast(&r->sync.wakeup_cond);
968}
969#endif
970
971static bool
972ractor_wakeup_all(rb_ractor_t *r, enum ractor_wakeup_status wakeup_status)
973{
974 ASSERT_ractor_unlocking(r);
975
976 RUBY_DEBUG_LOG("r:%u wakeup:%s", rb_ractor_id(r), wakeup_status_str(wakeup_status));
977
978 bool wakeup_p = false;
979
980 RACTOR_LOCK(r);
981 while (1) {
982 struct ractor_waiter *waiter = ccan_list_pop(&r->sync.waiters, struct ractor_waiter, node);
983
984 if (waiter) {
985 VM_ASSERT(waiter->wakeup_status == wakeup_none);
986
987 waiter->wakeup_status = wakeup_status;
988 rb_ractor_sched_wakeup(r, waiter->th);
989
990 wakeup_p = true;
991 }
992 else {
993 break;
994 }
995 }
996 RACTOR_UNLOCK(r);
997
998 return wakeup_p;
999}
1000
1001static void
1002ubf_ractor_wait(void *ptr)
1003{
1004 struct ractor_waiter *waiter = (struct ractor_waiter *)ptr;
1005
1006 rb_thread_t *th = waiter->th;
1007 rb_ractor_t *r = th->ractor;
1008
1009 // clear ubf and nobody can kick UBF
1010 th->unblock.func = NULL;
1011 th->unblock.arg = NULL;
1012
1013 rb_native_mutex_unlock(&th->interrupt_lock);
1014 {
1015 RACTOR_LOCK(r);
1016 {
1017 if (waiter->wakeup_status == wakeup_none) {
1018 RUBY_DEBUG_LOG("waiter:%p", (void *)waiter);
1019
1020 waiter->wakeup_status = wakeup_by_interrupt;
1021 ccan_list_del(&waiter->node);
1022
1023 rb_ractor_sched_wakeup(r, waiter->th);
1024 }
1025 }
1026 RACTOR_UNLOCK(r);
1027 }
1028 rb_native_mutex_lock(&th->interrupt_lock);
1029}
1030
1031static enum ractor_wakeup_status
1032ractor_wait(rb_execution_context_t *ec, rb_ractor_t *cr)
1033{
1034 rb_thread_t *th = rb_ec_thread_ptr(ec);
1035
1036 struct ractor_waiter waiter = {
1037 .wakeup_status = wakeup_none,
1038 .th = th,
1039 };
1040
1041 RUBY_DEBUG_LOG("wait%s", "");
1042
1043 ASSERT_ractor_locking(cr);
1044
1045 VM_ASSERT(GET_RACTOR() == cr);
1046 VM_ASSERT(!ractor_waiter_included(cr, th));
1047
1048 ccan_list_add_tail(&cr->sync.waiters, &waiter.node);
1049
1050 // resume another ready thread and wait for an event
1051 rb_ractor_sched_wait(ec, cr, ubf_ractor_wait, &waiter);
1052
1053 if (waiter.wakeup_status == wakeup_none) {
1054 ccan_list_del(&waiter.node);
1055 }
1056
1057 RUBY_DEBUG_LOG("wakeup_status:%s", wakeup_status_str(waiter.wakeup_status));
1058
1059 RACTOR_UNLOCK_SELF(cr);
1060 {
1061 rb_ec_check_ints(ec);
1062 }
1063 RACTOR_LOCK_SELF(cr);
1064
1065 VM_ASSERT(!ractor_waiter_included(cr, th));
1066 return waiter.wakeup_status;
1067}
1068
1069static void
1070ractor_deliver_incoming_messages(rb_execution_context_t *ec, rb_ractor_t *cr)
1071{
1072 ASSERT_ractor_locking(cr);
1073 struct ractor_queue *recv_q = cr->sync.recv_queue;
1074
1075 struct ractor_basket *b;
1076 while ((b = ractor_queue_deq(cr, recv_q)) != NULL) {
1077 ractor_queue_enq(cr, ractor_get_queue(cr, b->port_id, true), b);
1078 }
1079}
1080
1081static bool
1082ractor_check_received(rb_ractor_t *cr, struct ractor_queue *messages)
1083{
1084 struct ractor_queue *received_queue = cr->sync.recv_queue;
1085 bool received = false;
1086
1087 ASSERT_ractor_locking(cr);
1088
1089 if (ractor_queue_empty_p(cr, received_queue)) {
1090 RUBY_DEBUG_LOG("empty");
1091 }
1092 else {
1093 received = true;
1094
1095 // messages <- incoming
1096 ractor_queue_init(messages);
1097 ractor_queue_move(messages, received_queue);
1098 }
1099
1100 VM_ASSERT(ractor_queue_empty_p(cr, received_queue));
1101
1102 RUBY_DEBUG_LOG("received:%d", received);
1103 return received;
1104}
1105
1106static void
1107ractor_wait_receive(rb_execution_context_t *ec, rb_ractor_t *cr)
1108{
1109 struct ractor_queue messages;
1110 bool deliverred = false;
1111
1112 RACTOR_LOCK_SELF(cr);
1113 {
1114 if (ractor_check_received(cr, &messages)) {
1115 deliverred = true;
1116 }
1117 else {
1118 ractor_wait(ec, cr);
1119 }
1120 }
1121 RACTOR_UNLOCK_SELF(cr);
1122
1123 if (deliverred) {
1124 VM_ASSERT(!ractor_queue_empty_p(cr, &messages));
1125 struct ractor_basket *b;
1126
1127 while ((b = ractor_queue_deq(cr, &messages)) != NULL) {
1128 ractor_queue_enq(cr, ractor_get_queue(cr, b->port_id, false), b);
1129 }
1130 }
1131}
1132
1133static VALUE
1134ractor_try_receive(rb_execution_context_t *ec, rb_ractor_t *cr, const struct ractor_port *rp)
1135{
1136 struct ractor_queue *rq = ractor_get_queue(cr, ractor_port_id(rp), false);
1137
1138 if (rq == NULL) {
1139 rb_raise(rb_eRactorClosedError, "The port was already closed");
1140 }
1141
1142 struct ractor_basket *b = ractor_queue_deq(cr, rq);
1143
1144 if (rq->closed && ractor_queue_empty_p(cr, rq)) {
1145 ractor_delete_port(cr, ractor_port_id(rp), false);
1146 }
1147
1148 if (b) {
1149 return ractor_basket_accept(b);
1150 }
1151 else {
1152 return Qundef;
1153 }
1154}
1155
1156static VALUE
1157ractor_receive(rb_execution_context_t *ec, const struct ractor_port *rp)
1158{
1159 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1160 VM_ASSERT(cr == rp->r);
1161
1162 RUBY_DEBUG_LOG("port:%u", (unsigned int)ractor_port_id(rp));
1163
1164 while (1) {
1165 VALUE v = ractor_try_receive(ec, cr, rp);
1166
1167 if (v != Qundef) {
1168 return v;
1169 }
1170 else {
1171 ractor_wait_receive(ec, cr);
1172 }
1173 }
1174}
1175
1176// Ractor#send
1177
1178static void
1179ractor_send_basket(rb_execution_context_t *ec, const struct ractor_port *rp, struct ractor_basket *b, bool raise_on_error)
1180{
1181 bool closed = false;
1182
1183 RUBY_DEBUG_LOG("port:%u@r%u b:%s v:%p", (unsigned int)ractor_port_id(rp), rb_ractor_id(rp->r), basket_type_name(b->type), (void *)b->p.v);
1184
1185 RACTOR_LOCK(rp->r);
1186 {
1187 if (ractor_closed_port_p(ec, rp->r, rp)) {
1188 closed = true;
1189 }
1190 else {
1191 b->port_id = ractor_port_id(rp);
1192 ractor_queue_enq(rp->r, rp->r->sync.recv_queue, b);
1193 }
1194 }
1195 RACTOR_UNLOCK(rp->r);
1196
1197 // NOTE: ref r -> b->p.v is created, but Ractor is unprotected object, so no problem on that.
1198
1199 if (!closed) {
1200 ractor_wakeup_all(rp->r, wakeup_by_send);
1201 }
1202 else {
1203 RUBY_DEBUG_LOG("closed:%u@r%u", (unsigned int)ractor_port_id(rp), rb_ractor_id(rp->r));
1204
1205 if (raise_on_error) {
1206 ractor_basket_free(b);
1207 rb_raise(rb_eRactorClosedError, "The port was already closed");
1208 }
1209 }
1210}
1211
1212static VALUE
1213ractor_send0(rb_execution_context_t *ec, const struct ractor_port *rp, VALUE obj, VALUE move, bool raise_on_error)
1214{
1215 struct ractor_basket *b = ractor_basket_new(ec, obj, RTEST(move) ? basket_type_move : basket_type_none, false);
1216 ractor_send_basket(ec, rp, b, raise_on_error);
1217 RB_GC_GUARD(obj);
1218 return rp->r->pub.self;
1219}
1220
1221static VALUE
1222ractor_send(rb_execution_context_t *ec, const struct ractor_port *rp, VALUE obj, VALUE move)
1223{
1224 return ractor_send0(ec, rp, obj, move, true);
1225}
1226
1227static VALUE
1228ractor_try_send(rb_execution_context_t *ec, const struct ractor_port *rp, VALUE obj, VALUE move)
1229{
1230 return ractor_send0(ec, rp, obj, move, false);
1231}
1232
1233// Ractor::Selector
1234
1236 struct st_table *ports; // rpv -> rp
1237
1238};
1239
1240static int
1241ractor_selector_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
1242{
1243 rb_gc_mark((VALUE)key); // rpv
1244
1245 return ST_CONTINUE;
1246}
1247
1248static void
1249ractor_selector_mark(void *ptr)
1250{
1251 struct ractor_selector *s = ptr;
1252
1253 if (s->ports) {
1254 st_foreach(s->ports, ractor_selector_mark_i, 0);
1255 }
1256}
1257
1258static void
1259ractor_selector_free(void *ptr)
1260{
1261 struct ractor_selector *s = ptr;
1262 st_free_table(s->ports);
1263 ruby_xfree(ptr);
1264}
1265
1266static size_t
1267ractor_selector_memsize(const void *ptr)
1268{
1269 const struct ractor_selector *s = ptr;
1270 size_t size = sizeof(struct ractor_selector);
1271 if (s->ports) {
1272 size += st_memsize(s->ports);
1273 }
1274 return size;
1275}
1276
1277static const rb_data_type_t ractor_selector_data_type = {
1278 "ractor/selector",
1279 {
1280 ractor_selector_mark,
1281 ractor_selector_free,
1282 ractor_selector_memsize,
1283 NULL, // update
1284 },
1285 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
1286};
1287
1288static struct ractor_selector *
1289RACTOR_SELECTOR_PTR(VALUE selv)
1290{
1291 VM_ASSERT(rb_typeddata_is_kind_of(selv, &ractor_selector_data_type));
1292 return (struct ractor_selector *)DATA_PTR(selv);
1293}
1294
1295// Ractor::Selector.new
1296
1297static VALUE
1298ractor_selector_create(VALUE klass)
1299{
1300 struct ractor_selector *s;
1301 VALUE selv = TypedData_Make_Struct(klass, struct ractor_selector, &ractor_selector_data_type, s);
1302 s->ports = st_init_numtable(); // TODO
1303 return selv;
1304}
1305
1306// Ractor::Selector#add(r)
1307
1308/*
1309 * call-seq:
1310 * add(ractor) -> ractor
1311 *
1312 * Adds _ractor_ to +self+. Raises an exception if _ractor_ is already added.
1313 * Returns _ractor_.
1314 */
1315static VALUE
1316ractor_selector_add(VALUE selv, VALUE rpv)
1317{
1318 if (!ractor_port_p(rpv)) {
1319 rb_raise(rb_eArgError, "Not a Ractor::Port object");
1320 }
1321
1322 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1323 const struct ractor_port *rp = RACTOR_PORT_PTR(rpv);
1324
1325 if (st_lookup(s->ports, (st_data_t)rpv, NULL)) {
1326 rb_raise(rb_eArgError, "already added");
1327 }
1328
1329 st_insert(s->ports, (st_data_t)rpv, (st_data_t)rp);
1330 RB_OBJ_WRITTEN(selv, Qundef, rpv);
1331
1332 return selv;
1333}
1334
1335// Ractor::Selector#remove(r)
1336
1337/* call-seq:
1338 * remove(ractor) -> ractor
1339 *
1340 * Removes _ractor_ from +self+. Raises an exception if _ractor_ is not added.
1341 * Returns the removed _ractor_.
1342 */
1343static VALUE
1344ractor_selector_remove(VALUE selv, VALUE rpv)
1345{
1346 if (!ractor_port_p(rpv)) {
1347 rb_raise(rb_eArgError, "Not a Ractor::Port object");
1348 }
1349
1350 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1351
1352 if (!st_lookup(s->ports, (st_data_t)rpv, NULL)) {
1353 rb_raise(rb_eArgError, "not added yet");
1354 }
1355
1356 st_delete(s->ports, (st_data_t *)&rpv, NULL);
1357
1358 return selv;
1359}
1360
1361// Ractor::Selector#clear
1362
1363/*
1364 * call-seq:
1365 * clear -> self
1366 *
1367 * Removes all ractors from +self+. Raises +self+.
1368 */
1369static VALUE
1370ractor_selector_clear(VALUE selv)
1371{
1372 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1373 st_clear(s->ports);
1374 return selv;
1375}
1376
1377/*
1378 * call-seq:
1379 * empty? -> true or false
1380 *
1381 * Returns +true+ if no ractor is added.
1382 */
1383static VALUE
1384ractor_selector_empty_p(VALUE selv)
1385{
1386 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1387 return s->ports->num_entries == 0 ? Qtrue : Qfalse;
1388}
1389
1390// Ractor::Selector#wait
1391
1393 rb_ractor_t *cr;
1394 rb_execution_context_t *ec;
1395 bool found;
1396 VALUE v;
1397 VALUE rpv;
1398};
1399
1400static int
1401ractor_selector_wait_i(st_data_t key, st_data_t val, st_data_t data)
1402{
1403 struct ractor_selector_wait_data *p = (struct ractor_selector_wait_data *)data;
1404 const struct ractor_port *rp = (const struct ractor_port *)val;
1405
1406 VALUE v = ractor_try_receive(p->ec, p->cr, rp);
1407
1408 if (v != Qundef) {
1409 p->found = true;
1410 p->v = v;
1411 p->rpv = (VALUE)key;
1412 return ST_STOP;
1413 }
1414 else {
1415 return ST_CONTINUE;
1416 }
1417}
1418
1419static VALUE
1420ractor_selector__wait(rb_execution_context_t *ec, VALUE selector)
1421{
1422 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1423 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selector);
1424
1425 struct ractor_selector_wait_data data = {
1426 .ec = ec,
1427 .cr = cr,
1428 .found = false,
1429 };
1430
1431 while (1) {
1432 st_foreach(s->ports, ractor_selector_wait_i, (st_data_t)&data);
1433
1434 if (data.found) {
1435 return rb_ary_new_from_args(2, data.rpv, data.v);
1436 }
1437
1438 ractor_wait_receive(ec, cr);
1439 }
1440}
1441
1442/*
1443 * call-seq:
1444 * wait(receive: false, yield_value: undef, move: false) -> [ractor, value]
1445 *
1446 * Waits until any ractor in _selector_ can be active.
1447 */
1448static VALUE
1449ractor_selector_wait(VALUE selector)
1450{
1451 return ractor_selector__wait(GET_EC(), selector);
1452}
1453
1454static VALUE
1455ractor_selector_new(int argc, VALUE *ractors, VALUE klass)
1456{
1457 VALUE selector = ractor_selector_create(klass);
1458
1459 for (int i=0; i<argc; i++) {
1460 ractor_selector_add(selector, ractors[i]);
1461 }
1462
1463 return selector;
1464}
1465
1466static VALUE
1467ractor_select_internal(rb_execution_context_t *ec, VALUE self, VALUE ports)
1468{
1469 VALUE selector = ractor_selector_new(RARRAY_LENINT(ports), (VALUE *)RARRAY_CONST_PTR(ports), rb_cRactorSelector);
1470 VALUE result = ractor_selector__wait(ec, selector);
1471
1472 RB_GC_GUARD(selector);
1473 RB_GC_GUARD(ports);
1474 return result;
1475}
1476
1477#ifndef USE_RACTOR_SELECTOR
1478#define USE_RACTOR_SELECTOR 0
1479#endif
1480
1481RUBY_SYMBOL_EXPORT_BEGIN
1482void rb_init_ractor_selector(void);
1483RUBY_SYMBOL_EXPORT_END
1484
1485/*
1486 * Document-class: Ractor::Selector
1487 * :nodoc: currently
1488 *
1489 * Selects multiple Ractors to be activated.
1490 */
1491void
1492rb_init_ractor_selector(void)
1493{
1494 rb_cRactorSelector = rb_define_class_under(rb_cRactor, "Selector", rb_cObject);
1495 rb_undef_alloc_func(rb_cRactorSelector);
1496
1497 rb_define_singleton_method(rb_cRactorSelector, "new", ractor_selector_new , -1);
1498 rb_define_method(rb_cRactorSelector, "add", ractor_selector_add, 1);
1499 rb_define_method(rb_cRactorSelector, "remove", ractor_selector_remove, 1);
1500 rb_define_method(rb_cRactorSelector, "clear", ractor_selector_clear, 0);
1501 rb_define_method(rb_cRactorSelector, "empty?", ractor_selector_empty_p, 0);
1502 rb_define_method(rb_cRactorSelector, "wait", ractor_selector_wait, 0);
1503}
1504
1505static void
1506Init_RactorPort(void)
1507{
1508 rb_cRactorPort = rb_define_class_under(rb_cRactor, "Port", rb_cObject);
1509 rb_define_alloc_func(rb_cRactorPort, ractor_port_alloc);
1510 rb_define_method(rb_cRactorPort, "initialize", ractor_port_initialize, 0);
1511 rb_define_method(rb_cRactorPort, "initialize_copy", ractor_port_initialize_copy, 1);
1512
1513#if USE_RACTOR_SELECTOR
1514 rb_init_ractor_selector();
1515#endif
1516}
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
@ RUBY_FL_SHAREABLE
This flag has something to do with Ractor.
Definition fl_type.h:280
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1509
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define Qundef
Old name of RUBY_Qundef.
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define T_NONE
Old name of RUBY_T_NONE.
Definition value_type.h:74
#define Qtrue
Old name of RUBY_Qtrue.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define FL_SET_RAW
Old name of RB_FL_SET_RAW.
Definition fl_type.h:129
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:653
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:1381
VALUE rb_cRactor
Ractor class.
Definition ractor.c:24
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1342
#define RB_OBJ_WRITTEN(old, oldv, young)
Identical to RB_OBJ_WRITE(), except it doesn't write any values, but only a WB declaration.
Definition gc.h:615
VALUE rb_ary_new_from_args(long n,...)
Constructs an array from the passed objects.
#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:1671
void rb_unblock_function_t(void *)
This is the type of UBFs.
Definition thread.h:336
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:2030
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1664
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
static bool rb_ractor_shareable_p(VALUE obj)
Queries if multiple Ractors can share the passed object or not.
Definition ractor.h:249
#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:1593
#define RBIMPL_ATTR_MAYBE_UNUSED()
Wraps (or simulates) [[maybe_unused]].
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
Definition memory.h:167
VALUE type(ANYARGS)
ANYARGS-ed function type.
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:281
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
Definition rarray.h:52
#define DATA_PTR(obj)
Convenient getter macro.
Definition rdata.h:67
struct rb_data_type_struct rb_data_type_t
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:205
#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:508
#define RTEST
This is an old name of RB_TEST.
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_lock.
void rb_native_cond_initialize(rb_nativethread_cond_t *cond)
Fills the passed condition variable with an initial value.
void rb_native_cond_broadcast(rb_nativethread_cond_t *cond)
Signals a condition variable.
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_initialize.
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_unlock.
void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
Waits for the passed condition variable to be signalled.
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376
void ruby_xfree(void *ptr)
Deallocates a storage instance.
Definition gc.c:5262