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