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/jit.h"
81#include "internal/coverage.h"
82#include "internal/error.h"
83#include "internal/eval.h"
84#include "internal/gc.h"
85#include "internal/hash.h"
86#include "internal/io.h"
87#include "internal/object.h"
88#include "internal/proc.h"
90#include "internal/scheduler.h"
91#include "internal/signal.h"
92#include "internal/thread.h"
93#include "internal/time.h"
94#include "internal/warnings.h"
102#include "ractor_core.h"
107#include "ccan/list/list.h"
109#ifndef USE_NATIVE_THREAD_PRIORITY
110#define USE_NATIVE_THREAD_PRIORITY 0
111#define RUBY_THREAD_PRIORITY_MAX 3
112#define RUBY_THREAD_PRIORITY_MIN -3
115static VALUE rb_cThreadShield;
116static VALUE cThGroup;
118static VALUE sym_immediate;
119static VALUE sym_on_blocking;
120static VALUE sym_never;
122static uint32_t thread_default_quantum_ms = 100;
124#define THREAD_LOCAL_STORAGE_INITIALISED FL_USER13
125#define THREAD_LOCAL_STORAGE_INITIALISED_P(th) RB_FL_TEST_RAW((th), THREAD_LOCAL_STORAGE_INITIALISED)
128rb_thread_local_storage(
VALUE thread)
130 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
138 SLEEP_DEADLOCKABLE = 0x01,
139 SLEEP_SPURIOUS_CHECK = 0x02,
140 SLEEP_ALLOW_SPURIOUS = 0x04,
141 SLEEP_NO_CHECKINTS = 0x08,
144static void sleep_forever(
rb_thread_t *th,
unsigned int fl);
145static int sleep_hrtime(
rb_thread_t *, rb_hrtime_t,
unsigned int fl);
147static void rb_thread_sleep_deadly_allow_spurious_wakeup(
VALUE blocker,
VALUE timeout, rb_hrtime_t end);
150static int rb_threadptr_pending_interrupt_empty_p(
const rb_thread_t *th);
151static const char *thread_status_name(
rb_thread_t *th,
int detail);
152static int hrtime_update_expire(rb_hrtime_t *,
const rb_hrtime_t);
153NORETURN(
static void async_bug_fd(
const char *mesg,
int errno_arg,
int fd));
154MAYBE_UNUSED(
static int consume_communication_pipe(
int fd));
157static rb_internal_thread_specific_key_t specific_key_count;
161#define THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
164 enum rb_thread_status prev_status;
168static void unblock_function_clear(
rb_thread_t *th);
174#define THREAD_BLOCKING_BEGIN(th) do { \
175 struct rb_thread_sched * const sched = TH_SCHED(th); \
176 RB_VM_SAVE_MACHINE_CONTEXT(th); \
177 thread_sched_to_waiting((sched), (th), true);
179#define THREAD_BLOCKING_END(th) \
180 thread_sched_to_running((sched), (th)); \
181 rb_ractor_thread_switch(th->ractor, th, false); \
185#ifdef HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P
186#define only_if_constant(expr, notconst) __builtin_choose_expr(__builtin_constant_p(expr), (expr), (notconst))
188#define only_if_constant(expr, notconst) (__builtin_constant_p(expr) ? (expr) : (notconst))
191#define only_if_constant(expr, notconst) notconst
193#define RB_NOGVL_FAIL_FLAGS (RB_NOGVL_INTR_FAIL | RB_NOGVL_PENDING_INTR_FAIL)
194#define BLOCKING_REGION(th, exec, ubf, ubfarg, flags) do { \
195 struct rb_blocking_region_buffer __region; \
196 if (blocking_region_begin(th, &__region, (ubf), (ubfarg), flags) || \
198 !only_if_constant((flags) & RB_NOGVL_FAIL_FLAGS, TRUE)) { \
201 RB_VM_SAVE_MACHINE_CONTEXT(th); \
202 thread_sched_to_waiting(TH_SCHED(th), th, false); \
204 blocking_region_end(th, &__region); \
212#define RUBY_VM_CHECK_INTS_BLOCKING(ec) vm_check_ints_blocking(ec)
216#ifdef RUBY_ASSERT_CRITICAL_SECTION
217 VM_ASSERT(ec->assert_critical_section_entered == 0);
222 if (LIKELY(rb_threadptr_pending_interrupt_empty_p(th))) {
223 if (LIKELY(!RUBY_VM_INTERRUPTED_ANY(ec)))
return FALSE;
226 th->pending_interrupt_queue_checked = 0;
227 RUBY_VM_SET_INTERRUPT(ec);
230 int result = rb_threadptr_execute_interrupts(th, 1);
233 if (result || RUBY_VM_INTERRUPTED(ec)) {
235 if (scheduler !=
Qnil) {
246 return vm_check_ints_blocking(ec);
254#if defined(HAVE_POLL)
255# if defined(__linux__)
258# if defined(__FreeBSD_version) && __FreeBSD_version >= 1100000
261# define POLLERR_SET (POLLHUP | POLLERR)
266timeout_prepare(rb_hrtime_t **to, rb_hrtime_t *rel, rb_hrtime_t *end,
270 *rel = rb_timeval2hrtime(timeout);
271 *end = rb_hrtime_add(rb_hrtime_now(), *rel);
279MAYBE_UNUSED(NOINLINE(
static int thread_start_func_2(
rb_thread_t *th,
VALUE *stack_start)));
280MAYBE_UNUSED(
static bool th_has_dedicated_nt(
const rb_thread_t *th));
281MAYBE_UNUSED(
static int waitfd_to_waiting_flag(
int wfd_event));
283#ifdef RB_THREAD_SCHED_NONE
286# include THREAD_IMPL_SRC
290# include "thread_sched.c"
297#ifndef BUSY_WAIT_SIGNALS
298# define BUSY_WAIT_SIGNALS (0)
302# define USE_EVENTFD (0)
305#include "thread_sync.c"
336 if (RUBY_VM_INTERRUPTED_ANY(th->ec)) {
341 RUBY_VM_CHECK_INTS(th->ec);
344 if (!rb_threadptr_pending_interrupt_empty_p(th)) {
350 }
while (!th->ec->raised_flag && RUBY_VM_INTERRUPTED_ANY(th->ec) &&
353 VM_ASSERT(th->unblock.func == NULL);
355 th->unblock.func = func;
356 th->unblock.arg = arg;
366 th->unblock.func = 0;
371threadptr_set_interrupt_locked(
rb_thread_t *th,
bool trap)
375 RUBY_DEBUG_LOG(
"th:%u trap:%d", rb_th_serial(th), trap);
378 RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
381 RUBY_VM_SET_INTERRUPT(th->ec);
384 if (th->unblock.func != NULL) {
385 (th->unblock.func)(th->unblock.arg);
397 threadptr_set_interrupt_locked(th, trap);
406 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
407 threadptr_set_interrupt(th,
false);
413 threadptr_set_interrupt(th,
true);
421 ccan_list_for_each(&r->threads.set, th, lt_node) {
422 if (th != main_thread) {
423 RUBY_DEBUG_LOG(
"terminate start th:%u status:%s", rb_th_serial(th), thread_status_name(th, TRUE));
425 rb_threadptr_pending_interrupt_enque(th, RUBY_FATAL_THREAD_TERMINATED);
426 rb_threadptr_interrupt(th);
428 RUBY_DEBUG_LOG(
"terminate done th:%u status:%s", rb_th_serial(th), thread_status_name(th, TRUE));
431 RUBY_DEBUG_LOG(
"main thread th:%u", rb_th_serial(th));
439 while (thread->join_list) {
443 thread->join_list = join_list->next;
447 if (target_thread->scheduler !=
Qnil && join_list->fiber) {
451 rb_threadptr_interrupt(target_thread);
453 switch (target_thread->status) {
455 case THREAD_STOPPED_FOREVER:
456 target_thread->status = THREAD_RUNNABLE;
466rb_threadptr_unlock_all_locking_mutexes(
rb_thread_t *th)
468 while (th->keeping_mutexes) {
470 th->keeping_mutexes = mutex->next_mutex;
473 VM_ASSERT(mutex->ec_serial);
474 const char *error_message = rb_mutex_unlock_th(mutex, th, 0);
475 if (error_message) rb_bug(
"invalid keeping_mutexes: %s", error_message);
484 volatile int sleeping = 0;
486 if (cr->threads.main != th) {
487 rb_bug(
"rb_thread_terminate_all: called by child thread (%p, %p)",
488 (
void *)cr->threads.main, (
void *)th);
492 rb_threadptr_unlock_all_locking_mutexes(th);
496 cr->threads.terminating =
true;
499 if (EC_EXEC_TAG() == TAG_NONE) {
501 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
503 terminate_all(cr, th);
505 while (rb_ractor_living_thread_num(cr) > 1) {
506 rb_hrtime_t rel = RB_HRTIME_PER_SEC;
512 native_sleep(th, &rel);
513 RUBY_VM_CHECK_INTS_BLOCKING(ec);
531void rb_threadptr_root_fiber_terminate(
rb_thread_t *th);
532static void threadptr_interrupt_exec_cleanup(
rb_thread_t *th);
535thread_cleanup_func_before_exec(
void *th_ptr)
538 th->status = THREAD_KILLED;
541 th->ec->machine.stack_start = th->ec->machine.stack_end = NULL;
543 threadptr_interrupt_exec_cleanup(th);
544 rb_threadptr_root_fiber_terminate(th);
548thread_cleanup_func(
void *th_ptr,
int atfork)
552 th->locking_mutex =
Qfalse;
553 thread_cleanup_func_before_exec(th_ptr);
556 native_thread_destroy_atfork(th->nt);
574rb_thread_free_native_thread(
void *th_ptr)
581 if (th->sched.context == NULL) {
582 native_thread_destroy_atfork(th->nt);
591ruby_thread_init_stack(
rb_thread_t *th,
void *local_in_parent_frame)
593 native_thread_init_stack(th, local_in_parent_frame);
597rb_vm_proc_local_ep(
VALUE proc)
599 const VALUE *ep = vm_proc_ep(proc);
602 return rb_vm_ep_local_ep(ep);
611 int argc,
const VALUE *argv,
int kw_splat,
VALUE passed_block_handler,
617 VALUE args = th->invoke_arg.proc.args;
618 const VALUE *args_ptr;
620 VALUE procval = th->invoke_arg.proc.proc;
622 GetProcPtr(procval, proc);
623 const rb_cref_t *cref = rb_proc_refinements_cref_for_call(procval);
625 th->ec->errinfo =
Qnil;
626 th->ec->root_lep = rb_vm_proc_local_ep(procval);
627 th->ec->root_svar =
Qfalse;
629 vm_check_ints_blocking(th->ec);
631 if (th->invoke_type == thread_invoke_type_ractor_proc) {
632 VALUE self = rb_ractor_self(th->ractor);
633 th->thgroup = th->ractor->thgroup_default =
rb_obj_alloc(cThGroup);
638 rb_ractor_receive_parameters(th->ec, th->ractor, args_len, (
VALUE *)args_ptr);
639 vm_check_ints_blocking(th->ec);
641 return rb_vm_invoke_proc_with_self(
644 th->invoke_arg.proc.kw_splat,
645 VM_BLOCK_HANDLER_NONE,
655 th->invoke_arg.proc.args =
Qnil;
661 vm_check_ints_blocking(th->ec);
663 return rb_vm_invoke_proc(
666 th->invoke_arg.proc.kw_splat,
667 VM_BLOCK_HANDLER_NONE,
676 native_set_thread_name(th);
679 switch (th->invoke_type) {
680 case thread_invoke_type_proc:
681 result = thread_do_start_proc(th);
684 case thread_invoke_type_ractor_proc:
685 result = thread_do_start_proc(th);
686 rb_ractor_atexit(th->ec, result);
689 case thread_invoke_type_func:
690 result = (*th->invoke_arg.func.func)(th->invoke_arg.func.arg);
693 case thread_invoke_type_none:
694 rb_bug(
"unreachable");
705 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
706 VM_ASSERT(th != th->vm->ractor.main_thread);
708 enum ruby_tag_type state;
710 rb_thread_t *ractor_main_th = th->ractor->threads.main;
713 if (rb_ractor_status_p(th->ractor, ractor_blocking)) {
716 rb_vm_ractor_blocking_cnt_dec(th->vm, th->ractor, __FILE__, __LINE__);
729 VM_ASSERT(UNDEF_P(th->value));
731 volatile int fiber_scheduler_closed = 0, event_thread_end_hooked = 0;
736 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
739 result = thread_do_start(th);
742 if (!fiber_scheduler_closed) {
743 fiber_scheduler_closed = 1;
747 if (!event_thread_end_hooked) {
748 event_thread_end_hooked = 1;
752 if (state == TAG_NONE) {
757 errinfo = th->ec->errinfo;
759 VALUE exc = rb_vm_make_jump_tag_but_local_jump(state,
Qundef);
760 if (!
NIL_P(exc)) errinfo = exc;
762 if (state == TAG_FATAL) {
763 if (th->invoke_type == thread_invoke_type_ractor_proc) {
764 rb_ractor_atexit(th->ec,
Qnil);
769 if (th->invoke_type == thread_invoke_type_ractor_proc) {
770 rb_ractor_atexit_exception(th->ec);
776 if (th->report_on_exception) {
777 VALUE mesg = rb_thread_to_s(th->self);
778 rb_str_cat_cstr(mesg,
" terminated with exception (report_on_exception is true):\n");
779 rb_write_error_str(mesg);
780 rb_ec_error_print(th->ec, errinfo);
783 if (th->invoke_type == thread_invoke_type_ractor_proc) {
784 rb_ractor_atexit_exception(th->ec);
787 if (th->vm->thread_abort_on_exception ||
799 VM_ASSERT(!UNDEF_P(th->value));
801 rb_threadptr_join_list_wakeup(th);
802 rb_threadptr_unlock_all_locking_mutexes(th);
804 if (th->invoke_type == thread_invoke_type_ractor_proc) {
805 rb_thread_terminate_all(th);
806 rb_ractor_teardown(th->ec);
809 th->status = THREAD_KILLED;
810 RUBY_DEBUG_LOG(
"killed th:%u", rb_th_serial(th));
812 if (th->vm->ractor.main_thread == th) {
818 rb_threadptr_raise(ractor_main_th, 1, &errinfo);
823 rb_ec_clear_current_thread_trace_func(th->ec);
826 if (th->locking_mutex !=
Qfalse) {
827 rb_bug(
"thread_start_func_2: locking_mutex must not be set (%p:%"PRIxVALUE
")",
828 (
void *)th, th->locking_mutex);
831 if (th->ractor->threads.terminating &&
832 th->ractor->threads.cnt <= 2 ) {
834 rb_threadptr_interrupt(ractor_main_th);
837 rb_check_deadlock(th->ractor);
839 rb_fiber_close(th->ec->fiber_ptr);
841 thread_cleanup_func(th, FALSE);
842 VM_ASSERT(th->ec->vm_stack == NULL);
847 if (th->invoke_type == thread_invoke_type_ractor_proc) {
848 rb_ractor_postmortem(th, &pf);
851#if defined(USE_MN_THREADS) && USE_MN_THREADS
852 if (th_has_coroutine(th)) {
854 rb_thread_wake_fence(th);
859 coroutine_thread_terminated(th);
860 rb_ractor_postmortem_free(&pf);
865 if (th->invoke_type == thread_invoke_type_ractor_proc) {
875 rb_thread_sched_winding_begin(vm);
880 thread_sched_to_dead(TH_SCHED(th), th);
881 rb_ractor_living_threads_remove(th->ractor, th);
882 rb_ractor_postmortem_free(&pf);
884 rb_thread_sched_winding_end(vm);
887 rb_ractor_living_threads_remove(th->ractor, th);
888 thread_sched_to_dead(TH_SCHED(th), th);
895 enum thread_invoke_type type;
908static void thread_specific_storage_alloc(
rb_thread_t *th);
914 rb_thread_t *th = rb_thread_ptr(thval), *current_th = rb_ec_thread_ptr(ec);
917 thread_specific_storage_alloc(th);
921 "can't start a new thread (frozen ThreadGroup)");
927 if (params->type != thread_invoke_type_ractor_proc) {
928 rb_fiber_inherit_storage(ec, th->ec->fiber_ptr);
931 switch (params->type) {
932 case thread_invoke_type_proc:
933 th->invoke_type = thread_invoke_type_proc;
934 th->invoke_arg.proc.args = params->args;
935 th->invoke_arg.proc.proc = params->proc;
939 case thread_invoke_type_ractor_proc:
940 th->invoke_type = thread_invoke_type_ractor_proc;
941 th->ractor = params->g;
942 th->ec->ractor_id = rb_ractor_id(th->ractor);
943 th->ractor->threads.main = th;
944 th->invoke_arg.proc.proc = rb_proc_isolate_bang(params->proc,
Qnil);
949 case thread_invoke_type_func:
950 th->invoke_type = thread_invoke_type_func;
951 th->invoke_arg.func.func = params->fn;
952 th->invoke_arg.func.arg = (
void *)params->args;
956 rb_bug(
"unreachable");
959 th->priority = current_th->priority;
960 th->thgroup = current_th->thgroup;
962 if (th->invoke_type == thread_invoke_type_ractor_proc) {
966 th->pending_interrupt_queue = 0;
967 th->pending_interrupt_mask_stack = 0;
968 th->pending_interrupt_queue_checked = 0;
976 th->pending_interrupt_queue_checked = 0;
977 th->pending_interrupt_mask_stack =
rb_ary_dup(current_th->pending_interrupt_mask_stack);
978 RBASIC_CLEAR_CLASS(th->pending_interrupt_mask_stack);
981 RUBY_DEBUG_LOG(
"r:%"PRI_SERIALT_PREFIX
"u th:%u", rb_ractor_id(th->ractor), rb_th_serial(th));
983 rb_ractor_living_threads_insert(th->ractor, th);
985 if (th->invoke_type == thread_invoke_type_ractor_proc) {
990 enum ruby_tag_type state;
992 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
993 rb_ractor_setup_default_port(params->g);
994 rb_ractor_send_parameters(ec, params->g, params->args);
997 if (state != TAG_NONE) {
998 th->status = THREAD_KILLED;
999 rb_ractor_cancel_creation(params->g, th);
1000 EC_JUMP_TAG(ec, state);
1005 err = native_thread_create(th);
1007 th->status = THREAD_KILLED;
1008 if (th->invoke_type == thread_invoke_type_ractor_proc) {
1012 rb_ractor_cancel_creation(th->ractor, th);
1015 rb_ractor_living_threads_remove(th->ractor, th);
1022#define threadptr_initialized(th) ((th)->invoke_type != thread_invoke_type_none)
1045thread_s_new(
int argc,
VALUE *argv,
VALUE klass)
1048 VALUE thread = rb_thread_alloc(klass);
1050 if (GET_RACTOR()->threads.main->status == THREAD_KILLED) {
1055 th = rb_thread_ptr(thread);
1056 if (!threadptr_initialized(th)) {
1057 rb_raise(
rb_eThreadError,
"uninitialized thread - check '%"PRIsVALUE
"#initialize'",
1077 .type = thread_invoke_type_proc,
1081 return thread_create_core(rb_thread_alloc(klass), ¶ms);
1087 if (th->invoke_type == thread_invoke_type_proc) {
1088 return rb_proc_location(th->invoke_arg.proc.proc);
1104 else if (th->invoke_type != thread_invoke_type_none) {
1105 VALUE loc = threadptr_invoke_proc_location(th);
1108 "already initialized thread - %"PRIsVALUE
":%"PRIsVALUE,
1117 .type = thread_invoke_type_proc,
1121 return thread_create_core(thread, ¶ms);
1129 .type = thread_invoke_type_func,
1133 return thread_create_core(rb_thread_alloc(
rb_cThread), ¶ms);
1149 void *
const child_objspace = r->objspace;
1151 const bool multi_objspace = rb_gc_multi_objspace_p();
1152 enum ruby_tag_type alloc_state = TAG_NONE;
1154 if (multi_objspace) {
1156 cr->creating_child_objspace = child_objspace;
1160 VALUE gc_was_disabled = rb_gc_objspace_disable_no_rest(child_objspace);
1164 if ((alloc_state = EC_EXEC_TAG()) == TAG_NONE) {
1165 thval = rb_thread_alloc_in_objspace(
rb_cThread, child_objspace);
1168 if (gc_was_disabled ==
Qfalse) rb_gc_objspace_enable(child_objspace);
1169 if (alloc_state != TAG_NONE) {
1173 if (multi_objspace) cr->creating_child_objspace = NULL;
1175 rb_gc_objspace_disown(r->objspace);
1180 if (alloc_state != TAG_NONE) EC_JUMP_TAG(ec, alloc_state);
1188 .type = thread_invoke_type_ractor_proc,
1197 VALUE thval = create_ractor_alloc_thread(r, cr, ec);
1203 enum ruby_tag_type state;
1206 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1207 thret = thread_create_core(thval, ¶ms);
1210 if (state != TAG_NONE) {
1212 if (cr->creating_child_objspace == r->objspace) {
1213 cr->creating_child_objspace = NULL;
1216 rb_gc_objspace_disown(r->objspace);
1220 EC_JUMP_TAG(ec, state);
1234remove_from_join_list(
VALUE arg)
1239 if (target_thread->status != THREAD_KILLED) {
1242 while (*join_list) {
1243 if (*join_list == p->waiter) {
1244 *join_list = (*join_list)->next;
1248 join_list = &(*join_list)->next;
1258 return th->status == THREAD_KILLED || !UNDEF_P(th->value);
1262thread_join_sleep(
VALUE arg)
1265 rb_thread_t *target_th = p->target, *th = p->waiter->thread;
1266 rb_hrtime_t end = 0, *limit = p->limit;
1269 end = rb_hrtime_add(*limit, rb_hrtime_now());
1272 while (!thread_finished(target_th)) {
1276 if (scheduler !=
Qnil) {
1280 sleep_forever(th, SLEEP_DEADLOCKABLE | SLEEP_ALLOW_SPURIOUS | SLEEP_NO_CHECKINTS);
1284 if (hrtime_update_expire(limit, end)) {
1285 RUBY_DEBUG_LOG(
"timeout target_th:%u", rb_th_serial(target_th));
1289 if (scheduler !=
Qnil) {
1290 VALUE timeout = rb_float_new(hrtime2double(*limit));
1294 th->status = THREAD_STOPPED;
1295 native_sleep(th, limit);
1298 RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1299 th->status = THREAD_RUNNABLE;
1301 RUBY_DEBUG_LOG(
"interrupted target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1314 if (th == target_th) {
1315 rb_raise(
rb_eThreadError,
"Target thread must not be current thread");
1318 if (th->ractor->threads.main == target_th) {
1322 RUBY_DEBUG_LOG(
"target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1324 if (target_th->status != THREAD_KILLED) {
1326 waiter.next = target_th->join_list;
1328 waiter.fiber = rb_fiberptr_blocking(fiber) ? NULL : fiber;
1329 target_th->join_list = &waiter;
1332 arg.waiter = &waiter;
1333 arg.target = target_th;
1334 arg.timeout = timeout;
1342 RUBY_DEBUG_LOG(
"success target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1344 if (target_th->ec->errinfo !=
Qnil) {
1345 VALUE err = target_th->ec->errinfo;
1350 RUBY_DEBUG_LOG(
"terminated target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1355 if (err == RUBY_FATAL_FIBER_KILLED) {
1359 rb_bug(
"thread_join: Fixnum (%d) should not reach here.",
FIX2INT(err));
1362 else if (THROW_DATA_P(target_th->ec->errinfo)) {
1363 rb_bug(
"thread_join: THROW_DATA should not reach here.");
1370 return target_th->self;
1413thread_join_m(
int argc,
VALUE *argv,
VALUE self)
1416 rb_hrtime_t rel = 0, *limit = 0;
1427 if (
NIL_P(timeout)) {
1431 rel = rb_sec2hrtime(NUM2TIMET(timeout));
1435 limit = double2hrtime(&rel,
rb_num2dbl(timeout));
1438 return thread_join(rb_thread_ptr(self), timeout, limit);
1456thread_value(
VALUE self)
1459 thread_join(th,
Qnil, 0);
1460 if (UNDEF_P(th->value)) {
1474#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
1475 if (clock_gettime(CLOCK_MONOTONIC, ts) == 0)
1485NOINLINE(rb_hrtime_t rb_hrtime_now(
void));
1492 return rb_timespec2hrtime(&ts);
1499COMPILER_WARNING_PUSH
1500#if defined(__GNUC__) && __GNUC__ == 7 && __GNUC_MINOR__ <= 3
1501COMPILER_WARNING_IGNORED(-Wmaybe-uninitialized)
1504#define PRIu64 PRI_64_PREFIX "u"
1512hrtime_update_expire(rb_hrtime_t *timeout,
const rb_hrtime_t end)
1514 rb_hrtime_t now = rb_hrtime_now();
1516 if (now > end)
return 1;
1518 RUBY_DEBUG_LOG(
"%"PRIu64
" > %"PRIu64
"", (uint64_t)end, (uint64_t)now);
1520 *timeout = end - now;
1525static int sleep_hrtime_until(
rb_thread_t *th, rb_hrtime_t end,
unsigned int fl);
1528sleep_hrtime(
rb_thread_t *th, rb_hrtime_t rel,
unsigned int fl)
1530 return sleep_hrtime_until(th, rb_hrtime_add(rb_hrtime_now(), rel), fl);
1534sleep_hrtime_until(
rb_thread_t *th, rb_hrtime_t end,
unsigned int fl)
1536 enum rb_thread_status prev_status = th->status;
1538 rb_hrtime_t rel = rb_hrtime_sub(end, rb_hrtime_now());
1540 th->status = THREAD_STOPPED;
1541 RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1542 while (th->status == THREAD_STOPPED) {
1543 native_sleep(th, &rel);
1544 woke = vm_check_ints_blocking(th->ec);
1545 if (woke && !(fl & SLEEP_SPURIOUS_CHECK))
1547 if (hrtime_update_expire(&rel, end))
1551 th->status = prev_status;
1558 enum rb_thread_status prev_status = th->status;
1559 enum rb_thread_status status;
1562 status = fl & SLEEP_DEADLOCKABLE ? THREAD_STOPPED_FOREVER : THREAD_STOPPED;
1563 th->status = status;
1565 if (!(fl & SLEEP_NO_CHECKINTS)) RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1567 while (th->status == status) {
1568 if (fl & SLEEP_DEADLOCKABLE) {
1569 rb_ractor_sleeper_threads_inc(th->ractor);
1570 rb_check_deadlock(th->ractor);
1573 native_sleep(th, 0);
1575 if (fl & SLEEP_DEADLOCKABLE) {
1576 rb_ractor_sleeper_threads_dec(th->ractor);
1578 if (fl & SLEEP_ALLOW_SPURIOUS) {
1582 woke = vm_check_ints_blocking(th->ec);
1584 if (woke && !(fl & SLEEP_SPURIOUS_CHECK)) {
1588 th->status = prev_status;
1594 RUBY_DEBUG_LOG(
"forever");
1595 sleep_forever(GET_THREAD(), SLEEP_SPURIOUS_CHECK);
1601 RUBY_DEBUG_LOG(
"deadly");
1602 sleep_forever(GET_THREAD(), SLEEP_DEADLOCKABLE|SLEEP_SPURIOUS_CHECK);
1606rb_thread_sleep_deadly_allow_spurious_wakeup(
VALUE blocker,
VALUE timeout, rb_hrtime_t end)
1610 if (scheduler !=
Qnil) {
1614 RUBY_DEBUG_LOG(
"...");
1616 sleep_hrtime_until(th, end, SLEEP_SPURIOUS_CHECK);
1619 sleep_forever(th, SLEEP_DEADLOCKABLE);
1629 sleep_hrtime(th, rb_timeval2hrtime(&time), SLEEP_SPURIOUS_CHECK);
1635 RUBY_VM_CHECK_INTS_BLOCKING(ec);
1648 rb_ec_check_ints(GET_EC());
1656rb_thread_check_trap_pending(
void)
1658 return rb_signal_buff_size() != 0;
1665 return (
int)RUBY_VM_INTERRUPTED(rb_thread_ptr(thval)->ec);
1675rb_thread_schedule_limits(uint32_t limits_us)
1679 RUBY_DEBUG_LOG(
"us:%u", (
unsigned int)limits_us);
1681 if (th->running_time_us >= limits_us) {
1682 RUBY_DEBUG_LOG(
"switch %s",
"start");
1684 RB_VM_SAVE_MACHINE_CONTEXT(th);
1685 thread_sched_yield(TH_SCHED(th), th);
1686 rb_ractor_thread_switch(th->ractor, th,
true);
1688 RUBY_DEBUG_LOG(
"switch %s",
"done");
1696 rb_thread_schedule_limits(0);
1697 RUBY_VM_CHECK_INTS(GET_EC());
1706#ifdef RUBY_ASSERT_CRITICAL_SECTION
1707 VM_ASSERT(th->ec->assert_critical_section_entered == 0);
1709 VM_ASSERT(th == GET_THREAD());
1711 region->prev_status = th->status;
1712 if (unblock_function_set(th, ubf, arg, flags)) {
1713 th->blocking_region_buffer = region;
1714 th->status = THREAD_STOPPED;
1715 rb_ractor_blocking_threads_inc(th->ractor, __FILE__, __LINE__);
1717 RUBY_DEBUG_LOG(
"thread_id:%p", (
void *)th->nt->thread_id);
1729 unblock_function_clear(th);
1731 unregister_ubf_list(th);
1733 thread_sched_to_running(TH_SCHED(th), th);
1734 rb_ractor_thread_switch(th->ractor, th,
false);
1736 th->blocking_region_buffer = 0;
1737 rb_ractor_blocking_threads_dec(th->ractor, __FILE__, __LINE__);
1738 if (th->status == THREAD_STOPPED) {
1739 th->status = region->prev_status;
1742 RUBY_DEBUG_LOG(
"end");
1746 VM_ASSERT(th == GET_THREAD());
1766 *unblock_function = ubf_select;
1774rb_nogvl(
void *(*func)(
void *),
void *data1,
1783 !rb_threadptr_pending_interrupt_empty_p(th)
1793 if (scheduler !=
Qnil) {
1798 if (!UNDEF_P(result)) {
1799 rb_errno_set(state.saved_errno);
1800 return state.result;
1806 rb_vm_t *vm = rb_ec_vm_ptr(ec);
1807 bool is_main_thread = vm->ractor.main_thread == th;
1808 int saved_errno = 0;
1810 bool sentinel_ubf = rb_thread_resolve_unblock_function(&ubf, &data2, th);
1812 if (ubf && rb_ractor_living_thread_num(th->ractor) == 1 && is_main_thread) {
1817 vm->ubf_async_safe = 1;
1821 rb_vm_t *
volatile saved_vm = vm;
1822 BLOCKING_REGION(th, {
1824 saved_errno = rb_errno();
1825 }, ubf, data2, flags);
1828 if (is_main_thread) vm->ubf_async_safe = 0;
1831 RUBY_VM_CHECK_INTS_BLOCKING(ec);
1834 rb_errno_set(saved_errno);
1935 return rb_nogvl(func, data1, ubf, data2, 0);
1939waitfd_to_waiting_flag(
int wfd_event)
1941 return wfd_event << 1;
1944static struct ccan_list_head *
1945rb_io_blocking_operations(
struct rb_io *io)
1947 rb_serial_t fork_generation = GET_VM()->fork_gen;
1951 if (io->fork_generation != fork_generation) {
1953 io->fork_generation = fork_generation;
1972 ccan_list_add(rb_io_blocking_operations(io), &blocking_operation->list);
1978 ccan_list_del(&blocking_operation->list);
1987io_blocking_operation_exit(
VALUE _arguments)
1992 rb_io_blocking_operation_pop(arguments->io, blocking_operation);
1996 rb_fiber_t *fiber = io->closing_ec->fiber_ptr;
1998 if (thread->scheduler !=
Qnil) {
2022 VALUE wakeup_mutex = io->wakeup_mutex;
2025 blocking_operation->ec = NULL;
2027 if (!
NIL_P(blocking_operation->scheduler_interrupt_target)) {
2028 rb_fiber_scheduler_interrupt_target_invalidate(blocking_operation->scheduler_interrupt_target);
2034 .blocking_operation = blocking_operation
2041 rb_io_blocking_operation_pop(io, blocking_operation);
2046rb_thread_io_blocking_operation_ensure(
VALUE _argument)
2050 rb_io_blocking_operation_exit(arguments->io, arguments->blocking_operation);
2062rb_thread_io_blocking_operation_body(
VALUE _arguments)
2065 VALUE result = arguments->function(arguments->argument);
2066 VALUE target = arguments->blocking_operation->scheduler_interrupt_target;
2068 if (!
NIL_P(target)) {
2069 VALUE exception = rb_fiber_scheduler_interrupt_target_exception(target);
2071 if (!
NIL_P(exception)) {
2101 .scheduler_interrupt_target =
Qnil,
2103 rb_io_blocking_operation_enter(io, &blocking_operation);
2107 .blocking_operation = &blocking_operation
2111 .function = function,
2112 .argument = argument,
2113 .blocking_operation = &blocking_operation,
2122#if defined(USE_MN_THREADS) && USE_MN_THREADS
2126 return !th_has_dedicated_nt(th) && (events || timeout) && th->blocking &&
2127 !(events & ~(RB_WAITFD_IN | RB_WAITFD_OUT));
2133enum io_wait_result {
2142static enum io_wait_result
2143thread_io_wait_events(
rb_thread_t *th,
int fd,
int events,
const struct timeval *timeout,
2144 bool known_not_ready)
2146#if defined(USE_MN_THREADS) && USE_MN_THREADS
2147 if (thread_io_mn_schedulable(th, events, timeout)) {
2148 rb_hrtime_t rel, *prel;
2151 rel = rb_timeval2hrtime(timeout);
2158 VM_ASSERT(prel || (events & (RB_WAITFD_IN | RB_WAITFD_OUT)));
2160 enum thread_sched_waiting_flag flags = waitfd_to_waiting_flag(events);
2161 if (known_not_ready) flags |= thread_sched_waiting_io_force;
2163 switch (thread_sched_wait_events(TH_SCHED(th), th, fd, flags, prel)) {
2164 case thread_sched_wait_event:
2165 return io_wait_ready;
2166 case thread_sched_wait_timeout:
2167 return io_wait_timed_out;
2168 case thread_sched_wait_unavailable:
2171 return io_wait_unhandled;
2175 return io_wait_unhandled;
2180blocking_call_retryable_p(
int r,
int eno)
2182 if (r != -1)
return false;
2186#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
2196rb_thread_mn_schedulable(
VALUE thval)
2199 return th->mn_schedulable;
2203rb_thread_io_blocking_call(
struct rb_io* io, rb_blocking_function_t *func,
void *data1,
int events)
2208 RUBY_DEBUG_LOG(
"th:%u fd:%d ev:%d", rb_th_serial(th), io->
fd, events);
2211 volatile int saved_errno = 0;
2212 enum ruby_tag_type state;
2213 volatile bool prev_mn_schedulable = th->mn_schedulable;
2214 th->mn_schedulable = thread_io_mn_schedulable(th, events, NULL);
2226 .scheduler_interrupt_target =
Qnil,
2228 rb_io_blocking_operation_enter(io, &blocking_operation);
2232 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
2233 volatile enum ruby_tag_type saved_state = state;
2235 BLOCKING_REGION(th, {
2237 saved_errno =
errno;
2238 }, ubf_select, th, FALSE);
2241 if (events && blocking_call_retryable_p((
int)val, saved_errno)) {
2243 if (thread_io_wait_events(th, fd, events, NULL,
true) == io_wait_ready) {
2244 RUBY_VM_CHECK_INTS_BLOCKING(ec);
2247 else if (th->mn_schedulable) {
2250 rb_thread_wait_for_single_fd(th, fd, events, NULL);
2251 RUBY_VM_CHECK_INTS_BLOCKING(ec);
2256 RUBY_VM_CHECK_INTS_BLOCKING(ec);
2258 state = saved_state;
2262 th = rb_ec_thread_ptr(ec);
2263 th->mn_schedulable = prev_mn_schedulable;
2266 rb_io_blocking_operation_exit(io, &blocking_operation);
2269 EC_JUMP_TAG(ec, state);
2273 if (saved_errno == ETIMEDOUT) {
2277 errno = saved_errno;
2283rb_thread_io_blocking_region(
struct rb_io *io, rb_blocking_function_t *func,
void *data1)
2285 return rb_thread_io_blocking_call(io, func, data1, 0);
2333 fprintf(stderr,
"[BUG] rb_thread_call_with_gvl() is called by non-ruby thread\n");
2338 prev_unblock = th->unblock;
2342 return (*func)(data1);
2345 blocking_region_end(th, brb);
2349 int released = blocking_region_begin(th, brb, prev_unblock.func, prev_unblock.arg, FALSE);
2351 RB_VM_SAVE_MACHINE_CONTEXT(th);
2352 thread_sched_to_waiting(TH_SCHED(th), th,
true);
2361ruby_thread_has_gvl_p(
void)
2365 if (th && th->blocking_region_buffer == 0) {
2382thread_s_pass(
VALUE klass)
2407rb_threadptr_pending_interrupt_clear(
rb_thread_t *th)
2416 th->pending_interrupt_queue_checked = 0;
2420threadptr_check_pending_interrupt_queue(
rb_thread_t *th)
2422 if (!th->pending_interrupt_queue) {
2427enum handle_interrupt_timing {
2429 INTERRUPT_IMMEDIATE,
2430 INTERRUPT_ON_BLOCKING,
2434static enum handle_interrupt_timing
2437 if (sym == sym_immediate) {
2438 return INTERRUPT_IMMEDIATE;
2440 else if (sym == sym_on_blocking) {
2441 return INTERRUPT_ON_BLOCKING;
2443 else if (sym == sym_never) {
2444 return INTERRUPT_NEVER;
2451static enum handle_interrupt_timing
2455 long mask_stack_len =
RARRAY_LEN(th->pending_interrupt_mask_stack);
2460 for (i=0; i<mask_stack_len; i++) {
2461 mask = mask_stack[mask_stack_len-(i+1)];
2466 return rb_threadptr_pending_interrupt_from_symbol(th, mask);
2478 klass =
RBASIC(mod)->klass;
2480 else if (mod != RCLASS_ORIGIN(mod)) {
2484 if ((sym = rb_hash_aref(mask, klass)) !=
Qnil) {
2485 return rb_threadptr_pending_interrupt_from_symbol(th, sym);
2490 return INTERRUPT_NONE;
2494rb_threadptr_pending_interrupt_empty_p(
const rb_thread_t *th)
2496 return RARRAY_LEN(th->pending_interrupt_queue) == 0;
2503 for (i=0; i<
RARRAY_LEN(th->pending_interrupt_queue); i++) {
2513rb_threadptr_pending_interrupt_deque(
rb_thread_t *th,
enum handle_interrupt_timing timing)
2518 for (i=0; i<
RARRAY_LEN(th->pending_interrupt_queue); i++) {
2521 enum handle_interrupt_timing mask_timing = rb_threadptr_pending_interrupt_check_mask(th,
CLASS_OF(err));
2523 switch (mask_timing) {
2524 case INTERRUPT_ON_BLOCKING:
2525 if (timing != INTERRUPT_ON_BLOCKING) {
2529 case INTERRUPT_NONE:
2530 case INTERRUPT_IMMEDIATE:
2533 case INTERRUPT_NEVER:
2538 th->pending_interrupt_queue_checked = 1;
2542 if (rb_threadptr_pending_interrupt_empty_p(th)) {
2543 th->pending_interrupt_queue_checked = 1;
2550threadptr_pending_interrupt_active_p(
rb_thread_t *th)
2557 if (th->pending_interrupt_queue_checked) {
2561 if (rb_threadptr_pending_interrupt_empty_p(th)) {
2573 if (val != sym_immediate && val != sym_on_blocking && val != sym_never) {
2574 rb_raise(rb_eArgError,
"unknown mask signature");
2582 if (
RTEST(*maskp)) {
2584 VALUE prev = *maskp;
2585 *maskp = rb_ident_hash_new();
2590 rb_hash_aset(*maskp, key, val);
2684rb_thread_s_handle_interrupt(
VALUE self,
VALUE mask_arg)
2690 enum ruby_tag_type state;
2693 rb_raise(rb_eArgError,
"block is needed.");
2696 mask_arg = rb_to_hash_type(mask_arg);
2698 if (
OBJ_FROZEN(mask_arg) && rb_hash_compare_by_id_p(mask_arg)) {
2704 if (UNDEF_P(mask)) {
2715 rb_ary_push(th->pending_interrupt_mask_stack, mask);
2716 if (!rb_threadptr_pending_interrupt_empty_p(th)) {
2717 th->pending_interrupt_queue_checked = 0;
2718 RUBY_VM_SET_INTERRUPT(th->ec);
2721 EC_PUSH_TAG(th->ec);
2722 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
2727 rb_ary_pop(th->pending_interrupt_mask_stack);
2728 if (!rb_threadptr_pending_interrupt_empty_p(th)) {
2729 th->pending_interrupt_queue_checked = 0;
2730 RUBY_VM_SET_INTERRUPT(th->ec);
2733 RUBY_VM_CHECK_INTS(th->ec);
2736 EC_JUMP_TAG(th->ec, state);
2753rb_thread_pending_interrupt_p(
int argc,
VALUE *argv,
VALUE target_thread)
2755 rb_thread_t *target_th = rb_thread_ptr(target_thread);
2757 if (!target_th->pending_interrupt_queue) {
2760 if (rb_threadptr_pending_interrupt_empty_p(target_th)) {
2764 VALUE err = argv[0];
2766 rb_raise(
rb_eTypeError,
"class or module required for rescue clause");
2768 return RBOOL(rb_threadptr_pending_interrupt_include_p(target_th, err));
2833rb_thread_s_pending_interrupt_p(
int argc,
VALUE *argv,
VALUE self)
2835 return rb_thread_pending_interrupt_p(argc, argv, GET_THREAD()->self);
2838NORETURN(
static void rb_threadptr_to_kill(
rb_thread_t *th));
2843 VM_ASSERT(GET_THREAD() == th);
2844 rb_threadptr_pending_interrupt_clear(th);
2845 th->status = THREAD_RUNNABLE;
2847 th->ec->errinfo =
INT2FIX(TAG_FATAL);
2848 EC_JUMP_TAG(th->ec, TAG_FATAL);
2858 old = ATOMIC_LOAD_RELAXED(ec->interrupt_flag);
2861 old = ATOMIC_CAS(ec->interrupt_flag, interrupt, interrupt & ec->interrupt_mask);
2862 }
while (old != interrupt);
2863 return interrupt & (
rb_atomic_t)~ec->interrupt_mask;
2866static void threadptr_interrupt_exec_exec(
rb_thread_t *th);
2874rb_threadptr_execute_interrupts(
rb_thread_t *th,
int blocking_timing)
2877 int postponed_job_interrupt = 0;
2880 VM_ASSERT(GET_THREAD() == th);
2882 if (th->ec->raised_flag)
return ret;
2884 while ((interrupt = threadptr_get_interrupts(th)) != 0) {
2886 int timer_interrupt;
2887 int pending_interrupt;
2889 int terminate_interrupt;
2891 timer_interrupt = interrupt & TIMER_INTERRUPT_MASK;
2892 pending_interrupt = interrupt & PENDING_INTERRUPT_MASK;
2893 postponed_job_interrupt = interrupt & POSTPONED_JOB_INTERRUPT_MASK;
2894 trap_interrupt = interrupt & TRAP_INTERRUPT_MASK;
2895 terminate_interrupt = interrupt & TERMINATE_INTERRUPT_MASK;
2897 if (interrupt & VM_BARRIER_INTERRUPT_MASK) {
2901 if (postponed_job_interrupt) {
2902 rb_postponed_job_flush();
2905 if (trap_interrupt) {
2907 if (th == th->vm->ractor.main_thread) {
2908 enum rb_thread_status prev_status = th->status;
2910 th->status = THREAD_RUNNABLE;
2912 while ((sig = rb_get_next_signal()) != 0) {
2913 ret |= rb_signal_exec(th, sig);
2916 th->status = prev_status;
2919 if (!ccan_list_empty(&th->interrupt_exec_tasks)) {
2920 enum rb_thread_status prev_status = th->status;
2922 th->status = THREAD_RUNNABLE;
2924 threadptr_interrupt_exec_exec(th);
2926 th->status = prev_status;
2931 if (pending_interrupt && threadptr_pending_interrupt_active_p(th)) {
2932 VALUE err = rb_threadptr_pending_interrupt_deque(th, blocking_timing ? INTERRUPT_ON_BLOCKING : INTERRUPT_NONE);
2933 RUBY_DEBUG_LOG(
"err:%"PRIdVALUE, err);
2939 else if (err == RUBY_FATAL_THREAD_KILLED ||
2940 err == RUBY_FATAL_THREAD_TERMINATED ||
2942 terminate_interrupt = 1;
2945 if (err == th->vm->special_exceptions[ruby_error_stream_closed]) {
2947 err = ruby_vm_special_exception_copy(err);
2950 if (th->status == THREAD_STOPPED ||
2951 th->status == THREAD_STOPPED_FOREVER)
2952 th->status = THREAD_RUNNABLE;
2957 if (terminate_interrupt) {
2958 rb_threadptr_to_kill(th);
2961 if (timer_interrupt) {
2962 uint32_t limits_us = thread_default_quantum_ms * 1000;
2964 if (th->priority > 0)
2965 limits_us <<= th->priority;
2967 limits_us >>= -th->priority;
2969 if (th->status == THREAD_RUNNABLE)
2970 th->running_time_us += 10 * 1000;
2972 VM_ASSERT(th->ec->cfp);
2976 rb_thread_schedule_limits(limits_us);
2983rb_thread_execute_interrupts(
VALUE thval)
2985 rb_threadptr_execute_interrupts(rb_thread_ptr(thval), 1);
2991 rb_threadptr_interrupt(th);
2999 if (rb_threadptr_dead(target_th)) {
3007 exc = rb_make_exception(argc, argv);
3012 if (rb_threadptr_dead(target_th)) {
3016 rb_ec_setup_exception(GET_EC(), exc,
Qundef);
3017 rb_threadptr_pending_interrupt_enque(target_th, exc);
3018 rb_threadptr_interrupt(target_th);
3024rb_threadptr_signal_raise(
rb_thread_t *th,
int sig)
3030 rb_threadptr_raise(th->vm->ractor.main_thread, 2, argv);
3036 rb_thread_t *target_th = th->vm->ractor.main_thread;
3038 if (rb_threadptr_dead(target_th)) {
3047 if (rb_threadptr_dead(target_th)) {
3051 rb_ec_setup_exception(GET_EC(), exc,
Qundef);
3052 rb_threadptr_pending_interrupt_enque(target_th, exc);
3053 rb_threadptr_interrupt(target_th);
3065 rb_threadptr_raise(th->vm->ractor.main_thread, 2, argv);
3071 if (ec->raised_flag & RAISED_EXCEPTION) {
3074 ec->raised_flag |= RAISED_EXCEPTION;
3081 if (!(ec->raised_flag & RAISED_EXCEPTION)) {
3084 ec->raised_flag &= ~RAISED_EXCEPTION;
3109thread_io_close_notify_all(
VALUE _io)
3114 rb_vm_t *vm = io->closing_ec->thread_ptr->vm;
3115 VALUE error = vm->special_exceptions[ruby_error_stream_closed];
3118 ccan_list_for_each(rb_io_blocking_operations(io), blocking_operation, list) {
3125 if (thread->scheduler !=
Qnil) {
3126 VALUE target = blocking_operation->scheduler_interrupt_target;
3128 if (
NIL_P(target)) {
3129 VALUE fiber = rb_fiberptr_self(ec->fiber_ptr);
3130 target = rb_fiber_scheduler_interrupt_target_new(fiber, error);
3131 blocking_operation->scheduler_interrupt_target = target;
3139 rb_threadptr_pending_interrupt_enque(thread, error);
3140 rb_threadptr_interrupt(thread);
3147 return (
VALUE)count;
3151rb_thread_io_close_interrupt(
struct rb_io *io)
3154 if (io->closing_ec) {
3159 if (ccan_list_empty(rb_io_blocking_operations(io))) {
3165 io->closing_ec = ec;
3169 rb_mutex_allow_trap(io->wakeup_mutex, 1);
3174 return (
size_t)result;
3178rb_thread_io_close_wait(
struct rb_io* io)
3180 VALUE wakeup_mutex = io->wakeup_mutex;
3188 while (!ccan_list_empty(rb_io_blocking_operations(io))) {
3194 io->wakeup_mutex =
Qnil;
3195 io->closing_ec = NULL;
3201 rb_warn(
"rb_thread_fd_close is deprecated (and is now a no-op).");
3225thread_raise_m(
int argc,
VALUE *argv,
VALUE self)
3230 threadptr_check_pending_interrupt_queue(target_th);
3232 if (rb_threadptr_dead(target_th)) {
3236 VALUE exception = rb_exception_setup(argc, argv);
3237 rb_threadptr_pending_interrupt_enque(target_th, exception);
3238 rb_threadptr_interrupt(target_th);
3241 if (current_th == target_th) {
3242 RUBY_VM_CHECK_INTS(target_th->ec);
3267 if (target_th->to_kill || target_th->status == THREAD_KILLED) {
3270 if (target_th == target_th->vm->ractor.main_thread) {
3274 RUBY_DEBUG_LOG(
"target_th:%u", rb_th_serial(target_th));
3276 if (target_th == GET_THREAD()) {
3278 rb_threadptr_to_kill(target_th);
3281 threadptr_check_pending_interrupt_queue(target_th);
3282 rb_threadptr_pending_interrupt_enque(target_th, RUBY_FATAL_THREAD_KILLED);
3283 rb_threadptr_interrupt(target_th);
3290rb_thread_to_be_killed(
VALUE thread)
3294 if (target_th->to_kill || target_th->status == THREAD_KILLED) {
3370 if (target_th->status == THREAD_KILLED)
return Qnil;
3372 rb_threadptr_ready(target_th);
3374 if (target_th->status == THREAD_STOPPED ||
3375 target_th->status == THREAD_STOPPED_FOREVER) {
3376 target_th->status = THREAD_RUNNABLE;
3418 "stopping only thread\n\tnote: use sleep to stop forever");
3451 return rb_ractor_thread_list();
3477 return rb_thread_list();
3483 return GET_THREAD()->self;
3496thread_s_current(
VALUE klass)
3504 return GET_RACTOR()->threads.main->self;
3515rb_thread_s_main(
VALUE klass)
3542rb_thread_s_abort_exc(
VALUE _)
3544 return RBOOL(GET_THREAD()->vm->thread_abort_on_exception);
3579rb_thread_s_abort_exc_set(
VALUE self,
VALUE val)
3581 GET_THREAD()->vm->thread_abort_on_exception =
RTEST(val);
3602rb_thread_abort_exc(
VALUE thread)
3604 return RBOOL(rb_thread_ptr(thread)->abort_on_exception);
3622rb_thread_abort_exc_set(
VALUE thread,
VALUE val)
3624 rb_thread_ptr(thread)->abort_on_exception =
RTEST(val);
3672rb_thread_s_report_exc(
VALUE _)
3674 return RBOOL(GET_THREAD()->vm->thread_report_on_exception);
3709rb_thread_s_report_exc_set(
VALUE self,
VALUE val)
3711 GET_THREAD()->vm->thread_report_on_exception =
RTEST(val);
3728rb_thread_s_ignore_deadlock(
VALUE _)
3730 return RBOOL(GET_THREAD()->vm->thread_ignore_deadlock);
3755rb_thread_s_ignore_deadlock_set(
VALUE self,
VALUE val)
3757 GET_THREAD()->vm->thread_ignore_deadlock =
RTEST(val);
3779rb_thread_report_exc(
VALUE thread)
3781 return RBOOL(rb_thread_ptr(thread)->report_on_exception);
3799rb_thread_report_exc_set(
VALUE thread,
VALUE val)
3801 rb_thread_ptr(thread)->report_on_exception =
RTEST(val);
3816rb_thread_group(
VALUE thread)
3818 return rb_thread_ptr(thread)->thgroup;
3824 switch (th->status) {
3825 case THREAD_RUNNABLE:
3826 return th->to_kill ?
"aborting" :
"run";
3827 case THREAD_STOPPED_FOREVER:
3828 if (detail)
return "sleep_forever";
3829 case THREAD_STOPPED:
3841 return th->status == THREAD_KILLED;
3877rb_thread_status(
VALUE thread)
3881 if (rb_threadptr_dead(target_th)) {
3882 if (!
NIL_P(target_th->ec->errinfo) &&
3883 !
FIXNUM_P(target_th->ec->errinfo)) {
3891 return rb_str_new2(thread_status_name(target_th, FALSE));
3911rb_thread_alive_p(
VALUE thread)
3913 return RBOOL(!thread_finished(rb_thread_ptr(thread)));
3931rb_thread_stop_p(
VALUE thread)
3935 if (rb_threadptr_dead(th)) {
3938 return RBOOL(th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER);
3949rb_thread_getname(
VALUE thread)
3951 return rb_thread_ptr(thread)->name;
3970 enc = rb_enc_get(name);
3971 if (!rb_enc_asciicompat(enc)) {
3972 rb_raise(rb_eArgError,
"ASCII incompatible encoding (%s)",
3977 target_th->name = name;
3978 if (threadptr_initialized(target_th) && target_th->has_dedicated_nt) {
3979 native_set_another_thread_name(target_th->nt->thread_id, name);
3984#if USE_NATIVE_THREAD_NATIVE_THREAD_ID
4008rb_thread_native_thread_id(
VALUE thread)
4011 if (rb_threadptr_dead(target_th))
return Qnil;
4012 return native_thread_native_thread_id(target_th);
4015# define rb_thread_native_thread_id rb_f_notimplement
4026rb_thread_to_s(
VALUE thread)
4033 status = thread_status_name(target_th, TRUE);
4034 str = rb_sprintf(
"#<%"PRIsVALUE
":%p", cname, (
void *)thread);
4035 if (!
NIL_P(target_th->name)) {
4036 rb_str_catf(str,
"@%"PRIsVALUE, target_th->name);
4038 if ((loc = threadptr_invoke_proc_location(target_th)) !=
Qnil) {
4039 rb_str_catf(str,
" %"PRIsVALUE
":%"PRIsVALUE,
4042 rb_str_catf(str,
" %s>", status);
4048#define recursive_key id__recursive_key__
4053 if (
id == recursive_key) {
4054 return th->ec->local_storage_recursive_hash;
4058 struct rb_id_table *local_storage = th->ec->local_storage;
4060 if (local_storage != NULL && rb_id_table_lookup(local_storage,
id, &val)) {
4072 return threadptr_local_aref(rb_thread_ptr(thread),
id);
4139 if (!
id)
return Qnil;
4157rb_thread_fetch(
int argc,
VALUE *argv,
VALUE self)
4168 if (block_given && argc == 2) {
4169 rb_warn(
"block supersedes default value argument");
4174 if (
id == recursive_key) {
4175 return target_th->ec->local_storage_recursive_hash;
4177 else if (
id && target_th->ec->local_storage &&
4178 rb_id_table_lookup(target_th->ec->local_storage,
id, &val)) {
4181 else if (block_given) {
4184 else if (argc == 1) {
4185 rb_key_err_raise(rb_sprintf(
"key not found: %+"PRIsVALUE, key), self, key);
4195 if (
id == recursive_key) {
4196 th->ec->local_storage_recursive_hash = val;
4200 struct rb_id_table *local_storage = th->ec->local_storage;
4203 if (!local_storage)
return Qnil;
4204 rb_id_table_delete(local_storage,
id);
4208 if (local_storage == NULL) {
4209 th->ec->local_storage = local_storage = rb_id_table_create(0);
4211 rb_id_table_insert(local_storage,
id, val);
4224 return threadptr_local_aset(rb_thread_ptr(thread),
id, val);
4276rb_thread_variable_get(
VALUE thread,
VALUE key)
4281 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4284 locals = rb_thread_local_storage(thread);
4285 return rb_hash_aref(locals, symbol);
4311 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4315 locals = rb_thread_local_storage(thread);
4316 rb_hash_delete(locals, symbol);
4320 locals = rb_thread_local_storage(thread);
4321 return rb_hash_aset(locals, symbol, val);
4342 struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
4344 if (!
id || local_storage == NULL) {
4347 return RBOOL(rb_id_table_lookup(local_storage,
id, &val));
4350static enum rb_id_table_iterator_result
4351thread_keys_i(
ID key,
VALUE value,
void *ary)
4354 return ID_TABLE_CONTINUE;
4361 return rb_ractor_living_thread_num(GET_RACTOR()) == 1;
4379rb_thread_keys(
VALUE self)
4381 struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
4384 if (local_storage) {
4385 rb_id_table_foreach(local_storage, thread_keys_i, (
void *)ary);
4415rb_thread_variables(
VALUE thread)
4421 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4424 locals = rb_thread_local_storage(thread);
4452 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4455 locals = rb_thread_local_storage(thread);
4457 return RBOOL(rb_hash_lookup(locals, symbol) !=
Qnil);
4476rb_thread_priority(
VALUE thread)
4478 return INT2NUM(rb_thread_ptr(thread)->priority);
4509rb_thread_priority_set(
VALUE thread,
VALUE prio)
4514#if USE_NATIVE_THREAD_PRIORITY
4515 target_th->priority =
NUM2INT(prio);
4516 native_thread_apply_priority(th);
4519 if (priority > RUBY_THREAD_PRIORITY_MAX) {
4520 priority = RUBY_THREAD_PRIORITY_MAX;
4522 else if (priority < RUBY_THREAD_PRIORITY_MIN) {
4523 priority = RUBY_THREAD_PRIORITY_MIN;
4525 target_th->priority = (int8_t)priority;
4527 return INT2NUM(target_th->priority);
4532#if defined(NFDBITS) && defined(HAVE_RB_FD_INIT)
4568 FD_ZERO(fds->
fdset);
4572fdset_memsize(
int maxfd)
4574 size_t o = howmany(maxfd, NFDBITS) *
sizeof(fd_mask);
4575 if (o <
sizeof(fd_set)) {
4576 return sizeof(fd_set);
4584 size_t size = fdset_memsize(
rb_fd_max(src));
4593 ruby_xfree_sized(fds->
fdset, fdset_memsize(fds->
maxfd));
4608 size_t m = fdset_memsize(n + 1);
4609 size_t o = fdset_memsize(fds->
maxfd);
4612 fds->
fdset = ruby_xrealloc_sized(fds->
fdset, m, o);
4613 memset((
char *)fds->
fdset + o, 0, m - o);
4622 FD_SET(n, fds->
fdset);
4628 if (n >= fds->
maxfd)
return;
4629 FD_CLR(n, fds->
fdset);
4635 if (n >= fds->
maxfd)
return 0;
4636 return FD_ISSET(n, fds->
fdset) != 0;
4642 size_t size = fdset_memsize(max);
4643 dst->
fdset = ruby_xrealloc_sized(dst->
fdset, size, fdset_memsize(dst->
maxfd));
4645 memcpy(dst->
fdset, src, size);
4651 size_t size = fdset_memsize(
rb_fd_max(src));
4652 dst->
fdset = ruby_xrealloc_sized(dst->
fdset, size, fdset_memsize(dst->
maxfd));
4660 fd_set *r = NULL, *w = NULL, *e = NULL;
4673 return select(n, r, w, e, timeout);
4676#define rb_fd_no_init(fds) ((void)((fds)->fdset = 0), (void)((fds)->maxfd = 0))
4683#define FD_ZERO(f) rb_fd_zero(f)
4684#define FD_SET(i, f) rb_fd_set((i), (f))
4685#define FD_CLR(i, f) rb_fd_clr((i), (f))
4686#define FD_ISSET(i, f) rb_fd_isset((i), (f))
4688#elif defined(_WIN32)
4693 set->
capa = FD_SETSIZE;
4695 FD_ZERO(set->
fdset);
4706fdset_memsize(
int capa)
4708 if (
capa == FD_SETSIZE) {
4709 return sizeof(fd_set);
4711 return sizeof(
unsigned int) + (
capa *
sizeof(SOCKET));
4717 ruby_xfree_sized(set->
fdset, fdset_memsize(set->
capa));
4726 SOCKET s = rb_w32_get_osfhandle(fd);
4728 for (i = 0; i < set->
fdset->fd_count; i++) {
4729 if (set->
fdset->fd_array[i] == s) {
4733 if (set->
fdset->fd_count >= (
unsigned)set->
capa) {
4734 set->
capa = (set->
fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
4736 rb_xrealloc_mul_add(
4737 set->
fdset, set->
capa,
sizeof(SOCKET),
sizeof(
unsigned int));
4739 set->
fdset->fd_array[set->
fdset->fd_count++] = s;
4747#define FD_ZERO(f) rb_fd_zero(f)
4748#define FD_SET(i, f) rb_fd_set((i), (f))
4749#define FD_CLR(i, f) rb_fd_clr((i), (f))
4750#define FD_ISSET(i, f) rb_fd_isset((i), (f))
4752#define rb_fd_no_init(fds) (void)((fds)->fdset = 0)
4756#ifndef rb_fd_no_init
4757#define rb_fd_no_init(fds) (void)(fds)
4761wait_retryable(
volatile int *result,
int errnum, rb_hrtime_t *rel, rb_hrtime_t end)
4771 if (rel && hrtime_update_expire(rel, end)) {
4781 return !hrtime_update_expire(rel, end);
4801select_set_free(
VALUE p)
4816 volatile int result = 0;
4818 rb_hrtime_t *to, rel, end = 0;
4820 timeout_prepare(&to, &rel, &end, set->timeout);
4821 volatile rb_hrtime_t endtime = end;
4822#define restore_fdset(dst, src) \
4823 ((dst) ? rb_fd_dup(dst, src) : (void)0)
4824#define do_select_update() \
4825 (restore_fdset(set->rset, &set->orig_rset), \
4826 restore_fdset(set->wset, &set->orig_wset), \
4827 restore_fdset(set->eset, &set->orig_eset), \
4833 BLOCKING_REGION(set->th, {
4836 if (!RUBY_VM_INTERRUPTED(set->th->ec)) {
4837 result = native_fd_select(set->max,
4838 set->rset, set->wset, set->eset,
4839 rb_hrtime2timeval(&tv, to), set->th);
4840 if (result < 0) lerrno = errno;
4842 }, ubf_select, set->th, TRUE);
4844 RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec);
4845 }
while (wait_retryable(&result, lerrno, to, endtime) && do_select_update());
4847 RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec);
4853 return (
VALUE)result;
4862 set.th = GET_THREAD();
4863 RUBY_VM_CHECK_INTS_BLOCKING(set.th->ec);
4868 set.timeout = timeout;
4870 if (!set.rset && !set.wset && !set.eset) {
4879#define fd_init_copy(f) do { \
4881 rb_fd_resize(set.max - 1, set.f); \
4882 if (&set.orig_##f != set.f) { \
4883 rb_fd_init_copy(&set.orig_##f, set.f); \
4887 rb_fd_no_init(&set.orig_##f); \
4901#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
4902#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
4903#define POLLEX_SET (POLLPRI)
4906# define POLLERR_SET (0)
4910wait_for_single_fd_blocking_region(
rb_thread_t *th,
struct pollfd *fds, nfds_t nfds,
4911 rb_hrtime_t *
const to,
volatile int *lerrno)
4914 volatile int result = 0;
4917 BLOCKING_REGION(th, {
4918 if (!RUBY_VM_INTERRUPTED(th->ec)) {
4919 result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, to), 0);
4920 if (result < 0) *lerrno = errno;
4922 }, ubf_select, th, TRUE);
4932 struct pollfd fds[1] = {{
4934 .events = (short)events,
4937 volatile int result = 0;
4940 enum ruby_tag_type state = TAG_NONE;
4941 volatile int lerrno;
4947 blocking_operation.ec = ec;
4948 blocking_operation.scheduler_interrupt_target =
Qnil;
4949COMPILER_WARNING_PUSH
4950#if RBIMPL_COMPILER_SINCE(GCC, 12, 0, 0)
4951COMPILER_WARNING_IGNORED(-Wdangling-pointer)
4954 rb_io_blocking_operation_enter(io, &blocking_operation);
4959 bool mn_wait = timeout == NULL || timeout->tv_sec != 0 || timeout->tv_usec != 0;
4961 enum io_wait_result mn_result = io_wait_unhandled;
4964 rb_hrtime_t started = timeout ? rb_hrtime_now() : 0;
4965 mn_result = thread_io_wait_events(th, fd, events, timeout,
false);
4970 if (mn_result == io_wait_unhandled && timeout) {
4971 rb_hrtime_t total = rb_timeval2hrtime(timeout);
4972 rb_hrtime_t spent = rb_hrtime_sub(rb_hrtime_now(), started);
4973 rb_hrtime_t rest = spent < total ? total - spent : 0;
4974 rb_hrtime2timeval(&tv_rest, &rest);
4979 switch (mn_result) {
4981 fds[0].revents = events;
4984 case io_wait_timed_out:
4988 case io_wait_unhandled:
4990 struct timeval *
volatile blocking_timeout = timeout;
4991 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
4992 rb_hrtime_t *to, rel, end = 0;
4993 RUBY_VM_CHECK_INTS_BLOCKING(ec);
4994 timeout_prepare(&to, &rel, &end, blocking_timeout);
4996 nfds = numberof(fds);
4997 result = wait_for_single_fd_blocking_region(th, fds, nfds, to, &lerrno);
4999 RUBY_VM_CHECK_INTS_BLOCKING(ec);
5000 }
while (wait_retryable(&result, lerrno, to, end));
5002 RUBY_VM_CHECK_INTS_BLOCKING(ec);
5009 rb_io_blocking_operation_exit(io, &blocking_operation);
5013 EC_JUMP_TAG(ec, state);
5021 if (fds[0].revents & POLLNVAL) {
5031 if (fds[0].revents & POLLIN_SET)
5032 result |= RB_WAITFD_IN;
5033 if (fds[0].revents & POLLOUT_SET)
5034 result |= RB_WAITFD_OUT;
5035 if (fds[0].revents & POLLEX_SET)
5036 result |= RB_WAITFD_PRI;
5039 if (fds[0].revents & POLLERR_SET)
5060select_single(
VALUE ptr)
5066 args->read, args->write, args->except, args->tv);
5068 args->as.error =
errno;
5071 if (args->read &&
rb_fd_isset(args->as.fd, args->read))
5073 if (args->write &&
rb_fd_isset(args->as.fd, args->write))
5075 if (args->except &&
rb_fd_isset(args->as.fd, args->except))
5082select_single_cleanup(
VALUE ptr)
5086 if (args->blocking_operation) {
5087 rb_io_blocking_operation_exit(args->io, args->blocking_operation);
5119 blocking_operation.ec = th->ec;
5120 blocking_operation.scheduler_interrupt_target =
Qnil;
5121 rb_io_blocking_operation_enter(io, &blocking_operation);
5122 args.blocking_operation = &blocking_operation;
5126 blocking_operation.ec = NULL;
5127 blocking_operation.scheduler_interrupt_target =
Qnil;
5128 args.blocking_operation = NULL;
5132 args.read = (events & RB_WAITFD_IN) ? init_set_fd(fd, &rfds) : NULL;
5133 args.write = (events & RB_WAITFD_OUT) ? init_set_fd(fd, &wfds) : NULL;
5134 args.except = (events & RB_WAITFD_PRI) ? init_set_fd(fd, &efds) : NULL;
5137 int result = (int)
rb_ensure(select_single, ptr, select_single_cleanup, ptr);
5139 errno = args.as.error;
5146rb_thread_wait_for_single_fd(
rb_thread_t *th,
int fd,
int events,
struct timeval *timeout)
5148 return thread_io_wait(th, NULL, fd, events, timeout);
5154 return thread_io_wait(th, io, io->
fd, events, timeout);
5161#ifdef USE_CONSERVATIVE_STACK_END
5163rb_gc_set_stack_end(
VALUE **stack_end_p)
5166COMPILER_WARNING_PUSH
5167#if RBIMPL_COMPILER_SINCE(GCC, 12, 0, 0)
5168COMPILER_WARNING_IGNORED(-Wdangling-pointer);
5170 *stack_end_p = &stack_end;
5183 if (rb_signal_buff_size() > 0) {
5185 threadptr_trap_interrupt(mth);
5190async_bug_fd(
const char *mesg,
int errno_arg,
int fd)
5193 size_t n = strlcpy(buff, mesg,
sizeof(buff));
5194 if (n <
sizeof(buff)-3) {
5197 rb_async_bug_errno(buff, errno_arg);
5202consume_communication_pipe(
int fd)
5208 static char buff[1024];
5214 result = read(fd, buff,
sizeof(buff));
5216 RUBY_DEBUG_LOG(
"resultf:%d buff:%lu", (
int)result, (
unsigned long)buff[0]);
5218 RUBY_DEBUG_LOG(
"result:%d", (
int)result);
5222 if (USE_EVENTFD || result < (ssize_t)
sizeof(buff)) {
5226 else if (result == 0) {
5229 else if (result < 0) {
5235#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
5240 async_bug_fd(
"consume_communication_pipe: read", e, fd);
5247rb_thread_stop_timer_thread(
void)
5249 if (TIMER_THREAD_CREATED_P() && native_stop_timer_thread()) {
5250 native_reset_timer_thread();
5255rb_thread_reset_timer_thread(
void)
5257 native_reset_timer_thread();
5261rb_thread_start_timer_thread(
void)
5264 rb_thread_create_timer_thread();
5268clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
5276 if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
5298rb_clear_coverages(
void)
5300 VALUE coverages = rb_get_coverages();
5301 if (
RTEST(coverages)) {
5306#if defined(HAVE_WORKING_FORK)
5314 vm->ractor.main_ractor = r;
5315 vm->ractor.main_thread = th;
5316 r->threads.main = th;
5317 r->status_ = ractor_created;
5319 thread_sched_atfork(TH_SCHED(th));
5324 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
5325 if (r != vm->ractor.main_ractor) {
5326 rb_ractor_terminate_atfork(vm, r);
5328 ccan_list_for_each(&r->threads.set, i, lt_node) {
5332 rb_vm_living_threads_init(vm);
5334 rb_ractor_atfork(vm, th);
5335 rb_vm_postponed_job_atfork();
5342 rb_gc_zombie_objspaces_atfork();
5343 rb_gc_atfork_global_locks();
5344 rb_generic_fields_lock_atfork();
5345 ccan_list_head_init(&th->interrupt_exec_tasks);
5348 rb_ractor_sleeper_threads_clear(th->ractor);
5349 rb_clear_coverages();
5352 rb_thread_reset_timer_thread();
5353 rb_thread_start_timer_thread();
5355 VM_ASSERT(vm->ractor.blocking_cnt == 0);
5356 VM_ASSERT(vm->ractor.cnt == 1);
5362 if (th != current_th) {
5364 th->scheduler =
Qnil;
5367 rb_mutex_abandon_keeping_mutexes(th);
5368 rb_mutex_abandon_locking_mutex(th);
5369 thread_cleanup_func(th, TRUE);
5378 rb_threadptr_pending_interrupt_clear(th);
5379 rb_thread_atfork_internal(th, terminate_atfork_i);
5380 th->join_list = NULL;
5381 th->scheduler =
Qnil;
5382 rb_fiber_atfork(th);
5391 if (th != current_th) {
5392 thread_cleanup_func_before_exec(th);
5400 rb_thread_atfork_internal(th, terminate_atfork_before_exec_i);
5425 0, 0, RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
5448thgroup_s_alloc(
VALUE klass)
5469thgroup_list(
VALUE group)
5475 ccan_list_for_each(&r->threads.set, th, lt_node) {
5476 if (th->thgroup == group) {
5501thgroup_enclose(
VALUE group)
5520thgroup_enclosed_p(
VALUE group)
5525 return RBOOL(data->enclosed);
5565 if (data->enclosed) {
5573 if (data->enclosed) {
5575 "can't move from the enclosed thread group");
5578 target_th->thgroup = group;
5586thread_shield_mark(
void *ptr)
5588 rb_gc_mark((
VALUE)ptr);
5593 {thread_shield_mark, 0, 0,},
5594 0, 0, RUBY_TYPED_THREAD_SAFE_FREE
5598thread_shield_alloc(
VALUE klass)
5603#define GetThreadShieldPtr(obj) ((VALUE)rb_check_typeddata((obj), &thread_shield_data_type))
5604#define THREAD_SHIELD_WAITING_MASK (((FL_USER19-1)&~(FL_USER0-1))|FL_USER19)
5605#define THREAD_SHIELD_WAITING_SHIFT (FL_USHIFT)
5606#define THREAD_SHIELD_WAITING_MAX (THREAD_SHIELD_WAITING_MASK>>THREAD_SHIELD_WAITING_SHIFT)
5607STATIC_ASSERT(THREAD_SHIELD_WAITING_MAX, THREAD_SHIELD_WAITING_MAX <= UINT_MAX);
5608static inline unsigned int
5609rb_thread_shield_waiting(
VALUE b)
5611 return ((
RBASIC(b)->flags&THREAD_SHIELD_WAITING_MASK)>>THREAD_SHIELD_WAITING_SHIFT);
5615rb_thread_shield_waiting_inc(
VALUE b)
5617 unsigned int w = rb_thread_shield_waiting(b);
5619 if (w > THREAD_SHIELD_WAITING_MAX)
5621 RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
5622 RBASIC(b)->flags |= ((
VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
5626rb_thread_shield_waiting_dec(
VALUE b)
5628 unsigned int w = rb_thread_shield_waiting(b);
5631 RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
5632 RBASIC(b)->flags |= ((
VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
5636rb_thread_shield_new(
void)
5638 VALUE thread_shield = thread_shield_alloc(rb_cThreadShield);
5640 return thread_shield;
5644rb_thread_shield_owned(
VALUE self)
5646 VALUE mutex = GetThreadShieldPtr(self);
5647 if (!mutex)
return false;
5651 return m->ec_serial == rb_ec_serial(GET_EC());
5663rb_thread_shield_wait(
VALUE self)
5665 VALUE mutex = GetThreadShieldPtr(self);
5668 if (!mutex)
return Qfalse;
5669 m = mutex_ptr(mutex);
5670 if (m->ec_serial == rb_ec_serial(GET_EC()))
return Qnil;
5671 rb_thread_shield_waiting_inc(self);
5673 rb_thread_shield_waiting_dec(self);
5676 return rb_thread_shield_waiting(self) > 0 ?
Qnil :
Qfalse;
5680thread_shield_get_mutex(
VALUE self)
5682 VALUE mutex = GetThreadShieldPtr(self);
5684 rb_raise(
rb_eThreadError,
"destroyed thread shield - %p", (
void *)self);
5692rb_thread_shield_release(
VALUE self)
5694 VALUE mutex = thread_shield_get_mutex(self);
5696 return RBOOL(rb_thread_shield_waiting(self) > 0);
5703rb_thread_shield_destroy(
VALUE self)
5705 VALUE mutex = thread_shield_get_mutex(self);
5708 return RBOOL(rb_thread_shield_waiting(self) > 0);
5714 return th->ec->local_storage_recursive_hash;
5720 th->ec->local_storage_recursive_hash = hash;
5732recursive_list_access(
VALUE sym)
5735 VALUE hash = threadptr_recursive_hash(th);
5738 hash = rb_ident_hash_new();
5739 threadptr_recursive_hash_set(th, hash);
5743 list = rb_hash_aref(hash, sym);
5746 list = rb_ident_hash_new();
5747 rb_hash_aset(hash, sym, list);
5761#if SIZEOF_LONG == SIZEOF_VOIDP
5762 #define OBJ_ID_EQL(obj_id, other) ((obj_id) == (other))
5763#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
5764 #define OBJ_ID_EQL(obj_id, other) (RB_BIGNUM_TYPE_P((obj_id)) ? \
5765 rb_big_eql((obj_id), (other)) : ((obj_id) == (other)))
5768 VALUE pair_list = rb_hash_lookup2(list, obj,
Qundef);
5769 if (UNDEF_P(pair_list))
5771 if (paired_obj_id) {
5773 if (!OBJ_ID_EQL(paired_obj_id, pair_list))
5777 if (
NIL_P(rb_hash_lookup(pair_list, paired_obj_id)))
5799 rb_hash_aset(list, obj,
Qtrue);
5801 else if (UNDEF_P(pair_list = rb_hash_lookup2(list, obj,
Qundef))) {
5802 rb_hash_aset(list, obj, paired_obj);
5806 VALUE other_paired_obj = pair_list;
5807 pair_list = rb_hash_new();
5808 rb_hash_aset(pair_list, other_paired_obj,
Qtrue);
5809 rb_hash_aset(list, obj, pair_list);
5811 rb_hash_aset(pair_list, paired_obj,
Qtrue);
5827 VALUE pair_list = rb_hash_lookup2(list, obj,
Qundef);
5828 if (UNDEF_P(pair_list)) {
5832 rb_hash_delete_entry(pair_list, paired_obj);
5838 rb_hash_delete_entry(list, obj);
5854 return (*p->func)(p->obj, p->arg, FALSE);
5875 p.list = recursive_list_access(sym);
5879 outermost = outer && !recursive_check(p.list,
ID2SYM(recursive_key), 0);
5881 if (recursive_check(p.list, p.obj, pairid)) {
5882 if (outer && !outermost) {
5885 return (*func)(obj, arg, TRUE);
5888 enum ruby_tag_type state;
5893 recursive_push(p.list,
ID2SYM(recursive_key), 0);
5894 recursive_push(p.list, p.obj, p.pairid);
5895 result = rb_catch_protect(p.list, exec_recursive_i, (
VALUE)&p, &state);
5896 if (!recursive_pop(p.list, p.obj, p.pairid))
goto invalid;
5897 if (!recursive_pop(p.list,
ID2SYM(recursive_key), 0))
goto invalid;
5898 if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
5899 if (result == p.list) {
5900 result = (*func)(obj, arg, TRUE);
5905 recursive_push(p.list, p.obj, p.pairid);
5906 EC_PUSH_TAG(GET_EC());
5907 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
5908 ret = (*func)(obj, arg, FALSE);
5911 if (!recursive_pop(p.list, p.obj, p.pairid)) {
5914 if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
5923 "for %+"PRIsVALUE
" in %+"PRIsVALUE,
5947 return exec_recursive(func, obj, rb_memory_id(paired_obj), arg, 0,
rb_frame_last_func());
5965 return exec_recursive(func, obj, 0, arg, 1, mid);
5977 return exec_recursive(func, obj, rb_memory_id(paired_obj), arg, 1,
rb_frame_last_func());
5989rb_thread_backtrace_m(
int argc,
VALUE *argv,
VALUE thval)
5991 return rb_vm_thread_backtrace(argc, argv, thval);
6006rb_thread_backtrace_locations_m(
int argc,
VALUE *argv,
VALUE thval)
6008 return rb_vm_thread_backtrace_locations(argc, argv, thval);
6012Init_Thread_Mutex(
void)
6102 rb_vm_register_special_exception(ruby_error_stream_closed,
rb_eIOError,
6103 "stream closed in another thread");
6105 cThGroup = rb_define_class(
"ThreadGroup",
rb_cObject);
6112 const char * ptr = getenv(
"RUBY_THREAD_TIMESLICE");
6115 long quantum = strtol(ptr, NULL, 0);
6116 if (quantum > 0 && !(SIZEOF_LONG > 4 && quantum > UINT32_MAX)) {
6117 thread_default_quantum_ms = (uint32_t)quantum;
6120 fprintf(stderr,
"Ignored RUBY_THREAD_TIMESLICE=%s\n", ptr);
6125 th->thgroup = th->ractor->thgroup_default =
rb_obj_alloc(cThGroup);
6126 rb_define_const(cThGroup,
"Default", th->thgroup);
6136#ifdef HAVE_PTHREAD_NP_H
6137 VM_ASSERT(TH_SCHED(th)->running == th);
6144 th->pending_interrupt_queue_checked = 0;
6149 rb_thread_create_timer_thread();
6165#ifdef NON_SCALAR_THREAD_ID
6166 #define thread_id_str(th) (NULL)
6168 #define thread_id_str(th) ((void *)(uintptr_t)(th)->nt->thread_id)
6177 rb_str_catf(msg,
"\n%d threads, %d sleeps current:%p main thread:%p\n",
6178 rb_ractor_living_thread_num(r), rb_ractor_sleeper_thread_num(r),
6179 (
void *)GET_THREAD(), (
void *)r->threads.main);
6181 ccan_list_for_each(&r->threads.set, th, lt_node) {
6182 rb_str_catf(msg,
"* %+"PRIsVALUE
"\n rb_thread_t:%p "
6184 th->self, (
void *)th, th->nt ? thread_id_str(th) :
"N/A", th->ec->interrupt_flag);
6186 if (th->locking_mutex) {
6187 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
6188 rb_str_catf(msg,
" mutex:%llu cond:%"PRIuSIZE,
6189 (
unsigned long long)mutex->ec_serial, rb_mutex_num_waiting(mutex));
6195 rb_str_catf(msg,
"\n depended by: tb_thread_id:%p", (
void *)list->thread);
6199 rb_str_catf(msg,
"\n ");
6200 rb_str_concat(msg,
rb_ary_join(rb_ec_backtrace_str_ary(th->ec, RUBY_BACKTRACE_START, RUBY_ALL_BACKTRACE_LINES), sep));
6201 rb_str_catf(msg,
"\n");
6208 if (GET_THREAD()->vm->thread_ignore_deadlock)
return;
6210 if (r->threads.sched.readyq_cnt > 0)
return;
6212 int sleeper_num = rb_ractor_sleeper_thread_num(r);
6213 int ltnum = rb_ractor_living_thread_num(r);
6215 if (ltnum > sleeper_num)
return;
6216 if (ltnum < sleeper_num) rb_bug(
"sleeper must not be more than vm_living_thread_num(vm)");
6221 ccan_list_for_each(&r->threads.set, th, lt_node) {
6222 if (th->status != THREAD_STOPPED_FOREVER || RUBY_VM_INTERRUPTED(th->ec)) {
6225 else if (th->locking_mutex) {
6226 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
6227 if (mutex->ec_serial == rb_ec_serial(th->ec) || (!mutex->ec_serial && !ccan_list_empty(&mutex->waitq))) {
6238 argv[1] =
rb_str_new2(
"No live threads left. Deadlock?");
6239 debug_deadlock_check(r, argv[1]);
6240 rb_ractor_sleeper_threads_dec(GET_RACTOR());
6241 rb_threadptr_raise(r->threads.main, 2, argv);
6249 VALUE coverage = rb_iseq_coverage(CFP_ISEQ(cfp));
6254 VM_ASSERT(line >= 0);
6258 if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
6259 rb_iseq_clear_event_flags(CFP_ISEQ(cfp), CFP_PC(cfp) - ISEQ_BODY(CFP_ISEQ(cfp))->iseq_encoded - 1, RUBY_EVENT_COVERAGE_LINE);
6280 VALUE coverage = rb_iseq_coverage(CFP_ISEQ(cfp));
6284 long pc = CFP_PC(cfp) - ISEQ_BODY(CFP_ISEQ(cfp))->iseq_encoded - 1;
6299 VALUE path, beg_pos_lineno, beg_pos_column, end_pos_lineno, end_pos_column;
6301 if (!me->def)
return NULL;
6304 switch (me->def->type) {
6305 case VM_METHOD_TYPE_ISEQ: {
6308 path = rb_iseq_path(iseq);
6309 beg_pos_lineno =
INT2FIX(loc->code_location.beg_pos.lineno);
6310 beg_pos_column =
INT2FIX(loc->code_location.beg_pos.column);
6311 end_pos_lineno =
INT2FIX(loc->code_location.end_pos.lineno);
6312 end_pos_column =
INT2FIX(loc->code_location.end_pos.column);
6315 case VM_METHOD_TYPE_BMETHOD: {
6316 const rb_iseq_t *iseq = rb_proc_get_iseq(me->def->body.bmethod.proc, 0);
6319 rb_iseq_check(iseq);
6320 path = rb_iseq_path(iseq);
6321 loc = &ISEQ_BODY(iseq)->location;
6322 beg_pos_lineno =
INT2FIX(loc->code_location.beg_pos.lineno);
6323 beg_pos_column =
INT2FIX(loc->code_location.beg_pos.column);
6324 end_pos_lineno =
INT2FIX(loc->code_location.end_pos.lineno);
6325 end_pos_column =
INT2FIX(loc->code_location.end_pos.column);
6330 case VM_METHOD_TYPE_ALIAS:
6331 me = me->def->body.alias.original_me;
6333 case VM_METHOD_TYPE_REFINED:
6334 me = me->def->body.refined.orig_me;
6335 if (!me)
return NULL;
6346 if (resolved_location) {
6347 resolved_location[0] = path;
6348 resolved_location[1] = beg_pos_lineno;
6349 resolved_location[2] = beg_pos_column;
6350 resolved_location[3] = end_pos_lineno;
6351 resolved_location[4] = end_pos_column;
6357 rb_coverage_method_callback *callback;
6372 FIX2LONG(location[1]) <= 0)
return false;
6374 out->owner = me->owner;
6375 out->method_id =
ID2SYM(me->def->original_id);
6376 out->path = location[0];
6377 out->first_lineno = location[1];
6378 out->first_column = location[2];
6379 out->last_lineno = location[3];
6380 out->last_column = location[4];
6390 if (rb_coverage_method_data_of((
VALUE)me, count, &method)) {
6391 arg->callback(&method, arg->data);
6413rb_coverage_each_method(rb_coverage_method_callback callback,
void *data)
6416 VALUE me_set = GET_VM()->me_set;
6417 VALUE cme2counter = GET_VM()->cme2counter;
6419 if (
RTEST(me_set)) {
6422 if (
RTEST(cme2counter)) {
6436 me = rb_resolve_me_location(me, 0);
6439 rcount = rb_hash_aref(cme2counter, (
VALUE) me);
6456 if (!
RTEST(GET_VM()->coverages))
return;
6457 if (!(GET_VM()->coverage_mode & COVERAGE_TARGET_METHODS))
return;
6459 VALUE me_set = GET_VM()->me_set;
6460 if (!
RTEST(me_set))
return;
6462 if (rb_resolve_me_location(me, 0) == me) {
6468rb_get_coverages(
void)
6470 return GET_VM()->coverages;
6474rb_get_coverage_mode(
void)
6476 return GET_VM()->coverage_mode;
6480rb_set_coverages(
VALUE coverages,
int mode,
VALUE cme2counter,
VALUE me_set)
6482 GET_VM()->coverages = coverages;
6483 GET_VM()->cme2counter = cme2counter;
6484 GET_VM()->me_set = me_set;
6485 GET_VM()->coverage_mode = mode;
6489rb_resume_coverages(
void)
6491 int mode = GET_VM()->coverage_mode;
6492 VALUE cme2counter = GET_VM()->cme2counter;
6493 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);
6494 if (mode & COVERAGE_TARGET_BRANCHES) {
6495 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);
6497 if (mode & COVERAGE_TARGET_METHODS) {
6503rb_suspend_coverages(
void)
6506 if (GET_VM()->coverage_mode & COVERAGE_TARGET_BRANCHES) {
6509 if (GET_VM()->coverage_mode & COVERAGE_TARGET_METHODS) {
6516rb_reset_coverages(
void)
6518 rb_clear_coverages();
6519 rb_iseq_remove_coverage_all();
6520 GET_VM()->coverages =
Qfalse;
6521 GET_VM()->cme2counter =
Qnil;
6522 GET_VM()->me_set =
Qnil;
6526rb_default_coverage(
int n)
6528 VALUE coverage = rb_ary_hidden_new_fill(3);
6530 int mode = GET_VM()->coverage_mode;
6532 if (mode & COVERAGE_TARGET_LINES) {
6535 RARRAY_ASET(coverage, COVERAGE_INDEX_LINES, lines);
6537 if (mode & COVERAGE_TARGET_BRANCHES) {
6538 branches = rb_ary_hidden_new_fill(2);
6560 VALUE structure = rb_hash_new();
6566 RARRAY_ASET(coverage, COVERAGE_INDEX_BRANCHES, branches);
6572uninterruptible_exit(
VALUE v)
6575 rb_ary_pop(cur_th->pending_interrupt_mask_stack);
6577 cur_th->pending_interrupt_queue_checked = 0;
6578 if (!rb_threadptr_pending_interrupt_empty_p(cur_th)) {
6579 RUBY_VM_SET_INTERRUPT(cur_th->ec);
6587 VALUE interrupt_mask = rb_ident_hash_new();
6590 rb_hash_aset(interrupt_mask,
rb_cObject, sym_never);
6592 rb_ary_push(cur_th->pending_interrupt_mask_stack, interrupt_mask);
6596 RUBY_VM_CHECK_INTS(cur_th->ec);
6603 VM_ASSERT(th->specific_storage == NULL);
6605 if (UNLIKELY(specific_key_count > 0)) {
6606 th->specific_storage =
ZALLOC_N(
void *, RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6610rb_internal_thread_specific_key_t
6615 if (specific_key_count == 0 && vm->ractor.cnt > 1) {
6616 rb_raise(
rb_eThreadError,
"The first rb_internal_thread_specific_key_create() is called with multiple ractors");
6618 else if (specific_key_count > RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX) {
6619 rb_raise(
rb_eThreadError,
"rb_internal_thread_specific_key_create() is called more than %d times", RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6622 rb_internal_thread_specific_key_t key = specific_key_count++;
6629 ccan_list_for_each(&cr->threads.set, th, lt_node) {
6630 thread_specific_storage_alloc(th);
6643 VM_ASSERT(rb_thread_ptr(thread_val) == th);
6644 VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6645 VM_ASSERT(th->specific_storage);
6647 return th->specific_storage[key];
6656 VM_ASSERT(rb_thread_ptr(thread_val) == th);
6657 VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6658 VM_ASSERT(th->specific_storage);
6660 th->specific_storage[key] = data;
6666 struct ccan_list_node node;
6668 rb_interrupt_exec_func_t *func;
6670 enum rb_interrupt_exec_flag flags;
6674rb_threadptr_interrupt_exec_task_mark(
rb_thread_t *th)
6678 ccan_list_for_each(&th->interrupt_exec_tasks, task, node) {
6679 if (task->flags & rb_interrupt_exec_flag_value_data) {
6680 rb_gc_mark((
VALUE)task->data);
6688rb_threadptr_interrupt_exec(
rb_thread_t *th, rb_interrupt_exec_func_t *func,
void *data,
enum rb_interrupt_exec_flag flags)
6700 ccan_list_add_tail(&th->interrupt_exec_tasks, &task->node);
6701 threadptr_set_interrupt_locked(th,
true);
6718 RUBY_DEBUG_LOG(
"task:%p", task);
6721 if (task->flags & rb_interrupt_exec_flag_new_thread) {
6725 (*task->func)(task->data);
6753 rb_interrupt_exec_func_t *func,
void *data,
enum rb_interrupt_exec_flag flags)
6755 RUBY_DEBUG_LOG(
"flags:%d", (
int)flags);
6758 rb_threadptr_interrupt_exec(main_th, func, data, flags | rb_interrupt_exec_flag_new_thread);
#define RUBY_ASSERT_ALWAYS(expr,...)
A variant of RUBY_ASSERT that does not interface with RUBY_DEBUG.
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
#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().
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 Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
#define ID2SYM
Old name of RB_ID2SYM.
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
#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.
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
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_eInterrupt
Interrupt 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_cObject
Object class.
VALUE rb_obj_alloc(VALUE klass)
Allocates an instance of the given class.
VALUE rb_cInteger
Module class.
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
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.
VALUE rb_fiber_scheduler_blocking_operation_wait(VALUE scheduler, void *(*function)(void *), void *data, rb_unblock_function_t *unblock_function, void *data2, int flags, struct rb_fiber_scheduler_blocking_operation_state *state)
Defer the execution of the passed function to the scheduler.
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_fiber_interrupt(VALUE scheduler, VALUE target, VALUE exception)
Interrupt a target by raising an exception.
VALUE rb_fiber_scheduler_yield(VALUE scheduler)
Yield to the scheduler, to be resumed on the next scheduling cycle.
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_current_for_threadptr(struct rb_thread_struct *thread)
Identical to rb_fiber_scheduler_current_for_thread(), except it expects a threadptr instead of a thre...
VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber)
Wakes up a fiber previously blocked using rb_fiber_scheduler_block().
VALUE rb_ary_shift(VALUE ary)
Destructively deletes an element from the beginning of the passed array and returns what was deleted.
VALUE rb_ary_dup(VALUE ary)
Duplicates an array.
VALUE rb_ary_delete_at(VALUE ary, long pos)
Destructively removes an element which resides at the specific index of the passed array.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_pop(VALUE ary)
Destructively deletes an element from the end of the passed array and returns what was deleted.
VALUE rb_ary_hidden_new(long capa)
Allocates a hidden (no class) empty array.
VALUE rb_ary_clear(VALUE ary)
Destructively removes everything form an array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
VALUE rb_ary_join(VALUE ary, VALUE sep)
Recursively stringises the elements of the passed array, flattens that result, then joins the sequenc...
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_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
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)
This function is now a no-op.
void rb_thread_wait_for(struct timeval time)
Identical to rb_thread_sleep(), except it takes struct timeval instead.
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_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.
ID rb_to_id(VALUE str)
Identical to rb_intern_str(), except it tries to convert the parameter object to an instance of rb_cS...
int capa
Designed capacity of the buffer.
#define RB_IO_POINTER(obj, fp)
Queries the underlying IO pointer.
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 processing interrupts after the given function retur...
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.
#define RB_NOGVL_PENDING_INTR_FAIL
Passing this flag to rb_nogvl() prevents it from entering the blocking region if the current thread h...
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.
#define RB_NOGVL_OFFLOAD_SAFE
Passing this flag to rb_nogvl() indicates that the passed function is safe to offload to a background...
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.
#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 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 DATA_PTR(obj)
Convenient casting macro for backward compatibility.
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a 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...
#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.
int ruby_snprintf(char *str, size_t n, char const *fmt,...)
Our own locale-insensitive version of snprintf(3).
#define RB_PASS_CALLED_KEYWORDS
Pass keywords if current method is called with keywords, useful for argument delegation.
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.
This is the struct that holds necessary info for a struct.
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.
Ruby's IO, metadata and buffers.
VALUE self
The IO's Ruby level counterpart.
struct ccan_list_head blocking_operations
Threads that are performing a blocking operation without the GVL using this IO.
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_cond_initialize(rb_nativethread_cond_t *cond)
Fills the passed condition variable with an initial value.
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.
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.