Ruby 3.5.0dev (2025-08-16 revision 87c4ebd00115b9424f04813b4dd5f3d087a14eb9)
ractor_sync.c (87c4ebd00115b9424f04813b4dd5f3d087a14eb9)
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_initialzie(VALUE self)
102{
103 return ractor_port_init(self, GET_RACTOR());
104}
105
106/* :nodoc: */
107static VALUE
108ractor_port_initialzie_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 // memo: can cause GC, but GC doesn't use ractor locking.
399 st_insert(r->sync.ports, id, (st_data_t)rq);
400 }
401 RACTOR_UNLOCK(r);
402}
403
404static void
405ractor_delete_port_locked(rb_ractor_t *cr, st_data_t id)
406{
407 ASSERT_ractor_locking(cr);
408
409 RUBY_DEBUG_LOG("id:%u", (unsigned int)id);
410
411 struct ractor_queue *rq;
412
413 if (st_delete(cr->sync.ports, &id, (st_data_t *)&rq)) {
414 ractor_queue_free(rq);
415 }
416 else {
417 VM_ASSERT(0);
418 }
419}
420
421static void
422ractor_delete_port(rb_ractor_t *cr, st_data_t id, bool locked)
423{
424 if (locked) {
425 ractor_delete_port_locked(cr, id);
426 }
427 else {
428 RACTOR_LOCK_SELF(cr);
429 {
430 ractor_delete_port_locked(cr, id);
431 }
432 RACTOR_UNLOCK_SELF(cr);
433 }
434}
435
436static const struct ractor_port *
437ractor_default_port(rb_ractor_t *r)
438{
439 return RACTOR_PORT_PTR(r->sync.default_port_value);
440}
441
442static VALUE
443ractor_default_port_value(rb_ractor_t *r)
444{
445 return r->sync.default_port_value;
446}
447
448static bool
449ractor_closed_port_p(rb_execution_context_t *ec, rb_ractor_t *r, const struct ractor_port *rp)
450{
451 VM_ASSERT(rb_ec_ractor_ptr(ec) == rp->r ? 1 : (ASSERT_ractor_locking(rp->r), 1));
452
453 const struct ractor_queue *rq;
454
455 if (rp->r->sync.ports && st_lookup(rp->r->sync.ports, ractor_port_id(rp), (st_data_t *)&rq)) {
456 return rq->closed;
457 }
458 else {
459 return true;
460 }
461}
462
463static void ractor_deliver_incoming_messages(rb_execution_context_t *ec, rb_ractor_t *cr);
464static bool ractor_queue_empty_p(rb_ractor_t *r, const struct ractor_queue *rq);
465
466static bool
467ractor_close_port(rb_execution_context_t *ec, rb_ractor_t *cr, const struct ractor_port *rp)
468{
469 VM_ASSERT(cr == rp->r);
470 struct ractor_queue *rq = NULL;
471
472 RACTOR_LOCK_SELF(cr);
473 {
474 ractor_deliver_incoming_messages(ec, cr); // check incoming messages
475
476 if (st_lookup(rp->r->sync.ports, ractor_port_id(rp), (st_data_t *)&rq)) {
477 ractor_queue_close(rq);
478
479 if (ractor_queue_empty_p(cr, rq)) {
480 // delete from the table
481 ractor_delete_port(cr, ractor_port_id(rp), true);
482 }
483
484 // TODO: free rq
485 }
486 }
487 RACTOR_UNLOCK_SELF(cr);
488
489 return rq != NULL;
490}
491
492static int
493ractor_free_all_ports_i(st_data_t port_id, st_data_t val, st_data_t dat)
494{
495 struct ractor_queue *rq = (struct ractor_queue *)val;
496 // rb_ractor_t *cr = (rb_ractor_t *)dat;
497
498 ractor_queue_free(rq);
499 return ST_CONTINUE;
500}
501
502static void
503ractor_free_all_ports(rb_ractor_t *cr)
504{
505 if (cr->sync.ports) {
506 st_foreach(cr->sync.ports, ractor_free_all_ports_i, (st_data_t)cr);
507 st_free_table(cr->sync.ports);
508 cr->sync.ports = NULL;
509 }
510
511 if (cr->sync.recv_queue) {
512 ractor_queue_free(cr->sync.recv_queue);
513 cr->sync.recv_queue = NULL;
514 }
515}
516
517#if defined(HAVE_WORKING_FORK)
518static void
519ractor_sync_terminate_atfork(rb_vm_t *vm, rb_ractor_t *r)
520{
521 ractor_free_all_ports(r);
522 r->sync.legacy = Qnil;
523}
524#endif
525
526// Ractor#monitor
527
529 struct ractor_port port;
530 struct ccan_list_node node;
531};
532
533static void
534ractor_mark_monitors(rb_ractor_t *r)
535{
536 const struct ractor_monitor *rm;
537 ccan_list_for_each(&r->sync.monitors, rm, node) {
538 rb_gc_mark(rm->port.r->pub.self);
539 }
540}
541
542static VALUE
543ractor_exit_token(bool exc)
544{
545 if (exc) {
546 RUBY_DEBUG_LOG("aborted");
547 return ID2SYM(idAborted);
548 }
549 else {
550 RUBY_DEBUG_LOG("exited");
551 return ID2SYM(idExited);
552 }
553}
554
555static VALUE
557{
558 rb_ractor_t *r = RACTOR_PTR(self);
559 bool terminated = false;
560 const struct ractor_port *rp = RACTOR_PORT_PTR(port);
561 struct ractor_monitor *rm = ALLOC(struct ractor_monitor);
562 rm->port = *rp; // copy port information
563
564 RACTOR_LOCK(r);
565 {
566 if (UNDEF_P(r->sync.legacy)) { // not terminated
567 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));
568 ccan_list_add_tail(&r->sync.monitors, &rm->node);
569 }
570 else {
571 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));
572 terminated = true;
573 }
574 }
575 RACTOR_UNLOCK(r);
576
577 if (terminated) {
578 xfree(rm);
579 ractor_port_send(ec, port, ractor_exit_token(r->sync.legacy_exc), Qfalse);
580
581 return Qfalse;
582 }
583 else {
584 return Qtrue;
585 }
586}
587
588static VALUE
589ractor_unmonitor(rb_execution_context_t *ec, VALUE self, VALUE port)
590{
591 rb_ractor_t *r = RACTOR_PTR(self);
592 const struct ractor_port *rp = RACTOR_PORT_PTR(port);
593
594 RACTOR_LOCK(r);
595 {
596 if (UNDEF_P(r->sync.legacy)) { // not terminated
597 struct ractor_monitor *rm, *nxt;
598
599 ccan_list_for_each_safe(&r->sync.monitors, rm, nxt, node) {
600 if (ractor_port_id(&rm->port) == ractor_port_id(rp)) {
601 RUBY_DEBUG_LOG("r:%u -> port:%u@r%u",
602 (unsigned int)rb_ractor_id(r),
603 (unsigned int)ractor_port_id(&rm->port),
604 (unsigned int)rb_ractor_id(rm->port.r));
605 ccan_list_del(&rm->node);
606 xfree(rm);
607 }
608 }
609 }
610 }
611 RACTOR_UNLOCK(r);
612
613 return self;
614}
615
616static void
617ractor_notify_exit(rb_execution_context_t *ec, rb_ractor_t *cr, VALUE legacy, bool exc)
618{
619 RUBY_DEBUG_LOG("exc:%d", exc);
620 VM_ASSERT(!UNDEF_P(legacy));
621 VM_ASSERT(cr->sync.legacy == Qundef);
622
623 RACTOR_LOCK_SELF(cr);
624 {
625 ractor_free_all_ports(cr);
626
627 cr->sync.legacy = legacy;
628 cr->sync.legacy_exc = exc;
629 }
630 RACTOR_UNLOCK_SELF(cr);
631
632 // send token
633
634 VALUE token = ractor_exit_token(exc);
635 struct ractor_monitor *rm, *nxt;
636
637 ccan_list_for_each_safe(&cr->sync.monitors, rm, nxt, node)
638 {
639 RUBY_DEBUG_LOG("port:%u@r%u", (unsigned int)ractor_port_id(&rm->port), (unsigned int)rb_ractor_id(rm->port.r));
640
641 ractor_try_send(ec, &rm->port, token, false);
642
643 ccan_list_del(&rm->node);
644 xfree(rm);
645 }
646
647 VM_ASSERT(ccan_list_empty(&cr->sync.monitors));
648}
649
650// ractor-internal - initialize, mark, free, memsize
651
652static int
653ractor_mark_ports_i(st_data_t key, st_data_t val, st_data_t data)
654{
655 // id -> ractor_queue
656 const struct ractor_queue *rq = (struct ractor_queue *)val;
657 ractor_queue_mark(rq);
658 return ST_CONTINUE;
659}
660
661static void
662ractor_sync_mark(rb_ractor_t *r)
663{
664 rb_gc_mark(r->sync.default_port_value);
665
666 if (r->sync.ports) {
667 ractor_queue_mark(r->sync.recv_queue);
668 st_foreach(r->sync.ports, ractor_mark_ports_i, 0);
669 }
670
671 ractor_mark_monitors(r);
672}
673
674static int
675ractor_sync_free_ports_i(st_data_t _key, st_data_t val, st_data_t _args)
676{
677 struct ractor_queue *queue = (struct ractor_queue *)val;
678
679 ractor_queue_free(queue);
680
681 return ST_CONTINUE;
682}
683
684static void
685ractor_sync_free(rb_ractor_t *r)
686{
687 if (r->sync.recv_queue) {
688 ractor_queue_free(r->sync.recv_queue);
689 }
690
691 // maybe NULL
692 if (r->sync.ports) {
693 st_foreach(r->sync.ports, ractor_sync_free_ports_i, 0);
694 st_free_table(r->sync.ports);
695 r->sync.ports = NULL;
696 }
697}
698
699static size_t
700ractor_sync_memsize(const rb_ractor_t *r)
701{
702 return st_table_size(r->sync.ports);
703}
704
705static void
706ractor_sync_init(rb_ractor_t *r)
707{
708 // lock
709 rb_native_mutex_initialize(&r->sync.lock);
710
711 // monitors
712 ccan_list_head_init(&r->sync.monitors);
713
714 // waiters
715 ccan_list_head_init(&r->sync.waiters);
716
717 // receiving queue
718 r->sync.recv_queue = ractor_queue_new();
719
720 // ports
721 r->sync.ports = st_init_numtable();
722 r->sync.default_port_value = ractor_port_new(r);
723 FL_SET_RAW(r->sync.default_port_value, RUBY_FL_SHAREABLE); // only default ports are shareable
724
725 // legacy
726 r->sync.legacy = Qundef;
727
728#ifndef RUBY_THREAD_PTHREAD_H
729 rb_native_cond_initialize(&r->sync.wakeup_cond);
730#endif
731}
732
733// Ractor#value
734
735static rb_ractor_t *
736ractor_set_successor_once(rb_ractor_t *r, rb_ractor_t *cr)
737{
738 if (r->sync.successor == NULL) {
739 rb_ractor_t *successor = ATOMIC_PTR_CAS(r->sync.successor, NULL, cr);
740 return successor == NULL ? cr : successor;
741 }
742
743 return r->sync.successor;
744}
745
746static VALUE ractor_reset_belonging(VALUE obj);
747
748static VALUE
749ractor_make_remote_exception(VALUE cause, VALUE sender)
750{
751 VALUE err = rb_exc_new_cstr(rb_eRactorRemoteError, "thrown by remote Ractor.");
752 rb_ivar_set(err, rb_intern("@ractor"), sender);
753 rb_ec_setup_exception(NULL, err, cause);
754 return err;
755}
756
757static VALUE
758ractor_value(rb_execution_context_t *ec, VALUE self)
759{
760 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
761 rb_ractor_t *r = RACTOR_PTR(self);
762 rb_ractor_t *sr = ractor_set_successor_once(r, cr);
763
764 if (sr == cr) {
765 ractor_reset_belonging(r->sync.legacy);
766
767 if (r->sync.legacy_exc) {
768 rb_exc_raise(ractor_make_remote_exception(r->sync.legacy, self));
769 }
770 return r->sync.legacy;
771 }
772 else {
773 rb_raise(rb_eRactorError, "Only the successor ractor can take a value");
774 }
775}
776
777static VALUE ractor_move(VALUE obj); // in this file
778static VALUE ractor_copy(VALUE obj); // in this file
779
780static VALUE
781ractor_prepare_payload(rb_execution_context_t *ec, VALUE obj, enum ractor_basket_type *ptype)
782{
783 switch (*ptype) {
784 case basket_type_ref:
785 return obj;
786 case basket_type_move:
787 return ractor_move(obj);
788 default:
789 if (rb_ractor_shareable_p(obj)) {
790 *ptype = basket_type_ref;
791 return obj;
792 }
793 else {
794 *ptype = basket_type_copy;
795 return ractor_copy(obj);
796 }
797 }
798}
799
800static struct ractor_basket *
801ractor_basket_new(rb_execution_context_t *ec, VALUE obj, enum ractor_basket_type type, bool exc)
802{
803 VALUE v = ractor_prepare_payload(ec, obj, &type);
804
805 struct ractor_basket *b = ractor_basket_alloc();
806 b->type = type;
807 b->p.v = v;
808 b->p.exception = exc;
809 return b;
810}
811
812static VALUE
813ractor_basket_value(struct ractor_basket *b)
814{
815 switch (b->type) {
816 case basket_type_ref:
817 break;
818 case basket_type_copy:
819 case basket_type_move:
820 ractor_reset_belonging(b->p.v);
821 break;
822 default:
823 VM_ASSERT(0); // unreachable
824 }
825
826 VM_ASSERT(!RB_TYPE_P(b->p.v, T_NONE));
827 return b->p.v;
828}
829
830static VALUE
831ractor_basket_accept(struct ractor_basket *b)
832{
833 VALUE v = ractor_basket_value(b);
834
835 if (b->p.exception) {
836 VALUE err = ractor_make_remote_exception(v, b->sender);
837 ractor_basket_free(b);
838 rb_exc_raise(err);
839 }
840
841 ractor_basket_free(b);
842 return v;
843}
844
845// Ractor blocking by receive
846
847enum ractor_wakeup_status {
848 wakeup_none,
849 wakeup_by_send,
850 wakeup_by_interrupt,
851
852 // wakeup_by_close,
853};
854
856 enum ractor_wakeup_status wakeup_status;
857 rb_thread_t *th;
858 struct ccan_list_node node;
859};
860
861#if VM_CHECK_MODE > 0
862static bool
863ractor_waiter_included(rb_ractor_t *cr, rb_thread_t *th)
864{
865 ASSERT_ractor_locking(cr);
866
867 struct ractor_waiter *w;
868
869 ccan_list_for_each(&cr->sync.waiters, w, node) {
870 if (w->th == th) {
871 return true;
872 }
873 }
874
875 return false;
876}
877#endif
878
879#if USE_RUBY_DEBUG_LOG
880
881static const char *
882wakeup_status_str(enum ractor_wakeup_status wakeup_status)
883{
884 switch (wakeup_status) {
885 case wakeup_none: return "none";
886 case wakeup_by_send: return "by_send";
887 case wakeup_by_interrupt: return "by_interrupt";
888 // case wakeup_by_close: return "by_close";
889 }
890 rb_bug("unreachable");
891}
892
893static const char *
894basket_type_name(enum ractor_basket_type type)
895{
896 switch (type) {
897 case basket_type_none: return "none";
898 case basket_type_ref: return "ref";
899 case basket_type_copy: return "copy";
900 case basket_type_move: return "move";
901 }
902 VM_ASSERT(0);
903 return NULL;
904}
905
906#endif // USE_RUBY_DEBUG_LOG
907
908#ifdef RUBY_THREAD_PTHREAD_H
909
910//
911
912#else // win32
913
914static void
915ractor_cond_wait(rb_ractor_t *r)
916{
917#if RACTOR_CHECK_MODE > 0
918 VALUE locked_by = r->sync.locked_by;
919 r->sync.locked_by = Qnil;
920#endif
921 rb_native_cond_wait(&r->sync.wakeup_cond, &r->sync.lock);
922
923#if RACTOR_CHECK_MODE > 0
924 r->sync.locked_by = locked_by;
925#endif
926}
927
928static void *
929ractor_wait_no_gvl(void *ptr)
930{
931 struct ractor_waiter *waiter = (struct ractor_waiter *)ptr;
932 rb_ractor_t *cr = waiter->th->ractor;
933
934 RACTOR_LOCK_SELF(cr);
935 {
936 if (waiter->wakeup_status == wakeup_none) {
937 ractor_cond_wait(cr);
938 }
939 }
940 RACTOR_UNLOCK_SELF(cr);
941 return NULL;
942}
943
944static void
945rb_ractor_sched_wait(rb_execution_context_t *ec, rb_ractor_t *cr, rb_unblock_function_t *ubf, void *ptr)
946{
947 struct ractor_waiter *waiter = (struct ractor_waiter *)ptr;
948
949 RACTOR_UNLOCK(cr);
950 {
951 rb_nogvl(ractor_wait_no_gvl, waiter,
952 ubf, waiter,
954 }
955 RACTOR_LOCK(cr);
956}
957
958static void
959rb_ractor_sched_wakeup(rb_ractor_t *r, rb_thread_t *th)
960{
961 // ractor lock is acquired
962 rb_native_cond_broadcast(&r->sync.wakeup_cond);
963}
964#endif
965
966static bool
967ractor_wakeup_all(rb_ractor_t *r, enum ractor_wakeup_status wakeup_status)
968{
969 ASSERT_ractor_unlocking(r);
970
971 RUBY_DEBUG_LOG("r:%u wakeup:%s", rb_ractor_id(r), wakeup_status_str(wakeup_status));
972
973 bool wakeup_p = false;
974
975 RACTOR_LOCK(r);
976 while (1) {
977 struct ractor_waiter *waiter = ccan_list_pop(&r->sync.waiters, struct ractor_waiter, node);
978
979 if (waiter) {
980 VM_ASSERT(waiter->wakeup_status == wakeup_none);
981
982 waiter->wakeup_status = wakeup_status;
983 rb_ractor_sched_wakeup(r, waiter->th);
984
985 wakeup_p = true;
986 }
987 else {
988 break;
989 }
990 }
991 RACTOR_UNLOCK(r);
992
993 return wakeup_p;
994}
995
996static void
997ubf_ractor_wait(void *ptr)
998{
999 struct ractor_waiter *waiter = (struct ractor_waiter *)ptr;
1000
1001 rb_thread_t *th = waiter->th;
1002 rb_ractor_t *r = th->ractor;
1003
1004 // clear ubf and nobody can kick UBF
1005 th->unblock.func = NULL;
1006 th->unblock.arg = NULL;
1007
1008 rb_native_mutex_unlock(&th->interrupt_lock);
1009 {
1010 RACTOR_LOCK(r);
1011 {
1012 if (waiter->wakeup_status == wakeup_none) {
1013 RUBY_DEBUG_LOG("waiter:%p", (void *)waiter);
1014
1015 waiter->wakeup_status = wakeup_by_interrupt;
1016 ccan_list_del(&waiter->node);
1017
1018 rb_ractor_sched_wakeup(r, waiter->th);
1019 }
1020 }
1021 RACTOR_UNLOCK(r);
1022 }
1023 rb_native_mutex_lock(&th->interrupt_lock);
1024}
1025
1026static enum ractor_wakeup_status
1027ractor_wait(rb_execution_context_t *ec, rb_ractor_t *cr)
1028{
1029 rb_thread_t *th = rb_ec_thread_ptr(ec);
1030
1031 struct ractor_waiter waiter = {
1032 .wakeup_status = wakeup_none,
1033 .th = th,
1034 };
1035
1036 RUBY_DEBUG_LOG("wait%s", "");
1037
1038 ASSERT_ractor_locking(cr);
1039
1040 VM_ASSERT(GET_RACTOR() == cr);
1041 VM_ASSERT(!ractor_waiter_included(cr, th));
1042
1043 ccan_list_add_tail(&cr->sync.waiters, &waiter.node);
1044
1045 // resume another ready thread and wait for an event
1046 rb_ractor_sched_wait(ec, cr, ubf_ractor_wait, &waiter);
1047
1048 if (waiter.wakeup_status == wakeup_none) {
1049 ccan_list_del(&waiter.node);
1050 }
1051
1052 RUBY_DEBUG_LOG("wakeup_status:%s", wakeup_status_str(waiter.wakeup_status));
1053
1054 RACTOR_UNLOCK_SELF(cr);
1055 {
1056 rb_ec_check_ints(ec);
1057 }
1058 RACTOR_LOCK_SELF(cr);
1059
1060 VM_ASSERT(!ractor_waiter_included(cr, th));
1061 return waiter.wakeup_status;
1062}
1063
1064static void
1065ractor_deliver_incoming_messages(rb_execution_context_t *ec, rb_ractor_t *cr)
1066{
1067 ASSERT_ractor_locking(cr);
1068 struct ractor_queue *recv_q = cr->sync.recv_queue;
1069
1070 struct ractor_basket *b;
1071 while ((b = ractor_queue_deq(cr, recv_q)) != NULL) {
1072 ractor_queue_enq(cr, ractor_get_queue(cr, b->port_id, true), b);
1073 }
1074}
1075
1076static bool
1077ractor_check_received(rb_ractor_t *cr, struct ractor_queue *messages)
1078{
1079 struct ractor_queue *received_queue = cr->sync.recv_queue;
1080 bool received = false;
1081
1082 ASSERT_ractor_locking(cr);
1083
1084 if (ractor_queue_empty_p(cr, received_queue)) {
1085 RUBY_DEBUG_LOG("empty");
1086 }
1087 else {
1088 received = true;
1089
1090 // messages <- incoming
1091 ractor_queue_init(messages);
1092 ractor_queue_move(messages, received_queue);
1093 }
1094
1095 VM_ASSERT(ractor_queue_empty_p(cr, received_queue));
1096
1097 RUBY_DEBUG_LOG("received:%d", received);
1098 return received;
1099}
1100
1101static void
1102ractor_wait_receive(rb_execution_context_t *ec, rb_ractor_t *cr)
1103{
1104 struct ractor_queue messages;
1105 bool deliverred = false;
1106
1107 RACTOR_LOCK_SELF(cr);
1108 {
1109 if (ractor_check_received(cr, &messages)) {
1110 deliverred = true;
1111 }
1112 else {
1113 ractor_wait(ec, cr);
1114 }
1115 }
1116 RACTOR_UNLOCK_SELF(cr);
1117
1118 if (deliverred) {
1119 VM_ASSERT(!ractor_queue_empty_p(cr, &messages));
1120 struct ractor_basket *b;
1121
1122 while ((b = ractor_queue_deq(cr, &messages)) != NULL) {
1123 ractor_queue_enq(cr, ractor_get_queue(cr, b->port_id, false), b);
1124 }
1125 }
1126}
1127
1128static VALUE
1129ractor_try_receive(rb_execution_context_t *ec, rb_ractor_t *cr, const struct ractor_port *rp)
1130{
1131 struct ractor_queue *rq = ractor_get_queue(cr, ractor_port_id(rp), false);
1132
1133 if (rq == NULL) {
1134 rb_raise(rb_eRactorClosedError, "The port was already closed");
1135 }
1136
1137 struct ractor_basket *b = ractor_queue_deq(cr, rq);
1138
1139 if (rq->closed && ractor_queue_empty_p(cr, rq)) {
1140 ractor_delete_port(cr, ractor_port_id(rp), false);
1141 }
1142
1143 if (b) {
1144 return ractor_basket_accept(b);
1145 }
1146 else {
1147 return Qundef;
1148 }
1149}
1150
1151static VALUE
1152ractor_receive(rb_execution_context_t *ec, const struct ractor_port *rp)
1153{
1154 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1155 VM_ASSERT(cr == rp->r);
1156
1157 RUBY_DEBUG_LOG("port:%u", (unsigned int)ractor_port_id(rp));
1158
1159 while (1) {
1160 VALUE v = ractor_try_receive(ec, cr, rp);
1161
1162 if (v != Qundef) {
1163 return v;
1164 }
1165 else {
1166 ractor_wait_receive(ec, cr);
1167 }
1168 }
1169}
1170
1171// Ractor#send
1172
1173static void
1174ractor_send_basket(rb_execution_context_t *ec, const struct ractor_port *rp, struct ractor_basket *b, bool raise_on_error)
1175{
1176 bool closed = false;
1177
1178 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);
1179
1180 RACTOR_LOCK(rp->r);
1181 {
1182 if (ractor_closed_port_p(ec, rp->r, rp)) {
1183 closed = true;
1184 }
1185 else {
1186 b->port_id = ractor_port_id(rp);
1187 ractor_queue_enq(rp->r, rp->r->sync.recv_queue, b);
1188 }
1189 }
1190 RACTOR_UNLOCK(rp->r);
1191
1192 // NOTE: ref r -> b->p.v is created, but Ractor is unprotected object, so no problem on that.
1193
1194 if (!closed) {
1195 ractor_wakeup_all(rp->r, wakeup_by_send);
1196 }
1197 else {
1198 RUBY_DEBUG_LOG("closed:%u@r%u", (unsigned int)ractor_port_id(rp), rb_ractor_id(rp->r));
1199
1200 if (raise_on_error) {
1201 ractor_basket_free(b);
1202 rb_raise(rb_eRactorClosedError, "The port was already closed");
1203 }
1204 }
1205}
1206
1207static VALUE
1208ractor_send0(rb_execution_context_t *ec, const struct ractor_port *rp, VALUE obj, VALUE move, bool raise_on_error)
1209{
1210 struct ractor_basket *b = ractor_basket_new(ec, obj, RTEST(move) ? basket_type_move : basket_type_none, false);
1211 ractor_send_basket(ec, rp, b, raise_on_error);
1212 RB_GC_GUARD(obj);
1213 return rp->r->pub.self;
1214}
1215
1216static VALUE
1217ractor_send(rb_execution_context_t *ec, const struct ractor_port *rp, VALUE obj, VALUE move)
1218{
1219 return ractor_send0(ec, rp, obj, move, true);
1220}
1221
1222static VALUE
1223ractor_try_send(rb_execution_context_t *ec, const struct ractor_port *rp, VALUE obj, VALUE move)
1224{
1225 return ractor_send0(ec, rp, obj, move, false);
1226}
1227
1228// Ractor::Selector
1229
1231 struct st_table *ports; // rpv -> rp
1232
1233};
1234
1235static int
1236ractor_selector_mark_i(st_data_t key, st_data_t val, st_data_t dmy)
1237{
1238 rb_gc_mark((VALUE)key); // rpv
1239
1240 return ST_CONTINUE;
1241}
1242
1243static void
1244ractor_selector_mark(void *ptr)
1245{
1246 struct ractor_selector *s = ptr;
1247
1248 if (s->ports) {
1249 st_foreach(s->ports, ractor_selector_mark_i, 0);
1250 }
1251}
1252
1253static void
1254ractor_selector_free(void *ptr)
1255{
1256 struct ractor_selector *s = ptr;
1257 st_free_table(s->ports);
1258 ruby_xfree(ptr);
1259}
1260
1261static size_t
1262ractor_selector_memsize(const void *ptr)
1263{
1264 const struct ractor_selector *s = ptr;
1265 return sizeof(struct ractor_selector) + st_memsize(s->ports);
1266}
1267
1268static const rb_data_type_t ractor_selector_data_type = {
1269 "ractor/selector",
1270 {
1271 ractor_selector_mark,
1272 ractor_selector_free,
1273 ractor_selector_memsize,
1274 NULL, // update
1275 },
1276 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
1277};
1278
1279static struct ractor_selector *
1280RACTOR_SELECTOR_PTR(VALUE selv)
1281{
1282 VM_ASSERT(rb_typeddata_is_kind_of(selv, &ractor_selector_data_type));
1283 return (struct ractor_selector *)DATA_PTR(selv);
1284}
1285
1286// Ractor::Selector.new
1287
1288static VALUE
1289ractor_selector_create(VALUE klass)
1290{
1291 struct ractor_selector *s;
1292 VALUE selv = TypedData_Make_Struct(klass, struct ractor_selector, &ractor_selector_data_type, s);
1293 s->ports = st_init_numtable(); // TODO
1294 return selv;
1295}
1296
1297// Ractor::Selector#add(r)
1298
1299/*
1300 * call-seq:
1301 * add(ractor) -> ractor
1302 *
1303 * Adds _ractor_ to +self+. Raises an exception if _ractor_ is already added.
1304 * Returns _ractor_.
1305 */
1306static VALUE
1307ractor_selector_add(VALUE selv, VALUE rpv)
1308{
1309 if (!ractor_port_p(rpv)) {
1310 rb_raise(rb_eArgError, "Not a Ractor::Port object");
1311 }
1312
1313 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1314 const struct ractor_port *rp = RACTOR_PORT_PTR(rpv);
1315
1316 if (st_lookup(s->ports, (st_data_t)rpv, NULL)) {
1317 rb_raise(rb_eArgError, "already added");
1318 }
1319
1320 st_insert(s->ports, (st_data_t)rpv, (st_data_t)rp);
1321 RB_OBJ_WRITTEN(selv, Qundef, rpv);
1322
1323 return selv;
1324}
1325
1326// Ractor::Selector#remove(r)
1327
1328/* call-seq:
1329 * remove(ractor) -> ractor
1330 *
1331 * Removes _ractor_ from +self+. Raises an exception if _ractor_ is not added.
1332 * Returns the removed _ractor_.
1333 */
1334static VALUE
1335ractor_selector_remove(VALUE selv, VALUE rpv)
1336{
1337 if (!ractor_port_p(rpv)) {
1338 rb_raise(rb_eArgError, "Not a Ractor::Port object");
1339 }
1340
1341 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1342
1343 if (!st_lookup(s->ports, (st_data_t)rpv, NULL)) {
1344 rb_raise(rb_eArgError, "not added yet");
1345 }
1346
1347 st_delete(s->ports, (st_data_t *)&rpv, NULL);
1348
1349 return selv;
1350}
1351
1352// Ractor::Selector#clear
1353
1354/*
1355 * call-seq:
1356 * clear -> self
1357 *
1358 * Removes all ractors from +self+. Raises +self+.
1359 */
1360static VALUE
1361ractor_selector_clear(VALUE selv)
1362{
1363 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1364 st_clear(s->ports);
1365 return selv;
1366}
1367
1368/*
1369 * call-seq:
1370 * empty? -> true or false
1371 *
1372 * Returns +true+ if no ractor is added.
1373 */
1374static VALUE
1375ractor_selector_empty_p(VALUE selv)
1376{
1377 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selv);
1378 return s->ports->num_entries == 0 ? Qtrue : Qfalse;
1379}
1380
1381// Ractor::Selector#wait
1382
1384 rb_ractor_t *cr;
1386 bool found;
1387 VALUE v;
1388 VALUE rpv;
1389};
1390
1391static int
1392ractor_selector_wait_i(st_data_t key, st_data_t val, st_data_t data)
1393{
1394 struct ractor_selector_wait_data *p = (struct ractor_selector_wait_data *)data;
1395 const struct ractor_port *rp = (const struct ractor_port *)val;
1396
1397 VALUE v = ractor_try_receive(p->ec, p->cr, rp);
1398
1399 if (v != Qundef) {
1400 p->found = true;
1401 p->v = v;
1402 p->rpv = (VALUE)key;
1403 return ST_STOP;
1404 }
1405 else {
1406 return ST_CONTINUE;
1407 }
1408}
1409
1410static VALUE
1411ractor_selector__wait(rb_execution_context_t *ec, VALUE selector)
1412{
1413 rb_ractor_t *cr = rb_ec_ractor_ptr(ec);
1414 struct ractor_selector *s = RACTOR_SELECTOR_PTR(selector);
1415
1416 struct ractor_selector_wait_data data = {
1417 .ec = ec,
1418 .cr = cr,
1419 .found = false,
1420 };
1421
1422 while (1) {
1423 st_foreach(s->ports, ractor_selector_wait_i, (st_data_t)&data);
1424
1425 if (data.found) {
1426 return rb_ary_new_from_args(2, data.rpv, data.v);
1427 }
1428
1429 ractor_wait_receive(ec, cr);
1430 }
1431}
1432
1433/*
1434 * call-seq:
1435 * wait(receive: false, yield_value: undef, move: false) -> [ractor, value]
1436 *
1437 * Waits until any ractor in _selector_ can be active.
1438 */
1439static VALUE
1440ractor_selector_wait(VALUE selector)
1441{
1442 return ractor_selector__wait(GET_EC(), selector);
1443}
1444
1445static VALUE
1446ractor_selector_new(int argc, VALUE *ractors, VALUE klass)
1447{
1448 VALUE selector = ractor_selector_create(klass);
1449
1450 for (int i=0; i<argc; i++) {
1451 ractor_selector_add(selector, ractors[i]);
1452 }
1453
1454 return selector;
1455}
1456
1457static VALUE
1458ractor_select_internal(rb_execution_context_t *ec, VALUE self, VALUE ports)
1459{
1460 VALUE selector = ractor_selector_new(RARRAY_LENINT(ports), (VALUE *)RARRAY_CONST_PTR(ports), rb_cRactorSelector);
1461 VALUE result = ractor_selector__wait(ec, selector);
1462
1463 RB_GC_GUARD(selector);
1464 RB_GC_GUARD(ports);
1465 return result;
1466}
1467
1468#ifndef USE_RACTOR_SELECTOR
1469#define USE_RACTOR_SELECTOR 0
1470#endif
1471
1472RUBY_SYMBOL_EXPORT_BEGIN
1473void rb_init_ractor_selector(void);
1474RUBY_SYMBOL_EXPORT_END
1475
1476/*
1477 * Document-class: Ractor::Selector
1478 * :nodoc: currently
1479 *
1480 * Selects multiple Ractors to be activated.
1481 */
1482void
1483rb_init_ractor_selector(void)
1484{
1485 rb_cRactorSelector = rb_define_class_under(rb_cRactor, "Selector", rb_cObject);
1486 rb_undef_alloc_func(rb_cRactorSelector);
1487
1488 rb_define_singleton_method(rb_cRactorSelector, "new", ractor_selector_new , -1);
1489 rb_define_method(rb_cRactorSelector, "add", ractor_selector_add, 1);
1490 rb_define_method(rb_cRactorSelector, "remove", ractor_selector_remove, 1);
1491 rb_define_method(rb_cRactorSelector, "clear", ractor_selector_clear, 0);
1492 rb_define_method(rb_cRactorSelector, "empty?", ractor_selector_empty_p, 0);
1493 rb_define_method(rb_cRactorSelector, "wait", ractor_selector_wait, 0);
1494}
1495
1496static void
1497Init_RactorPort(void)
1498{
1499 rb_cRactorPort = rb_define_class_under(rb_cRactor, "Port", rb_cObject);
1500 rb_define_alloc_func(rb_cRactorPort, ractor_port_alloc);
1501 rb_define_method(rb_cRactorPort, "initialize", ractor_port_initialzie, 0);
1502 rb_define_method(rb_cRactorPort, "initialize_copy", ractor_port_initialzie_copy, 1);
1503
1504#if USE_RACTOR_SELECTOR
1505 rb_init_ractor_selector();
1506#endif
1507}
#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:24
VALUE rb_obj_freeze(VALUE obj)
Just calls rb_obj_freeze_inline() inside.
Definition object.c:1297
#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:2126
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
Definition vm_method.c:1602
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