61#undef __USE_FORTIFY_LEVEL
62#define __USE_FORTIFY_LEVEL 0
66#include "ruby/internal/config.h"
73#define TH_SCHED(th) (&(th)->ractor->threads.sched)
75#include "eval_intern.h"
78#include "internal/class.h"
79#include "internal/cont.h"
80#include "internal/error.h"
81#include "internal/gc.h"
82#include "internal/hash.h"
83#include "internal/io.h"
84#include "internal/object.h"
85#include "internal/proc.h"
87#include "internal/signal.h"
88#include "internal/thread.h"
89#include "internal/time.h"
90#include "internal/warnings.h"
99#include "ractor_core.h"
103#if USE_RJIT && defined(HAVE_SYS_WAIT_H)
107#ifndef USE_NATIVE_THREAD_PRIORITY
108#define USE_NATIVE_THREAD_PRIORITY 0
109#define RUBY_THREAD_PRIORITY_MAX 3
110#define RUBY_THREAD_PRIORITY_MIN -3
113static VALUE rb_cThreadShield;
115static VALUE sym_immediate;
116static VALUE sym_on_blocking;
117static VALUE sym_never;
119#define THREAD_LOCAL_STORAGE_INITIALISED FL_USER13
120#define THREAD_LOCAL_STORAGE_INITIALISED_P(th) RB_FL_TEST_RAW((th), THREAD_LOCAL_STORAGE_INITIALISED)
123rb_thread_local_storage(
VALUE thread)
125 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
133 SLEEP_DEADLOCKABLE = 0x01,
134 SLEEP_SPURIOUS_CHECK = 0x02,
135 SLEEP_ALLOW_SPURIOUS = 0x04,
136 SLEEP_NO_CHECKINTS = 0x08,
139static void sleep_forever(rb_thread_t *th,
unsigned int fl);
140static int sleep_hrtime(rb_thread_t *, rb_hrtime_t,
unsigned int fl);
142static void rb_thread_sleep_deadly_allow_spurious_wakeup(
VALUE blocker,
VALUE timeout, rb_hrtime_t end);
143static int rb_threadptr_dead(rb_thread_t *th);
144static void rb_check_deadlock(rb_ractor_t *r);
145static int rb_threadptr_pending_interrupt_empty_p(
const rb_thread_t *th);
146static const char *thread_status_name(rb_thread_t *th,
int detail);
147static int hrtime_update_expire(rb_hrtime_t *,
const rb_hrtime_t);
148NORETURN(
static void async_bug_fd(
const char *mesg,
int errno_arg,
int fd));
149MAYBE_UNUSED(
static int consume_communication_pipe(
int fd));
151static volatile int system_working = 1;
152static rb_internal_thread_specific_key_t specific_key_count;
155 struct ccan_list_node wfd_node;
163#define THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
166 enum rb_thread_status prev_status;
169static int unblock_function_set(rb_thread_t *th,
rb_unblock_function_t *func,
void *arg,
int fail_if_interrupted);
170static void unblock_function_clear(rb_thread_t *th);
176#define THREAD_BLOCKING_BEGIN(th) do { \
177 struct rb_thread_sched * const sched = TH_SCHED(th); \
178 RB_VM_SAVE_MACHINE_CONTEXT(th); \
179 thread_sched_to_waiting((sched), (th));
181#define THREAD_BLOCKING_END(th) \
182 thread_sched_to_running((sched), (th)); \
183 rb_ractor_thread_switch(th->ractor, th); \
187#ifdef HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P
188#define only_if_constant(expr, notconst) __builtin_choose_expr(__builtin_constant_p(expr), (expr), (notconst))
190#define only_if_constant(expr, notconst) (__builtin_constant_p(expr) ? (expr) : (notconst))
193#define only_if_constant(expr, notconst) notconst
195#define BLOCKING_REGION(th, exec, ubf, ubfarg, fail_if_interrupted) do { \
196 struct rb_blocking_region_buffer __region; \
197 if (blocking_region_begin(th, &__region, (ubf), (ubfarg), fail_if_interrupted) || \
199 !only_if_constant(fail_if_interrupted, TRUE)) { \
202 RB_VM_SAVE_MACHINE_CONTEXT(th); \
203 thread_sched_to_waiting(TH_SCHED(th), th); \
205 blocking_region_end(th, &__region); \
213#define RUBY_VM_CHECK_INTS_BLOCKING(ec) vm_check_ints_blocking(ec)
215vm_check_ints_blocking(rb_execution_context_t *ec)
217 rb_thread_t *th = rb_ec_thread_ptr(ec);
219 if (LIKELY(rb_threadptr_pending_interrupt_empty_p(th))) {
220 if (LIKELY(!RUBY_VM_INTERRUPTED_ANY(ec)))
return FALSE;
223 th->pending_interrupt_queue_checked = 0;
224 RUBY_VM_SET_INTERRUPT(ec);
226 return rb_threadptr_execute_interrupts(th, 1);
230rb_vm_check_ints_blocking(rb_execution_context_t *ec)
232 return vm_check_ints_blocking(ec);
240#if defined(HAVE_POLL)
241# if defined(__linux__)
244# if defined(__FreeBSD_version) && __FreeBSD_version >= 1100000
247# define POLLERR_SET (POLLHUP | POLLERR)
252timeout_prepare(rb_hrtime_t **to, rb_hrtime_t *rel, rb_hrtime_t *end,
256 *rel = rb_timeval2hrtime(timeout);
257 *end = rb_hrtime_add(rb_hrtime_now(), *rel);
265MAYBE_UNUSED(NOINLINE(
static int thread_start_func_2(rb_thread_t *th,
VALUE *stack_start)));
266MAYBE_UNUSED(
static bool th_has_dedicated_nt(
const rb_thread_t *th));
267MAYBE_UNUSED(
static int waitfd_to_waiting_flag(
int wfd_event));
269#include THREAD_IMPL_SRC
276#ifndef BUSY_WAIT_SIGNALS
277# define BUSY_WAIT_SIGNALS (0)
281# define USE_EVENTFD (0)
284#include "thread_sync.c"
311unblock_function_set(rb_thread_t *th,
rb_unblock_function_t *func,
void *arg,
int fail_if_interrupted)
314 if (fail_if_interrupted) {
315 if (RUBY_VM_INTERRUPTED_ANY(th->ec)) {
320 RUBY_VM_CHECK_INTS(th->ec);
324 }
while (!th->ec->raised_flag && RUBY_VM_INTERRUPTED_ANY(th->ec) &&
327 VM_ASSERT(th->unblock.func == NULL);
329 th->unblock.func = func;
330 th->unblock.arg = arg;
337unblock_function_clear(rb_thread_t *th)
340 th->unblock.func = 0;
345rb_threadptr_interrupt_common(rb_thread_t *th,
int trap)
347 RUBY_DEBUG_LOG(
"th:%u trap:%d", rb_th_serial(th), trap);
352 RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
355 RUBY_VM_SET_INTERRUPT(th->ec);
358 if (th->unblock.func != NULL) {
359 (th->unblock.func)(th->unblock.arg);
369rb_threadptr_interrupt(rb_thread_t *th)
371 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
372 rb_threadptr_interrupt_common(th, 0);
376threadptr_trap_interrupt(rb_thread_t *th)
378 rb_threadptr_interrupt_common(th, 1);
382terminate_all(rb_ractor_t *r,
const rb_thread_t *main_thread)
386 ccan_list_for_each(&r->threads.set, th, lt_node) {
387 if (th != main_thread) {
388 RUBY_DEBUG_LOG(
"terminate start th:%u status:%s", rb_th_serial(th), thread_status_name(th, TRUE));
390 rb_threadptr_pending_interrupt_enque(th, RUBY_FATAL_THREAD_TERMINATED);
391 rb_threadptr_interrupt(th);
393 RUBY_DEBUG_LOG(
"terminate done th:%u status:%s", rb_th_serial(th), thread_status_name(th, TRUE));
396 RUBY_DEBUG_LOG(
"main thread th:%u", rb_th_serial(th));
402rb_threadptr_join_list_wakeup(rb_thread_t *thread)
404 while (thread->join_list) {
408 thread->join_list = join_list->next;
410 rb_thread_t *target_thread = join_list->thread;
412 if (target_thread->scheduler !=
Qnil && join_list->fiber) {
416 rb_threadptr_interrupt(target_thread);
418 switch (target_thread->status) {
420 case THREAD_STOPPED_FOREVER:
421 target_thread->status = THREAD_RUNNABLE;
431rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th)
433 while (th->keeping_mutexes) {
434 rb_mutex_t *mutex = th->keeping_mutexes;
435 th->keeping_mutexes = mutex->next_mutex;
439 const char *error_message = rb_mutex_unlock_th(mutex, th, mutex->fiber);
440 if (error_message) rb_bug(
"invalid keeping_mutexes: %s", error_message);
445rb_thread_terminate_all(rb_thread_t *th)
447 rb_ractor_t *cr = th->ractor;
448 rb_execution_context_t *
volatile ec = th->ec;
449 volatile int sleeping = 0;
451 if (cr->threads.main != th) {
452 rb_bug(
"rb_thread_terminate_all: called by child thread (%p, %p)",
453 (
void *)cr->threads.main, (
void *)th);
457 rb_threadptr_unlock_all_locking_mutexes(th);
460 if (EC_EXEC_TAG() == TAG_NONE) {
462 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
464 terminate_all(cr, th);
466 while (rb_ractor_living_thread_num(cr) > 1) {
467 rb_hrtime_t rel = RB_HRTIME_PER_SEC;
473 native_sleep(th, &rel);
474 RUBY_VM_CHECK_INTS_BLOCKING(ec);
492void rb_threadptr_root_fiber_terminate(rb_thread_t *th);
495thread_cleanup_func_before_exec(
void *th_ptr)
497 rb_thread_t *th = th_ptr;
498 th->status = THREAD_KILLED;
501 th->ec->machine.stack_start = th->ec->machine.stack_end = NULL;
503 rb_threadptr_root_fiber_terminate(th);
507thread_cleanup_func(
void *th_ptr,
int atfork)
509 rb_thread_t *th = th_ptr;
511 th->locking_mutex =
Qfalse;
512 thread_cleanup_func_before_exec(th_ptr);
527static VALUE rb_threadptr_raise(rb_thread_t *,
int,
VALUE *);
531ruby_thread_init_stack(rb_thread_t *th)
533 native_thread_init_stack(th);
537rb_vm_proc_local_ep(
VALUE proc)
539 const VALUE *ep = vm_proc_ep(proc);
542 return rb_vm_ep_local_ep(ep);
551 int argc,
const VALUE *argv,
int kw_splat,
VALUE passed_block_handler);
554thread_do_start_proc(rb_thread_t *th)
556 VALUE args = th->invoke_arg.proc.args;
557 const VALUE *args_ptr;
559 VALUE procval = th->invoke_arg.proc.proc;
561 GetProcPtr(procval, proc);
563 th->ec->errinfo =
Qnil;
564 th->ec->root_lep = rb_vm_proc_local_ep(procval);
565 th->ec->root_svar =
Qfalse;
567 vm_check_ints_blocking(th->ec);
569 if (th->invoke_type == thread_invoke_type_ractor_proc) {
570 VALUE self = rb_ractor_self(th->ractor);
574 rb_ractor_receive_parameters(th->ec, th->ractor, args_len, (
VALUE *)args_ptr);
575 vm_check_ints_blocking(th->ec);
577 return rb_vm_invoke_proc_with_self(
580 th->invoke_arg.proc.kw_splat,
581 VM_BLOCK_HANDLER_NONE
590 th->invoke_arg.proc.args =
Qnil;
596 vm_check_ints_blocking(th->ec);
598 return rb_vm_invoke_proc(
601 th->invoke_arg.proc.kw_splat,
602 VM_BLOCK_HANDLER_NONE
608thread_do_start(rb_thread_t *th)
610 native_set_thread_name(th);
613 switch (th->invoke_type) {
614 case thread_invoke_type_proc:
615 result = thread_do_start_proc(th);
618 case thread_invoke_type_ractor_proc:
619 result = thread_do_start_proc(th);
620 rb_ractor_atexit(th->ec, result);
623 case thread_invoke_type_func:
624 result = (*th->invoke_arg.func.func)(th->invoke_arg.func.arg);
627 case thread_invoke_type_none:
628 rb_bug(
"unreachable");
634void rb_ec_clear_current_thread_trace_func(
const rb_execution_context_t *ec);
637thread_start_func_2(rb_thread_t *th,
VALUE *stack_start)
639 STACK_GROW_DIR_DETECTION;
641 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
642 VM_ASSERT(th != th->vm->ractor.main_thread);
644 enum ruby_tag_type state;
646 rb_thread_t *ractor_main_th = th->ractor->threads.main;
649 if (rb_ractor_status_p(th->ractor, ractor_blocking)) {
652 rb_vm_ractor_blocking_cnt_dec(th->vm, th->ractor, __FILE__, __LINE__);
653 rb_ractor_t *r = th->ractor;
654 r->r_stdin = rb_io_prep_stdin();
655 r->r_stdout = rb_io_prep_stdout();
656 r->r_stderr = rb_io_prep_stderr();
662 VM_ASSERT(UNDEF_P(th->value));
664 int fiber_scheduler_closed = 0, event_thread_end_hooked = 0;
669 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
672 SAVE_ROOT_JMPBUF(th, result = thread_do_start(th));
675 if (!fiber_scheduler_closed) {
676 fiber_scheduler_closed = 1;
680 if (!event_thread_end_hooked) {
681 event_thread_end_hooked = 1;
685 if (state == TAG_NONE) {
689 errinfo = th->ec->errinfo;
691 VALUE exc = rb_vm_make_jump_tag_but_local_jump(state,
Qundef);
692 if (!
NIL_P(exc)) errinfo = exc;
694 if (state == TAG_FATAL) {
695 if (th->invoke_type == thread_invoke_type_ractor_proc) {
696 rb_ractor_atexit(th->ec,
Qnil);
704 if (th->report_on_exception) {
705 VALUE mesg = rb_thread_to_s(th->self);
706 rb_str_cat_cstr(mesg,
" terminated with exception (report_on_exception is true):\n");
707 rb_write_error_str(mesg);
708 rb_ec_error_print(th->ec, errinfo);
711 if (th->invoke_type == thread_invoke_type_ractor_proc) {
712 rb_ractor_atexit_exception(th->ec);
715 if (th->vm->thread_abort_on_exception ||
727 VM_ASSERT(!UNDEF_P(th->value));
729 rb_threadptr_join_list_wakeup(th);
730 rb_threadptr_unlock_all_locking_mutexes(th);
732 if (th->invoke_type == thread_invoke_type_ractor_proc) {
733 rb_thread_terminate_all(th);
734 rb_ractor_teardown(th->ec);
737 th->status = THREAD_KILLED;
738 RUBY_DEBUG_LOG(
"killed th:%u", rb_th_serial(th));
740 if (th->vm->ractor.main_thread == th) {
746 rb_threadptr_raise(ractor_main_th, 1, &errinfo);
751 rb_ec_clear_current_thread_trace_func(th->ec);
754 if (th->locking_mutex !=
Qfalse) {
755 rb_bug(
"thread_start_func_2: locking_mutex must not be set (%p:%"PRIxVALUE
")",
756 (
void *)th, th->locking_mutex);
759 if (ractor_main_th->status == THREAD_KILLED &&
760 th->ractor->threads.cnt <= 2 ) {
762 rb_threadptr_interrupt(ractor_main_th);
765 rb_check_deadlock(th->ractor);
767 rb_fiber_close(th->ec->fiber_ptr);
769 thread_cleanup_func(th, FALSE);
770 VM_ASSERT(th->ec->vm_stack == NULL);
772 if (th->invoke_type == thread_invoke_type_ractor_proc) {
776 thread_sched_to_dead(TH_SCHED(th), th);
777 rb_ractor_living_threads_remove(th->ractor, th);
780 rb_ractor_living_threads_remove(th->ractor, th);
781 thread_sched_to_dead(TH_SCHED(th), th);
788 enum thread_invoke_type type;
801static void thread_specific_storage_alloc(rb_thread_t *th);
806 rb_execution_context_t *ec = GET_EC();
807 rb_thread_t *th = rb_thread_ptr(thval), *current_th = rb_ec_thread_ptr(ec);
810 thread_specific_storage_alloc(th);
814 "can't start a new thread (frozen ThreadGroup)");
817 rb_fiber_inherit_storage(ec, th->ec->fiber_ptr);
819 switch (params->type) {
820 case thread_invoke_type_proc:
821 th->invoke_type = thread_invoke_type_proc;
822 th->invoke_arg.proc.args = params->args;
823 th->invoke_arg.proc.proc = params->proc;
827 case thread_invoke_type_ractor_proc:
828#if RACTOR_CHECK_MODE > 0
829 rb_ractor_setup_belonging_to(thval, rb_ractor_id(params->g));
831 th->invoke_type = thread_invoke_type_ractor_proc;
832 th->ractor = params->g;
833 th->ractor->threads.main = th;
834 th->invoke_arg.proc.proc = rb_proc_isolate_bang(params->proc);
837 rb_ractor_send_parameters(ec, params->g, params->args);
840 case thread_invoke_type_func:
841 th->invoke_type = thread_invoke_type_func;
842 th->invoke_arg.func.func = params->fn;
843 th->invoke_arg.func.arg = (
void *)params->args;
847 rb_bug(
"unreachable");
850 th->priority = current_th->priority;
851 th->thgroup = current_th->thgroup;
853 th->pending_interrupt_queue = rb_ary_hidden_new(0);
854 th->pending_interrupt_queue_checked = 0;
855 th->pending_interrupt_mask_stack = rb_ary_dup(current_th->pending_interrupt_mask_stack);
856 RBASIC_CLEAR_CLASS(th->pending_interrupt_mask_stack);
860 RUBY_DEBUG_LOG(
"r:%u th:%u", rb_ractor_id(th->ractor), rb_th_serial(th));
862 rb_ractor_living_threads_insert(th->ractor, th);
865 err = native_thread_create(th);
867 th->status = THREAD_KILLED;
868 rb_ractor_living_threads_remove(th->ractor, th);
874#define threadptr_initialized(th) ((th)->invoke_type != thread_invoke_type_none)
897thread_s_new(
int argc,
VALUE *argv,
VALUE klass)
900 VALUE thread = rb_thread_alloc(klass);
902 if (GET_RACTOR()->threads.main->status == THREAD_KILLED) {
907 th = rb_thread_ptr(thread);
908 if (!threadptr_initialized(th)) {
909 rb_raise(
rb_eThreadError,
"uninitialized thread - check `%"PRIsVALUE
"#initialize'",
929 .type = thread_invoke_type_proc,
933 return thread_create_core(rb_thread_alloc(klass), ¶ms);
937threadptr_invoke_proc_location(rb_thread_t *th)
939 if (th->invoke_type == thread_invoke_type_proc) {
940 return rb_proc_location(th->invoke_arg.proc.proc);
951 rb_thread_t *th = rb_thread_ptr(thread);
956 else if (th->invoke_type != thread_invoke_type_none) {
957 VALUE loc = threadptr_invoke_proc_location(th);
960 "already initialized thread - %"PRIsVALUE
":%"PRIsVALUE,
969 .type = thread_invoke_type_proc,
973 return thread_create_core(thread, ¶ms);
981 .type = thread_invoke_type_func,
985 return thread_create_core(rb_thread_alloc(
rb_cThread), ¶ms);
989rb_thread_create_ractor(rb_ractor_t *r,
VALUE args,
VALUE proc)
992 .type = thread_invoke_type_ractor_proc,
997 return thread_create_core(rb_thread_alloc(
rb_cThread), ¶ms);
1003 rb_thread_t *target;
1009remove_from_join_list(
VALUE arg)
1012 rb_thread_t *target_thread = p->target;
1014 if (target_thread->status != THREAD_KILLED) {
1017 while (*join_list) {
1018 if (*join_list == p->waiter) {
1019 *join_list = (*join_list)->next;
1023 join_list = &(*join_list)->next;
1031thread_finished(rb_thread_t *th)
1033 return th->status == THREAD_KILLED || !UNDEF_P(th->value);
1037thread_join_sleep(
VALUE arg)
1040 rb_thread_t *target_th = p->target, *th = p->waiter->thread;
1041 rb_hrtime_t end = 0, *limit = p->limit;
1044 end = rb_hrtime_add(*limit, rb_hrtime_now());
1047 while (!thread_finished(target_th)) {
1050 if (scheduler !=
Qnil) {
1053 if (thread_finished(target_th))
break;
1058 sleep_forever(th, SLEEP_DEADLOCKABLE | SLEEP_ALLOW_SPURIOUS | SLEEP_NO_CHECKINTS);
1061 if (hrtime_update_expire(limit, end)) {
1062 RUBY_DEBUG_LOG(
"timeout target_th:%u", rb_th_serial(target_th));
1065 th->status = THREAD_STOPPED;
1066 native_sleep(th, limit);
1068 RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1069 th->status = THREAD_RUNNABLE;
1071 RUBY_DEBUG_LOG(
"interrupted target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1078thread_join(rb_thread_t *target_th,
VALUE timeout, rb_hrtime_t *limit)
1080 rb_execution_context_t *ec = GET_EC();
1081 rb_thread_t *th = ec->thread_ptr;
1082 rb_fiber_t *fiber = ec->fiber_ptr;
1084 if (th == target_th) {
1085 rb_raise(
rb_eThreadError,
"Target thread must not be current thread");
1088 if (th->ractor->threads.main == target_th) {
1092 RUBY_DEBUG_LOG(
"target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1094 if (target_th->status != THREAD_KILLED) {
1096 waiter.next = target_th->join_list;
1098 waiter.fiber = rb_fiberptr_blocking(fiber) ? NULL : fiber;
1099 target_th->join_list = &waiter;
1102 arg.waiter = &waiter;
1103 arg.target = target_th;
1104 arg.timeout = timeout;
1112 RUBY_DEBUG_LOG(
"success target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1114 if (target_th->ec->errinfo !=
Qnil) {
1115 VALUE err = target_th->ec->errinfo;
1120 RUBY_DEBUG_LOG(
"terminated target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1125 rb_bug(
"thread_join: Fixnum (%d) should not reach here.",
FIX2INT(err));
1128 else if (THROW_DATA_P(target_th->ec->errinfo)) {
1129 rb_bug(
"thread_join: THROW_DATA should not reach here.");
1136 return target_th->self;
1179thread_join_m(
int argc,
VALUE *argv,
VALUE self)
1182 rb_hrtime_t rel = 0, *limit = 0;
1193 if (
NIL_P(timeout)) {
1197 rel = rb_sec2hrtime(NUM2TIMET(timeout));
1201 limit = double2hrtime(&rel,
rb_num2dbl(timeout));
1204 return thread_join(rb_thread_ptr(self), timeout, limit);
1222thread_value(
VALUE self)
1224 rb_thread_t *th = rb_thread_ptr(self);
1225 thread_join(th,
Qnil, 0);
1226 if (UNDEF_P(th->value)) {
1240#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
1241 if (clock_gettime(CLOCK_MONOTONIC, ts) == 0)
1251NOINLINE(rb_hrtime_t rb_hrtime_now(
void));
1258 return rb_timespec2hrtime(&ts);
1265COMPILER_WARNING_PUSH
1266#if defined(__GNUC__) && __GNUC__ == 7 && __GNUC_MINOR__ <= 3
1267COMPILER_WARNING_IGNORED(-Wmaybe-uninitialized)
1270#define PRIu64 PRI_64_PREFIX "u"
1278hrtime_update_expire(rb_hrtime_t *timeout,
const rb_hrtime_t end)
1280 rb_hrtime_t now = rb_hrtime_now();
1282 if (now > end)
return 1;
1284 RUBY_DEBUG_LOG(
"%"PRIu64
" > %"PRIu64
"", (uint64_t)end, (uint64_t)now);
1286 *timeout = end - now;
1292sleep_hrtime(rb_thread_t *th, rb_hrtime_t rel,
unsigned int fl)
1294 enum rb_thread_status prev_status = th->status;
1296 rb_hrtime_t end = rb_hrtime_add(rb_hrtime_now(), rel);
1298 th->status = THREAD_STOPPED;
1299 RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1300 while (th->status == THREAD_STOPPED) {
1301 native_sleep(th, &rel);
1302 woke = vm_check_ints_blocking(th->ec);
1303 if (woke && !(fl & SLEEP_SPURIOUS_CHECK))
1305 if (hrtime_update_expire(&rel, end))
1309 th->status = prev_status;
1314sleep_hrtime_until(rb_thread_t *th, rb_hrtime_t end,
unsigned int fl)
1316 enum rb_thread_status prev_status = th->status;
1318 rb_hrtime_t rel = rb_hrtime_sub(end, rb_hrtime_now());
1320 th->status = THREAD_STOPPED;
1321 RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1322 while (th->status == THREAD_STOPPED) {
1323 native_sleep(th, &rel);
1324 woke = vm_check_ints_blocking(th->ec);
1325 if (woke && !(fl & SLEEP_SPURIOUS_CHECK))
1327 if (hrtime_update_expire(&rel, end))
1331 th->status = prev_status;
1336sleep_forever(rb_thread_t *th,
unsigned int fl)
1338 enum rb_thread_status prev_status = th->status;
1339 enum rb_thread_status status;
1342 status = fl & SLEEP_DEADLOCKABLE ? THREAD_STOPPED_FOREVER : THREAD_STOPPED;
1343 th->status = status;
1345 if (!(fl & SLEEP_NO_CHECKINTS)) RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1347 while (th->status == status) {
1348 if (fl & SLEEP_DEADLOCKABLE) {
1349 rb_ractor_sleeper_threads_inc(th->ractor);
1350 rb_check_deadlock(th->ractor);
1353 native_sleep(th, 0);
1355 if (fl & SLEEP_DEADLOCKABLE) {
1356 rb_ractor_sleeper_threads_dec(th->ractor);
1358 if (fl & SLEEP_ALLOW_SPURIOUS) {
1362 woke = vm_check_ints_blocking(th->ec);
1364 if (woke && !(fl & SLEEP_SPURIOUS_CHECK)) {
1368 th->status = prev_status;
1374 RUBY_DEBUG_LOG(
"forever");
1375 sleep_forever(GET_THREAD(), SLEEP_SPURIOUS_CHECK);
1381 RUBY_DEBUG_LOG(
"deadly");
1382 sleep_forever(GET_THREAD(), SLEEP_DEADLOCKABLE|SLEEP_SPURIOUS_CHECK);
1386rb_thread_sleep_deadly_allow_spurious_wakeup(
VALUE blocker,
VALUE timeout, rb_hrtime_t end)
1389 if (scheduler !=
Qnil) {
1393 RUBY_DEBUG_LOG(
"...");
1395 sleep_hrtime_until(GET_THREAD(), end, SLEEP_SPURIOUS_CHECK);
1398 sleep_forever(GET_THREAD(), SLEEP_DEADLOCKABLE);
1406 rb_thread_t *th = GET_THREAD();
1408 sleep_hrtime(th, rb_timeval2hrtime(&time), SLEEP_SPURIOUS_CHECK);
1421 RUBY_VM_CHECK_INTS_BLOCKING(GET_EC());
1429rb_thread_check_trap_pending(
void)
1431 return rb_signal_buff_size() != 0;
1438 return (
int)RUBY_VM_INTERRUPTED(rb_thread_ptr(thval)->ec);
1448rb_thread_schedule_limits(uint32_t limits_us)
1451 rb_thread_t *th = GET_THREAD();
1452 RUBY_DEBUG_LOG(
"us:%u", (
unsigned int)limits_us);
1454 if (th->running_time_us >= limits_us) {
1455 RUBY_DEBUG_LOG(
"switch %s",
"start");
1457 RB_VM_SAVE_MACHINE_CONTEXT(th);
1458 thread_sched_yield(TH_SCHED(th), th);
1459 rb_ractor_thread_switch(th->ractor, th);
1461 RUBY_DEBUG_LOG(
"switch %s",
"done");
1469 rb_thread_schedule_limits(0);
1470 RUBY_VM_CHECK_INTS(GET_EC());
1479#ifdef RUBY_VM_CRITICAL_SECTION
1480 VM_ASSERT(ruby_assert_critical_section_entered == 0);
1482 VM_ASSERT(th == GET_THREAD());
1484 region->prev_status = th->status;
1485 if (unblock_function_set(th, ubf, arg, fail_if_interrupted)) {
1486 th->blocking_region_buffer = region;
1487 th->status = THREAD_STOPPED;
1488 rb_ractor_blocking_threads_inc(th->ractor, __FILE__, __LINE__);
1490 RUBY_DEBUG_LOG(
"thread_id:%p", (
void *)th->nt->thread_id);
1502 unblock_function_clear(th);
1504 unregister_ubf_list(th);
1506 thread_sched_to_running(TH_SCHED(th), th);
1507 rb_ractor_thread_switch(th->ractor, th);
1509 th->blocking_region_buffer = 0;
1510 rb_ractor_blocking_threads_dec(th->ractor, __FILE__, __LINE__);
1511 if (th->status == THREAD_STOPPED) {
1512 th->status = region->prev_status;
1515 RUBY_DEBUG_LOG(
"end");
1519 VM_ASSERT(th == GET_THREAD());
1524rb_nogvl(
void *(*func)(
void *),
void *data1,
1529 rb_execution_context_t *ec = GET_EC();
1530 rb_thread_t *th = rb_ec_thread_ptr(ec);
1531 rb_vm_t *vm = rb_ec_vm_ptr(ec);
1532 bool is_main_thread = vm->ractor.main_thread == th;
1533 int saved_errno = 0;
1540 else if (ubf && rb_ractor_living_thread_num(th->ractor) == 1 && is_main_thread) {
1542 vm->ubf_async_safe = 1;
1546 BLOCKING_REGION(th, {
1548 saved_errno = rb_errno();
1551 if (is_main_thread) vm->ubf_async_safe = 0;
1554 RUBY_VM_CHECK_INTS_BLOCKING(ec);
1561 rb_errno_set(saved_errno);
1662 return rb_nogvl(func, data1, ubf, data2, 0);
1666waitfd_to_waiting_flag(
int wfd_event)
1668 return wfd_event << 1;
1672thread_io_setup_wfd(rb_thread_t *th,
int fd,
struct waiting_fd *wfd)
1680 ccan_list_add(&th->vm->waiting_fds, &wfd->wfd_node);
1686thread_io_wake_pending_closer(
struct waiting_fd *wfd)
1688 bool has_waiter = wfd->busy &&
RB_TEST(wfd->busy->wakeup_mutex);
1697 ccan_list_del(&wfd->wfd_node);
1701 rb_thread_t *th = rb_thread_ptr(wfd->busy->closing_thread);
1702 if (th->scheduler !=
Qnil) {
1712thread_io_wait_events(rb_thread_t *th, rb_execution_context_t *ec,
int fd,
int events,
struct timeval *timeout,
struct waiting_fd *wfd)
1714#if defined(USE_MN_THREADS) && USE_MN_THREADS
1715 if (!th_has_dedicated_nt(th) &&
1716 (events || timeout) &&
1720 rb_hrtime_t rel, *prel;
1723 rel = rb_timeval2hrtime(timeout);
1730 VM_ASSERT(prel || (events & (RB_WAITFD_IN | RB_WAITFD_OUT)));
1732 thread_io_setup_wfd(th, fd, wfd);
1735 r = thread_sched_wait_events(TH_SCHED(th), th, fd, waitfd_to_waiting_flag(events), prel);
1737 thread_io_wake_pending_closer(wfd);
1739 RUBY_VM_CHECK_INTS_BLOCKING(ec);
1749rb_thread_io_blocking_call(rb_blocking_function_t *func,
void *data1,
int fd,
int events)
1751 rb_execution_context_t *
volatile ec = GET_EC();
1752 rb_thread_t *th = rb_ec_thread_ptr(ec);
1754 RUBY_DEBUG_LOG(
"th:%u fd:%d ev:%d", rb_th_serial(th), fd, events);
1758 thread_io_wait_events(th, ec, fd, events, NULL, &
waiting_fd);
1761 volatile int saved_errno = 0;
1762 enum ruby_tag_type state;
1773 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1776 saved_errno = errno;
1788 EC_JUMP_TAG(ec, state);
1791 RUBY_VM_CHECK_INTS_BLOCKING(ec);
1794 if (saved_errno == ETIMEDOUT) {
1798 errno = saved_errno;
1804rb_thread_io_blocking_region(rb_blocking_function_t *func,
void *data1,
int fd)
1806 return rb_thread_io_blocking_call(func, data1, fd, 0);
1840 rb_thread_t *th = ruby_thread_from_native();
1851 fprintf(stderr,
"[BUG] rb_thread_call_with_gvl() is called by non-ruby thread\n");
1856 prev_unblock = th->unblock;
1859 rb_bug(
"rb_thread_call_with_gvl: called by a thread which has GVL.");
1862 blocking_region_end(th, brb);
1866 int released = blocking_region_begin(th, brb, prev_unblock.func, prev_unblock.arg, FALSE);
1868 RB_VM_SAVE_MACHINE_CONTEXT(th);
1869 thread_sched_to_waiting(TH_SCHED(th), th);
1883ruby_thread_has_gvl_p(
void)
1885 rb_thread_t *th = ruby_thread_from_native();
1887 if (th && th->blocking_region_buffer == 0) {
1904thread_s_pass(
VALUE klass)
1929rb_threadptr_pending_interrupt_clear(rb_thread_t *th)
1931 rb_ary_clear(th->pending_interrupt_queue);
1935rb_threadptr_pending_interrupt_enque(rb_thread_t *th,
VALUE v)
1937 rb_ary_push(th->pending_interrupt_queue, v);
1938 th->pending_interrupt_queue_checked = 0;
1942threadptr_check_pending_interrupt_queue(rb_thread_t *th)
1944 if (!th->pending_interrupt_queue) {
1949enum handle_interrupt_timing {
1951 INTERRUPT_IMMEDIATE,
1952 INTERRUPT_ON_BLOCKING,
1956static enum handle_interrupt_timing
1957rb_threadptr_pending_interrupt_from_symbol(rb_thread_t *th,
VALUE sym)
1959 if (sym == sym_immediate) {
1960 return INTERRUPT_IMMEDIATE;
1962 else if (sym == sym_on_blocking) {
1963 return INTERRUPT_ON_BLOCKING;
1965 else if (sym == sym_never) {
1966 return INTERRUPT_NEVER;
1973static enum handle_interrupt_timing
1974rb_threadptr_pending_interrupt_check_mask(rb_thread_t *th,
VALUE err)
1977 long mask_stack_len =
RARRAY_LEN(th->pending_interrupt_mask_stack);
1982 for (i=0; i<mask_stack_len; i++) {
1983 mask = mask_stack[mask_stack_len-(i+1)];
1988 return rb_threadptr_pending_interrupt_from_symbol(th, mask);
2000 klass =
RBASIC(mod)->klass;
2002 else if (mod != RCLASS_ORIGIN(mod)) {
2006 if ((sym = rb_hash_aref(mask, klass)) !=
Qnil) {
2007 return rb_threadptr_pending_interrupt_from_symbol(th, sym);
2012 return INTERRUPT_NONE;
2016rb_threadptr_pending_interrupt_empty_p(
const rb_thread_t *th)
2018 return RARRAY_LEN(th->pending_interrupt_queue) == 0;
2022rb_threadptr_pending_interrupt_include_p(rb_thread_t *th,
VALUE err)
2025 for (i=0; i<
RARRAY_LEN(th->pending_interrupt_queue); i++) {
2035rb_threadptr_pending_interrupt_deque(rb_thread_t *th,
enum handle_interrupt_timing timing)
2040 for (i=0; i<
RARRAY_LEN(th->pending_interrupt_queue); i++) {
2043 enum handle_interrupt_timing mask_timing = rb_threadptr_pending_interrupt_check_mask(th,
CLASS_OF(err));
2045 switch (mask_timing) {
2046 case INTERRUPT_ON_BLOCKING:
2047 if (timing != INTERRUPT_ON_BLOCKING) {
2051 case INTERRUPT_NONE:
2052 case INTERRUPT_IMMEDIATE:
2053 rb_ary_delete_at(th->pending_interrupt_queue, i);
2055 case INTERRUPT_NEVER:
2060 th->pending_interrupt_queue_checked = 1;
2063 VALUE err = rb_ary_shift(th->pending_interrupt_queue);
2064 if (rb_threadptr_pending_interrupt_empty_p(th)) {
2065 th->pending_interrupt_queue_checked = 1;
2072threadptr_pending_interrupt_active_p(rb_thread_t *th)
2079 if (th->pending_interrupt_queue_checked) {
2083 if (rb_threadptr_pending_interrupt_empty_p(th)) {
2095 if (val != sym_immediate && val != sym_on_blocking && val != sym_never) {
2096 rb_raise(rb_eArgError,
"unknown mask signature");
2104 if (
RTEST(*maskp)) {
2105 if (!RB_TYPE_P(*maskp,
T_HASH)) {
2106 VALUE prev = *maskp;
2107 *maskp = rb_ident_hash_new();
2112 rb_hash_aset(*maskp, key, val);
2230rb_thread_s_handle_interrupt(
VALUE self,
VALUE mask_arg)
2233 rb_execution_context_t *
volatile ec = GET_EC();
2234 rb_thread_t *
volatile th = rb_ec_thread_ptr(ec);
2236 enum ruby_tag_type state;
2239 rb_raise(rb_eArgError,
"block is needed.");
2242 mask_arg = rb_to_hash_type(mask_arg);
2244 if (
OBJ_FROZEN(mask_arg) && rb_hash_compare_by_id_p(mask_arg)) {
2250 if (UNDEF_P(mask)) {
2257 else if (RB_TYPE_P(mask,
T_HASH)) {
2261 rb_ary_push(th->pending_interrupt_mask_stack, mask);
2262 if (!rb_threadptr_pending_interrupt_empty_p(th)) {
2263 th->pending_interrupt_queue_checked = 0;
2264 RUBY_VM_SET_INTERRUPT(th->ec);
2267 EC_PUSH_TAG(th->ec);
2268 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
2273 rb_ary_pop(th->pending_interrupt_mask_stack);
2274 if (!rb_threadptr_pending_interrupt_empty_p(th)) {
2275 th->pending_interrupt_queue_checked = 0;
2276 RUBY_VM_SET_INTERRUPT(th->ec);
2279 RUBY_VM_CHECK_INTS(th->ec);
2282 EC_JUMP_TAG(th->ec, state);
2299rb_thread_pending_interrupt_p(
int argc,
VALUE *argv,
VALUE target_thread)
2301 rb_thread_t *target_th = rb_thread_ptr(target_thread);
2303 if (!target_th->pending_interrupt_queue) {
2306 if (rb_threadptr_pending_interrupt_empty_p(target_th)) {
2310 VALUE err = argv[0];
2312 rb_raise(
rb_eTypeError,
"class or module required for rescue clause");
2314 return RBOOL(rb_threadptr_pending_interrupt_include_p(target_th, err));
2379rb_thread_s_pending_interrupt_p(
int argc,
VALUE *argv,
VALUE self)
2381 return rb_thread_pending_interrupt_p(argc, argv, GET_THREAD()->self);
2384NORETURN(
static void rb_threadptr_to_kill(rb_thread_t *th));
2387rb_threadptr_to_kill(rb_thread_t *th)
2389 rb_threadptr_pending_interrupt_clear(th);
2390 th->status = THREAD_RUNNABLE;
2392 th->ec->errinfo =
INT2FIX(TAG_FATAL);
2393 EC_JUMP_TAG(th->ec, TAG_FATAL);
2397threadptr_get_interrupts(rb_thread_t *th)
2399 rb_execution_context_t *ec = th->ec;
2404 interrupt = ec->interrupt_flag;
2405 old = ATOMIC_CAS(ec->interrupt_flag, interrupt, interrupt & ec->interrupt_mask);
2406 }
while (old != interrupt);
2407 return interrupt & (
rb_atomic_t)~ec->interrupt_mask;
2411rb_threadptr_execute_interrupts(rb_thread_t *th,
int blocking_timing)
2414 int postponed_job_interrupt = 0;
2417 if (th->ec->raised_flag)
return ret;
2419 while ((interrupt = threadptr_get_interrupts(th)) != 0) {
2421 int timer_interrupt;
2422 int pending_interrupt;
2424 int terminate_interrupt;
2426 timer_interrupt = interrupt & TIMER_INTERRUPT_MASK;
2427 pending_interrupt = interrupt & PENDING_INTERRUPT_MASK;
2428 postponed_job_interrupt = interrupt & POSTPONED_JOB_INTERRUPT_MASK;
2429 trap_interrupt = interrupt & TRAP_INTERRUPT_MASK;
2430 terminate_interrupt = interrupt & TERMINATE_INTERRUPT_MASK;
2432 if (interrupt & VM_BARRIER_INTERRUPT_MASK) {
2437 if (postponed_job_interrupt) {
2438 rb_postponed_job_flush(th->vm);
2442 if (trap_interrupt && (th == th->vm->ractor.main_thread)) {
2443 enum rb_thread_status prev_status = th->status;
2445 th->status = THREAD_RUNNABLE;
2447 while ((sig = rb_get_next_signal()) != 0) {
2448 ret |= rb_signal_exec(th, sig);
2451 th->status = prev_status;
2455 if (pending_interrupt && threadptr_pending_interrupt_active_p(th)) {
2456 VALUE err = rb_threadptr_pending_interrupt_deque(th, blocking_timing ? INTERRUPT_ON_BLOCKING : INTERRUPT_NONE);
2457 RUBY_DEBUG_LOG(
"err:%"PRIdVALUE, err);
2463 else if (err == RUBY_FATAL_THREAD_KILLED ||
2464 err == RUBY_FATAL_THREAD_TERMINATED ||
2466 terminate_interrupt = 1;
2469 if (err == th->vm->special_exceptions[ruby_error_stream_closed]) {
2471 err = ruby_vm_special_exception_copy(err);
2474 if (th->status == THREAD_STOPPED ||
2475 th->status == THREAD_STOPPED_FOREVER)
2476 th->status = THREAD_RUNNABLE;
2481 if (terminate_interrupt) {
2482 rb_threadptr_to_kill(th);
2485 if (timer_interrupt) {
2486 uint32_t limits_us = TIME_QUANTUM_USEC;
2488 if (th->priority > 0)
2489 limits_us <<= th->priority;
2491 limits_us >>= -th->priority;
2493 if (th->status == THREAD_RUNNABLE)
2494 th->running_time_us += 10 * 1000;
2496 VM_ASSERT(th->ec->cfp);
2500 rb_thread_schedule_limits(limits_us);
2507rb_thread_execute_interrupts(
VALUE thval)
2509 rb_threadptr_execute_interrupts(rb_thread_ptr(thval), 1);
2513rb_threadptr_ready(rb_thread_t *th)
2515 rb_threadptr_interrupt(th);
2519rb_threadptr_raise(rb_thread_t *target_th,
int argc,
VALUE *argv)
2523 if (rb_threadptr_dead(target_th)) {
2531 exc = rb_make_exception(argc, argv);
2536 if (rb_threadptr_dead(target_th)) {
2540 rb_ec_setup_exception(GET_EC(), exc,
Qundef);
2541 rb_threadptr_pending_interrupt_enque(target_th, exc);
2542 rb_threadptr_interrupt(target_th);
2547rb_threadptr_signal_raise(rb_thread_t *th,
int sig)
2553 rb_threadptr_raise(th->vm->ractor.main_thread, 2, argv);
2557rb_threadptr_signal_exit(rb_thread_t *th)
2565 rb_threadptr_raise(th->vm->ractor.main_thread, 2, argv);
2569rb_ec_set_raised(rb_execution_context_t *ec)
2571 if (ec->raised_flag & RAISED_EXCEPTION) {
2574 ec->raised_flag |= RAISED_EXCEPTION;
2579rb_ec_reset_raised(rb_execution_context_t *ec)
2581 if (!(ec->raised_flag & RAISED_EXCEPTION)) {
2584 ec->raised_flag &= ~RAISED_EXCEPTION;
2591 rb_vm_t *vm = GET_THREAD()->vm;
2593 ccan_list_head_init(&busy->pending_fd_users);
2599 ccan_list_for_each_safe(&vm->waiting_fds, wfd, next, wfd_node) {
2600 if (wfd->fd == fd) {
2601 rb_thread_t *th = wfd->th;
2604 ccan_list_del(&wfd->wfd_node);
2605 ccan_list_add(&busy->pending_fd_users, &wfd->wfd_node);
2608 err = th->vm->special_exceptions[ruby_error_stream_closed];
2609 rb_threadptr_pending_interrupt_enque(th, err);
2610 rb_threadptr_interrupt(th);
2615 has_any = !ccan_list_empty(&busy->pending_fd_users);
2617 busy->closing_fiber = rb_fiber_current();
2618 wakeup_mutex =
Qnil;
2621 RBASIC_CLEAR_CLASS(wakeup_mutex);
2623 busy->wakeup_mutex = wakeup_mutex;
2637 if (!
RB_TEST(busy->wakeup_mutex)) {
2644 while (!ccan_list_empty(&busy->pending_fd_users)) {
2655 if (rb_notify_fd_close(fd, &busy)) {
2656 rb_notify_fd_close_wait(&busy);
2682thread_raise_m(
int argc,
VALUE *argv,
VALUE self)
2684 rb_thread_t *target_th = rb_thread_ptr(self);
2685 const rb_thread_t *current_th = GET_THREAD();
2687 threadptr_check_pending_interrupt_queue(target_th);
2688 rb_threadptr_raise(target_th, argc, argv);
2691 if (current_th == target_th) {
2692 RUBY_VM_CHECK_INTS(target_th->ec);
2712 rb_thread_t *target_th = rb_thread_ptr(thread);
2714 if (target_th->to_kill || target_th->status == THREAD_KILLED) {
2717 if (target_th == target_th->vm->ractor.main_thread) {
2721 RUBY_DEBUG_LOG(
"target_th:%u", rb_th_serial(target_th));
2723 if (target_th == GET_THREAD()) {
2725 rb_threadptr_to_kill(target_th);
2728 threadptr_check_pending_interrupt_queue(target_th);
2729 rb_threadptr_pending_interrupt_enque(target_th, RUBY_FATAL_THREAD_KILLED);
2730 rb_threadptr_interrupt(target_th);
2737rb_thread_to_be_killed(
VALUE thread)
2739 rb_thread_t *target_th = rb_thread_ptr(thread);
2741 if (target_th->to_kill || target_th->status == THREAD_KILLED) {
2783 rb_thread_t *th = GET_THREAD();
2816 rb_thread_t *target_th = rb_thread_ptr(thread);
2817 if (target_th->status == THREAD_KILLED)
return Qnil;
2819 rb_threadptr_ready(target_th);
2821 if (target_th->status == THREAD_STOPPED ||
2822 target_th->status == THREAD_STOPPED_FOREVER) {
2823 target_th->status = THREAD_RUNNABLE;
2865 "stopping only thread\n\tnote: use sleep to stop forever");
2898 return rb_ractor_thread_list();
2924 return rb_thread_list();
2930 return GET_THREAD()->self;
2943thread_s_current(
VALUE klass)
2951 return GET_RACTOR()->threads.main->self;
2962rb_thread_s_main(
VALUE klass)
2989rb_thread_s_abort_exc(
VALUE _)
2991 return RBOOL(GET_THREAD()->vm->thread_abort_on_exception);
3026rb_thread_s_abort_exc_set(
VALUE self,
VALUE val)
3028 GET_THREAD()->vm->thread_abort_on_exception =
RTEST(val);
3049rb_thread_abort_exc(
VALUE thread)
3051 return RBOOL(rb_thread_ptr(thread)->abort_on_exception);
3069rb_thread_abort_exc_set(
VALUE thread,
VALUE val)
3071 rb_thread_ptr(thread)->abort_on_exception =
RTEST(val);
3119rb_thread_s_report_exc(
VALUE _)
3121 return RBOOL(GET_THREAD()->vm->thread_report_on_exception);
3156rb_thread_s_report_exc_set(
VALUE self,
VALUE val)
3158 GET_THREAD()->vm->thread_report_on_exception =
RTEST(val);
3175rb_thread_s_ignore_deadlock(
VALUE _)
3177 return RBOOL(GET_THREAD()->vm->thread_ignore_deadlock);
3202rb_thread_s_ignore_deadlock_set(
VALUE self,
VALUE val)
3204 GET_THREAD()->vm->thread_ignore_deadlock =
RTEST(val);
3226rb_thread_report_exc(
VALUE thread)
3228 return RBOOL(rb_thread_ptr(thread)->report_on_exception);
3246rb_thread_report_exc_set(
VALUE thread,
VALUE val)
3248 rb_thread_ptr(thread)->report_on_exception =
RTEST(val);
3263rb_thread_group(
VALUE thread)
3265 return rb_thread_ptr(thread)->thgroup;
3269thread_status_name(rb_thread_t *th,
int detail)
3271 switch (th->status) {
3272 case THREAD_RUNNABLE:
3273 return th->to_kill ?
"aborting" :
"run";
3274 case THREAD_STOPPED_FOREVER:
3275 if (detail)
return "sleep_forever";
3276 case THREAD_STOPPED:
3286rb_threadptr_dead(rb_thread_t *th)
3288 return th->status == THREAD_KILLED;
3324rb_thread_status(
VALUE thread)
3326 rb_thread_t *target_th = rb_thread_ptr(thread);
3328 if (rb_threadptr_dead(target_th)) {
3329 if (!
NIL_P(target_th->ec->errinfo) &&
3330 !
FIXNUM_P(target_th->ec->errinfo)) {
3338 return rb_str_new2(thread_status_name(target_th, FALSE));
3358rb_thread_alive_p(
VALUE thread)
3360 return RBOOL(!thread_finished(rb_thread_ptr(thread)));
3378rb_thread_stop_p(
VALUE thread)
3380 rb_thread_t *th = rb_thread_ptr(thread);
3382 if (rb_threadptr_dead(th)) {
3385 return RBOOL(th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER);
3396rb_thread_getname(
VALUE thread)
3398 return rb_thread_ptr(thread)->name;
3412 rb_thread_t *target_th = rb_thread_ptr(thread);
3417 enc = rb_enc_get(name);
3418 if (!rb_enc_asciicompat(enc)) {
3419 rb_raise(rb_eArgError,
"ASCII incompatible encoding (%s)",
3422 name = rb_str_new_frozen(name);
3424 target_th->name = name;
3425 if (threadptr_initialized(target_th) && target_th->has_dedicated_nt) {
3426 native_set_another_thread_name(target_th->nt->thread_id, name);
3431#if USE_NATIVE_THREAD_NATIVE_THREAD_ID
3455rb_thread_native_thread_id(
VALUE thread)
3457 rb_thread_t *target_th = rb_thread_ptr(thread);
3458 if (rb_threadptr_dead(target_th))
return Qnil;
3459 return native_thread_native_thread_id(target_th);
3462# define rb_thread_native_thread_id rb_f_notimplement
3473rb_thread_to_s(
VALUE thread)
3476 rb_thread_t *target_th = rb_thread_ptr(thread);
3480 status = thread_status_name(target_th, TRUE);
3481 str = rb_sprintf(
"#<%"PRIsVALUE
":%p", cname, (
void *)thread);
3482 if (!
NIL_P(target_th->name)) {
3483 rb_str_catf(str,
"@%"PRIsVALUE, target_th->name);
3485 if ((loc = threadptr_invoke_proc_location(target_th)) !=
Qnil) {
3486 rb_str_catf(str,
" %"PRIsVALUE
":%"PRIsVALUE,
3489 rb_str_catf(str,
" %s>", status);
3495#define recursive_key id__recursive_key__
3498threadptr_local_aref(rb_thread_t *th,
ID id)
3500 if (
id == recursive_key) {
3501 return th->ec->local_storage_recursive_hash;
3505 struct rb_id_table *local_storage = th->ec->local_storage;
3507 if (local_storage != NULL && rb_id_table_lookup(local_storage,
id, &val)) {
3519 return threadptr_local_aref(rb_thread_ptr(thread),
id);
3586 if (!
id)
return Qnil;
3604rb_thread_fetch(
int argc,
VALUE *argv,
VALUE self)
3608 rb_thread_t *target_th = rb_thread_ptr(self);
3615 if (block_given && argc == 2) {
3616 rb_warn(
"block supersedes default value argument");
3621 if (
id == recursive_key) {
3622 return target_th->ec->local_storage_recursive_hash;
3624 else if (
id && target_th->ec->local_storage &&
3625 rb_id_table_lookup(target_th->ec->local_storage,
id, &val)) {
3628 else if (block_given) {
3631 else if (argc == 1) {
3632 rb_key_err_raise(rb_sprintf(
"key not found: %+"PRIsVALUE, key), self, key);
3640threadptr_local_aset(rb_thread_t *th,
ID id,
VALUE val)
3642 if (
id == recursive_key) {
3643 th->ec->local_storage_recursive_hash = val;
3647 struct rb_id_table *local_storage = th->ec->local_storage;
3650 if (!local_storage)
return Qnil;
3651 rb_id_table_delete(local_storage,
id);
3655 if (local_storage == NULL) {
3656 th->ec->local_storage = local_storage = rb_id_table_create(0);
3658 rb_id_table_insert(local_storage,
id, val);
3671 return threadptr_local_aset(rb_thread_ptr(thread),
id, val);
3722rb_thread_variable_get(
VALUE thread,
VALUE key)
3726 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
3729 locals = rb_thread_local_storage(thread);
3751 locals = rb_thread_local_storage(thread);
3773 struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
3775 if (!
id || local_storage == NULL) {
3778 return RBOOL(rb_id_table_lookup(local_storage,
id, &val));
3781static enum rb_id_table_iterator_result
3782thread_keys_i(
ID key,
VALUE value,
void *ary)
3785 return ID_TABLE_CONTINUE;
3792 return rb_ractor_living_thread_num(GET_RACTOR()) == 1;
3810rb_thread_keys(
VALUE self)
3812 struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
3813 VALUE ary = rb_ary_new();
3815 if (local_storage) {
3816 rb_id_table_foreach(local_storage, thread_keys_i, (
void *)ary);
3824 rb_ary_push(ary, key);
3846rb_thread_variables(
VALUE thread)
3852 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
3855 locals = rb_thread_local_storage(thread);
3882 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
3885 locals = rb_thread_local_storage(thread);
3906rb_thread_priority(
VALUE thread)
3908 return INT2NUM(rb_thread_ptr(thread)->priority);
3939rb_thread_priority_set(
VALUE thread,
VALUE prio)
3941 rb_thread_t *target_th = rb_thread_ptr(thread);
3944#if USE_NATIVE_THREAD_PRIORITY
3945 target_th->priority =
NUM2INT(prio);
3946 native_thread_apply_priority(th);
3949 if (priority > RUBY_THREAD_PRIORITY_MAX) {
3950 priority = RUBY_THREAD_PRIORITY_MAX;
3952 else if (priority < RUBY_THREAD_PRIORITY_MIN) {
3953 priority = RUBY_THREAD_PRIORITY_MIN;
3955 target_th->priority = (int8_t)priority;
3957 return INT2NUM(target_th->priority);
3962#if defined(NFDBITS) && defined(HAVE_RB_FD_INIT)
3998 FD_ZERO(fds->
fdset);
4004 size_t size = howmany(
rb_fd_max(src), NFDBITS) *
sizeof(fd_mask);
4006 if (size <
sizeof(fd_set))
4007 size =
sizeof(fd_set);
4031 size_t m = howmany(n + 1, NFDBITS) *
sizeof(fd_mask);
4032 size_t o = howmany(fds->
maxfd, NFDBITS) *
sizeof(fd_mask);
4034 if (m <
sizeof(fd_set)) m =
sizeof(fd_set);
4035 if (o <
sizeof(fd_set)) o =
sizeof(fd_set);
4039 memset((
char *)fds->
fdset + o, 0, m - o);
4048 FD_SET(n, fds->
fdset);
4054 if (n >= fds->
maxfd)
return;
4055 FD_CLR(n, fds->
fdset);
4061 if (n >= fds->
maxfd)
return 0;
4062 return FD_ISSET(n, fds->
fdset) != 0;
4068 size_t size = howmany(max, NFDBITS) *
sizeof(fd_mask);
4070 if (size <
sizeof(fd_set)) size =
sizeof(fd_set);
4073 memcpy(dst->
fdset, src, size);
4079 size_t size = howmany(
rb_fd_max(src), NFDBITS) *
sizeof(fd_mask);
4081 if (size <
sizeof(fd_set))
4082 size =
sizeof(fd_set);
4091 fd_set *r = NULL, *w = NULL, *e = NULL;
4104 return select(n, r, w, e, timeout);
4107#define rb_fd_no_init(fds) ((void)((fds)->fdset = 0), (void)((fds)->maxfd = 0))
4114#define FD_ZERO(f) rb_fd_zero(f)
4115#define FD_SET(i, f) rb_fd_set((i), (f))
4116#define FD_CLR(i, f) rb_fd_clr((i), (f))
4117#define FD_ISSET(i, f) rb_fd_isset((i), (f))
4119#elif defined(_WIN32)
4124 set->
capa = FD_SETSIZE;
4126 FD_ZERO(set->
fdset);
4148 SOCKET s = rb_w32_get_osfhandle(fd);
4150 for (i = 0; i < set->
fdset->fd_count; i++) {
4151 if (set->
fdset->fd_array[i] == s) {
4155 if (set->
fdset->fd_count >= (
unsigned)set->
capa) {
4156 set->
capa = (set->
fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
4158 rb_xrealloc_mul_add(
4159 set->
fdset, set->
capa,
sizeof(SOCKET),
sizeof(
unsigned int));
4161 set->
fdset->fd_array[set->
fdset->fd_count++] = s;
4169#define FD_ZERO(f) rb_fd_zero(f)
4170#define FD_SET(i, f) rb_fd_set((i), (f))
4171#define FD_CLR(i, f) rb_fd_clr((i), (f))
4172#define FD_ISSET(i, f) rb_fd_isset((i), (f))
4174#define rb_fd_no_init(fds) (void)((fds)->fdset = 0)
4178#ifndef rb_fd_no_init
4179#define rb_fd_no_init(fds) (void)(fds)
4183wait_retryable(
volatile int *result,
int errnum, rb_hrtime_t *rel, rb_hrtime_t end)
4192 if (rel && hrtime_update_expire(rel, end)) {
4199 else if (*result == 0) {
4202 return !hrtime_update_expire(rel, end);
4222select_set_free(
VALUE p)
4237 volatile int result = 0;
4239 rb_hrtime_t *to, rel, end = 0;
4241 timeout_prepare(&to, &rel, &end, set->timeout);
4242 volatile rb_hrtime_t endtime = end;
4243#define restore_fdset(dst, src) \
4244 ((dst) ? rb_fd_dup(dst, src) : (void)0)
4245#define do_select_update() \
4246 (restore_fdset(set->rset, &set->orig_rset), \
4247 restore_fdset(set->wset, &set->orig_wset), \
4248 restore_fdset(set->eset, &set->orig_eset), \
4254 BLOCKING_REGION(set->th, {
4257 if (!RUBY_VM_INTERRUPTED(set->th->ec)) {
4258 result = native_fd_select(set->max,
4259 set->rset, set->wset, set->eset,
4260 rb_hrtime2timeval(&tv, to), set->th);
4261 if (result < 0) lerrno = errno;
4263 }, ubf_select, set->th, TRUE);
4265 RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec);
4266 }
while (wait_retryable(&result, lerrno, to, endtime) && do_select_update());
4272 return (
VALUE)result;
4281 set.th = GET_THREAD();
4282 RUBY_VM_CHECK_INTS_BLOCKING(set.th->ec);
4287 set.timeout = timeout;
4289 if (!set.rset && !set.wset && !set.eset) {
4298#define fd_init_copy(f) do { \
4300 rb_fd_resize(set.max - 1, set.f); \
4301 if (&set.orig_##f != set.f) { \
4302 rb_fd_init_copy(&set.orig_##f, set.f); \
4306 rb_fd_no_init(&set.orig_##f); \
4320#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
4321#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
4322#define POLLEX_SET (POLLPRI)
4325# define POLLERR_SET (0)
4332rb_thread_wait_for_single_fd(
int fd,
int events,
struct timeval *timeout)
4334 struct pollfd fds[1];
4335 volatile int result = 0;
4339 volatile int lerrno;
4341 rb_execution_context_t *ec = GET_EC();
4342 rb_thread_t *th = rb_ec_thread_ptr(ec);
4344 if (thread_io_wait_events(th, ec, fd, events, timeout, &wfd)) {
4348 thread_io_setup_wfd(th, fd, &wfd);
4350 EC_PUSH_TAG(wfd.th->ec);
4351 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
4352 rb_hrtime_t *to, rel, end = 0;
4353 RUBY_VM_CHECK_INTS_BLOCKING(wfd.th->ec);
4354 timeout_prepare(&to, &rel, &end, timeout);
4355 volatile rb_hrtime_t endtime = end;
4357 fds[0].events = (short)events;
4363 BLOCKING_REGION(wfd.th, {
4366 if (!RUBY_VM_INTERRUPTED(wfd.th->ec)) {
4367 result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, to), 0);
4368 if (result < 0) lerrno = errno;
4370 }, ubf_select, wfd.th, TRUE);
4372 RUBY_VM_CHECK_INTS_BLOCKING(wfd.th->ec);
4373 }
while (wait_retryable(&result, lerrno, to, endtime));
4377 thread_io_wake_pending_closer(&wfd);
4380 EC_JUMP_TAG(wfd.th->ec, state);
4388 if (fds[0].revents & POLLNVAL) {
4398 if (fds[0].revents & POLLIN_SET)
4399 result |= RB_WAITFD_IN;
4400 if (fds[0].revents & POLLOUT_SET)
4401 result |= RB_WAITFD_OUT;
4402 if (fds[0].revents & POLLEX_SET)
4403 result |= RB_WAITFD_PRI;
4406 if (fds[0].revents & POLLERR_SET)
4420 struct waiting_fd wfd;
4425select_single(
VALUE ptr)
4431 args->read, args->write, args->except, args->tv);
4433 args->as.error =
errno;
4436 if (args->read &&
rb_fd_isset(args->as.fd, args->read))
4438 if (args->write &&
rb_fd_isset(args->as.fd, args->write))
4440 if (args->except &&
rb_fd_isset(args->as.fd, args->except))
4447select_single_cleanup(
VALUE ptr)
4451 thread_io_wake_pending_closer(&args->wfd);
4472rb_thread_wait_for_single_fd(
int fd,
int events,
struct timeval *timeout)
4478 rb_execution_context_t *ec = GET_EC();
4479 rb_thread_t *th = rb_ec_thread_ptr(ec);
4481 if (thread_io_wait_events(th, ec, fd, events, timeout, &args.wfd)) {
4486 args.read = (events & RB_WAITFD_IN) ? init_set_fd(fd, &rfds) : NULL;
4487 args.write = (events & RB_WAITFD_OUT) ? init_set_fd(fd, &wfds) : NULL;
4488 args.except = (events & RB_WAITFD_PRI) ? init_set_fd(fd, &efds) : NULL;
4492 args.wfd.busy = NULL;
4496 ccan_list_add(&args.wfd.th->vm->waiting_fds, &args.wfd.wfd_node);
4500 r = (int)
rb_ensure(select_single, ptr, select_single_cleanup, ptr);
4502 errno = args.as.error;
4512#ifdef USE_CONSERVATIVE_STACK_END
4514rb_gc_set_stack_end(
VALUE **stack_end_p)
4517 *stack_end_p = &stack_end;
4526rb_threadptr_check_signal(rb_thread_t *mth)
4529 if (rb_signal_buff_size() > 0) {
4531 threadptr_trap_interrupt(mth);
4536async_bug_fd(
const char *mesg,
int errno_arg,
int fd)
4539 size_t n = strlcpy(buff, mesg,
sizeof(buff));
4540 if (n <
sizeof(buff)-3) {
4541 ruby_snprintf(buff+n,
sizeof(buff)-n,
"(%d)", fd);
4543 rb_async_bug_errno(buff, errno_arg);
4548consume_communication_pipe(
int fd)
4554 static char buff[1024];
4560 result = read(fd, buff,
sizeof(buff));
4562 RUBY_DEBUG_LOG(
"resultf:%d buff:%lu", (
int)result, (
unsigned long)buff[0]);
4564 RUBY_DEBUG_LOG(
"result:%d", (
int)result);
4568 if (USE_EVENTFD || result < (ssize_t)
sizeof(buff)) {
4572 else if (result == 0) {
4575 else if (result < 0) {
4581#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
4586 async_bug_fd(
"consume_communication_pipe: read", e, fd);
4593rb_thread_stop_timer_thread(
void)
4595 if (TIMER_THREAD_CREATED_P() && native_stop_timer_thread()) {
4596 native_reset_timer_thread();
4601rb_thread_reset_timer_thread(
void)
4603 native_reset_timer_thread();
4607rb_thread_start_timer_thread(
void)
4610 rb_thread_create_timer_thread();
4614clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
4622 if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
4623 rb_ary_clear(lines);
4644rb_clear_coverages(
void)
4646 VALUE coverages = rb_get_coverages();
4647 if (
RTEST(coverages)) {
4652#if defined(HAVE_WORKING_FORK)
4655rb_thread_atfork_internal(rb_thread_t *th,
void (*atfork)(rb_thread_t *,
const rb_thread_t *))
4658 rb_vm_t *vm = th->vm;
4659 rb_ractor_t *r = th->ractor;
4660 vm->ractor.main_ractor = r;
4661 vm->ractor.main_thread = th;
4662 r->threads.main = th;
4663 r->status_ = ractor_created;
4665 thread_sched_atfork(TH_SCHED(th));
4669 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
4670 ccan_list_for_each(&r->threads.set, i, lt_node) {
4674 rb_vm_living_threads_init(vm);
4676 rb_ractor_atfork(vm, th);
4677 rb_vm_postponed_job_atfork();
4686 rb_ractor_sleeper_threads_clear(th->ractor);
4687 rb_clear_coverages();
4690 rb_thread_reset_timer_thread();
4691 rb_thread_start_timer_thread();
4693 VM_ASSERT(vm->ractor.blocking_cnt == 0);
4694 VM_ASSERT(vm->ractor.cnt == 1);
4698terminate_atfork_i(rb_thread_t *th,
const rb_thread_t *current_th)
4700 if (th != current_th) {
4701 rb_mutex_abandon_keeping_mutexes(th);
4702 rb_mutex_abandon_locking_mutex(th);
4703 thread_cleanup_func(th, TRUE);
4707void rb_fiber_atfork(rb_thread_t *);
4711 rb_thread_t *th = GET_THREAD();
4712 rb_threadptr_pending_interrupt_clear(th);
4713 rb_thread_atfork_internal(th, terminate_atfork_i);
4714 th->join_list = NULL;
4715 rb_fiber_atfork(th);
4722terminate_atfork_before_exec_i(rb_thread_t *th,
const rb_thread_t *current_th)
4724 if (th != current_th) {
4725 thread_cleanup_func_before_exec(th);
4732 rb_thread_t *th = GET_THREAD();
4733 rb_thread_atfork_internal(th, terminate_atfork_before_exec_i);
4758 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
4781thgroup_s_alloc(
VALUE klass)
4802thgroup_list(
VALUE group)
4804 VALUE ary = rb_ary_new();
4805 rb_thread_t *th = 0;
4806 rb_ractor_t *r = GET_RACTOR();
4808 ccan_list_for_each(&r->threads.set, th, lt_node) {
4809 if (th->thgroup == group) {
4810 rb_ary_push(ary, th->self);
4834thgroup_enclose(
VALUE group)
4853thgroup_enclosed_p(
VALUE group)
4858 return RBOOL(data->enclosed);
4891 rb_thread_t *target_th = rb_thread_ptr(thread);
4898 if (data->enclosed) {
4906 if (data->enclosed) {
4908 "can't move from the enclosed thread group");
4911 target_th->thgroup = group;
4919thread_shield_mark(
void *ptr)
4921 rb_gc_mark((
VALUE)ptr);
4926 {thread_shield_mark, 0, 0,},
4927 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
4931thread_shield_alloc(
VALUE klass)
4936#define GetThreadShieldPtr(obj) ((VALUE)rb_check_typeddata((obj), &thread_shield_data_type))
4937#define THREAD_SHIELD_WAITING_MASK (((FL_USER19-1)&~(FL_USER0-1))|FL_USER19)
4938#define THREAD_SHIELD_WAITING_SHIFT (FL_USHIFT)
4939#define THREAD_SHIELD_WAITING_MAX (THREAD_SHIELD_WAITING_MASK>>THREAD_SHIELD_WAITING_SHIFT)
4940STATIC_ASSERT(THREAD_SHIELD_WAITING_MAX, THREAD_SHIELD_WAITING_MAX <= UINT_MAX);
4941static inline unsigned int
4942rb_thread_shield_waiting(
VALUE b)
4944 return ((
RBASIC(b)->flags&THREAD_SHIELD_WAITING_MASK)>>THREAD_SHIELD_WAITING_SHIFT);
4948rb_thread_shield_waiting_inc(
VALUE b)
4950 unsigned int w = rb_thread_shield_waiting(b);
4952 if (w > THREAD_SHIELD_WAITING_MAX)
4954 RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
4955 RBASIC(b)->flags |= ((
VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
4959rb_thread_shield_waiting_dec(
VALUE b)
4961 unsigned int w = rb_thread_shield_waiting(b);
4964 RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
4965 RBASIC(b)->flags |= ((
VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
4969rb_thread_shield_new(
void)
4971 VALUE thread_shield = thread_shield_alloc(rb_cThreadShield);
4973 return thread_shield;
4977rb_thread_shield_owned(
VALUE self)
4979 VALUE mutex = GetThreadShieldPtr(self);
4980 if (!mutex)
return false;
4982 rb_mutex_t *m = mutex_ptr(mutex);
4984 return m->fiber == GET_EC()->fiber_ptr;
4996rb_thread_shield_wait(
VALUE self)
4998 VALUE mutex = GetThreadShieldPtr(self);
5001 if (!mutex)
return Qfalse;
5002 m = mutex_ptr(mutex);
5003 if (m->fiber == GET_EC()->fiber_ptr)
return Qnil;
5004 rb_thread_shield_waiting_inc(self);
5006 rb_thread_shield_waiting_dec(self);
5009 return rb_thread_shield_waiting(self) > 0 ?
Qnil :
Qfalse;
5013thread_shield_get_mutex(
VALUE self)
5015 VALUE mutex = GetThreadShieldPtr(self);
5017 rb_raise(
rb_eThreadError,
"destroyed thread shield - %p", (
void *)self);
5025rb_thread_shield_release(
VALUE self)
5027 VALUE mutex = thread_shield_get_mutex(self);
5029 return RBOOL(rb_thread_shield_waiting(self) > 0);
5036rb_thread_shield_destroy(
VALUE self)
5038 VALUE mutex = thread_shield_get_mutex(self);
5041 return RBOOL(rb_thread_shield_waiting(self) > 0);
5045threadptr_recursive_hash(rb_thread_t *th)
5047 return th->ec->local_storage_recursive_hash;
5051threadptr_recursive_hash_set(rb_thread_t *th,
VALUE hash)
5053 th->ec->local_storage_recursive_hash = hash;
5065recursive_list_access(
VALUE sym)
5067 rb_thread_t *th = GET_THREAD();
5068 VALUE hash = threadptr_recursive_hash(th);
5071 hash = rb_ident_hash_new();
5072 threadptr_recursive_hash_set(th, hash);
5076 list = rb_hash_aref(hash, sym);
5079 list = rb_ident_hash_new();
5080 rb_hash_aset(hash, sym, list);
5094#if SIZEOF_LONG == SIZEOF_VOIDP
5095 #define OBJ_ID_EQL(obj_id, other) ((obj_id) == (other))
5096#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
5097 #define OBJ_ID_EQL(obj_id, other) (RB_BIGNUM_TYPE_P((obj_id)) ? \
5098 rb_big_eql((obj_id), (other)) : ((obj_id) == (other)))
5101 VALUE pair_list = rb_hash_lookup2(list, obj,
Qundef);
5102 if (UNDEF_P(pair_list))
5104 if (paired_obj_id) {
5105 if (!RB_TYPE_P(pair_list,
T_HASH)) {
5106 if (!OBJ_ID_EQL(paired_obj_id, pair_list))
5110 if (
NIL_P(rb_hash_lookup(pair_list, paired_obj_id)))
5132 rb_hash_aset(list, obj,
Qtrue);
5134 else if (UNDEF_P(pair_list = rb_hash_lookup2(list, obj,
Qundef))) {
5135 rb_hash_aset(list, obj, paired_obj);
5138 if (!RB_TYPE_P(pair_list,
T_HASH)){
5139 VALUE other_paired_obj = pair_list;
5140 pair_list = rb_hash_new();
5141 rb_hash_aset(pair_list, other_paired_obj,
Qtrue);
5142 rb_hash_aset(list, obj, pair_list);
5144 rb_hash_aset(pair_list, paired_obj,
Qtrue);
5160 VALUE pair_list = rb_hash_lookup2(list, obj,
Qundef);
5161 if (UNDEF_P(pair_list)) {
5164 if (RB_TYPE_P(pair_list,
T_HASH)) {
5165 rb_hash_delete_entry(pair_list, paired_obj);
5171 rb_hash_delete_entry(list, obj);
5187 return (*p->func)(p->obj, p->arg, FALSE);
5208 p.list = recursive_list_access(sym);
5212 outermost = outer && !recursive_check(p.list,
ID2SYM(recursive_key), 0);
5214 if (recursive_check(p.list, p.obj, pairid)) {
5215 if (outer && !outermost) {
5218 return (*func)(obj, arg, TRUE);
5221 enum ruby_tag_type state;
5226 recursive_push(p.list,
ID2SYM(recursive_key), 0);
5227 recursive_push(p.list, p.obj, p.pairid);
5228 result = rb_catch_protect(p.list, exec_recursive_i, (
VALUE)&p, &state);
5229 if (!recursive_pop(p.list, p.obj, p.pairid))
goto invalid;
5230 if (!recursive_pop(p.list,
ID2SYM(recursive_key), 0))
goto invalid;
5231 if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
5232 if (result == p.list) {
5233 result = (*func)(obj, arg, TRUE);
5238 recursive_push(p.list, p.obj, p.pairid);
5239 EC_PUSH_TAG(GET_EC());
5240 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
5241 ret = (*func)(obj, arg, FALSE);
5244 if (!recursive_pop(p.list, p.obj, p.pairid)) {
5247 if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
5256 "for %+"PRIsVALUE
" in %+"PRIsVALUE,
5280 return exec_recursive(func, obj, rb_memory_id(paired_obj), arg, 0,
rb_frame_last_func());
5298 return exec_recursive(func, obj, 0, arg, 1, mid);
5310 return exec_recursive(func, obj, rb_memory_id(paired_obj), arg, 1,
rb_frame_last_func());
5322rb_thread_backtrace_m(
int argc,
VALUE *argv,
VALUE thval)
5324 return rb_vm_thread_backtrace(argc, argv, thval);
5339rb_thread_backtrace_locations_m(
int argc,
VALUE *argv,
VALUE thval)
5341 return rb_vm_thread_backtrace_locations(argc, argv, thval);
5345Init_Thread_Mutex(
void)
5347 rb_thread_t *th = GET_THREAD();
5372 rb_thread_t *th = GET_THREAD();
5435 rb_vm_register_special_exception(ruby_error_stream_closed,
rb_eIOError,
5436 "stream closed in another thread");
5446 th->thgroup = th->ractor->thgroup_default =
rb_obj_alloc(cThGroup);
5457#ifdef HAVE_PTHREAD_NP_H
5458 VM_ASSERT(TH_SCHED(th)->running == th);
5464 th->pending_interrupt_queue = rb_ary_hidden_new(0);
5465 th->pending_interrupt_queue_checked = 0;
5466 th->pending_interrupt_mask_stack = rb_ary_hidden_new(0);
5470 rb_thread_create_timer_thread();
5481 rb_thread_t *th = ruby_thread_from_native();
5486#ifdef NON_SCALAR_THREAD_ID
5487 #define thread_id_str(th) (NULL)
5489 #define thread_id_str(th) ((void *)(uintptr_t)(th)->nt->thread_id)
5493debug_deadlock_check(rb_ractor_t *r,
VALUE msg)
5495 rb_thread_t *th = 0;
5498 rb_str_catf(msg,
"\n%d threads, %d sleeps current:%p main thread:%p\n",
5499 rb_ractor_living_thread_num(r), rb_ractor_sleeper_thread_num(r),
5500 (
void *)GET_THREAD(), (
void *)r->threads.main);
5502 ccan_list_for_each(&r->threads.set, th, lt_node) {
5503 rb_str_catf(msg,
"* %+"PRIsVALUE
"\n rb_thread_t:%p "
5505 th->self, (
void *)th, th->nt ? thread_id_str(th) :
"N/A", th->ec->interrupt_flag);
5507 if (th->locking_mutex) {
5508 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
5509 rb_str_catf(msg,
" mutex:%p cond:%"PRIuSIZE,
5510 (
void *)mutex->fiber, rb_mutex_num_waiting(mutex));
5516 rb_str_catf(msg,
"\n depended by: tb_thread_id:%p", (
void *)list->thread);
5520 rb_str_catf(msg,
"\n ");
5521 rb_str_concat(msg, rb_ary_join(rb_ec_backtrace_str_ary(th->ec, 0, 0), sep));
5522 rb_str_catf(msg,
"\n");
5527rb_check_deadlock(rb_ractor_t *r)
5529 if (GET_THREAD()->vm->thread_ignore_deadlock)
return;
5531#ifdef RUBY_THREAD_PTHREAD_H
5532 if (r->threads.sched.readyq_cnt > 0)
return;
5535 int sleeper_num = rb_ractor_sleeper_thread_num(r);
5536 int ltnum = rb_ractor_living_thread_num(r);
5538 if (ltnum > sleeper_num)
return;
5539 if (ltnum < sleeper_num) rb_bug(
"sleeper must not be more than vm_living_thread_num(vm)");
5542 rb_thread_t *th = NULL;
5544 ccan_list_for_each(&r->threads.set, th, lt_node) {
5545 if (th->status != THREAD_STOPPED_FOREVER || RUBY_VM_INTERRUPTED(th->ec)) {
5548 else if (th->locking_mutex) {
5549 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
5550 if (mutex->fiber == th->ec->fiber_ptr || (!mutex->fiber && !ccan_list_empty(&mutex->waitq))) {
5561 argv[1] =
rb_str_new2(
"No live threads left. Deadlock?");
5562 debug_deadlock_check(r, argv[1]);
5563 rb_ractor_sleeper_threads_dec(GET_RACTOR());
5564 rb_threadptr_raise(r->threads.main, 2, argv);
5571rb_vm_memsize_waiting_fds(
struct ccan_list_head *waiting_fds)
5576 ccan_list_for_each(waiting_fds, waitfd, wfd_node) {
5586 const rb_control_frame_t *cfp = GET_EC()->cfp;
5587 VALUE coverage = rb_iseq_coverage(cfp->iseq);
5594 void rb_iseq_clear_event_flags(
const rb_iseq_t *iseq,
size_t pos,
rb_event_flag_t reset);
5595 if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
5596 rb_iseq_clear_event_flags(cfp->iseq, cfp->pc - ISEQ_BODY(cfp->iseq)->iseq_encoded - 1, RUBY_EVENT_COVERAGE_LINE);
5597 rb_ary_push(lines,
LONG2FIX(line + 1));
5616 const rb_control_frame_t *cfp = GET_EC()->cfp;
5617 VALUE coverage = rb_iseq_coverage(cfp->iseq);
5621 long pc = cfp->pc - ISEQ_BODY(cfp->iseq)->iseq_encoded - 1;
5633const rb_method_entry_t *
5634rb_resolve_me_location(
const rb_method_entry_t *me,
VALUE resolved_location[5])
5636 VALUE path, beg_pos_lineno, beg_pos_column, end_pos_lineno, end_pos_column;
5638 if (!me->def)
return NULL;
5641 switch (me->def->type) {
5642 case VM_METHOD_TYPE_ISEQ: {
5643 const rb_iseq_t *iseq = me->def->body.iseq.
iseqptr;
5644 rb_iseq_location_t *loc = &ISEQ_BODY(iseq)->location;
5645 path = rb_iseq_path(iseq);
5646 beg_pos_lineno =
INT2FIX(loc->code_location.beg_pos.lineno);
5647 beg_pos_column =
INT2FIX(loc->code_location.beg_pos.column);
5648 end_pos_lineno =
INT2FIX(loc->code_location.end_pos.lineno);
5649 end_pos_column =
INT2FIX(loc->code_location.end_pos.column);
5652 case VM_METHOD_TYPE_BMETHOD: {
5653 const rb_iseq_t *iseq = rb_proc_get_iseq(me->def->body.bmethod.proc, 0);
5655 rb_iseq_location_t *loc;
5656 rb_iseq_check(iseq);
5657 path = rb_iseq_path(iseq);
5658 loc = &ISEQ_BODY(iseq)->location;
5659 beg_pos_lineno =
INT2FIX(loc->code_location.beg_pos.lineno);
5660 beg_pos_column =
INT2FIX(loc->code_location.beg_pos.column);
5661 end_pos_lineno =
INT2FIX(loc->code_location.end_pos.lineno);
5662 end_pos_column =
INT2FIX(loc->code_location.end_pos.column);
5667 case VM_METHOD_TYPE_ALIAS:
5668 me = me->def->body.alias.original_me;
5670 case VM_METHOD_TYPE_REFINED:
5671 me = me->def->body.refined.orig_me;
5672 if (!me)
return NULL;
5679 if (RB_TYPE_P(path,
T_ARRAY)) {
5680 path = rb_ary_entry(path, 1);
5681 if (!RB_TYPE_P(path,
T_STRING))
return NULL;
5683 if (resolved_location) {
5684 resolved_location[0] = path;
5685 resolved_location[1] = beg_pos_lineno;
5686 resolved_location[2] = beg_pos_column;
5687 resolved_location[3] = end_pos_lineno;
5688 resolved_location[4] = end_pos_column;
5696 const rb_control_frame_t *cfp = GET_EC()->cfp;
5697 const rb_callable_method_entry_t *cme = rb_vm_frame_method_entry(cfp);
5698 const rb_method_entry_t *me = (
const rb_method_entry_t *)cme;
5702 me = rb_resolve_me_location(me, 0);
5705 rcount = rb_hash_aref(me2counter, (
VALUE) me);
5713rb_get_coverages(
void)
5715 return GET_VM()->coverages;
5719rb_get_coverage_mode(
void)
5721 return GET_VM()->coverage_mode;
5725rb_set_coverages(
VALUE coverages,
int mode,
VALUE me2counter)
5727 GET_VM()->coverages = coverages;
5728 GET_VM()->me2counter = me2counter;
5729 GET_VM()->coverage_mode = mode;
5733rb_resume_coverages(
void)
5735 int mode = GET_VM()->coverage_mode;
5736 VALUE me2counter = GET_VM()->me2counter;
5737 rb_add_event_hook2((
rb_event_hook_func_t) update_line_coverage, RUBY_EVENT_COVERAGE_LINE,
Qnil, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
5738 if (mode & COVERAGE_TARGET_BRANCHES) {
5739 rb_add_event_hook2((
rb_event_hook_func_t) update_branch_coverage, RUBY_EVENT_COVERAGE_BRANCH,
Qnil, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
5741 if (mode & COVERAGE_TARGET_METHODS) {
5747rb_suspend_coverages(
void)
5750 if (GET_VM()->coverage_mode & COVERAGE_TARGET_BRANCHES) {
5753 if (GET_VM()->coverage_mode & COVERAGE_TARGET_METHODS) {
5760rb_reset_coverages(
void)
5762 rb_clear_coverages();
5763 rb_iseq_remove_coverage_all();
5764 GET_VM()->coverages =
Qfalse;
5768rb_default_coverage(
int n)
5770 VALUE coverage = rb_ary_hidden_new_fill(3);
5772 int mode = GET_VM()->coverage_mode;
5774 if (mode & COVERAGE_TARGET_LINES) {
5775 lines = n > 0 ? rb_ary_hidden_new_fill(n) : rb_ary_hidden_new(0);
5777 RARRAY_ASET(coverage, COVERAGE_INDEX_LINES, lines);
5779 if (mode & COVERAGE_TARGET_BRANCHES) {
5780 branches = rb_ary_hidden_new_fill(2);
5802 VALUE structure = rb_hash_new();
5803 rb_obj_hide(structure);
5808 RARRAY_ASET(coverage, COVERAGE_INDEX_BRANCHES, branches);
5814uninterruptible_exit(
VALUE v)
5816 rb_thread_t *cur_th = GET_THREAD();
5817 rb_ary_pop(cur_th->pending_interrupt_mask_stack);
5819 cur_th->pending_interrupt_queue_checked = 0;
5820 if (!rb_threadptr_pending_interrupt_empty_p(cur_th)) {
5821 RUBY_VM_SET_INTERRUPT(cur_th->ec);
5829 VALUE interrupt_mask = rb_ident_hash_new();
5830 rb_thread_t *cur_th = GET_THREAD();
5832 rb_hash_aset(interrupt_mask, rb_cObject, sym_never);
5834 rb_ary_push(cur_th->pending_interrupt_mask_stack, interrupt_mask);
5838 RUBY_VM_CHECK_INTS(cur_th->ec);
5843thread_specific_storage_alloc(rb_thread_t *th)
5845 VM_ASSERT(th->specific_storage == NULL);
5847 if (UNLIKELY(specific_key_count > 0)) {
5848 th->specific_storage =
ZALLOC_N(
void *, RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
5852rb_internal_thread_specific_key_t
5855 rb_vm_t *vm = GET_VM();
5857 if (specific_key_count == 0 && vm->ractor.cnt > 1) {
5858 rb_raise(
rb_eThreadError,
"The first rb_internal_thread_specific_key_create() is called with multiple ractors");
5860 else if (specific_key_count > RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX) {
5861 rb_raise(
rb_eThreadError,
"rb_internal_thread_specific_key_create() is called more than %d times", RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
5864 rb_internal_thread_specific_key_t key = specific_key_count++;
5868 rb_ractor_t *cr = GET_RACTOR();
5871 ccan_list_for_each(&cr->threads.set, th, lt_node) {
5872 thread_specific_storage_alloc(th);
5883 rb_thread_t *th =
DATA_PTR(thread_val);
5885 VM_ASSERT(rb_thread_ptr(thread_val) == th);
5886 VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
5887 VM_ASSERT(th->specific_storage);
5889 return th->specific_storage[key];
5896 rb_thread_t *th =
DATA_PTR(thread_val);
5898 VM_ASSERT(rb_thread_ptr(thread_val) == th);
5899 VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
5900 VM_ASSERT(th->specific_storage);
5902 th->specific_storage[key] = data;
#define RUBY_ASSERT_ALWAYS(expr)
A variant of RUBY_ASSERT that does not interface with RUBY_DEBUG.
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
struct rb_trace_arg_struct rb_trace_arg_t
Type that represents a specific trace event.
#define RUBY_INTERNAL_EVENT_SWITCH
Thread switched.
int rb_remove_event_hook(rb_event_hook_func_t func)
Removes the passed function from the list of event hooks.
#define RUBY_EVENT_THREAD_BEGIN
Encountered a new thread.
void(* rb_event_hook_func_t)(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
Type of event hooks.
uint32_t rb_event_flag_t
Represents event(s).
#define RUBY_EVENT_CALL
A method, written in Ruby, is called.
#define RUBY_EVENT_THREAD_END
Encountered an end of a thread.
static void RB_FL_SET_RAW(VALUE obj, VALUE flags)
This is an implementation detail of RB_FL_SET().
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
ID rb_frame_last_func(void)
Returns the ID of the last method in the call stack.
int rb_keyword_given_p(void)
Determines if the current method is given a keyword argument.
int rb_block_given_p(void)
Determines if the current method is given a block.
#define rb_str_new2
Old name of rb_str_new_cstr.
#define ALLOC
Old name of RB_ALLOC.
#define T_STRING
Old name of RUBY_T_STRING.
#define xfree
Old name of ruby_xfree.
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
#define xrealloc
Old name of ruby_xrealloc.
#define ID2SYM
Old name of RB_ID2SYM.
#define OBJ_FREEZE_RAW
Old name of RB_OBJ_FREEZE_RAW.
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
#define CLASS_OF
Old name of rb_class_of.
#define xmalloc
Old name of ruby_xmalloc.
#define LONG2FIX
Old name of RB_INT2FIX.
#define FIX2INT
Old name of RB_FIX2INT.
#define ZALLOC_N
Old name of RB_ZALLOC_N.
#define T_ICLASS
Old name of RUBY_T_ICLASS.
#define T_HASH
Old name of RUBY_T_HASH.
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
#define INT2NUM
Old name of RB_INT2NUM.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define FIX2LONG
Old name of RB_FIX2LONG.
#define T_ARRAY
Old name of RUBY_T_ARRAY.
#define T_OBJECT
Old name of RUBY_T_OBJECT.
#define NIL_P
Old name of RB_NIL_P.
#define POSFIXABLE
Old name of RB_POSFIXABLE.
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define SYMBOL_P
Old name of RB_SYMBOL_P.
void ruby_stop(int ex)
Calls ruby_cleanup() and exits the process.
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
VALUE rb_eSystemExit
SystemExit exception.
VALUE rb_eIOError
IOError exception.
VALUE rb_eStandardError
StandardError exception.
VALUE rb_eTypeError
TypeError exception.
void rb_frozen_error_raise(VALUE frozen_obj, const char *fmt,...)
Raises an instance of rb_eFrozenError.
VALUE rb_eFatal
fatal exception.
VALUE rb_eRuntimeError
RuntimeError exception.
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
VALUE rb_exc_new(VALUE etype, const char *ptr, long len)
Creates an instance of the passed exception class.
VALUE rb_eException
Mother of all exceptions.
VALUE rb_eThreadError
ThreadError exception.
void rb_exit(int status)
Terminates the current execution context.
VALUE rb_eSignal
SignalException exception.
VALUE rb_obj_alloc(VALUE klass)
Allocates an instance of the given class.
VALUE rb_cInteger
Module class.
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
VALUE rb_cThread
Thread class.
VALUE rb_cModule
Module class.
double rb_num2dbl(VALUE num)
Converts an instance of rb_cNumeric into C's double.
VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass)
Queries if the given object is an instance (of possibly descendants) of the given class.
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
VALUE rb_block_proc(void)
Constructs a Proc object from implicitly passed components.
void rb_reset_random_seed(void)
Resets the RNG behind rb_genrand_int32()/rb_genrand_real().
VALUE rb_str_concat(VALUE dst, VALUE src)
Identical to rb_str_append(), except it also accepts an integer as a codepoint.
#define rb_str_cat_cstr(buf, str)
Identical to rb_str_cat(), except it assumes the passed pointer is a pointer to a C string.
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
int rb_thread_interrupted(VALUE thval)
Checks if the thread's execution was recently interrupted.
VALUE rb_thread_local_aref(VALUE thread, ID key)
This badly named function reads from a Fiber local storage.
VALUE rb_mutex_new(void)
Creates a mutex.
VALUE rb_thread_kill(VALUE thread)
Terminates the given thread.
#define RUBY_UBF_IO
A special UBF for blocking IO operations.
VALUE rb_thread_main(void)
Obtains the "main" thread.
VALUE rb_exec_recursive(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h)
"Recursion" API entry point.
void rb_thread_sleep_forever(void)
Blocks indefinitely.
void rb_thread_fd_close(int fd)
Notifies a closing of a file descriptor to other threads.
void rb_thread_wait_for(struct timeval time)
Identical to rb_thread_sleep(), except it takes struct timeval instead.
VALUE rb_thread_stop(void)
Stops the current thread.
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_exec_recursive_paired(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h)
Identical to rb_exec_recursive(), except it checks for the recursion on the ordered pair of { g,...
void rb_unblock_function_t(void *)
This is the type of UBFs.
void rb_thread_atfork_before_exec(void)
:FIXME: situation of this function is unclear.
void rb_thread_check_ints(void)
Checks for interrupts.
VALUE rb_thread_run(VALUE thread)
This is a rb_thread_wakeup() + rb_thread_schedule() combo.
VALUE rb_thread_wakeup(VALUE thread)
Marks a given thread as eligible for scheduling.
VALUE rb_mutex_unlock(VALUE mutex)
Releases the mutex.
VALUE rb_exec_recursive_paired_outer(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h)
Identical to rb_exec_recursive_outer(), except it checks for the recursion on the ordered pair of { g...
void rb_thread_sleep_deadly(void)
Identical to rb_thread_sleep_forever(), except the thread calling this function is considered "dead" ...
void rb_thread_atfork(void)
A pthread_atfork(3posix)-like API.
VALUE rb_thread_current(void)
Obtains the "current" thread.
int rb_thread_alone(void)
Checks if the thread this function is running is the only thread that is currently alive.
VALUE rb_thread_local_aset(VALUE thread, ID key, VALUE val)
This badly named function writes to a Fiber local storage.
void rb_thread_schedule(void)
Tries to switch to another thread.
#define RUBY_UBF_PROCESS
A special UBF for blocking process operations.
VALUE rb_exec_recursive_outer(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h)
Identical to rb_exec_recursive(), except it calls f for outermost recursion only.
VALUE rb_thread_wakeup_alive(VALUE thread)
Identical to rb_thread_wakeup(), except it doesn't raise on an already killed thread.
VALUE rb_mutex_lock(VALUE mutex)
Attempts to lock the mutex.
void rb_thread_sleep(int sec)
Blocks for the given period of time.
void rb_timespec_now(struct timespec *ts)
Fills the current time into the given struct.
struct timeval rb_time_timeval(VALUE time)
Converts an instance of rb_cTime to a struct timeval that represents the identical point of time.
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.
VALUE rb_ivar_get(VALUE obj, ID name)
Identical to rb_iv_get(), except it accepts the name as an ID instead of a C string.
VALUE rb_class_path(VALUE mod)
Identical to rb_mod_name(), except it returns #<Class: ...> style inspection for anonymous modules.
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
int rb_sourceline(void)
Resembles __LINE__.
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
void rb_define_const(VALUE klass, const char *name, VALUE val)
Defines a Ruby level constant under a namespace.
VALUE rb_eIOTimeoutError
Indicates that a timeout has occurred while performing an IO operation.
#define RB_NOGVL_UBF_ASYNC_SAFE
Passing this flag to rb_nogvl() indicates that the passed UBF is async-signal-safe.
void * rb_internal_thread_specific_get(VALUE thread_val, rb_internal_thread_specific_key_t key)
Get thread and tool specific data.
#define RB_NOGVL_INTR_FAIL
Passing this flag to rb_nogvl() prevents it from checking interrupts.
void rb_internal_thread_specific_set(VALUE thread_val, rb_internal_thread_specific_key_t key, void *data)
Set thread and tool specific data.
rb_internal_thread_specific_key_t rb_internal_thread_specific_key_create(void)
Create a key to store thread specific data.
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...
void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
(Re-)acquires the GVL.
void * rb_thread_call_without_gvl2(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
Identical to rb_thread_call_without_gvl(), except it does not interface with signals etc.
void * rb_thread_call_without_gvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
Allows the passed function to run in parallel with other Ruby threads.
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
Shim for block function parameters.
VALUE rb_yield(VALUE val)
Yields the block.
void rb_throw_obj(VALUE tag, VALUE val)
Identical to rb_throw(), except it allows arbitrary Ruby object to become a tag.
static int rb_fd_max(const rb_fdset_t *f)
It seems this function has no use.
void rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
Destructively overwrites an fdset with another.
void rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
Identical to rb_fd_copy(), except it copies unlimited number of file descriptors.
void rb_fd_term(rb_fdset_t *f)
Destroys the rb_fdset_t, releasing any memory and resources it used.
static fd_set * rb_fd_ptr(const rb_fdset_t *f)
Raw pointer to fd_set.
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
#define ALLOCA_N(type, n)
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
VALUE rb_thread_create(type *q, void *w)
Creates a rb_cThread instance.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
#define rb_fd_isset
Queries if the given fd is in the rb_fdset_t.
#define rb_fd_select
Waits for multiple file descriptors at once.
#define rb_fd_init
Initialises the :given :rb_fdset_t.
#define rb_fd_set
Sets the given fd to the rb_fdset_t.
fd_set rb_fdset_t
The data structure which wraps the fd_set bitmap used by select(2).
#define rb_fd_zero
Clears the given rb_fdset_t.
#define rb_fd_clr
Unsets the given fd from the rb_fdset_t.
#define RARRAY_LEN
Just another name of rb_array_len.
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
#define RARRAY_AREF(a, i)
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
#define RBASIC(obj)
Convenient casting macro.
#define RCLASS_SUPER
Just another name of rb_class_get_superclass.
#define DATA_PTR(obj)
Convenient getter macro.
#define RHASH_EMPTY_P(h)
Checks if the hash is empty.
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
#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.
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a 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...
#define errno
Ractor-aware version of errno.
int ruby_native_thread_p(void)
Queries if the thread which calls this function is a ruby's thread.
#define RB_PASS_CALLED_KEYWORDS
Pass keywords if current method is called with keywords, useful for argument delegation.
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_set(VALUE scheduler)
Destructively assigns the passed scheduler to that of the current thread that is calling this functio...
VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber)
Wakes up a fiber previously blocked using rb_fiber_scheduler_block().
int rb_thread_fd_select(int nfds, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout)
Waits for multiple file descriptors at once.
#define rb_fd_resize(n, f)
Does nothing (defined for compatibility).
static bool RB_TEST(VALUE obj)
Emulates Ruby's "if" statement.
#define RTEST
This is an old name of RB_TEST.
#define _(args)
This was a transition path from K&R to ANSI.
The data structure which wraps the fd_set bitmap used by select(2).
int maxfd
Maximum allowed number of FDs.
fd_set * fdset
File descriptors buffer.
int capa
Maximum allowed number of FDs.
const rb_iseq_t * iseqptr
iseq pointer, should be separated from iseqval
void rb_nativethread_lock_lock(rb_nativethread_lock_t *lock)
Blocks until the current thread obtains a lock.
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_lock.
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_nativethread_lock_unlock(rb_nativethread_lock_t *lock)
Releases a lock.
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_destroy.
void rb_nativethread_lock_initialize(rb_nativethread_lock_t *lock)
Fills the passed lock with an initial value.
void rb_nativethread_lock_destroy(rb_nativethread_lock_t *lock)
Destroys the passed mutex.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.