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