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