Ruby 4.1.0dev (2026-03-06 revision 9aca729140424bbf465c11ab8ab53e5cc6602c01)
ractor_sync.c (9aca729140424bbf465c11ab8ab53e5cc6602c01)
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
838enum ractor_wakeup_status {
839 wakeup_none,
840 wakeup_by_send,
841 wakeup_by_interrupt,
842
843 // wakeup_by_close,
844};
845
847 enum ractor_wakeup_status wakeup_status;
848 rb_thread_t *th;
849 struct ccan_list_node node;
850};
851
852#if VM_CHECK_MODE > 0
853static bool
854ractor_waiter_included(rb_ractor_t *cr, rb_thread_t *th)
855{
856 ASSERT_ractor_locking(cr);
857
858 struct ractor_waiter *w;
859
860 ccan_list_for_each(&cr->sync.waiters, w, node) {
861 if (w->th == th) {
862 return true;
863 }
864 }
865
866 return false;
867}
868#endif
869
870#if USE_RUBY_DEBUG_LOG
871
872static const char *
873wakeup_status_str(enum ractor_wakeup_status wakeup_status)
874{
875 switch (wakeup_status) {
876 case wakeup_none: return "none";
877 case wakeup_by_send: return "by_send";
878 case wakeup_by_interrupt: return "by_interrupt";
879 // case wakeup_by_close: return "by_close";
880 }
881 rb_bug("unreachable");
882}
883
884static const char *
885basket_type_name(enum ractor_basket_type type)
886{
887 switch (type) {
888 case basket_type_none: return "none";
889 case basket_type_ref: return "ref";
890 case basket_type_copy: return "copy";
891 case basket_type_move: return "move";
892 }
893 VM_ASSERT(0);
894 return NULL;
895}
896
897#endif // USE_RUBY_DEBUG_LOG
898
899#ifdef RUBY_THREAD_PTHREAD_H
900
901//
902
903#else // win32
904
905static void
906ractor_cond_wait(rb_ractor_t *r)
907{
908#if RACTOR_CHECK_MODE > 0
909 VALUE locked_by = r->sync.locked_by;
910 r->sync.locked_by = Qnil;
911#endif
912 rb_native_cond_wait(&r->sync.wakeup_cond, &r->sync.lock);
913
914#if RACTOR_CHECK_MODE > 0
915 r->sync.locked_by = locked_by;
916#endif
917}
918
919static void *
920ractor_wait_no_gvl(void *ptr)
921{
922 struct ractor_waiter *waiter = (struct ractor_waiter *)ptr;
923 rb_ractor_t *cr = waiter->th->ractor;
924
925 RACTOR_LOCK_SELF(cr);
926 {
927 if (waiter->wakeup_status == wakeup_none) {
928 ractor_cond_wait(cr);
929 }
930 }
931 RACTOR_UNLOCK_SELF(cr);
932 return NULL;
933}
934
935static void
936rb_ractor_sched_wait(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_function_t *ubf, void *ptr)
937{
938 struct ractor_waiter *waiter = (struct ractor_waiter *)ptr;
939
940 RACTOR_UNLOCK(cr);
941 {
942 rb_nogvl(ractor_wait_no_gvl, waiter,
943 ubf, waiter,
945 }
946 RACTOR_LOCK(cr);
947}
948
949static void
950rb_ractor_sched_wakeup(rb_ractor_t *r, rb_thread_t *th)
951{
952 // ractor lock is acquired
953 rb_native_cond_broadcast(&r->sync.wakeup_cond);
954}
955#endif
956
957static bool
958ractor_wakeup_all(rb_ractor_t *r, enum ractor_wakeup_status wakeup_status)
959{
960 ASSERT_ractor_unlocking(r);
961
962 RUBY_DEBUG_LOG("r:%u wakeup:%s", rb_ractor_id(r), wakeup_status_str(wakeup_status));
963
964 bool wakeup_p = false;
965
966 RACTOR_LOCK(r);
967 while (1) {
968 struct ractor_waiter *waiter = ccan_list_pop(&r->sync.waiters, struct ractor_waiter, node);
969
970 if (waiter) {
971 VM_ASSERT(waiter->wakeup_status == wakeup_none);
972
973 waiter->wakeup_status = wakeup_status;
974 rb_ractor_sched_wakeup(r, waiter->th);
975
976 wakeup_p = true;
977 }
978 else {
979 break;
980 }
981 }
982 RACTOR_UNLOCK(r);
983
984 return wakeup_p;
985}
986
987static void
988ubf_ractor_wait(void *ptr)
989{
990 struct ractor_waiter *waiter = (struct ractor_waiter *)ptr;
991
992 rb_thread_t *th = waiter->th;
993 rb_ractor_t *r = th->ractor;
994
995 // clear ubf and nobody can kick UBF
996 th->unblock.func = NULL;
997 th->unblock.arg = NULL;
998
999 rb_native_mutex_unlock(&th->interrupt_lock);
1000 {
1001 RACTOR_LOCK(r);
1002 {
1003 if (waiter->wakeup_status == wakeup_none) {
1004 RUBY_DEBUG_LOG("waiter:%p", (void *)waiter);
1005
1006 waiter->wakeup_status = wakeup_by_interrupt;
1007 ccan_list_del(&waiter->node);
1008
1009 rb_ractor_sched_wakeup(r, waiter->th);
1010 }
1011 }
1012 RACTOR_UNLOCK(r);
1013 }
1014 rb_native_mutex_lock(&th->interrupt_lock);
1015}
1016
1017static enum ractor_wakeup_status
1018ractor_wait(rb_execution_context_t *ec, rb_ractor_t *cr)
1019{
1020 rb_thread_t *th = rb_ec_thread_ptr(ec);
1021
1022 struct ractor_waiter waiter = {
1023 .wakeup_status = wakeup_none,
1024 .th = th,
1025 };
1026
1027 RUBY_DEBUG_LOG("wait%s", "");
1028
1029 ASSERT_ractor_locking(cr);
1030
1031 VM_ASSERT(GET_RACTOR() == cr);
1032 VM_ASSERT(!ractor_waiter_included(cr, th));
1033
1034 ccan_list_add_tail(&cr->sync.waiters, &waiter.node);
1035
1036 // resume another ready thread and wait for an event
1037 rb_ractor_sched_wait(ec, cr, ubf_ractor_wait, &waiter);
1038
1039 if (waiter.wakeup_status == wakeup_none) {
1040 ccan_list_del(&waiter.node);
1041 }
1042
1043 RUBY_DEBUG_LOG("wakeup_status:%s", wakeup_status_str(waiter.wakeup_status));
1044
1045 RACTOR_UNLOCK_SELF(cr);
1046 {
1047 rb_ec_check_ints(ec);
1048 }
1049 RACTOR_LOCK_SELF(cr);
1050
1051 VM_ASSERT(!ractor_waiter_included(cr, th));
1052 return waiter.wakeup_status;
1053}
1054
1055static void
1056ractor_deliver_incoming_messages(rb_execution_context_t *ec, rb_ractor_t *cr)
1057{
1058 ASSERT_ractor_locking(cr);
1059 struct ractor_queue *recv_q = cr->sync.recv_queue;
1060
1061 struct ractor_basket *b;
1062 while ((b = ractor_queue_deq(cr, recv_q)) != NULL) {
1063 ractor_queue_enq(cr, ractor_get_queue(cr, b->port_id, true), b);
1064 }
1065}
1066
1067static bool
1068ractor_check_received(rb_ractor_t *cr, struct ractor_queue *messages)
1069{
1070 struct ractor_queue *received_queue = cr->sync.recv_queue;
1071 bool received = false;
1072
1073 ASSERT_ractor_locking(cr);
1074
1075 if (ractor_queue_empty_p(cr, received_queue)) {
1076 RUBY_DEBUG_LOG("empty");
1077 }
1078 else {
1079 received = true;
1080
1081 // messages <- incoming
1082 ractor_queue_init(messages);
1083 ractor_queue_move(messages, received_queue);
1084 }
1085
1086 VM_ASSERT(ractor_queue_empty_p(cr, received_queue));
1087
1088 RUBY_DEBUG_LOG("received:%d", received);
1089 return received;
1090}
1091
1092static void
1093ractor_wait_receive(rb_execution_context_t *ec, rb_ractor_t *cr)
1094{
1095 struct ractor_queue messages;
1096 bool deliverred = false;
1097
1098 RACTOR_LOCK_SELF(cr);
1099 {
1100 if (ractor_check_received(cr, &messages)) {
1101 deliverred = true;
1102 }
1103 else {
1104 ractor_wait(ec, cr);
1105 }
1106 }
1107 RACTOR_UNLOCK_SELF(cr);
1108
1109 if (deliverred) {
1110 VM_ASSERT(!ractor_queue_empty_p(cr, &messages));
1111 struct ractor_basket *b;
1112
1113 while ((b = ractor_queue_deq(cr, &messages)) != NULL) {
1114 ractor_queue_enq(cr, ractor_get_queue(cr, b->port_id, false), b);
1115 }
1116 }
1117}
1118
1119static VALUE
1120ractor_try_receive(rb_execution_context_t *ec, rb_ractor_t *cr, const struct ractor_port *rp)
1121{
1122 struct ractor_queue *rq = ractor_get_queue(cr, ractor_port_id(rp), false);
1123
1124 if (rq == NULL) {
1125 rb_raise(rb_eRactorClosedError, "The port was already closed");
1126 }
1127
1128 struct ractor_basket *b = ractor_queue_deq(cr, rq);
1129
1130 if (rq->closed && ractor_queue_empty_p(cr, rq)) {
1131 ractor_delete_port(cr, ractor_port_id(rp), false);
1132 }
1133
1134 if (b) {
1135 return ractor_basket_accept(b);
1136 }
1137 else {
1138 return Qundef;
1139 }
1140}
1141
1142static VALUE
1143ractor_receive(rb_execution_context_t *ec, const struct ractor_port *rp)
1144{
1145 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1146 VM_ASSERT(cr == rp->r);
1147
1148 RUBY_DEBUG_LOG("port:%u", (unsigned int)ractor_port_id(rp));
1149
1150 while (1) {
1151 VALUE v = ractor_try_receive(ec, cr, rp);
1152
1153 if (v != Qundef) {
1154 return v;
1155 }
1156 else {
1157 ractor_wait_receive(ec, cr);
1158 }
1159 }
1160}
1161
1162// Ractor#send
1163
1164static void
1165ractor_send_basket(rb_execution_context_t *ec, const struct ractor_port *rp, struct ractor_basket *b, bool raise_on_error)
1166{
1167 bool closed = false;
1168
1169 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);
1170
1171 RACTOR_LOCK(rp->r);
1172 {
1173 if (ractor_closed_port_p(ec, rp->r, rp)) {
1174 closed = true;
1175 }
1176 else {
1177 b->port_id = ractor_port_id(rp);
1178 ractor_queue_enq(rp->r, rp->r->sync.recv_queue, b);
1179 }
1180 }
1181 RACTOR_UNLOCK(rp->r);
1182
1183 // NOTE: ref r -> b->p.v is created, but Ractor is unprotected object, so no problem on that.
1184
1185 if (!closed) {
1186 ractor_wakeup_all(rp->r, wakeup_by_send);
1187 }
1188 else {
1189 RUBY_DEBUG_LOG("closed:%u@r%u", (unsigned int)ractor_port_id(rp), rb_ractor_id(rp->r));
1190
1191 if (raise_on_error) {
1192 ractor_basket_free(b);
1193 rb_raise(rb_eRactorClosedError, "The port was already closed");
1194 }
1195 }
1196}
1197
1198static VALUE
1199ractor_send0(rb_execution_context_t *ec, const struct ractor_port *rp, VALUE obj, VALUE move, bool raise_on_error)
1200{
1201 struct ractor_basket *b = ractor_basket_new(ec, obj, RTEST(move) ? basket_type_move : basket_type_none, false);
1202 ractor_send_basket(ec, rp, b, raise_on_error);
1203 RB_GC_GUARD(obj);
1204 return rp->r->pub.self;
1205}
1206
1207static VALUE
1208ractor_send(rb_execution_context_t *ec, const struct ractor_port *rp, VALUE obj, VALUE move)
1209{
1210 return ractor_send0(ec, rp, obj, move, true);
1211}
1212
1213static VALUE
1214ractor_try_send(rb_execution_context_t *ec, const struct ractor_port *rp, VALUE obj, VALUE move)
1215{
1216 return ractor_send0(ec, rp, obj, move, false);
1217}
1218
1219// Ractor::Selector
1220
1222 struct st_table *ports; // rpv -> rp
1223
1224};
1225
1226static int
1227ractor_selector_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
1228{
1229 rb_gc_mark((VALUE)key); // rpv
1230
1231 return ST_CONTINUE;
1232}
1233
1234static void
1235ractor_selector_mark(void *ptr)
1236{
1237 struct ractor_selector *s = ptr;
1238
1239 if (s->ports) {
1240 st_foreach(s->ports, ractor_selector_mark_i, 0);
1241 }
1242}
1243
1244static void
1245ractor_selector_free(void *ptr)
1246{
1247 struct ractor_selector *s = ptr;
1248 st_free_table(s->ports);
1249 SIZED_FREE(s);
1250}
1251
1252static size_t
1253ractor_selector_memsize(const void *ptr)
1254{
1255 const struct ractor_selector *s = ptr;
1256 size_t size = sizeof(struct ractor_selector);
1257 if (s->ports) {
1258 size += st_memsize(s->ports);
1259 }
1260 return size;
1261}
1262
1263static const rb_data_type_t ractor_selector_data_type = {
1264 "ractor/selector",
1265 {
1266 ractor_selector_mark,
1267 ractor_selector_free,
1268 ractor_selector_memsize,
1269 NULL, // update
1270 },
1271 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
1272};
1273
1274static struct ractor_selector *
1275RACTOR_SELECTOR_PTR(VALUE selv)
1276{
1277 VM_ASSERT(rb_typeddata_is_kind_of(selv, &ractor_selector_data_type));
1278 return (struct ractor_selector *)DATA_PTR(selv);
1279}
1280
1281// Ractor::Selector.new
1282
1283static VALUE
1284ractor_selector_create(VALUE klass)
1285{
1286 struct ractor_selector *s;
1287 VALUE selv = TypedData_Make_Struct(klass, struct ractor_selector, &ractor_selector_data_type, s);
1288 s->ports = st_init_numtable(); // TODO
1289 return selv;
1290}
1291
1292// Ractor::Selector#add(r)
1293
1294/*
1295 * call-seq:
1296 * add(ractor) -> ractor
1297 *
1298 * Adds _ractor_ to +self+. Raises an exception if _ractor_ is already added.
1299 * Returns _ractor_.
1300 */
1301static VALUE
1302ractor_selector_add(VALUE selv, VALUE rpv)
1303{
1304 if (!ractor_port_p(rpv)) {
1305 rb_raise(rb_eArgError, "Not a Ractor::Port object");
1306 }
1307
1308 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1309 const struct ractor_port *rp = RACTOR_PORT_PTR(rpv);
1310
1311 if (st_lookup(s->ports, (st_data_t)rpv, NULL)) {
1312 rb_raise(rb_eArgError, "already added");
1313 }
1314
1315 st_insert(s->ports, (st_data_t)rpv, (st_data_t)rp);
1316 RB_OBJ_WRITTEN(selv, Qundef, rpv);
1317
1318 return selv;
1319}
1320
1321// Ractor::Selector#remove(r)
1322
1323/* call-seq:
1324 * remove(ractor) -> ractor
1325 *
1326 * Removes _ractor_ from +self+. Raises an exception if _ractor_ is not added.
1327 * Returns the removed _ractor_.
1328 */
1329static VALUE
1330ractor_selector_remove(VALUE selv, VALUE rpv)
1331{
1332 if (!ractor_port_p(rpv)) {
1333 rb_raise(rb_eArgError, "Not a Ractor::Port object");
1334 }
1335
1336 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1337
1338 if (!st_lookup(s->ports, (st_data_t)rpv, NULL)) {
1339 rb_raise(rb_eArgError, "not added yet");
1340 }
1341
1342 st_delete(s->ports, (st_data_t *)&rpv, NULL);
1343
1344 return selv;
1345}
1346
1347// Ractor::Selector#clear
1348
1349/*
1350 * call-seq:
1351 * clear -> self
1352 *
1353 * Removes all ractors from +self+. Raises +self+.
1354 */
1355static VALUE
1356ractor_selector_clear(VALUE selv)
1357{
1358 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1359 st_clear(s->ports);
1360 return selv;
1361}
1362
1363/*
1364 * call-seq:
1365 * empty? -> true or false
1366 *
1367 * Returns +true+ if no ractor is added.
1368 */
1369static VALUE
1370ractor_selector_empty_p(VALUE selv)
1371{
1372 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1373 return s->ports->num_entries == 0 ? Qtrue : Qfalse;
1374}
1375
1376// Ractor::Selector#wait
1377
1379 rb_ractor_t *cr;
1381 bool found;
1382 VALUE v;
1383 VALUE rpv;
1384};
1385
1386static int
1387ractor_selector_wait_i(st_data_t key, st_data_t val, st_data_t data)
1388{
1389 struct ractor_selector_wait_data *p = (struct ractor_selector_wait_data *)data;
1390 const struct ractor_port *rp = (const struct ractor_port *)val;
1391
1392 VALUE v = ractor_try_receive(p->ec, p->cr, rp);
1393
1394 if (v != Qundef) {
1395 p->found = true;
1396 p->v = v;
1397 p->rpv = (VALUE)key;
1398 return ST_STOP;
1399 }
1400 else {
1401 return ST_CONTINUE;
1402 }
1403}
1404
1405static VALUE
1406ractor_selector__wait(rb_execution_context_t *ec, VALUE selector)
1407{
1408 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1409 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selector);
1410
1411 struct ractor_selector_wait_data data = {
1412 .ec = ec,
1413 .cr = cr,
1414 .found = false,
1415 };
1416
1417 while (1) {
1418 st_foreach(s->ports, ractor_selector_wait_i, (st_data_t)&data);
1419
1420 if (data.found) {
1421 return rb_ary_new_from_args(2, data.rpv, data.v);
1422 }
1423
1424 ractor_wait_receive(ec, cr);
1425 }
1426}
1427
1428/*
1429 * call-seq:
1430 * wait(receive: false, yield_value: undef, move: false) -> [ractor, value]
1431 *
1432 * Waits until any ractor in _selector_ can be active.
1433 */
1434static VALUE
1435ractor_selector_wait(VALUE selector)
1436{
1437 return ractor_selector__wait(GET_EC(), selector);
1438}
1439
1440static VALUE
1441ractor_selector_new(int argc, VALUE *ractors, VALUE klass)
1442{
1443 VALUE selector = ractor_selector_create(klass);
1444
1445 for (int i=0; i<argc; i++) {
1446 ractor_selector_add(selector, ractors[i]);
1447 }
1448
1449 return selector;
1450}
1451
1452static VALUE
1453ractor_select_internal(rb_execution_context_t *ec, VALUE self, VALUE ports)
1454{
1455 VALUE selector = ractor_selector_new(RARRAY_LENINT(ports), (VALUE *)RARRAY_CONST_PTR(ports), rb_cRactorSelector);
1456 VALUE result = ractor_selector__wait(ec, selector);
1457
1458 RB_GC_GUARD(selector);
1459 RB_GC_GUARD(ports);
1460 return result;
1461}
1462
1463#ifndef USE_RACTOR_SELECTOR
1464#define USE_RACTOR_SELECTOR 0
1465#endif
1466
1467RUBY_SYMBOL_EXPORT_BEGIN
1468void rb_init_ractor_selector(void);
1469RUBY_SYMBOL_EXPORT_END
1470
1471/*
1472 * Document-class: Ractor::Selector
1473 * :nodoc: currently
1474 *
1475 * Selects multiple Ractors to be activated.
1476 */
1477void
1478rb_init_ractor_selector(void)
1479{
1480 rb_cRactorSelector = rb_define_class_under(rb_cRactor, "Selector", rb_cObject);
1481 rb_undef_alloc_func(rb_cRactorSelector);
1482
1483 rb_define_singleton_method(rb_cRactorSelector, "new", ractor_selector_new , -1);
1484 rb_define_method(rb_cRactorSelector, "add", ractor_selector_add, 1);
1485 rb_define_method(rb_cRactorSelector, "remove", ractor_selector_remove, 1);
1486 rb_define_method(rb_cRactorSelector, "clear", ractor_selector_clear, 0);
1487 rb_define_method(rb_cRactorSelector, "empty?", ractor_selector_empty_p, 0);
1488 rb_define_method(rb_cRactorSelector, "wait", ractor_selector_wait, 0);
1489}
1490
1491static void
1492Init_RactorPort(void)
1493{
1494 rb_cRactorPort = rb_define_class_under(rb_cRactor, "Port", rb_cObject);
1495 rb_define_alloc_func(rb_cRactorPort, ractor_port_alloc);
1496 rb_define_method(rb_cRactorPort, "initialize", ractor_port_initialize, 0);
1497 rb_define_method(rb_cRactorPort, "initialize_copy", ractor_port_initialize_copy, 1);
1498
1499#if USE_RACTOR_SELECTOR
1500 rb_init_ractor_selector();
1501#endif
1502}
#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:1627
#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:660
VALUE rb_cObject
Object class.
Definition object.c:61
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
#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:2024
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1705
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
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
Definition rtypeddata.h:80
#define RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
Definition rtypeddata.h:119
#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:561
#define RTEST
This is an old name of RB_TEST.
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:211
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