2#include "ccan/list/list.h"
5static VALUE rb_cMutex, rb_eClosedQueueError;
12 struct ccan_list_head waitq;
20 struct ccan_list_node node;
26 if (rb_fiberptr_blocking(fiber)) {
39#define MUTEX_ALLOW_TRAP FL_USER1
42sync_wakeup(
struct ccan_list_head *head,
long max)
44 RUBY_DEBUG_LOG(
"max:%ld", max);
48 ccan_list_for_each_safe(head, cur, next, node) {
49 ccan_list_del_init(&cur->node);
51 if (cur->th->status != THREAD_KILLED) {
52 if (cur->th->scheduler !=
Qnil && cur->fiber) {
56 RUBY_DEBUG_LOG(
"target_th:%u", rb_th_serial(cur->th));
57 rb_threadptr_interrupt(cur->th);
58 cur->th->status = THREAD_RUNNABLE;
61 if (--max == 0)
return;
67wakeup_one(
struct ccan_list_head *head)
73wakeup_all(
struct ccan_list_head *head)
75 sync_wakeup(head, LONG_MAX);
78#if defined(HAVE_WORKING_FORK)
79static void rb_mutex_abandon_all(
rb_mutex_t *mutexes);
80static void rb_mutex_abandon_keeping_mutexes(
rb_thread_t *th);
81static void rb_mutex_abandon_locking_mutex(
rb_thread_t *th);
91 ccan_list_for_each(&mutex->waitq, w, node) {
103 return mutex->ec_serial != 0;
112 if (mutex_locked_p(mutex)) {
113 thread_mutex_remove(mutex->th, mutex);
119mutex_memsize(
const void *ptr)
126 {NULL, mutex_free, mutex_memsize,},
141rb_obj_is_mutex(
VALUE obj)
143 return RBOOL(rb_typeddata_is_kind_of(obj, &mutex_data_type));
147mutex_alloc(
VALUE klass)
154 ccan_list_head_init(&mutex->waitq);
161 return mutex_alloc(rb_cMutex);
169 return RBOOL(mutex_locked_p(mutex));
176 if (thread->keeping_mutexes) {
177 mutex->next_mutex = thread->keeping_mutexes;
180 thread->keeping_mutexes = mutex;
186 rb_mutex_t **keeping_mutexes = &thread->keeping_mutexes;
188 while (*keeping_mutexes && *keeping_mutexes != mutex) {
190 keeping_mutexes = &(*keeping_mutexes)->next_mutex;
193 if (*keeping_mutexes) {
194 *keeping_mutexes = mutex->next_mutex;
195 mutex->next_mutex = NULL;
203 mutex->ec_serial = ec_serial;
209 mutex_set_owner(mutex, th, ec_serial);
210 thread_mutex_insert(th, mutex);
216 if (mutex->ec_serial == 0) {
217 RUBY_DEBUG_LOG(
"%p ok", mutex);
219 mutex_locked(mutex, th, ec_serial);
223 RUBY_DEBUG_LOG(
"%p ng", mutex);
231 return RBOOL(do_mutex_trylock(mutex_ptr(self), ec->thread_ptr, rb_ec_serial(ec)));
237 return rb_mut_trylock(GET_EC(), self);
241mutex_owned_p(rb_serial_t ec_serial,
rb_mutex_t *mutex)
243 return RBOOL(mutex->ec_serial == ec_serial);
247call_rb_fiber_scheduler_block(
VALUE mutex)
253delete_from_waitq(
VALUE value)
273 args->mutex = mutex_ptr(mutex);
278do_mutex_lock(
struct mutex_args *args,
int interruptible_p)
280 VALUE self = args->self;
284 rb_serial_t ec_serial = rb_ec_serial(ec);
290 th->ec->interrupt_mask & TRAP_INTERRUPT_MASK) {
294 if (!do_mutex_trylock(mutex, th, ec_serial)) {
295 if (mutex->ec_serial == ec_serial) {
299 while (mutex->ec_serial != ec_serial) {
300 VM_ASSERT(mutex->ec_serial != 0);
303 if (scheduler !=
Qnil) {
307 .fiber = nonblocking_fiber(fiber)
310 ccan_list_add_tail(&mutex->waitq, &
sync_waiter.node);
314 if (!mutex->ec_serial) {
315 mutex_set_owner(mutex, th, ec_serial);
319 if (!th->vm->thread_ignore_deadlock && mutex->th == th) {
320 rb_raise(
rb_eThreadError,
"deadlock; lock already owned by another fiber belonging to the same thread");
326 .fiber = nonblocking_fiber(fiber),
329 RUBY_DEBUG_LOG(
"%p wait", mutex);
340 enum rb_thread_status prev_status = th->status;
341 th->status = THREAD_STOPPED_FOREVER;
342 rb_ractor_sleeper_threads_inc(th->ractor);
343 rb_check_deadlock(th->ractor);
346 th->locking_mutex = self;
348 ccan_list_add_tail(&mutex->waitq, &
sync_waiter.node);
350 native_sleep(th, NULL);
355 if (!mutex->ec_serial) {
356 mutex_set_owner(mutex, th, ec_serial);
359 rb_ractor_sleeper_threads_dec(th->ractor);
360 th->status = prev_status;
361 th->locking_mutex =
Qfalse;
363 RUBY_DEBUG_LOG(
"%p wakeup", mutex);
366 if (interruptible_p) {
369 if (mutex->ec_serial == ec_serial) {
371 mutex->ec_serial = 0;
373 RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
374 if (!mutex->ec_serial) {
375 mutex_set_owner(mutex, th, ec_serial);
380 if (RUBY_VM_INTERRUPTED(th->ec)) {
382 if (saved_ints == 0) {
383 saved_ints = threadptr_get_interrupts(th);
387 threadptr_get_interrupts(th);
393 if (saved_ints) th->ec->interrupt_flag = saved_ints;
394 if (mutex->ec_serial == ec_serial) mutex_locked(mutex, th, ec_serial);
397 RUBY_DEBUG_LOG(
"%p locked", mutex);
400 if (mutex_owned_p(ec_serial, mutex) ==
Qfalse) rb_bug(
"do_mutex_lock: mutex is not owned.");
406mutex_lock_uninterruptible(
VALUE self)
409 mutex_args_init(&args, self);
410 return do_mutex_lock(&args, 0);
418 .mutex = mutex_ptr(self),
421 return do_mutex_lock(&args, 1);
428 mutex_args_init(&args, self);
429 return do_mutex_lock(&args, 1);
435 return mutex_owned_p(rb_ec_serial(ec), mutex_ptr(self));
439rb_mutex_owned_p(
VALUE self)
441 return rb_mut_owned_p(GET_EC(), self);
447 RUBY_DEBUG_LOG(
"%p", mutex);
449 if (mutex->ec_serial == 0) {
450 return "Attempt to unlock a mutex which is not locked";
452 else if (ec_serial && mutex->ec_serial != ec_serial) {
453 return "Attempt to unlock a mutex which is locked by another thread/fiber";
458 mutex->ec_serial = 0;
459 thread_mutex_remove(th, mutex);
461 ccan_list_for_each_safe(&mutex->waitq, cur, next, node) {
462 ccan_list_del_init(&cur->node);
464 if (cur->th->scheduler !=
Qnil && cur->fiber) {
469 switch (cur->th->status) {
470 case THREAD_RUNNABLE:
471 case THREAD_STOPPED_FOREVER:
472 RUBY_DEBUG_LOG(
"wakeup th:%u", rb_th_serial(cur->th));
473 rb_threadptr_interrupt(cur->th);
476 rb_bug(
"unexpected THREAD_STOPPED");
479 rb_bug(
"unexpected THREAD_KILLED");
496 err = rb_mutex_unlock_th(mutex, th, rb_ec_serial(args->ec));
501do_mutex_unlock_safe(
VALUE args)
511 mutex_args_init(&args, self);
512 do_mutex_unlock(&args);
521 .mutex = mutex_ptr(self),
524 do_mutex_unlock(&args);
528#if defined(HAVE_WORKING_FORK)
532 rb_mutex_abandon_all(th->keeping_mutexes);
533 th->keeping_mutexes = NULL;
539 if (th->locking_mutex) {
540 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
542 ccan_list_head_init(&mutex->waitq);
543 th->locking_mutex =
Qfalse;
554 mutexes = mutex->next_mutex;
555 mutex->ec_serial = 0;
556 mutex->next_mutex = 0;
557 ccan_list_head_init(&mutex->waitq);
568mutex_sleep_begin(
VALUE _arguments)
571 VALUE timeout = arguments->timeout;
575 if (scheduler !=
Qnil) {
579 if (
NIL_P(timeout)) {
580 rb_thread_sleep_deadly_allow_spurious_wakeup(arguments->self,
Qnil, 0);
584 rb_hrtime_t relative_timeout = rb_timeval2hrtime(&timeout_value);
586 woken = RBOOL(sleep_hrtime(GET_THREAD(), relative_timeout, 0));
596 if (!
NIL_P(timeout)) {
601 rb_mut_unlock(ec, self);
602 time_t beg = time(0);
609 VALUE woken = rb_ec_ensure(ec, mutex_sleep_begin, (
VALUE)&arguments, mutex_lock_uninterruptible, self);
611 RUBY_VM_CHECK_INTS_BLOCKING(ec);
612 if (!woken)
return Qnil;
613 time_t end = time(0) - beg;
614 return TIMET2NUM(end);
620 return rb_mut_sleep(GET_EC(), self, timeout);
627 mutex_args_init(&args, self);
628 do_mutex_lock(&args, 1);
629 return rb_ec_ensure(args.ec, func, arg, do_mutex_unlock_safe, (
VALUE)&args);
633do_ec_yield(
VALUE _ec)
643 .mutex = mutex_ptr(self),
646 do_mutex_lock(&args, 1);
647 return rb_ec_ensure(args.ec, do_ec_yield, (
VALUE)ec, do_mutex_unlock_safe, (
VALUE)&args);
651rb_mutex_allow_trap(
VALUE self,
int val)
664 struct ccan_list_head waitq;
665 rb_serial_t fork_gen;
673#define szqueue_waitq(sq) &sq->q.waitq
674#define szqueue_pushq(sq) &sq->pushq
678 int num_waiting_push;
679 struct ccan_list_head pushq;
684queue_mark_and_move(
void *ptr)
688 for (
long index = 0; index < q->len; index++) {
689 rb_gc_mark_and_move(&q->buffer[((q->offset + index) % q->capa)]);
694queue_free_buffer(
struct rb_queue *q)
697 SIZED_FREE_N(q->buffer, q->capa);
705 queue_free_buffer(q);
710queue_memsize(
const void *ptr)
719 .dmark = queue_mark_and_move,
721 .dsize = queue_memsize,
722 .dcompact = queue_mark_and_move,
724 .flags = RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_WB_PROTECTED,
728queue_alloc(
VALUE klass)
734 ccan_list_head_init(&q->waitq);
741 rb_serial_t fork_gen = GET_VM()->fork_gen;
743 if (RB_LIKELY(q->fork_gen == fork_gen)) {
747 q->fork_gen = fork_gen;
748 ccan_list_head_init(&q->waitq);
754raw_queue_ptr(
VALUE obj)
767 if (RB_UNLIKELY(q->buffer == NULL)) {
768 rb_raise(
rb_eTypeError,
"%+"PRIsVALUE
" not initialized", obj);
775 struct rb_queue *q = raw_queue_ptr(obj);
780#define QUEUE_CLOSED FL_USER5
783queue_timeout2hrtime(
VALUE timeout)
785 if (
NIL_P(timeout)) {
786 return (rb_hrtime_t)0;
790 rel = rb_sec2hrtime(NUM2TIMET(timeout));
795 return rb_hrtime_add(rel, rb_hrtime_now());
799szqueue_mark_and_move(
void *ptr)
803 queue_mark_and_move(&sq->q);
807szqueue_free(
void *ptr)
810 queue_free_buffer(&sq->q);
815szqueue_memsize(
const void *ptr)
824 .dmark = szqueue_mark_and_move,
825 .dfree = szqueue_free,
826 .dsize = szqueue_memsize,
827 .dcompact = szqueue_mark_and_move,
829 .parent = &queue_data_type,
830 .
flags = RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_WB_PROTECTED,
834szqueue_alloc(
VALUE klass)
838 &szqueue_data_type, sq);
839 ccan_list_head_init(szqueue_waitq(sq));
840 ccan_list_head_init(szqueue_pushq(sq));
845raw_szqueue_ptr(
VALUE obj)
850 if (RB_UNLIKELY(queue_fork_check(&sq->q))) {
851 ccan_list_head_init(szqueue_pushq(sq));
852 sq->num_waiting_push = 0;
859szqueue_ptr(
VALUE obj)
862 check_queue(obj, &sq->q);
867queue_closed_p(
VALUE self)
879NORETURN(
static void raise_closed_queue_error(
VALUE self));
882raise_closed_queue_error(
VALUE self)
884 rb_raise(rb_eClosedQueueError,
"queue closed");
894#define QUEUE_INITIAL_CAPA 8
897ring_buffer_init(
struct rb_queue *q,
long initial_capa)
900 q->capa = initial_capa;
904ring_buffer_expand(
struct rb_queue *q)
908 MEMCPY(new_buffer, q->buffer + q->offset,
VALUE, q->capa - q->offset);
909 MEMCPY(new_buffer + (q->capa - q->offset), q->buffer,
VALUE, q->offset);
910 VALUE *old_buffer = q->buffer;
911 q->buffer = new_buffer;
913 ruby_xfree_sized(old_buffer, q->capa *
sizeof(
VALUE));
920 if (RB_UNLIKELY(q->len >= q->capa)) {
921 ring_buffer_expand(q);
924 long index = (q->offset + q->len) % q->capa;
930ring_buffer_shift(
struct rb_queue *q)
936 VALUE obj = q->buffer[q->offset];
942 q->offset = (q->offset + 1) % q->capa;
950 struct rb_queue *q = raw_queue_ptr(self);
951 ccan_list_head_init(&q->waitq);
952 if (
NIL_P(initial)) {
953 ring_buffer_init(q, QUEUE_INITIAL_CAPA);
956 initial = rb_to_array(initial);
958 long initial_capa = QUEUE_INITIAL_CAPA;
959 while (initial_capa <
len) {
962 ring_buffer_init(q, initial_capa);
972 check_queue(self, q);
973 if (queue_closed_p(self)) {
974 raise_closed_queue_error(self);
976 ring_buffer_push(self, q, obj);
977 wakeup_one(&q->waitq);
982queue_sleep(
VALUE _args)
985 rb_thread_sleep_deadly_allow_spurious_wakeup(args->self, args->timeout, args->end);
998queue_sleep_done(
VALUE p)
1002 ccan_list_del(&qw->w.node);
1003 qw->as.q->num_waiting--;
1009szqueue_sleep_done(
VALUE p)
1013 ccan_list_del(&qw->w.node);
1014 qw->as.sq->num_waiting_push--;
1023 if (
RTEST(non_block)) {
1032 rb_hrtime_t end = queue_timeout2hrtime(timeout);
1033 while (q->len == 0) {
1034 if (queue_closed_p(self)) {
1035 return queue_closed_result(self, q);
1042 .w = {.self = self, .th = ec->thread_ptr, .fiber = nonblocking_fiber(ec->fiber_ptr)},
1046 struct ccan_list_head *waitq = &q->waitq;
1058 if (!
NIL_P(timeout) && (rb_hrtime_now() >= end))
1063 return ring_buffer_shift(q);
1069 return queue_do_pop(ec, self, queue_ptr(self), non_block, timeout);
1083 struct rb_szqueue *sq = raw_szqueue_ptr(self);
1086 rb_raise(rb_eArgError,
"queue size must be positive");
1088 ring_buffer_init(&sq->q, QUEUE_INITIAL_CAPA);
1089 ccan_list_head_init(szqueue_waitq(sq));
1090 ccan_list_head_init(szqueue_pushq(sq));
1101 if (sq->q.len >= sq->max) {
1102 if (
RTEST(non_block)) {
1111 rb_hrtime_t end = queue_timeout2hrtime(timeout);
1112 while (sq->q.len >= sq->max) {
1113 if (queue_closed_p(self)) {
1114 raise_closed_queue_error(self);
1118 .w = {.self = self, .th = ec->thread_ptr, .fiber = nonblocking_fiber(ec->fiber_ptr)},
1122 struct ccan_list_head *pushq = szqueue_pushq(sq);
1125 sq->num_waiting_push++;
1133 if (!
NIL_P(timeout) && rb_hrtime_now() >= end) {
1139 return queue_do_push(self, &sq->q,
object);
1146 VALUE retval = queue_do_pop(ec, self, &sq->q, non_block, timeout);
1148 if (sq->q.len < sq->max) {
1149 wakeup_one(szqueue_pushq(sq));
1157 struct ccan_list_head waitq;
1158 rb_serial_t fork_gen;
1162condvar_memsize(
const void *ptr)
1170 0, 0, RUBY_TYPED_THREAD_SAFE_FREE|RUBY_TYPED_WB_PROTECTED
1174condvar_ptr(
VALUE self)
1177 rb_serial_t fork_gen = GET_VM()->fork_gen;
1182 if (cv->fork_gen != fork_gen) {
1183 cv->fork_gen = fork_gen;
1184 ccan_list_head_init(&cv->waitq);
1191condvar_alloc(
VALUE klass)
1197 ccan_list_head_init(&cv->waitq);
1214 if (
CLASS_OF(p->mutex) == rb_cMutex) {
1215 return rb_mut_sleep(p->ec, p->mutex, p->timeout);
1218 return rb_funcallv(p->mutex, id_sleep, 1, &p->timeout);
1234 .th = ec->thread_ptr,
1235 .fiber = nonblocking_fiber(ec->fiber_ptr)
1238 ccan_list_add_tail(&cv->waitq, &
sync_waiter.node);
1246 wakeup_one(&cv->waitq);
1254 wakeup_all(&cv->waitq);
1262 rb_serial_t ec_serial;
1267monitor_mark(
void *ptr)
1270 rb_gc_mark_movable(mc->mutex);
1274monitor_compact(
void *ptr)
1277 mc->mutex = rb_gc_location(mc->mutex);
1283 .dmark = monitor_mark,
1286 .dcompact = monitor_compact,
1292monitor_alloc(
VALUE klass)
1306monitor_ptr(
VALUE monitor)
1314mc_owner_p(
struct rb_monitor *mc, rb_serial_t current_fiber_serial)
1316 return mc->ec_serial == current_fiber_serial;
1322 struct rb_monitor *mc = monitor_ptr(monitor);
1324 rb_serial_t current_fiber_serial = rb_ec_serial(ec);
1325 if (!mc_owner_p(mc, current_fiber_serial)) {
1326 if (!rb_mut_trylock(ec, mc->mutex)) {
1329 mc->ec_serial = current_fiber_serial;
1339 rb_serial_t current_fiber_serial;
1346 if (!mc_owner_p(args->mc, args->current_fiber_serial)) {
1348 .self = args->mc->mutex,
1349 .mutex = mutex_ptr(args->mc->mutex),
1352 do_mutex_lock(&mut_args, 1);
1353 args->mc->ec_serial = args->current_fiber_serial;
1354 args->mc->count = 0;
1364 .mc = monitor_ptr(monitor),
1366 .current_fiber_serial = rb_ec_serial(ec),
1368 monitor_enter0(&args);
1375 if (!mc_owner_p(args->mc, args->current_fiber_serial)) {
1385 .mc = monitor_ptr(monitor),
1387 .current_fiber_serial = rb_ec_serial(ec),
1389 monitor_check_owner0(&args);
1396 monitor_check_owner0(args);
1398 if (args->mc->count <= 0) rb_bug(
"monitor_exit: count:%d", (
int)args->mc->count);
1401 if (args->mc->count == 0) {
1402 args->mc->ec_serial = 0;
1405 .self = args->mc->mutex,
1406 .mutex = mutex_ptr(args->mc->mutex),
1409 do_mutex_unlock(&mut_args);
1418 .mc = monitor_ptr(monitor),
1420 .current_fiber_serial = rb_ec_serial(ec),
1422 monitor_exit0(&args);
1429 struct rb_monitor *mc = monitor_ptr(monitor);
1436 struct rb_monitor *mc = monitor_ptr(monitor);
1441monitor_exit_for_cond(
VALUE monitor)
1443 struct rb_monitor *mc = monitor_ptr(monitor);
1444 long cnt = mc->count;
1458monitor_wait_for_cond_body(
VALUE v)
1461 struct rb_monitor *mc = monitor_ptr(data->monitor);
1463 VALUE signaled =
rb_funcall(data->cond, rb_intern(
"wait"), 2, mc->mutex, data->timeout);
1468monitor_enter_for_cond(
VALUE v)
1474 struct rb_monitor *mc = monitor_ptr(data->monitor);
1475 mc->ec_serial = rb_ec_serial(GET_EC());
1483 VALUE count = monitor_exit_for_cond(monitor);
1492 monitor_enter_for_cond, (
VALUE)&data);
1496monitor_sync_ensure(
VALUE v_args)
1507 .mc = monitor_ptr(monitor),
1509 .current_fiber_serial = rb_ec_serial(ec),
1511 monitor_enter0(&args);
1512 return rb_ec_ensure(ec, do_ec_yield, (
VALUE)ec, monitor_sync_ensure, (
VALUE)&args);
1516Init_thread_sync(
void)
1528 VALUE rb_cSizedQueue = rb_define_class_id_under_no_pin(
rb_cThread, rb_intern(
"SizedQueue"), rb_cQueue);
1535 id_sleep = rb_intern(
"sleep");
1545#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.
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.
#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 CLASS_OF
Old name of rb_class_of.
#define ALLOC_N
Old name of RB_ALLOC_N.
#define FL_TEST_RAW
Old name of RB_FL_TEST_RAW.
#define LONG2NUM
Old name of RB_LONG2NUM.
#define Qtrue
Old name of RUBY_Qtrue.
#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.
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_cObject
Object class.
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_funcall(VALUE recv, ID mid, int n,...)
Calls a method.
VALUE rb_funcallv(VALUE recv, ID mid, int argc, const VALUE *argv)
Identical to rb_funcall(), except it takes the method arguments as a C array.
Defines RBIMPL_HAS_BUILTIN.
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_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
int capa
Designed capacity of the buffer.
int len
Length of the buffer.
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
#define RARRAY_LEN
Just another name of rb_array_len.
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
#define RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
#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.
This is the struct that holds necessary info for a struct.
const char * wrap_struct_name
Name of structs of this kind.
VALUE flags
Type-specific behavioural characteristics.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.