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