Ruby 3.5.0dev (2025-09-07 revision 85bd3fb870aca52c778cf32be424e13a1372fbf1)
ractor_sync.c (85bd3fb870aca52c778cf32be424e13a1372fbf1)
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 RB_OBJ_WRITTEN(rpv, Qundef, r->pub.self);
85 rp->id_ = ractor_genid_for_port(r);
86
87 ractor_add_port(r, ractor_port_id(rp));
88
89 rb_obj_freeze(rpv);
90
91 return rpv;
92}
93
94/*
95 * call-seq:
96 * Ractor::Port.new -> new_port
97 *
98 * Returns a new Ractor::Port object.
99 */
100static VALUE
101ractor_port_initialize(VALUE self)
102{
103 return ractor_port_init(self, GET_RACTOR());
104}
105
106/* :nodoc: */
107static VALUE
108ractor_port_initialize_copy(VALUE self, VALUE orig)
109{
110 struct ractor_port *dst = RACTOR_PORT_PTR(self);
111 struct ractor_port *src = RACTOR_PORT_PTR(orig);
112 dst->r = src->r;
113 RB_OBJ_WRITTEN(self, Qundef, dst->r->pub.self);
114 dst->id_ = ractor_port_id(src);
115
116 return self;
117}
118
119static VALUE
120ractor_port_new(rb_ractor_t *r)
121{
122 VALUE rpv = ractor_port_alloc(rb_cRactorPort);
123 ractor_port_init(rpv, r);
124 return rpv;
125}
126
127static bool
128ractor_port_p(VALUE self)
129{
130 return rb_typeddata_is_kind_of(self, &ractor_port_data_type);
131}
132
133static VALUE
134ractor_port_receive(rb_execution_context_t *ec, VALUE self)
135{
136 const struct ractor_port *rp = RACTOR_PORT_PTR(self);
137
138 if (rp->r != rb_ec_ractor_ptr(ec)) {
139 rb_raise(rb_eRactorError, "only allowed from the creator Ractor of this port");
140 }
141
142 return ractor_receive(ec, rp);
143}
144
145static VALUE
146ractor_port_send(rb_execution_context_t *ec, VALUE self, VALUE obj, VALUE move)
147{
148 const struct ractor_port *rp = RACTOR_PORT_PTR(self);
149 ractor_send(ec, rp, obj, RTEST(move));
150 return self;
151}
152
153static bool ractor_closed_port_p(rb_execution_context_t *ec, rb_ractor_t *r, const struct ractor_port *rp);
154static bool ractor_close_port(rb_execution_context_t *ec, rb_ractor_t *r, const struct ractor_port *rp);
155
156static VALUE
157ractor_port_closed_p(rb_execution_context_t *ec, VALUE self)
158{
159 const struct ractor_port *rp = RACTOR_PORT_PTR(self);
160
161 if (ractor_closed_port_p(ec, rp->r, rp)) {
162 return Qtrue;
163 }
164 else {
165 return Qfalse;
166 }
167}
168
169static VALUE
170ractor_port_close(rb_execution_context_t *ec, VALUE self)
171{
172 const struct ractor_port *rp = RACTOR_PORT_PTR(self);
173 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
174
175 if (cr != rp->r) {
176 rb_raise(rb_eRactorError, "closing port by other ractors is not allowed");
177 }
178
179 ractor_close_port(ec, cr, rp);
180 return self;
181}
182
183// ractor-internal
184
185// ractor-internal - ractor_basket
186
187enum ractor_basket_type {
188 // basket is empty
189 basket_type_none,
190
191 // value is available
192 basket_type_ref,
193 basket_type_copy,
194 basket_type_move,
195};
196
198 enum ractor_basket_type type;
199 VALUE sender;
200 st_data_t port_id;
201
202 struct {
203 VALUE v;
204 bool exception;
205 } p; // payload
206
207 struct ccan_list_node node;
208};
209
210#if 0
211static inline bool
212ractor_basket_type_p(const struct ractor_basket *b, enum ractor_basket_type type)
213{
214 return b->type == type;
215}
216
217static inline bool
218ractor_basket_none_p(const struct ractor_basket *b)
219{
220 return ractor_basket_type_p(b, basket_type_none);
221}
222#endif
223
224static void
225ractor_basket_mark(const struct ractor_basket *b)
226{
227 rb_gc_mark(b->p.v);
228}
229
230static void
231ractor_basket_free(struct ractor_basket *b)
232{
233 xfree(b);
234}
235
236static struct ractor_basket *
237ractor_basket_alloc(void)
238{
239 struct ractor_basket *b = ALLOC(struct ractor_basket);
240 return b;
241}
242
243// ractor-internal - ractor_queue
244
246 struct ccan_list_head set;
247 bool closed;
248};
249
250static void
251ractor_queue_init(struct ractor_queue *rq)
252{
253 ccan_list_head_init(&rq->set);
254 rq->closed = false;
255}
256
257static struct ractor_queue *
258ractor_queue_new(void)
259{
260 struct ractor_queue *rq = ALLOC(struct ractor_queue);
261 ractor_queue_init(rq);
262 return rq;
263}
264
265static void
266ractor_queue_mark(const struct ractor_queue *rq)
267{
268 const struct ractor_basket *b;
269
270 ccan_list_for_each(&rq->set, b, node) {
271 ractor_basket_mark(b);
272 }
273}
274
275static void
276ractor_queue_free(struct ractor_queue *rq)
277{
278 struct ractor_basket *b, *nxt;
279
280 ccan_list_for_each_safe(&rq->set, b, nxt, node) {
281 ccan_list_del_init(&b->node);
282 ractor_basket_free(b);
283 }
284
285 VM_ASSERT(ccan_list_empty(&rq->set));
286
287 xfree(rq);
288}
289
291static size_t
292ractor_queue_size(const struct ractor_queue *rq)
293{
294 size_t size = 0;
295 const struct ractor_basket *b;
296
297 ccan_list_for_each(&rq->set, b, node) {
298 size++;
299 }
300 return size;
301}
302
303static void
304ractor_queue_close(struct ractor_queue *rq)
305{
306 rq->closed = true;
307}
308
309static void
310ractor_queue_move(struct ractor_queue *dst_rq, struct ractor_queue *src_rq)
311{
312 struct ccan_list_head *src = &src_rq->set;
313 struct ccan_list_head *dst = &dst_rq->set;
314
315 dst->n.next = src->n.next;
316 dst->n.prev = src->n.prev;
317 dst->n.next->prev = &dst->n;
318 dst->n.prev->next = &dst->n;
319 ccan_list_head_init(src);
320}
321
322#if 0
323static struct ractor_basket *
324ractor_queue_head(rb_ractor_t *r, struct ractor_queue *rq)
325{
326 return ccan_list_top(&rq->set, struct ractor_basket, node);
327}
328#endif
329
330static bool
331ractor_queue_empty_p(rb_ractor_t *r, const struct ractor_queue *rq)
332{
333 return ccan_list_empty(&rq->set);
334}
335
336static struct ractor_basket *
337ractor_queue_deq(rb_ractor_t *r, struct ractor_queue *rq)
338{
339 VM_ASSERT(GET_RACTOR() == r);
340
341 return ccan_list_pop(&rq->set, struct ractor_basket, node);
342}
343
344static void
345ractor_queue_enq(rb_ractor_t *r, struct ractor_queue *rq, struct ractor_basket *basket)
346{
347 ccan_list_add_tail(&rq->set, &basket->node);
348}
349
350#if 0
351static void
352rq_dump(const struct ractor_queue *rq)
353{
354 int i=0;
355 struct ractor_basket *b;
356 ccan_list_for_each(&rq->set, b, node) {
357 fprintf(stderr, "%d type:%s %p\n", i, basket_type_name(b->type), (void *)b);
358 i++;
359 }
360}
361#endif
362
363static void ractor_delete_port(rb_ractor_t *cr, st_data_t id, bool locked);
364
365static struct ractor_queue *
366ractor_get_queue(rb_ractor_t *cr, st_data_t id, bool locked)
367{
368 VM_ASSERT(cr == GET_RACTOR());
369
370 struct ractor_queue *rq;
371
372 if (cr->sync.ports && st_lookup(cr->sync.ports, id, (st_data_t *)&rq)) {
373 if (rq->closed && ractor_queue_empty_p(cr, rq)) {
374 ractor_delete_port(cr, id, locked);
375 return NULL;
376 }
377 else {
378 return rq;
379 }
380 }
381 else {
382 return NULL;
383 }
384}
385
386// ractor-internal - ports
387
388static void
389ractor_add_port(rb_ractor_t *r, st_data_t id)
390{
391 struct ractor_queue *rq = ractor_queue_new();
392 ASSERT_ractor_unlocking(r);
393
394 RUBY_DEBUG_LOG("id:%u", (unsigned int)id);
395
396 RACTOR_LOCK(r);
397 {
398 st_insert(r->sync.ports, id, (st_data_t)rq);
399 }
400 RACTOR_UNLOCK(r);
401}
402
403static void
404ractor_delete_port_locked(rb_ractor_t *cr, st_data_t id)
405{
406 ASSERT_ractor_locking(cr);
407
408 RUBY_DEBUG_LOG("id:%u", (unsigned int)id);
409
410 struct ractor_queue *rq;
411
412 if (st_delete(cr->sync.ports, &id, (st_data_t *)&rq)) {
413 ractor_queue_free(rq);
414 }
415 else {
416 VM_ASSERT(0);
417 }
418}
419
420static void
421ractor_delete_port(rb_ractor_t *cr, st_data_t id, bool locked)
422{
423 if (locked) {
424 ractor_delete_port_locked(cr, id);
425 }
426 else {
427 RACTOR_LOCK_SELF(cr);
428 {
429 ractor_delete_port_locked(cr, id);
430 }
431 RACTOR_UNLOCK_SELF(cr);
432 }
433}
434
435static const struct ractor_port *
436ractor_default_port(rb_ractor_t *r)
437{
438 return RACTOR_PORT_PTR(r->sync.default_port_value);
439}
440
441static VALUE
442ractor_default_port_value(rb_ractor_t *r)
443{
444 return r->sync.default_port_value;
445}
446
447static bool
448ractor_closed_port_p(rb_execution_context_t *ec, rb_ractor_t *r, const struct ractor_port *rp)
449{
450 VM_ASSERT(rb_ec_ractor_ptr(ec) == rp->r ? 1 : (ASSERT_ractor_locking(rp->r), 1));
451
452 const struct ractor_queue *rq;
453
454 if (rp->r->sync.ports && st_lookup(rp->r->sync.ports, ractor_port_id(rp), (st_data_t *)&rq)) {
455 return rq->closed;
456 }
457 else {
458 return true;
459 }
460}
461
462static void ractor_deliver_incoming_messages(rb_execution_context_t *ec, rb_ractor_t *cr);
463static bool ractor_queue_empty_p(rb_ractor_t *r, const struct ractor_queue *rq);
464
465static bool
466ractor_close_port(rb_execution_context_t *ec, rb_ractor_t *cr, const struct ractor_port *rp)
467{
468 VM_ASSERT(cr == rp->r);
469 struct ractor_queue *rq = NULL;
470
471 RACTOR_LOCK_SELF(cr);
472 {
473 ractor_deliver_incoming_messages(ec, cr); // check incoming messages
474
475 if (st_lookup(rp->r->sync.ports, ractor_port_id(rp), (st_data_t *)&rq)) {
476 ractor_queue_close(rq);
477
478 if (ractor_queue_empty_p(cr, rq)) {
479 // delete from the table
480 ractor_delete_port(cr, ractor_port_id(rp), true);
481 }
482
483 // TODO: free rq
484 }
485 }
486 RACTOR_UNLOCK_SELF(cr);
487
488 return rq != NULL;
489}
490
491static int
492ractor_free_all_ports_i(st_data_t port_id, st_data_t val, st_data_t dat)
493{
494 struct ractor_queue *rq = (struct ractor_queue *)val;
495 // rb_ractor_t *cr = (rb_ractor_t *)dat;
496
497 ractor_queue_free(rq);
498 return ST_CONTINUE;
499}
500
501static void
502ractor_free_all_ports(rb_ractor_t *cr)
503{
504 if (cr->sync.ports) {
505 st_foreach(cr->sync.ports, ractor_free_all_ports_i, (st_data_t)cr);
506 st_free_table(cr->sync.ports);
507 cr->sync.ports = NULL;
508 }
509
510 if (cr->sync.recv_queue) {
511 ractor_queue_free(cr->sync.recv_queue);
512 cr->sync.recv_queue = NULL;
513 }
514}
515
516#if defined(HAVE_WORKING_FORK)
517static void
518ractor_sync_terminate_atfork(rb_vm_t *vm, rb_ractor_t *r)
519{
520 ractor_free_all_ports(r);
521 r->sync.legacy = Qnil;
522}
523#endif
524
525// Ractor#monitor
526
528 struct ractor_port port;
529 struct ccan_list_node node;
530};
531
532static void
533ractor_mark_monitors(rb_ractor_t *r)
534{
535 const struct ractor_monitor *rm;
536 ccan_list_for_each(&r->sync.monitors, rm, node) {
537 rb_gc_mark(rm->port.r->pub.self);
538 }
539}
540
541static VALUE
542ractor_exit_token(bool exc)
543{
544 if (exc) {
545 RUBY_DEBUG_LOG("aborted");
546 return ID2SYM(idAborted);
547 }
548 else {
549 RUBY_DEBUG_LOG("exited");
550 return ID2SYM(idExited);
551 }
552}
553
554static VALUE
556{
557 rb_ractor_t *r = RACTOR_PTR(self);
558 bool terminated = false;
559 const struct ractor_port *rp = RACTOR_PORT_PTR(port);
560 struct ractor_monitor *rm = ALLOC(struct ractor_monitor);
561 rm->port = *rp; // copy port information
562
563 RACTOR_LOCK(r);
564 {
565 if (UNDEF_P(r->sync.legacy)) { // not terminated
566 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));
567 ccan_list_add_tail(&r->sync.monitors, &rm->node);
568 }
569 else {
570 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));
571 terminated = true;
572 }
573 }
574 RACTOR_UNLOCK(r);
575
576 if (terminated) {
577 xfree(rm);
578 ractor_port_send(ec, port, ractor_exit_token(r->sync.legacy_exc), Qfalse);
579
580 return Qfalse;
581 }
582 else {
583 return Qtrue;
584 }
585}
586
587static VALUE
588ractor_unmonitor(rb_execution_context_t *ec, VALUE self, VALUE port)
589{
590 rb_ractor_t *r = RACTOR_PTR(self);
591 const struct ractor_port *rp = RACTOR_PORT_PTR(port);
592
593 RACTOR_LOCK(r);
594 {
595 if (UNDEF_P(r->sync.legacy)) { // not terminated
596 struct ractor_monitor *rm, *nxt;
597
598 ccan_list_for_each_safe(&r->sync.monitors, rm, nxt, node) {
599 if (ractor_port_id(&rm->port) == ractor_port_id(rp)) {
600 RUBY_DEBUG_LOG("r:%u -> port:%u@r%u",
601 (unsigned int)rb_ractor_id(r),
602 (unsigned int)ractor_port_id(&rm->port),
603 (unsigned int)rb_ractor_id(rm->port.r));
604 ccan_list_del(&rm->node);
605 xfree(rm);
606 }
607 }
608 }
609 }
610 RACTOR_UNLOCK(r);
611
612 return self;
613}
614
615static void
616ractor_notify_exit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE legacy, bool exc)
617{
618 RUBY_DEBUG_LOG("exc:%d", exc);
619 VM_ASSERT(!UNDEF_P(legacy));
620 VM_ASSERT(cr->sync.legacy == Qundef);
621
622 RACTOR_LOCK_SELF(cr);
623 {
624 ractor_free_all_ports(cr);
625
626 cr->sync.legacy = legacy;
627 cr->sync.legacy_exc = exc;
628 }
629 RACTOR_UNLOCK_SELF(cr);
630
631 // send token
632
633 VALUE token = ractor_exit_token(exc);
634 struct ractor_monitor *rm, *nxt;
635
636 ccan_list_for_each_safe(&cr->sync.monitors, rm, nxt, node)
637 {
638 RUBY_DEBUG_LOG("port:%u@r%u", (unsigned int)ractor_port_id(&rm->port), (unsigned int)rb_ractor_id(rm->port.r));
639
640 ractor_try_send(ec, &rm->port, token, false);
641
642 ccan_list_del(&rm->node);
643 xfree(rm);
644 }
645
646 VM_ASSERT(ccan_list_empty(&cr->sync.monitors));
647}
648
649// ractor-internal - initialize, mark, free, memsize
650
651static int
652ractor_mark_ports_i(st_data_t key, st_data_t val, st_data_t data)
653{
654 // id -> ractor_queue
655 const struct ractor_queue *rq = (struct ractor_queue *)val;
656 ractor_queue_mark(rq);
657 return ST_CONTINUE;
658}
659
660static void
661ractor_sync_mark(rb_ractor_t *r)
662{
663 rb_gc_mark(r->sync.default_port_value);
664
665 if (r->sync.ports) {
666 ractor_queue_mark(r->sync.recv_queue);
667 st_foreach(r->sync.ports, ractor_mark_ports_i, 0);
668 }
669
670 ractor_mark_monitors(r);
671}
672
673static int
674ractor_sync_free_ports_i(st_data_t _key, st_data_t val, st_data_t _args)
675{
676 struct ractor_queue *queue = (struct ractor_queue *)val;
677
678 ractor_queue_free(queue);
679
680 return ST_CONTINUE;
681}
682
683static void
684ractor_sync_free(rb_ractor_t *r)
685{
686 if (r->sync.recv_queue) {
687 ractor_queue_free(r->sync.recv_queue);
688 }
689
690 // maybe NULL
691 if (r->sync.ports) {
692 st_foreach(r->sync.ports, ractor_sync_free_ports_i, 0);
693 st_free_table(r->sync.ports);
694 r->sync.ports = NULL;
695 }
696}
697
698static size_t
699ractor_sync_memsize(const rb_ractor_t *r)
700{
701 return st_table_size(r->sync.ports);
702}
703
704static void
705ractor_sync_init(rb_ractor_t *r)
706{
707 // lock
708 rb_native_mutex_initialize(&r->sync.lock);
709
710 // monitors
711 ccan_list_head_init(&r->sync.monitors);
712
713 // waiters
714 ccan_list_head_init(&r->sync.waiters);
715
716 // receiving queue
717 r->sync.recv_queue = ractor_queue_new();
718
719 // ports
720 r->sync.ports = st_init_numtable();
721 r->sync.default_port_value = ractor_port_new(r);
722 FL_SET_RAW(r->sync.default_port_value, RUBY_FL_SHAREABLE); // only default ports are shareable
723
724 // legacy
725 r->sync.legacy = Qundef;
726
727#ifndef RUBY_THREAD_PTHREAD_H
728 rb_native_cond_initialize(&r->sync.wakeup_cond);
729#endif
730}
731
732// Ractor#value
733
734static rb_ractor_t *
735ractor_set_successor_once(rb_ractor_t *r, rb_ractor_t *cr)
736{
737 if (r->sync.successor == NULL) {
738 rb_ractor_t *successor = ATOMIC_PTR_CAS(r->sync.successor, NULL, cr);
739 return successor == NULL ? cr : successor;
740 }
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 struct st_table *ports; // rpv -> rp
1231
1232};
1233
1234static int
1235ractor_selector_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
1236{
1237 rb_gc_mark((VALUE)key); // rpv
1238
1239 return ST_CONTINUE;
1240}
1241
1242static void
1243ractor_selector_mark(void *ptr)
1244{
1245 struct ractor_selector *s = ptr;
1246
1247 if (s->ports) {
1248 st_foreach(s->ports, ractor_selector_mark_i, 0);
1249 }
1250}
1251
1252static void
1253ractor_selector_free(void *ptr)
1254{
1255 struct ractor_selector *s = ptr;
1256 st_free_table(s->ports);
1257 ruby_xfree(ptr);
1258}
1259
1260static size_t
1261ractor_selector_memsize(const void *ptr)
1262{
1263 const struct ractor_selector *s = ptr;
1264 return sizeof(struct ractor_selector) + st_memsize(s->ports);
1265}
1266
1267static const rb_data_type_t ractor_selector_data_type = {
1268 "ractor/selector",
1269 {
1270 ractor_selector_mark,
1271 ractor_selector_free,
1272 ractor_selector_memsize,
1273 NULL, // update
1274 },
1275 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
1276};
1277
1278static struct ractor_selector *
1279RACTOR_SELECTOR_PTR(VALUE selv)
1280{
1281 VM_ASSERT(rb_typeddata_is_kind_of(selv, &ractor_selector_data_type));
1282 return (struct ractor_selector *)DATA_PTR(selv);
1283}
1284
1285// Ractor::Selector.new
1286
1287static VALUE
1288ractor_selector_create(VALUE klass)
1289{
1290 struct ractor_selector *s;
1291 VALUE selv = TypedData_Make_Struct(klass, struct ractor_selector, &ractor_selector_data_type, s);
1292 s->ports = st_init_numtable(); // TODO
1293 return selv;
1294}
1295
1296// Ractor::Selector#add(r)
1297
1298/*
1299 * call-seq:
1300 * add(ractor) -> ractor
1301 *
1302 * Adds _ractor_ to +self+. Raises an exception if _ractor_ is already added.
1303 * Returns _ractor_.
1304 */
1305static VALUE
1306ractor_selector_add(VALUE selv, VALUE rpv)
1307{
1308 if (!ractor_port_p(rpv)) {
1309 rb_raise(rb_eArgError, "Not a Ractor::Port object");
1310 }
1311
1312 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1313 const struct ractor_port *rp = RACTOR_PORT_PTR(rpv);
1314
1315 if (st_lookup(s->ports, (st_data_t)rpv, NULL)) {
1316 rb_raise(rb_eArgError, "already added");
1317 }
1318
1319 st_insert(s->ports, (st_data_t)rpv, (st_data_t)rp);
1320 RB_OBJ_WRITTEN(selv, Qundef, rpv);
1321
1322 return selv;
1323}
1324
1325// Ractor::Selector#remove(r)
1326
1327/* call-seq:
1328 * remove(ractor) -> ractor
1329 *
1330 * Removes _ractor_ from +self+. Raises an exception if _ractor_ is not added.
1331 * Returns the removed _ractor_.
1332 */
1333static VALUE
1334ractor_selector_remove(VALUE selv, VALUE rpv)
1335{
1336 if (!ractor_port_p(rpv)) {
1337 rb_raise(rb_eArgError, "Not a Ractor::Port object");
1338 }
1339
1340 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1341
1342 if (!st_lookup(s->ports, (st_data_t)rpv, NULL)) {
1343 rb_raise(rb_eArgError, "not added yet");
1344 }
1345
1346 st_delete(s->ports, (st_data_t *)&rpv, NULL);
1347
1348 return selv;
1349}
1350
1351// Ractor::Selector#clear
1352
1353/*
1354 * call-seq:
1355 * clear -> self
1356 *
1357 * Removes all ractors from +self+. Raises +self+.
1358 */
1359static VALUE
1360ractor_selector_clear(VALUE selv)
1361{
1362 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1363 st_clear(s->ports);
1364 return selv;
1365}
1366
1367/*
1368 * call-seq:
1369 * empty? -> true or false
1370 *
1371 * Returns +true+ if no ractor is added.
1372 */
1373static VALUE
1374ractor_selector_empty_p(VALUE selv)
1375{
1376 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1377 return s->ports->num_entries == 0 ? Qtrue : Qfalse;
1378}
1379
1380// Ractor::Selector#wait
1381
1383 rb_ractor_t *cr;
1385 bool found;
1386 VALUE v;
1387 VALUE rpv;
1388};
1389
1390static int
1391ractor_selector_wait_i(st_data_t key, st_data_t val, st_data_t data)
1392{
1393 struct ractor_selector_wait_data *p = (struct ractor_selector_wait_data *)data;
1394 const struct ractor_port *rp = (const struct ractor_port *)val;
1395
1396 VALUE v = ractor_try_receive(p->ec, p->cr, rp);
1397
1398 if (v != Qundef) {
1399 p->found = true;
1400 p->v = v;
1401 p->rpv = (VALUE)key;
1402 return ST_STOP;
1403 }
1404 else {
1405 return ST_CONTINUE;
1406 }
1407}
1408
1409static VALUE
1410ractor_selector__wait(rb_execution_context_t *ec, VALUE selector)
1411{
1412 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1413 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selector);
1414
1415 struct ractor_selector_wait_data data = {
1416 .ec = ec,
1417 .cr = cr,
1418 .found = false,
1419 };
1420
1421 while (1) {
1422 st_foreach(s->ports, ractor_selector_wait_i, (st_data_t)&data);
1423
1424 if (data.found) {
1425 return rb_ary_new_from_args(2, data.rpv, data.v);
1426 }
1427
1428 ractor_wait_receive(ec, cr);
1429 }
1430}
1431
1432/*
1433 * call-seq:
1434 * wait(receive: false, yield_value: undef, move: false) -> [ractor, value]
1435 *
1436 * Waits until any ractor in _selector_ can be active.
1437 */
1438static VALUE
1439ractor_selector_wait(VALUE selector)
1440{
1441 return ractor_selector__wait(GET_EC(), selector);
1442}
1443
1444static VALUE
1445ractor_selector_new(int argc, VALUE *ractors, VALUE klass)
1446{
1447 VALUE selector = ractor_selector_create(klass);
1448
1449 for (int i=0; i<argc; i++) {
1450 ractor_selector_add(selector, ractors[i]);
1451 }
1452
1453 return selector;
1454}
1455
1456static VALUE
1457ractor_select_internal(rb_execution_context_t *ec, VALUE self, VALUE ports)
1458{
1459 VALUE selector = ractor_selector_new(RARRAY_LENINT(ports), (VALUE *)RARRAY_CONST_PTR(ports), rb_cRactorSelector);
1460 VALUE result = ractor_selector__wait(ec, selector);
1461
1462 RB_GC_GUARD(selector);
1463 RB_GC_GUARD(ports);
1464 return result;
1465}
1466
1467#ifndef USE_RACTOR_SELECTOR
1468#define USE_RACTOR_SELECTOR 0
1469#endif
1470
1471RUBY_SYMBOL_EXPORT_BEGIN
1472void rb_init_ractor_selector(void);
1473RUBY_SYMBOL_EXPORT_END
1474
1475/*
1476 * Document-class: Ractor::Selector
1477 * :nodoc: currently
1478 *
1479 * Selects multiple Ractors to be activated.
1480 */
1481void
1482rb_init_ractor_selector(void)
1483{
1484 rb_cRactorSelector = rb_define_class_under(rb_cRactor, "Selector", rb_cObject);
1485 rb_undef_alloc_func(rb_cRactorSelector);
1486
1487 rb_define_singleton_method(rb_cRactorSelector, "new", ractor_selector_new , -1);
1488 rb_define_method(rb_cRactorSelector, "add", ractor_selector_add, 1);
1489 rb_define_method(rb_cRactorSelector, "remove", ractor_selector_remove, 1);
1490 rb_define_method(rb_cRactorSelector, "clear", ractor_selector_clear, 0);
1491 rb_define_method(rb_cRactorSelector, "empty?", ractor_selector_empty_p, 0);
1492 rb_define_method(rb_cRactorSelector, "wait", ractor_selector_wait, 0);
1493}
1494
1495static void
1496Init_RactorPort(void)
1497{
1498 rb_cRactorPort = rb_define_class_under(rb_cRactor, "Port", rb_cObject);
1499 rb_define_alloc_func(rb_cRactorPort, ractor_port_alloc);
1500 rb_define_method(rb_cRactorPort, "initialize", ractor_port_initialize, 0);
1501 rb_define_method(rb_cRactorPort, "initialize_copy", ractor_port_initialize_copy, 1);
1502
1503#if USE_RACTOR_SELECTOR
1504 rb_init_ractor_selector();
1505#endif
1506}
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
@ RUBY_FL_SHAREABLE
This flag has something to do with Ractor.
Definition fl_type.h:280
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
Definition class.c:1512
#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:682
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:24
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1326
#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:1669
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:1984
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1603
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:1571
#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:505
#define RTEST
This is an old name of RB_TEST.
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:204
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