2#include "ccan/list/list.h"
5static VALUE rb_cMutex, rb_cQueue, rb_cSizedQueue, rb_cConditionVariable;
6static VALUE rb_eClosedQueueError;
10 rb_serial_t ec_serial;
13 struct ccan_list_head waitq;
21 struct ccan_list_node node;
24static inline rb_fiber_t*
25nonblocking_fiber(rb_fiber_t *fiber)
27 if (rb_fiberptr_blocking(fiber)) {
40#define MUTEX_ALLOW_TRAP FL_USER1
43sync_wakeup(
struct ccan_list_head *head,
long max)
45 RUBY_DEBUG_LOG(
"max:%ld", max);
49 ccan_list_for_each_safe(head, cur, next, node) {
50 ccan_list_del_init(&cur->node);
52 if (cur->th->status != THREAD_KILLED) {
53 if (cur->th->scheduler !=
Qnil && cur->fiber) {
57 RUBY_DEBUG_LOG(
"target_th:%u", rb_th_serial(cur->th));
58 rb_threadptr_interrupt(cur->th);
59 cur->th->status = THREAD_RUNNABLE;
62 if (--max == 0)
return;
68wakeup_one(
struct ccan_list_head *head)
74wakeup_all(
struct ccan_list_head *head)
76 sync_wakeup(head, LONG_MAX);
79#if defined(HAVE_WORKING_FORK)
80static void rb_mutex_abandon_all(rb_mutex_t *mutexes);
81static void rb_mutex_abandon_keeping_mutexes(rb_thread_t *th);
82static void rb_mutex_abandon_locking_mutex(rb_thread_t *th);
84static const char* rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t *th, rb_serial_t ec_serial);
111rb_mutex_num_waiting(rb_mutex_t *mutex)
116 ccan_list_for_each(&mutex->waitq, w, node) {
123rb_thread_t* rb_fiber_threadptr(
const rb_fiber_t *fiber);
126mutex_locked_p(rb_mutex_t *mutex)
128 return mutex->ec_serial != 0;
131static void thread_mutex_remove(rb_thread_t *thread, rb_mutex_t *mutex);
136 rb_mutex_t *mutex = ptr;
137 if (mutex_locked_p(mutex)) {
138 thread_mutex_remove(mutex->th, mutex);
144mutex_memsize(
const void *ptr)
146 return sizeof(rb_mutex_t);
151 {NULL, mutex_free, mutex_memsize,},
152 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
166rb_obj_is_mutex(
VALUE obj)
172mutex_alloc(
VALUE klass)
179 ccan_list_head_init(&mutex->waitq);
186 return mutex_alloc(rb_cMutex);
192 rb_mutex_t *mutex = mutex_ptr(self);
194 return RBOOL(mutex_locked_p(mutex));
198thread_mutex_insert(rb_thread_t *thread, rb_mutex_t *mutex)
201 if (thread->keeping_mutexes) {
202 mutex->next_mutex = thread->keeping_mutexes;
205 thread->keeping_mutexes = mutex;
209thread_mutex_remove(rb_thread_t *thread, rb_mutex_t *mutex)
211 rb_mutex_t **keeping_mutexes = &thread->keeping_mutexes;
213 while (*keeping_mutexes && *keeping_mutexes != mutex) {
215 keeping_mutexes = &(*keeping_mutexes)->next_mutex;
218 if (*keeping_mutexes) {
219 *keeping_mutexes = mutex->next_mutex;
220 mutex->next_mutex = NULL;
225mutex_set_owner(rb_mutex_t *mutex, rb_thread_t *th, rb_serial_t ec_serial)
228 mutex->ec_serial = ec_serial;
232mutex_locked(rb_mutex_t *mutex, rb_thread_t *th, rb_serial_t ec_serial)
234 mutex_set_owner(mutex, th, ec_serial);
235 thread_mutex_insert(th, mutex);
239do_mutex_trylock(rb_mutex_t *mutex, rb_thread_t *th, rb_serial_t ec_serial)
241 if (mutex->ec_serial == 0) {
242 RUBY_DEBUG_LOG(
"%p ok", mutex);
244 mutex_locked(mutex, th, ec_serial);
248 RUBY_DEBUG_LOG(
"%p ng", mutex);
254rb_mut_trylock(rb_execution_context_t *ec,
VALUE self)
256 return RBOOL(do_mutex_trylock(mutex_ptr(self), ec->thread_ptr, rb_ec_serial(ec)));
262 return rb_mut_trylock(GET_EC(), self);
266mutex_owned_p(rb_serial_t ec_serial, rb_mutex_t *mutex)
268 return RBOOL(mutex->ec_serial == ec_serial);
272call_rb_fiber_scheduler_block(
VALUE mutex)
278delete_from_waitq(
VALUE value)
286static inline rb_atomic_t threadptr_get_interrupts(rb_thread_t *th);
291 rb_execution_context_t *ec;
298 args->mutex = mutex_ptr(mutex);
303do_mutex_lock(
struct mutex_args *args,
int interruptible_p)
305 VALUE self = args->self;
306 rb_execution_context_t *ec = args->ec;
307 rb_thread_t *th = ec->thread_ptr;
308 rb_fiber_t *fiber = ec->fiber_ptr;
309 rb_serial_t ec_serial = rb_ec_serial(ec);
310 rb_mutex_t *mutex = args->mutex;
315 th->ec->interrupt_mask & TRAP_INTERRUPT_MASK) {
319 if (!do_mutex_trylock(mutex, th, ec_serial)) {
320 if (mutex->ec_serial == ec_serial) {
324 while (mutex->ec_serial != ec_serial) {
325 VM_ASSERT(mutex->ec_serial != 0);
328 if (scheduler !=
Qnil) {
332 .fiber = nonblocking_fiber(fiber)
335 ccan_list_add_tail(&mutex->waitq, &
sync_waiter.node);
339 if (!mutex->ec_serial) {
340 mutex_set_owner(mutex, th, ec_serial);
344 if (!th->vm->thread_ignore_deadlock && mutex->th == th) {
345 rb_raise(
rb_eThreadError,
"deadlock; lock already owned by another fiber belonging to the same thread");
351 .fiber = nonblocking_fiber(fiber),
354 RUBY_DEBUG_LOG(
"%p wait", mutex);
365 enum rb_thread_status prev_status = th->status;
366 th->status = THREAD_STOPPED_FOREVER;
367 rb_ractor_sleeper_threads_inc(th->ractor);
368 rb_check_deadlock(th->ractor);
371 th->locking_mutex = self;
373 ccan_list_add_tail(&mutex->waitq, &
sync_waiter.node);
375 native_sleep(th, NULL);
380 if (!mutex->ec_serial) {
381 mutex_set_owner(mutex, th, ec_serial);
384 rb_ractor_sleeper_threads_dec(th->ractor);
385 th->status = prev_status;
386 th->locking_mutex =
Qfalse;
388 RUBY_DEBUG_LOG(
"%p wakeup", mutex);
391 if (interruptible_p) {
394 if (mutex->ec_serial == ec_serial) {
396 mutex->ec_serial = 0;
398 RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
399 if (!mutex->ec_serial) {
400 mutex_set_owner(mutex, th, ec_serial);
405 if (RUBY_VM_INTERRUPTED(th->ec)) {
407 if (saved_ints == 0) {
408 saved_ints = threadptr_get_interrupts(th);
412 threadptr_get_interrupts(th);
418 if (saved_ints) th->ec->interrupt_flag = saved_ints;
419 if (mutex->ec_serial == ec_serial) mutex_locked(mutex, th, ec_serial);
422 RUBY_DEBUG_LOG(
"%p locked", mutex);
425 if (mutex_owned_p(ec_serial, mutex) ==
Qfalse) rb_bug(
"do_mutex_lock: mutex is not owned.");
431mutex_lock_uninterruptible(
VALUE self)
434 mutex_args_init(&args, self);
435 return do_mutex_lock(&args, 0);
439rb_mut_lock(rb_execution_context_t *ec,
VALUE self)
443 .mutex = mutex_ptr(self),
446 return do_mutex_lock(&args, 1);
453 mutex_args_init(&args, self);
454 return do_mutex_lock(&args, 1);
458rb_mut_owned_p(rb_execution_context_t *ec,
VALUE self)
460 return mutex_owned_p(rb_ec_serial(ec), mutex_ptr(self));
464rb_mutex_owned_p(
VALUE self)
466 return rb_mut_owned_p(GET_EC(), self);
470rb_mutex_unlock_th(rb_mutex_t *mutex, rb_thread_t *th, rb_serial_t ec_serial)
472 RUBY_DEBUG_LOG(
"%p", mutex);
474 if (mutex->ec_serial == 0) {
475 return "Attempt to unlock a mutex which is not locked";
477 else if (ec_serial && mutex->ec_serial != ec_serial) {
478 return "Attempt to unlock a mutex which is locked by another thread/fiber";
483 mutex->ec_serial = 0;
484 thread_mutex_remove(th, mutex);
486 ccan_list_for_each_safe(&mutex->waitq, cur, next, node) {
487 ccan_list_del_init(&cur->node);
489 if (cur->th->scheduler !=
Qnil && cur->fiber) {
494 switch (cur->th->status) {
495 case THREAD_RUNNABLE:
496 case THREAD_STOPPED_FOREVER:
497 RUBY_DEBUG_LOG(
"wakeup th:%u", rb_th_serial(cur->th));
498 rb_threadptr_interrupt(cur->th);
501 rb_bug(
"unexpected THREAD_STOPPED");
504 rb_bug(
"unexpected THREAD_KILLED");
518 rb_mutex_t *mutex = args->mutex;
519 rb_thread_t *th = rb_ec_thread_ptr(args->ec);
521 err = rb_mutex_unlock_th(mutex, th, rb_ec_serial(args->ec));
526do_mutex_unlock_safe(
VALUE args)
536 mutex_args_init(&args, self);
537 do_mutex_unlock(&args);
542rb_mut_unlock(rb_execution_context_t *ec,
VALUE self)
546 .mutex = mutex_ptr(self),
549 do_mutex_unlock(&args);
553#if defined(HAVE_WORKING_FORK)
555rb_mutex_abandon_keeping_mutexes(rb_thread_t *th)
557 rb_mutex_abandon_all(th->keeping_mutexes);
558 th->keeping_mutexes = NULL;
562rb_mutex_abandon_locking_mutex(rb_thread_t *th)
564 if (th->locking_mutex) {
565 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
567 ccan_list_head_init(&mutex->waitq);
568 th->locking_mutex =
Qfalse;
573rb_mutex_abandon_all(rb_mutex_t *mutexes)
579 mutexes = mutex->next_mutex;
580 mutex->ec_serial = 0;
581 mutex->next_mutex = 0;
582 ccan_list_head_init(&mutex->waitq);
593mutex_sleep_begin(
VALUE _arguments)
596 VALUE timeout = arguments->timeout;
600 if (scheduler !=
Qnil) {
604 if (
NIL_P(timeout)) {
605 rb_thread_sleep_deadly_allow_spurious_wakeup(arguments->self,
Qnil, 0);
609 rb_hrtime_t relative_timeout = rb_timeval2hrtime(&timeout_value);
611 woken = RBOOL(sleep_hrtime(GET_THREAD(), relative_timeout, 0));
619rb_mut_sleep(rb_execution_context_t *ec,
VALUE self,
VALUE timeout)
621 if (!
NIL_P(timeout)) {
626 rb_mut_unlock(ec, self);
627 time_t beg = time(0);
634 VALUE woken = rb_ec_ensure(ec, mutex_sleep_begin, (
VALUE)&arguments, mutex_lock_uninterruptible, self);
636 RUBY_VM_CHECK_INTS_BLOCKING(ec);
637 if (!woken)
return Qnil;
638 time_t end = time(0) - beg;
639 return TIMET2NUM(end);
645 return rb_mut_sleep(GET_EC(), self, timeout);
652 mutex_args_init(&args, self);
653 do_mutex_lock(&args, 1);
654 return rb_ec_ensure(args.ec, func, arg, do_mutex_unlock_safe, (
VALUE)&args);
658do_ec_yield(
VALUE _ec)
660 return rb_ec_yield((rb_execution_context_t *)_ec,
Qundef);
664rb_mut_synchronize(rb_execution_context_t *ec,
VALUE self)
668 .mutex = mutex_ptr(self),
671 do_mutex_lock(&args, 1);
672 return rb_ec_ensure(args.ec, do_ec_yield, (
VALUE)ec, do_mutex_unlock_safe, (
VALUE)&args);
676rb_mutex_allow_trap(
VALUE self,
int val)
688#define queue_waitq(q) UNALIGNED_MEMBER_PTR(q, waitq)
689#define queue_list(q) UNALIGNED_MEMBER_PTR(q, que)
690RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN()
692 struct ccan_list_head waitq;
693 rb_serial_t fork_gen;
696} RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END();
698#define szqueue_waitq(sq) UNALIGNED_MEMBER_PTR(sq, q.waitq)
699#define szqueue_list(sq) UNALIGNED_MEMBER_PTR(sq, q.que)
700#define szqueue_pushq(sq) UNALIGNED_MEMBER_PTR(sq, pushq)
701RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_BEGIN()
704 int num_waiting_push;
705 struct ccan_list_head pushq;
707} RBIMPL_ATTR_PACKED_STRUCT_UNALIGNED_END();
710queue_mark_and_move(
void *ptr)
715 rb_gc_mark_and_move((
VALUE *)UNALIGNED_MEMBER_PTR(q, que));
719queue_memsize(
const void *ptr)
725 .wrap_struct_name =
"Thread::Queue",
727 .dmark = queue_mark_and_move,
729 .dsize = queue_memsize,
730 .dcompact = queue_mark_and_move,
732 .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
736queue_alloc(
VALUE klass)
742 ccan_list_head_init(queue_waitq(q));
749 rb_serial_t fork_gen = GET_VM()->fork_gen;
751 if (q->fork_gen == fork_gen) {
755 q->fork_gen = fork_gen;
756 ccan_list_head_init(queue_waitq(q));
772#define QUEUE_CLOSED FL_USER5
775queue_timeout2hrtime(
VALUE timeout)
777 if (
NIL_P(timeout)) {
778 return (rb_hrtime_t)0;
782 rel = rb_sec2hrtime(NUM2TIMET(timeout));
787 return rb_hrtime_add(rel, rb_hrtime_now());
791szqueue_mark_and_move(
void *ptr)
795 queue_mark_and_move(&sq->q);
799szqueue_memsize(
const void *ptr)
805 .wrap_struct_name =
"Thread::SizedQueue",
807 .dmark = szqueue_mark_and_move,
809 .dsize = szqueue_memsize,
810 .dcompact = szqueue_mark_and_move,
812 .parent = &queue_data_type,
813 .flags = RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED,
817szqueue_alloc(
VALUE klass)
821 &szqueue_data_type, sq);
822 ccan_list_head_init(szqueue_waitq(sq));
823 ccan_list_head_init(szqueue_pushq(sq));
828szqueue_ptr(
VALUE obj)
833 if (queue_fork_check(&sq->q)) {
834 ccan_list_head_init(szqueue_pushq(sq));
835 sq->num_waiting_push = 0;
850 if (RB_LIKELY(ary)) {
853 rb_raise(
rb_eTypeError,
"%+"PRIsVALUE
" not initialized", obj);
863queue_closed_p(
VALUE self)
875NORETURN(
static void raise_closed_queue_error(
VALUE self));
878raise_closed_queue_error(
VALUE self)
880 rb_raise(rb_eClosedQueueError,
"queue closed");
951rb_queue_initialize(
int argc,
VALUE *argv,
VALUE self)
954 struct rb_queue *q = queue_ptr(self);
955 if ((argc =
rb_scan_args(argc, argv,
"01", &initial)) == 1) {
956 initial = rb_to_array(initial);
959 ccan_list_head_init(queue_waitq(q));
969 if (queue_closed_p(self)) {
970 raise_closed_queue_error(self);
973 wakeup_one(queue_waitq(q));
1011rb_queue_close(
VALUE self)
1013 struct rb_queue *q = queue_ptr(self);
1015 if (!queue_closed_p(self)) {
1016 FL_SET(self, QUEUE_CLOSED);
1018 wakeup_all(queue_waitq(q));
1032rb_queue_closed_p(
VALUE self)
1034 return RBOOL(queue_closed_p(self));
1050 return queue_do_push(self, queue_ptr(self), obj);
1054queue_sleep(
VALUE _args)
1057 rb_thread_sleep_deadly_allow_spurious_wakeup(args->self, args->timeout, args->end);
1070queue_sleep_done(
VALUE p)
1074 ccan_list_del(&qw->w.node);
1075 qw->as.q->num_waiting--;
1081szqueue_sleep_done(
VALUE p)
1085 ccan_list_del(&qw->w.node);
1086 qw->as.sq->num_waiting_push--;
1094 check_array(self, q->que);
1096 if (
RTEST(non_block)) {
1105 rb_hrtime_t end = queue_timeout2hrtime(timeout);
1107 if (queue_closed_p(self)) {
1108 return queue_closed_result(self, q);
1115 .w = {.self = self, .th = ec->thread_ptr, .fiber = nonblocking_fiber(ec->fiber_ptr)},
1119 struct ccan_list_head *waitq = queue_waitq(q);
1131 if (!
NIL_P(timeout) && (rb_hrtime_now() >= end))
1140rb_queue_pop(rb_execution_context_t *ec,
VALUE self,
VALUE non_block,
VALUE timeout)
1142 return queue_do_pop(ec, self, queue_ptr(self), non_block, timeout);
1153rb_queue_empty_p(
VALUE self)
1155 return RBOOL(queue_length(self, queue_ptr(self)) == 0);
1165rb_queue_clear(
VALUE self)
1167 struct rb_queue *q = queue_ptr(self);
1183rb_queue_length(
VALUE self)
1185 return LONG2NUM(queue_length(self, queue_ptr(self)));
1188NORETURN(
static VALUE rb_queue_freeze(
VALUE self));
1198rb_queue_freeze(
VALUE self)
1200 rb_raise(
rb_eTypeError,
"cannot freeze " "%+"PRIsVALUE, self);
1211rb_queue_num_waiting(
VALUE self)
1213 struct rb_queue *q = queue_ptr(self);
1215 return INT2NUM(q->num_waiting);
1242 rb_raise(rb_eArgError,
"queue size must be positive");
1246 ccan_list_head_init(szqueue_waitq(sq));
1247 ccan_list_head_init(szqueue_pushq(sq));
1266rb_szqueue_close(
VALUE self)
1268 if (!queue_closed_p(self)) {
1271 FL_SET(self, QUEUE_CLOSED);
1272 wakeup_all(szqueue_waitq(sq));
1273 wakeup_all(szqueue_pushq(sq));
1285rb_szqueue_max_get(
VALUE self)
1287 return LONG2NUM(szqueue_ptr(self)->max);
1305 rb_raise(rb_eArgError,
"queue size must be positive");
1307 if (max > sq->max) {
1308 diff = max - sq->max;
1311 sync_wakeup(szqueue_pushq(sq), diff);
1316rb_szqueue_push(rb_execution_context_t *ec,
VALUE self,
VALUE object,
VALUE non_block,
VALUE timeout)
1320 if (queue_length(self, &sq->q) >= sq->max) {
1321 if (
RTEST(non_block)) {
1330 rb_hrtime_t end = queue_timeout2hrtime(timeout);
1331 while (queue_length(self, &sq->q) >= sq->max) {
1332 if (queue_closed_p(self)) {
1333 raise_closed_queue_error(self);
1337 .w = {.self = self, .th = ec->thread_ptr, .fiber = nonblocking_fiber(ec->fiber_ptr)},
1341 struct ccan_list_head *pushq = szqueue_pushq(sq);
1344 sq->num_waiting_push++;
1352 if (!
NIL_P(timeout) && rb_hrtime_now() >= end) {
1358 return queue_do_push(self, &sq->q,
object);
1362rb_szqueue_pop(rb_execution_context_t *ec,
VALUE self,
VALUE non_block,
VALUE timeout)
1365 VALUE retval = queue_do_pop(ec, self, &sq->q, non_block, timeout);
1367 if (queue_length(self, &sq->q) < sq->max) {
1368 wakeup_one(szqueue_pushq(sq));
1381rb_szqueue_clear(
VALUE self)
1386 wakeup_all(szqueue_pushq(sq));
1397rb_szqueue_num_waiting(
VALUE self)
1401 return INT2NUM(sq->q.num_waiting + sq->num_waiting_push);
1407 struct ccan_list_head waitq;
1408 rb_serial_t fork_gen;
1494condvar_memsize(
const void *ptr)
1502 0, 0, RUBY_TYPED_FREE_IMMEDIATELY|RUBY_TYPED_WB_PROTECTED
1506condvar_ptr(
VALUE self)
1509 rb_serial_t fork_gen = GET_VM()->fork_gen;
1514 if (cv->fork_gen != fork_gen) {
1515 cv->fork_gen = fork_gen;
1516 ccan_list_head_init(&cv->waitq);
1523condvar_alloc(
VALUE klass)
1529 ccan_list_head_init(&cv->waitq);
1535 rb_execution_context_t *ec;
1546 if (
CLASS_OF(p->mutex) == rb_cMutex) {
1547 return rb_mut_sleep(p->ec, p->mutex, p->timeout);
1550 return rb_funcallv(p->mutex, id_sleep, 1, &p->timeout);
1555rb_condvar_wait(rb_execution_context_t *ec,
VALUE self,
VALUE mutex,
VALUE timeout)
1566 .th = ec->thread_ptr,
1567 .fiber = nonblocking_fiber(ec->fiber_ptr)
1570 ccan_list_add_tail(&cv->waitq, &
sync_waiter.node);
1575rb_condvar_signal(rb_execution_context_t *ec,
VALUE self)
1578 wakeup_one(&cv->waitq);
1583rb_condvar_broadcast(rb_execution_context_t *ec,
VALUE self)
1586 wakeup_all(&cv->waitq);
1593undumpable(
VALUE obj)
1600define_thread_class(
VALUE outer,
const ID name,
VALUE super)
1608Init_thread_sync(
void)
1611#if defined(TEACH_RDOC) && TEACH_RDOC == 42
1618#define DEFINE_CLASS(name, super) \
1619 rb_c##name = define_thread_class(rb_cThread, rb_intern(#name), rb_c##super)
1622 DEFINE_CLASS(Mutex, Object);
1626 DEFINE_CLASS(Queue, Object);
1647 DEFINE_CLASS(SizedQueue, Queue);
1655 rb_define_method(rb_cSizedQueue,
"num_waiting", rb_szqueue_num_waiting, 0);
1658 DEFINE_CLASS(ConditionVariable, Object);
1661 id_sleep = rb_intern(
"sleep");
1666#include "thread_sync.rbinc"
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
VALUE rb_define_class_id_under(VALUE outer, ID id, VALUE super)
Identical to rb_define_class_under(), except it takes the name in ID instead of C's string.
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
void rb_undef_method(VALUE klass, const char *name)
Defines an undef of a method.
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
#define FL_UNSET_RAW
Old name of RB_FL_UNSET_RAW.
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
#define CLASS_OF
Old name of rb_class_of.
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
#define FL_SET
Old name of RB_FL_SET.
#define LONG2NUM
Old name of RB_LONG2NUM.
#define Qtrue
Old name of RUBY_Qtrue.
#define INT2NUM
Old name of RB_INT2NUM.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#define Check_TypedStruct(v, t)
Old name of rb_check_typeddata.
#define NUM2LONG
Old name of RB_NUM2LONG.
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define FL_SET_RAW
Old name of RB_FL_SET_RAW.
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Checks if the given object is of given kind.
VALUE rb_eTypeError
TypeError exception.
VALUE rb_eStopIteration
StopIteration exception.
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
VALUE rb_eThreadError
ThreadError exception.
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
VALUE rb_cThread
Thread class.
double rb_num2dbl(VALUE num)
Converts an instance of rb_cNumeric into C's double.
VALUE rb_equal(VALUE lhs, VALUE rhs)
This function is an optimised version of calling #==.
#define RB_OBJ_WRITE(old, slot, young)
Declaration of a "back" pointer.
VALUE rb_ary_concat(VALUE lhs, VALUE rhs)
Destructively appends the contents of latter into the end of former.
VALUE rb_ary_shift(VALUE ary)
Destructively deletes an element from the beginning of the passed array and returns what was deleted.
VALUE rb_ary_hidden_new(long capa)
Allocates a hidden (no class) empty array.
VALUE rb_ary_clear(VALUE ary)
Destructively removes everything form an array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
void rb_provide(const char *feature)
Declares that the given feature is already provided by someone else.
VALUE rb_mutex_new(void)
Creates a mutex.
VALUE rb_mutex_trylock(VALUE mutex)
Attempts to lock the mutex, without waiting for other threads to unlock it.
VALUE rb_mutex_locked_p(VALUE mutex)
Queries if there are any threads that holds the lock.
VALUE rb_mutex_synchronize(VALUE mutex, VALUE(*func)(VALUE arg), VALUE arg)
Obtains the lock, runs the passed function, and releases the lock when it completes.
VALUE rb_mutex_sleep(VALUE self, VALUE timeout)
Releases the lock held in the mutex and waits for the period of time; reacquires the lock on wakeup.
VALUE rb_mutex_unlock(VALUE mutex)
Releases the mutex.
VALUE rb_mutex_lock(VALUE mutex)
Attempts to lock the mutex.
struct timeval rb_time_interval(VALUE num)
Creates a "time interval".
void rb_const_set(VALUE space, ID name, VALUE val)
Names a constant.
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
#define RARRAY_LEN
Just another name of rb_array_len.
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
struct rb_data_type_struct rb_data_type_t
This is the struct that holds necessary info for a struct.
#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...
VALUE rb_fiber_scheduler_current(void)
Identical to rb_fiber_scheduler_get(), except it also returns RUBY_Qnil in case of a blocking fiber.
VALUE rb_fiber_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout)
Non-blocking wait for the passed "blocker", which is for instance Thread.join or Mutex....
VALUE rb_fiber_scheduler_kernel_sleep(VALUE scheduler, VALUE duration)
Non-blocking sleep.
VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber)
Wakes up a fiber previously blocked using rb_fiber_scheduler_block().
#define RTEST
This is an old name of RB_TEST.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.
void ruby_xfree(void *ptr)
Deallocates a storage instance.