Ruby
2.7.2p137(2020-10-01revision5445e0435260b449decf2ac16f9d09bae3cafe72)
|
Go to the documentation of this file.
60 #undef _FORTIFY_SOURCE
61 #undef __USE_FORTIFY_LEVEL
62 #define __USE_FORTIFY_LEVEL 0
66 #include "ruby/config.h"
85 #ifndef USE_NATIVE_THREAD_PRIORITY
86 #define USE_NATIVE_THREAD_PRIORITY 0
87 #define RUBY_THREAD_PRIORITY_MAX 3
88 #define RUBY_THREAD_PRIORITY_MIN -3
92 #define THREAD_DEBUG 0
95 static VALUE rb_cThreadShield;
97 static VALUE sym_immediate;
98 static VALUE sym_on_blocking;
99 static VALUE sym_never;
106 #define THREAD_LOCAL_STORAGE_INITIALISED FL_USER13
107 #define THREAD_LOCAL_STORAGE_INITIALISED_P(th) RB_FL_TEST_RAW((th), THREAD_LOCAL_STORAGE_INITIALISED)
110 rb_thread_local_storage(
VALUE thread)
120 static void sleep_forever(
rb_thread_t *th,
unsigned int fl);
121 static void rb_thread_sleep_deadly_allow_spurious_wakeup(
void);
123 static void rb_check_deadlock(
rb_vm_t *vm);
124 static int rb_threadptr_pending_interrupt_empty_p(
const rb_thread_t *th);
125 static const char *thread_status_name(
rb_thread_t *th,
int detail);
127 NORETURN(
static void async_bug_fd(
const char *mesg,
int errno_arg,
int fd));
128 static int consume_communication_pipe(
int fd);
129 static int check_signals_nogvl(
rb_thread_t *,
int sigwait_fd);
132 #define eKillSignal INT2FIX(0)
133 #define eTerminateSignal INT2FIX(1)
134 static volatile int system_working = 1;
150 #define THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
157 static void unblock_function_clear(
rb_thread_t *th);
163 #define RB_GC_SAVE_MACHINE_CONTEXT(th) \
165 FLUSH_REGISTER_WINDOWS; \
166 setjmp((th)->ec->machine.regs); \
167 SET_MACHINE_STACK_END(&(th)->ec->machine.stack_end); \
170 #define GVL_UNLOCK_BEGIN(th) do { \
171 RB_GC_SAVE_MACHINE_CONTEXT(th); \
174 #define GVL_UNLOCK_END(th) \
175 gvl_acquire(th->vm, th); \
176 rb_thread_set_current(th); \
180 #ifdef HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P
181 #define only_if_constant(expr, notconst) __builtin_choose_expr(__builtin_constant_p(expr), (expr), (notconst))
183 #define only_if_constant(expr, notconst) (__builtin_constant_p(expr) ? (expr) : (notconst))
186 #define only_if_constant(expr, notconst) notconst
188 #define BLOCKING_REGION(th, exec, ubf, ubfarg, fail_if_interrupted) do { \
189 struct rb_blocking_region_buffer __region; \
190 if (blocking_region_begin(th, &__region, (ubf), (ubfarg), fail_if_interrupted) || \
192 !only_if_constant(fail_if_interrupted, TRUE)) { \
194 blocking_region_end(th, &__region); \
202 #define RUBY_VM_CHECK_INTS_BLOCKING(ec) vm_check_ints_blocking(ec)
208 if (
LIKELY(rb_threadptr_pending_interrupt_empty_p(th))) {
219 vm_living_thread_num(
const rb_vm_t *vm)
229 #if defined(HAVE_POLL)
230 # if defined(__linux__)
233 # if defined(__FreeBSD_version) && __FreeBSD_version >= 1100000
236 # define POLLERR_SET (POLLHUP | POLLERR)
245 *rel = rb_timeval2hrtime(timeout);
255 #ifdef HAVE_VA_ARGS_MACRO
256 void rb_thread_debug(
const char *file,
int line,
const char *
fmt, ...);
257 #define thread_debug(...) rb_thread_debug(__FILE__, __LINE__, __VA_ARGS__)
258 #define POSITION_FORMAT "%s:%d:"
259 #define POSITION_ARGS ,file, line
261 void rb_thread_debug(
const char *
fmt, ...);
262 #define thread_debug rb_thread_debug
263 #define POSITION_FORMAT
264 #define POSITION_ARGS
267 # ifdef NON_SCALAR_THREAD_ID
268 #define fill_thread_id_string ruby_fill_thread_id_string
277 for (
i = 0;
i <
sizeof(thid);
i++) {
278 # ifdef LITTLE_ENDIAN
279 size_t j =
sizeof(thid) -
i - 1;
283 unsigned char c = (
unsigned char)((
char *)&thid)[j];
290 # define fill_thread_id_str(th) fill_thread_id_string((th)->thread_id, (th)->thread_id_string)
291 # define thread_id_str(th) ((th)->thread_id_string)
292 # define PRI_THREAD_ID "s"
295 # if THREAD_DEBUG < 0
296 static int rb_thread_debug_enabled;
307 rb_thread_s_debug(
void)
309 return INT2NUM(rb_thread_debug_enabled);
323 rb_thread_debug_enabled =
RTEST(val) ?
NUM2INT(val) : 0;
327 # define rb_thread_debug_enabled THREAD_DEBUG
330 #define thread_debug if(0)printf
333 #ifndef fill_thread_id_str
334 # define fill_thread_id_string(thid, buf) ((void *)(uintptr_t)(thid))
335 # define fill_thread_id_str(th) (void)0
336 # define thread_id_str(th) ((void *)(uintptr_t)(th)->thread_id)
337 # define PRI_THREAD_ID "p"
341 static void timer_thread_function(
void);
345 ubf_sigwait(
void *ignore)
353 #define DEBUG_OUT() \
354 WaitForSingleObject(&debug_mutex, INFINITE); \
355 printf(POSITION_FORMAT"%#lx - %s" POSITION_ARGS, GetCurrentThreadId(), buf); \
357 ReleaseMutex(&debug_mutex);
359 #elif defined(HAVE_PTHREAD_H)
362 #define DEBUG_OUT() \
363 pthread_mutex_lock(&debug_mutex); \
364 printf(POSITION_FORMAT"%"PRI_THREAD_ID" - %s" POSITION_ARGS, \
365 fill_thread_id_string(pthread_self(), thread_id_string), buf); \
367 pthread_mutex_unlock(&debug_mutex);
370 #error "unsupported thread type"
378 #ifndef BUSY_WAIT_SIGNALS
379 # define BUSY_WAIT_SIGNALS (0)
383 # define USE_EVENTFD (0)
387 static int debug_mutex_initialized = 1;
393 const char *file,
int line,
395 const char *
fmt, ...)
399 #ifdef NON_SCALAR_THREAD_ID
403 if (!rb_thread_debug_enabled)
return;
405 if (debug_mutex_initialized == 1) {
406 debug_mutex_initialized = 0;
460 if (fail_if_interrupted) {
491 rb_threadptr_interrupt_common(
rb_thread_t *th,
int trap)
512 rb_threadptr_interrupt_common(th, 0);
518 rb_threadptr_interrupt_common(th, 1);
527 if (th != main_thread) {
536 thread_debug(
"terminate_all: main thread (%p)\n", (
void *)th);
553 err = rb_mutex_unlock_th(mutex, th);
564 volatile int sleeping = 0;
567 rb_bug(
"rb_thread_terminate_all: called by child thread (%p, %p)",
577 thread_debug(
"rb_thread_terminate_all (main thread: %p)\n", (
void *)th);
578 terminate_all(vm, th);
580 while (vm_living_thread_num(vm) > 1) {
587 native_sleep(th, &rel);
609 thread_cleanup_func_before_exec(
void *th_ptr)
620 thread_cleanup_func(
void *th_ptr,
int atfork)
625 thread_cleanup_func_before_exec(th_ptr);
640 native_thread_destroy(th);
649 native_thread_init_stack(th);
655 const VALUE *ep = vm_proc_ep(proc);
668 native_set_thread_name(th);
674 const VALUE *args_ptr;
684 vm_check_ints_blocking(th->
ec);
722 rb_bug(
"thread_start_func_2 must not be used for main thread");
731 gvl_acquire(th->
vm, th);
737 ruby_thread_set_native(th);
740 thread_debug(
"thread start (get lock): %p\n", (
void *)th);
741 rb_thread_set_current(th);
758 rb_str_cat_cstr(mesg,
" terminated with exception (report_on_exception is true):\n");
782 rb_threadptr_raise(main_th, 1, &errinfo);
790 rb_bug(
"thread_start_func_2: locking_mutex must not be set (%p:%"PRIxVALUE")",
791 (
void *)th, th->locking_mutex);
795 rb_vm_living_threads_remove(th->
vm, th);
810 join_list = join_list->
next;
814 rb_check_deadlock(th->
vm);
819 thread_cleanup_func(th,
FALSE);
835 "can't start a new thread (frozen ThreadGroup)");
853 th->
priority = current_th->priority;
854 th->
thgroup = current_th->thgroup;
864 err = native_thread_create(th);
869 rb_vm_living_threads_insert(th->
vm, th);
873 #define threadptr_initialized(th) ((th)->invoke_type != thread_invoke_type_none)
905 th = rb_thread_ptr(thread);
949 else if (th->
invoke_type != thread_invoke_type_none) {
950 VALUE loc = threadptr_invoke_proc_location(th);
961 return thread_create_core(thread, args,
NULL);
987 if ((*p)->th == th) {
1013 rb_check_deadlock(th->
vm);
1014 native_sleep(th, 0);
1018 if (hrtime_update_expire(p->
limit, end)) {
1024 native_sleep(th, p->
limit);
1040 if (th == target_th) {
1043 if (
GET_VM()->main_thread == target_th) {
1047 arg.target = target_th;
1060 remove_from_join_list, (
VALUE)&
arg)) {
1084 rb_bug(
"thread_join: THROW_DATA should not reach here.");
1091 return target_th->
self;
1156 return thread_join(rb_thread_ptr(
self), to);
1174 thread_value(
VALUE self)
1192 #define TIMESPEC_SEC_MAX TIMET_MAX
1193 #define TIMESPEC_SEC_MIN TIMET_MIN
1201 if (TIMESPEC_SEC_MAX_PLUS_ONE <= d) {
1216 #if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
1234 return rb_timespec2hrtime(&ts);
1247 while (th->
status == status) {
1250 rb_check_deadlock(th->
vm);
1252 native_sleep(th, 0);
1256 woke = vm_check_ints_blocking(th->
ec);
1260 th->
status = prev_status;
1268 #if defined(__GNUC__) && __GNUC__ == 7 && __GNUC_MINOR__ <= 3
1272 #define PRIu64 PRI_64_PREFIX "u"
1284 if (now > end)
return 1;
1288 *timeout = end - now;
1303 native_sleep(th, &rel);
1304 woke = vm_check_ints_blocking(th->
ec);
1307 if (hrtime_update_expire(&rel, end))
1310 th->
status = prev_status;
1334 native_sleep(th, 0);
1336 th->
status = prev_status;
1340 rb_thread_sleep_deadly_allow_spurious_wakeup(
void)
1342 thread_debug(
"rb_thread_sleep_deadly_allow_spurious_wakeup\n");
1391 rb_thread_schedule_limits(
uint32_t limits_us)
1400 gvl_yield(th->
vm, th);
1401 rb_thread_set_current(th);
1410 rb_thread_schedule_limits(0);
1421 if (unblock_function_set(th, ubf,
arg, fail_if_interrupted)) {
1424 thread_debug(
"enter blocking region (%p)\n", (
void *)th);
1426 gvl_release(th->
vm);
1438 unblock_function_clear(th);
1440 unregister_ubf_list(th);
1442 gvl_acquire(th->
vm, th);
1443 rb_thread_set_current(th);
1444 thread_debug(
"leave blocking region (%p)\n", (
void *)th);
1459 int saved_errno = 0;
1466 else if (ubf && vm_living_thread_num(th->
vm) == 1) {
1471 ubf_th = rb_thread_start_unblock_thread();
1477 saved_errno =
errno;
1490 errno = saved_errno;
1591 return rb_nogvl(func, data1, ubf, data2, 0);
1599 volatile int saved_errno = 0;
1604 wfd.
th = rb_ec_thread_ptr(ec);
1611 saved_errno = errno;
1612 }, ubf_select, wfd.
th,
FALSE);
1628 errno = saved_errno;
1675 fprintf(
stderr,
"[BUG] rb_thread_call_with_gvl() is called by non-ruby thread\n");
1683 rb_bug(
"rb_thread_call_with_gvl: called by a thread which has GVL.");
1686 blocking_region_end(th, brb);
1690 int released = blocking_region_begin(th, brb, prev_unblock.
func, prev_unblock.
arg,
FALSE);
1764 threadptr_check_pending_interrupt_queue(
rb_thread_t *th)
1787 for (
i=0;
i<mask_stack_len;
i++) {
1788 mask = mask_stack[mask_stack_len-(
i+1)];
1802 if (
sym == sym_immediate) {
1805 else if (
sym == sym_on_blocking) {
1808 else if (
sym == sym_never) {
1822 rb_threadptr_pending_interrupt_empty_p(
const rb_thread_t *th)
1851 switch (mask_timing) {
1870 if (rb_threadptr_pending_interrupt_empty_p(th)) {
1878 threadptr_pending_interrupt_active_p(
rb_thread_t *th)
1889 if (rb_threadptr_pending_interrupt_empty_p(th)) {
1901 if (val != sym_immediate && val != sym_on_blocking && val != sym_never) {
2020 rb_thread_s_handle_interrupt(
VALUE self,
VALUE mask_arg)
2040 if (!rb_threadptr_pending_interrupt_empty_p(th)) {
2052 if (!rb_threadptr_pending_interrupt_empty_p(th)) {
2079 rb_thread_t *target_th = rb_thread_ptr(target_thread);
2084 if (rb_threadptr_pending_interrupt_empty_p(target_th)) {
2092 if (rb_threadptr_pending_interrupt_include_p(target_th,
err)) {
2189 }
while (
old != interrupt);
2197 int postponed_job_interrupt = 0;
2202 while ((interrupt = threadptr_get_interrupts(th)) != 0) {
2204 int timer_interrupt;
2205 int pending_interrupt;
2213 if (postponed_job_interrupt) {
2222 if (sigwait_fd >= 0) {
2223 (
void)consume_communication_pipe(sigwait_fd);
2236 if (pending_interrupt && threadptr_pending_interrupt_active_p(th)) {
2247 rb_threadptr_to_kill(th);
2262 if (timer_interrupt) {
2263 uint32_t limits_us = TIME_QUANTUM_USEC;
2277 rb_thread_schedule_limits(limits_us);
2300 if (rb_threadptr_dead(target_th)) {
2313 if (rb_threadptr_dead(target_th)) {
2370 if (wfd->
fd ==
fd) {
2390 list_head_init(&busy);
2423 threadptr_check_pending_interrupt_queue(target_th);
2424 rb_threadptr_raise(target_th,
argc,
argv);
2427 if (current_th == target_th) {
2461 rb_threadptr_to_kill(th);
2464 threadptr_check_pending_interrupt_queue(th);
2554 rb_threadptr_ready(target_th);
2600 "stopping only thread\n\tnote: use sleep to stop forever");
2737 rb_thread_s_abort_exc(
VALUE _)
2774 rb_thread_s_abort_exc_set(
VALUE self,
VALUE val)
2797 rb_thread_abort_exc(
VALUE thread)
2799 return rb_thread_ptr(thread)->abort_on_exception ?
Qtrue :
Qfalse;
2817 rb_thread_abort_exc_set(
VALUE thread,
VALUE val)
2819 rb_thread_ptr(thread)->abort_on_exception =
RTEST(val);
2867 rb_thread_s_report_exc(
VALUE _)
2904 rb_thread_s_report_exc_set(
VALUE self,
VALUE val)
2928 rb_thread_report_exc(
VALUE thread)
2930 return rb_thread_ptr(thread)->report_on_exception ?
Qtrue :
Qfalse;
2948 rb_thread_report_exc_set(
VALUE thread,
VALUE val)
2950 rb_thread_ptr(thread)->report_on_exception =
RTEST(val);
2968 VALUE group = rb_thread_ptr(thread)->thgroup;
2969 return group == 0 ?
Qnil : group;
2977 return th->
to_kill ?
"aborting" :
"run";
2979 if (detail)
return "sleep_forever";
3032 if (rb_threadptr_dead(target_th)) {
3062 rb_thread_alive_p(
VALUE thread)
3064 if (rb_threadptr_dead(rb_thread_ptr(thread))) {
3087 rb_thread_stop_p(
VALUE thread)
3091 if (rb_threadptr_dead(th)) {
3114 rb_thread_safe_level(
VALUE thread)
3116 rb_warn(
"Thread#safe_level will be removed in Ruby 3.0");
3128 rb_thread_getname(
VALUE thread)
3130 return rb_thread_ptr(thread)->name;
3171 rb_thread_to_s(
VALUE thread)
3178 status = thread_status_name(target_th,
TRUE);
3183 if ((loc = threadptr_invoke_proc_location(target_th)) !=
Qnil) {
3194 static ID recursive_key;
3199 if (
id == recursive_key) {
3206 if (local_storage !=
NULL &&
st_lookup(local_storage,
id, &val)) {
3218 return threadptr_local_aref(rb_thread_ptr(thread),
id);
3285 if (!
id)
return Qnil;
3314 if (block_given &&
argc == 2) {
3315 rb_warn(
"block supersedes default value argument");
3320 if (
id == recursive_key) {
3327 else if (block_given) {
3330 else if (
argc == 1) {
3341 if (
id == recursive_key) {
3349 if (!local_storage)
return Qnil;
3350 st_delete_wrap(local_storage,
id);
3354 if (local_storage ==
NULL) {
3370 return threadptr_local_aset(rb_thread_ptr(thread),
id, val);
3428 locals = rb_thread_local_storage(thread);
3450 locals = rb_thread_local_storage(thread);
3471 st_table *local_storage = rb_thread_ptr(
self)->ec->local_storage;
3473 if (!
id || local_storage ==
NULL) {
3494 return vm_living_thread_num(
GET_VM()) == 1;
3512 rb_thread_keys(
VALUE self)
3514 st_table *local_storage = rb_thread_ptr(
self)->ec->local_storage;
3517 if (local_storage) {
3518 st_foreach(local_storage, thread_keys_i, ary);
3548 rb_thread_variables(
VALUE thread)
3557 locals = rb_thread_local_storage(thread);
3590 locals = rb_thread_local_storage(thread);
3618 rb_thread_priority(
VALUE thread)
3620 return INT2NUM(rb_thread_ptr(thread)->priority);
3651 rb_thread_priority_set(
VALUE thread,
VALUE prio)
3656 #if USE_NATIVE_THREAD_PRIORITY
3658 native_thread_apply_priority(th);
3674 #if defined(NFDBITS) && defined(HAVE_RB_FD_INIT)
3768 if (
n >= fds->
maxfd)
return;
3775 if (
n >= fds->
maxfd)
return 0;
3818 return select(
n, r, w, e, timeout);
3821 #define rb_fd_no_init(fds) ((void)((fds)->fdset = 0), (void)((fds)->maxfd = 0))
3828 #define FD_ZERO(f) rb_fd_zero(f)
3829 #define FD_SET(i, f) rb_fd_set((i), (f))
3830 #define FD_CLR(i, f) rb_fd_clr((i), (f))
3831 #define FD_ISSET(i, f) rb_fd_isset((i), (f))
3833 #elif defined(_WIN32)
3864 for (
i = 0;
i < set->
fdset->fd_count;
i++) {
3865 if (set->
fdset->fd_array[
i] == s) {
3869 if (set->
fdset->fd_count >= (
unsigned)set->capa) {
3873 set->
fdset, set->capa,
sizeof(SOCKET),
sizeof(
unsigned int));
3875 set->
fdset->fd_array[set->
fdset->fd_count++] = s;
3883 #define FD_ZERO(f) rb_fd_zero(f)
3884 #define FD_SET(i, f) rb_fd_set((i), (f))
3885 #define FD_CLR(i, f) rb_fd_clr((i), (f))
3886 #define FD_ISSET(i, f) rb_fd_isset((i), (f))
3888 #define rb_fd_no_init(fds) (void)((fds)->fdset = 0)
3892 #ifndef rb_fd_no_init
3893 #define rb_fd_no_init(fds) (void)(fds)
3906 if (rel && hrtime_update_expire(rel, end)) {
3913 else if (*result == 0) {
3916 return !hrtime_update_expire(rel, end);
3937 select_set_free(
VALUE p)
3957 static const rb_hrtime_t quantum = TIME_QUANTUM_USEC * 1000;
3961 if (!orig || *orig > quantum)
3976 timeout_prepare(&to, &rel, &end, set->
timeout);
3977 #define restore_fdset(dst, src) \
3978 ((dst) ? rb_fd_dup(dst, src) : (void)0)
3979 #define do_select_update() \
3980 (restore_fdset(set->rset, &set->orig_rset), \
3981 restore_fdset(set->wset, &set->orig_wset), \
3982 restore_fdset(set->eset, &set->orig_eset), \
3990 const rb_hrtime_t *sto;
3993 sto = sigwait_timeout(set->th, set->sigwait_fd, to, &drained);
3994 if (!RUBY_VM_INTERRUPTED(set->th->ec)) {
3995 result = native_fd_select(set->max, set->rset, set->wset,
3997 rb_hrtime2timeval(&tv, sto), set->th);
3998 if (result < 0) lerrno = errno;
4007 (
void)check_signals_nogvl(set->
th, -1);
4018 return (
VALUE)result;
4022 rb_thread_wait_fd_rw(
int fd,
int read)
4027 thread_debug(
"rb_thread_wait_fd_rw(%d, %s)\n", fd,
read ?
"read" :
"write");
4038 thread_debug(
"rb_thread_wait_fd_rw(%d, %s): done\n", fd,
read ?
"read" :
"write");
4044 rb_thread_wait_fd_rw(fd, 1);
4050 rb_thread_wait_fd_rw(fd, 0);
4099 #define fd_init_copy(f) do { \
4101 rb_fd_resize(set.max - 1, set.f); \
4102 if (&set.orig_##f != set.f) { \
4103 rb_fd_init_copy(&set.orig_##f, set.f); \
4107 rb_fd_no_init(&set.orig_##f); \
4121 #define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
4122 #define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
4123 #define POLLEX_SET (POLLPRI)
4126 # define POLLERR_SET (0)
4135 struct pollfd fds[2];
4136 int result = 0, lerrno;
4146 list_add(&wfd.th->vm->waiting_fds, &wfd.wfd_node);
4150 timeout_prepare(&to, &rel, &end, timeout);
4152 fds[0].events = (
short)events;
4157 if (fds[1].
fd >= 0) {
4158 fds[1].events = POLLIN;
4170 const rb_hrtime_t *sto;
4173 sto = sigwait_timeout(wfd.th, fds[1].fd, to, &drained);
4174 if (!RUBY_VM_INTERRUPTED(wfd.th->ec)) {
4175 result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, sto), 0);
4176 if (result < 0) lerrno = errno;
4178 }, ubf, wfd.th,
TRUE);
4180 if (fds[1].
fd >= 0) {
4181 if (result > 0 && fds[1].revents) {
4183 (
void)check_signals_nogvl(wfd.th, fds[1].fd);
4185 (
void)check_signals_nogvl(wfd.th, -1);
4191 }
while (wait_retryable(&result, lerrno, to, end));
4204 if (fds[0].revents & POLLNVAL) {
4214 if (fds[0].revents & POLLIN_SET)
4216 if (fds[0].revents & POLLOUT_SET)
4218 if (fds[0].revents & POLLEX_SET)
4222 if (fds[0].revents & POLLERR_SET)
4304 #ifdef USE_CONSERVATIVE_STACK_END
4309 *stack_end_p = &stack_end;
4323 threadptr_trap_interrupt(mth);
4328 timer_thread_function(
void)
4339 async_bug_fd(
const char *mesg,
int errno_arg,
int fd)
4342 size_t n =
strlcpy(buff, mesg,
sizeof(buff));
4343 if (
n <
sizeof(buff)-3) {
4351 consume_communication_pipe(
int fd)
4357 static char buff[1024];
4371 result =
read(
fd, buff,
sizeof(buff));
4378 else if (result == 0) {
4381 else if (result < 0) {
4387 #if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
4392 async_bug_fd(
"consume_communication_pipe: read", e,
fd);
4399 check_signals_nogvl(
rb_thread_t *th,
int sigwait_fd)
4402 int ret = sigwait_fd >= 0 ? consume_communication_pipe(sigwait_fd) :
FALSE;
4403 ubf_wakeup_all_threads();
4419 if (TIMER_THREAD_CREATED_P() && native_stop_timer_thread()) {
4420 native_reset_timer_thread();
4427 native_reset_timer_thread();
4434 rb_thread_create_timer_thread();
4471 if (
RTEST(coverages)) {
4476 #if defined(HAVE_WORKING_FORK)
4490 rb_vm_living_threads_init(vm);
4491 rb_vm_living_threads_insert(vm, th);
4509 if (th != current_th) {
4510 rb_mutex_abandon_keeping_mutexes(th);
4511 rb_mutex_abandon_locking_mutex(th);
4512 thread_cleanup_func(th,
TRUE);
4521 rb_thread_atfork_internal(th, terminate_atfork_i);
4523 rb_fiber_atfork(th);
4535 if (th != current_th) {
4536 thread_cleanup_func_before_exec(th);
4544 rb_thread_atfork_internal(th, terminate_atfork_before_exec_i);
4564 thgroup_memsize(
const void *
ptr)
4566 return sizeof(
struct thgroup);
4729 "can't move from the enclosed thread group");
4740 thread_shield_mark(
void *
ptr)
4747 {thread_shield_mark, 0, 0,},
4757 #define GetThreadShieldPtr(obj) ((VALUE)rb_check_typeddata((obj), &thread_shield_data_type))
4758 #define THREAD_SHIELD_WAITING_MASK (((FL_USER19-1)&~(FL_USER0-1))|FL_USER19)
4759 #define THREAD_SHIELD_WAITING_SHIFT (FL_USHIFT)
4760 #define THREAD_SHIELD_WAITING_MAX (THREAD_SHIELD_WAITING_MASK>>THREAD_SHIELD_WAITING_SHIFT)
4762 static inline unsigned int
4763 rb_thread_shield_waiting(
VALUE b)
4769 rb_thread_shield_waiting_inc(
VALUE b)
4771 unsigned int w = rb_thread_shield_waiting(b);
4780 rb_thread_shield_waiting_dec(
VALUE b)
4782 unsigned int w = rb_thread_shield_waiting(b);
4792 VALUE thread_shield = thread_shield_alloc(rb_cThreadShield);
4794 return thread_shield;
4811 if (!mutex)
return Qfalse;
4812 m = mutex_ptr(mutex);
4814 rb_thread_shield_waiting_inc(
self);
4816 rb_thread_shield_waiting_dec(
self);
4819 return rb_thread_shield_waiting(
self) > 0 ?
Qnil :
Qfalse;
4823 thread_shield_get_mutex(
VALUE self)
4837 VALUE mutex = thread_shield_get_mutex(
self);
4839 return rb_thread_shield_waiting(
self) > 0 ?
Qtrue :
Qfalse;
4848 VALUE mutex = thread_shield_get_mutex(
self);
4851 return rb_thread_shield_waiting(
self) > 0 ?
Qtrue :
Qfalse;
4878 VALUE hash = threadptr_recursive_hash(th);
4882 threadptr_recursive_hash_set(th, hash);
4904 #if SIZEOF_LONG == SIZEOF_VOIDP
4905 #define OBJ_ID_EQL(obj_id, other) ((obj_id) == (other))
4906 #elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
4907 #define OBJ_ID_EQL(obj_id, other) (RB_TYPE_P((obj_id), T_BIGNUM) ? \
4908 rb_big_eql((obj_id), (other)) : ((obj_id) == (other)))
4914 if (paired_obj_id) {
4949 VALUE other_paired_obj = pair_list;
4971 if (pair_list ==
Qundef) {
5019 p.
list = recursive_list_access(
sym);
5023 outermost = outer && !recursive_check(p.list,
ID2SYM(recursive_key), 0);
5025 if (recursive_check(p.list, p.obj,
pairid)) {
5026 if (outer && !outermost) {
5037 recursive_push(p.list,
ID2SYM(recursive_key), 0);
5038 recursive_push(p.list, p.obj, p.pairid);
5040 if (!recursive_pop(p.list, p.obj, p.pairid))
goto invalid;
5041 if (!recursive_pop(p.list,
ID2SYM(recursive_key), 0))
goto invalid;
5043 if (result == p.list) {
5049 recursive_push(p.list, p.obj, p.pairid);
5055 if (!recursive_pop(p.list, p.obj, p.pairid)) {
5165 #define rb_intern(str) rb_intern_const(str)
5188 #if THREAD_DEBUG < 0
5234 "stream closed in another thread");
5248 recursive_key =
rb_intern(
"__recursive_key__");
5257 gvl_acquire(th->
vm, th);
5268 rb_thread_create_timer_thread();
5271 (
void)native_mutex_trylock;
5290 rb_str_catf(msg,
"\n%d threads, %d sleeps current:%p main thread:%p\n",
5296 if (th->locking_mutex) {
5297 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
5299 (
void *)mutex->
th, rb_mutex_num_waiting(mutex));
5304 rb_str_catf(msg,
"\n depended by: tb_thread_id:%p", (
void *)
list->th);
5315 rb_check_deadlock(
rb_vm_t *vm)
5320 if (vm_living_thread_num(vm) > vm->
sleeper)
return;
5321 if (vm_living_thread_num(vm) < vm->
sleeper)
rb_bug(
"sleeper must not be more than vm_living_thread_num(vm)");
5322 if (patrol_thread && patrol_thread !=
GET_THREAD())
return;
5343 debug_deadlock_check(vm,
argv[1]);
5402 VALUE path, beg_pos_lineno, beg_pos_column, end_pos_lineno, end_pos_column;
5420 rb_iseq_check(
iseq);
5447 if (resolved_location) {
5448 resolved_location[0] =
path;
5449 resolved_location[1] = beg_pos_lineno;
5450 resolved_location[2] = beg_pos_column;
5451 resolved_location[3] = end_pos_lineno;
5452 resolved_location[4] = end_pos_column;
5479 return GET_VM()->coverages;
5485 return GET_VM()->coverage_mode;
5491 GET_VM()->coverages = coverages;
5492 GET_VM()->coverage_mode = mode;
5523 int mode =
GET_VM()->coverage_mode;
#define RB_GC_SAVE_MACHINE_CONTEXT(th)
void rb_thread_reset_timer_thread(void)
void rb_reset_random_seed(void)
void rb_nativethread_lock_unlock(rb_nativethread_lock_t *lock)
void ruby_stop(int ex)
Calls ruby_cleanup() and exits the process.
void rb_threadptr_interrupt(rb_thread_t *th)
VALUE rb_to_hash_type(VALUE hash)
VALUE rb_str_concat(VALUE, VALUE)
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
VALUE rb_thread_shield_destroy(VALUE self)
#define TypedData_Make_Struct(klass, type, data_type, sval)
#define rb_fd_init_copy(d, s)
rb_execution_context_t * ruby_current_execution_context_ptr
void rb_ec_error_print(rb_execution_context_t *volatile ec, volatile VALUE errinfo)
VALUE rb_memory_id(VALUE obj)
int rb_ec_reset_raised(rb_execution_context_t *ec)
ID rb_check_id(volatile VALUE *)
Returns ID for the given name if it is interned already, or 0.
#define RB_HRTIME_PER_SEC
VALUE rb_exc_new(VALUE etype, const char *ptr, long len)
#define RUBY_VM_INTERRUPTED(ec)
int ruby_snprintf(char *str, size_t n, char const *fmt,...)
VALUE rb_thread_alloc(VALUE klass)
void rb_thread_terminate_all(void)
#define RUBY_VM_INTERRUPTED_ANY(ec)
int rb_thread_alone(void)
@ VM_METHOD_TYPE_REFINED
refinement
#define RB_PASS_CALLED_KEYWORDS
#define RUBY_ASSERT_ALWAYS(expr)
MJIT_FUNC_EXPORTED VALUE rb_vm_invoke_proc(rb_execution_context_t *ec, rb_proc_t *proc, int argc, const VALUE *argv, int kw_splat, VALUE passed_block_handler)
VALUE rb_ident_hash_new(void)
void rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th)
void rb_timespec_now(struct timespec *)
VALUE rb_vm_thread_backtrace_locations(int argc, const VALUE *argv, VALUE thval)
VALUE rb_ary_tmp_new_fill(long capa)
void rb_warn(const char *fmt,...)
int rb_block_given_p(void)
Determines if the current method is given a block.
@ ruby_error_stream_closed
rb_hrtime_t rb_hrtime_now(void)
#define RBASIC_CLEAR_CLASS(obj)
void rb_thread_sleep_interruptible(void)
void rb_clear_coverages(void)
void rb_gc_force_recycle(VALUE obj)
VALUE rb_to_symbol(VALUE name)
#define st_is_member(table, key)
@ RUBY_EVENT_HOOK_FLAG_RAW_ARG
#define RB_FL_SET_RAW(x, f)
VALUE rb_thread_shield_new(void)
VALUE rb_thread_kill(VALUE thread)
void rb_threadptr_signal_exit(rb_thread_t *th)
int rb_empty_keyword_given_p(void)
#define EXEC_EVENT_HOOK(ec_, flag_, self_, id_, called_id_, klass_, data_)
void rb_throw_obj(VALUE tag, VALUE value)
void rb_thread_sleep_deadly(void)
VALUE rb_hash_aref(VALUE hash, VALUE key)
st_table * st_init_numtable(void)
VALUE rb_exec_recursive_paired(VALUE(*func)(VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
#define COVERAGE_INDEX_BRANCHES
enum rb_thread_struct::@15 invoke_type
VALUE rb_thread_wakeup(VALUE thread)
rb_code_location_t code_location
int st_delete(st_table *tab, st_data_t *key, st_data_t *value)
VALUE rb_thread_local_aref(VALUE thread, ID id)
#define RB_TYPE_P(obj, type)
RUBY_EXTERN VALUE rb_cModule
void rb_thread_execute_interrupts(VALUE thval)
void rb_reset_coverages(void)
rb_encoding * rb_enc_get(VALUE obj)
#define rb_enc_asciicompat(enc)
void rb_obj_call_init_kw(VALUE obj, int argc, const VALUE *argv, int kw_splat)
#define OBJ_ID_EQL(obj_id, other)
void rb_gc_set_stack_end(VALUE **stack_end_p)
struct rb_mutex_struct * next_mutex
rb_nativethread_id_t thread_id
void rb_sigwait_fd_migrate(rb_vm_t *)
VALUE rb_iseq_path(const rb_iseq_t *iseq)
#define EC_JUMP_TAG(ec, st)
unsigned long long uint64_t
ID rb_frame_last_func(void)
Returns the ID of the last method in the call stack.
NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_start))
rb_iseq_location_t location
#define RB_PASS_EMPTY_KEYWORDS
VALUE rb_proc_location(VALUE self)
VALUE rb_thread_stop(void)
rb_method_refined_t refined
VALUE rb_hash_lookup2(VALUE hash, VALUE key, VALUE def)
int rb_thread_interrupted(VALUE thval)
const VALUE * rb_vm_ep_local_ep(const VALUE *ep)
void rb_thread_atfork_before_exec(void)
struct rb_method_entry_struct * orig_me
#define THROW_DATA_P(err)
void rb_define_singleton_method(VALUE obj, const char *name, VALUE(*func)(ANYARGS), int argc)
Defines a singleton method for obj.
int rb_signal_buff_size(void)
#define BUSY_WAIT_SIGNALS
void * rb_thread_call_without_gvl(void *(*func)(void *data), void *data1, rb_unblock_function_t *ubf, void *data2)
VALUE rb_make_exception(int argc, const VALUE *argv)
Make an Exception object from the list of arguments in a manner similar to Kernel#raise.
void rb_define_method(VALUE klass, const char *name, VALUE(*func)(ANYARGS), int argc)
#define RUBY_VM_CHECK_INTS(ec)
RUBY_EXTERN VALUE rb_eIOError
VALUE rb_ary_shift(VALUE ary)
union rb_thread_struct::@14 invoke_arg
rb_iseq_t * iseqptr
iseq pointer, should be separated from iseqval
#define RARRAY_ASET(a, i, v)
void * rb_nogvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2, int flags)
struct list_head waiting_fds
int rb_get_coverage_mode(void)
#define ISEQ_PC2BRANCHINDEX(iseq)
VALUE rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd)
struct rb_execution_context_struct::@13 machine
int st_insert(st_table *tab, st_data_t key, st_data_t value)
void rb_set_coverages(VALUE coverages, int mode, VALUE me2counter)
rb_method_bmethod_t bmethod
rb_execution_context_t * ec
VALUE rb_blocking_function_t(void *)
VALUE rb_ary_pop(VALUE ary)
struct list_node wfd_node
void rb_thread_wait_for(struct timeval time)
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
void rb_thread_fd_close(int fd)
unsigned int abort_on_exception
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
VALUE rb_iseq_coverage(const rb_iseq_t *iseq)
void rb_thread_atfork(void)
#define VM_BLOCK_HANDLER_NONE
#define RARRAY_LENINT(ary)
VALUE rb_mutex_lock(VALUE mutex)
#define rb_vm_register_special_exception(sp, e, m)
void ruby_thread_init_stack(rb_thread_t *th)
@ POSTPONED_JOB_INTERRUPT_MASK
#define RUBY_TYPED_DEFAULT_FREE
VALUE rb_hash_delete_entry(VALUE hash, VALUE key)
void rb_raise(VALUE exc, const char *fmt,...)
VALUE rb_ary_entry(VALUE ary, long offset)
VALUE rb_exec_recursive(VALUE(*func)(VALUE, VALUE, int), VALUE obj, VALUE arg)
VALUE rb_ivar_get(VALUE, ID)
VALUE rb_get_coverages(void)
void rb_thread_start_timer_thread(void)
if((ID)(DISPID) nameid !=nameid)
void * rb_xrealloc_mul_add(const void *p, size_t x, size_t y, size_t z)
VALUE rb_obj_class(VALUE)
Equivalent to Object#class in Ruby.
void rb_threadptr_check_signal(rb_thread_t *mth)
const VALUE * rb_vm_proc_local_ep(VALUE proc)
int rb_thread_fd_writable(int fd)
#define RUBY_INTERNAL_EVENT_SWITCH
RUBY_EXTERN char * strerror(int)
const rb_method_entry_t * rb_resolve_me_location(const rb_method_entry_t *me, VALUE resolved_location[5])
#define do_select_update()
#define RUBY_EVENT_COVERAGE_LINE
void * rb_thread_call_without_gvl2(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
#define RUBY_THREAD_PRIORITY_MAX
struct rb_thread_struct * main_thread
#define COVERAGE_TARGET_BRANCHES
void rb_add_event_hook2(rb_event_hook_func_t func, rb_event_flag_t events, VALUE data, rb_event_hook_flag_t hook_flag)
void rb_postponed_job_flush(rb_vm_t *vm)
#define GetThreadShieldPtr(obj)
void * blocking_region_buffer
#define thread_id_str(th)
enum rb_thread_status status
VALUE rb_ary_tmp_new(long capa)
void rb_nativethread_lock_initialize(rb_nativethread_lock_t *lock)
#define COVERAGE_TARGET_ONESHOT_LINES
#define THREAD_LOCAL_STORAGE_INITIALISED
VALUE rb_thread_create(VALUE(*fn)(void *), void *arg)
rb_atomic_t interrupt_flag
void rb_thread_schedule(void)
VALUE rb_thread_group(VALUE thread)
VALUE rb_hash_lookup(VALUE hash, VALUE key)
VALUE rb_ary_push(VALUE ary, VALUE item)
#define COMPILER_WARNING_POP
const VALUE special_exceptions[ruby_special_error_count]
#define RUBY_VM_SET_TIMER_INTERRUPT(ec)
#define RARRAY_AREF(a, i)
VALUE rb_mutex_unlock(VALUE mutex)
#define TypedData_Wrap_Struct(klass, data_type, sval)
void rb_sys_fail(const char *mesg)
#define BLOCKING_REGION(th, exec, ubf, ubfarg, fail_if_interrupted)
VALUE rb_thread_shield_release(VALUE self)
void rb_iseq_remove_coverage_all(void)
#define RBASIC_CLASS(obj)
#define COVERAGE_TARGET_METHODS
enum rb_thread_status prev_status
size_t thread_vm_stack_size
struct rb_method_entry_struct * original_me
VALUE rb_thread_main(void)
#define THREAD_SHIELD_WAITING_MASK
#define ALLOCA_N(type, n)
void rb_async_bug_errno(const char *mesg, int errno_arg)
void rb_unblock_function_t(void *)
RUBY_EXTERN size_t strlcpy(char *, const char *, size_t)
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
void rb_thread_sleep_forever(void)
#define rb_fd_resize(n, f)
#define COVERAGE_INDEX_LINES
void rb_frozen_error_raise(VALUE frozen_obj, const char *fmt,...)
void rb_hash_foreach(VALUE hash, rb_foreach_func *func, VALUE farg)
struct rb_encoding_entry * list
#define MEMZERO(p, type, n)
void ruby_sigchld_handler(rb_vm_t *)
STATIC_ASSERT(THREAD_SHIELD_WAITING_MAX, THREAD_SHIELD_WAITING_MAX<=UINT_MAX)
VALUE rb_str_new_frozen(VALUE)
unsigned int thread_abort_on_exception
struct timeval rb_time_timeval(VALUE time)
void(* rb_event_hook_func_t)(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
VALUE rb_default_coverage(int n)
void rb_thread_wakeup_timer_thread(int)
#define StringValueCStr(v)
#define GetProcPtr(obj, ptr)
#define COMPILER_WARNING_PUSH
RUBY_EXTERN VALUE rb_cThread
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
int rb_thread_fd_select(int max, rb_fdset_t *read, rb_fdset_t *write, rb_fdset_t *except, struct timeval *timeout)
VALUE rb_ary_clear(VALUE ary)
#define ACCESS_ONCE(type, x)
void rb_ec_initialize_vm_stack(rb_execution_context_t *ec, VALUE *stack, size_t size)
SOCKET rb_w32_get_osfhandle(int)
MJIT_FUNC_EXPORTED int rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
MJIT_STATIC const rb_callable_method_entry_t * rb_vm_frame_method_entry(const rb_control_frame_t *cfp)
int st_foreach(st_table *tab, st_foreach_callback_func *func, st_data_t arg)
rb_thread_list_t * join_list
RUBY_EXTERN VALUE rb_cObject
#define THREAD_SHIELD_WAITING_SHIFT
unsigned char buf[MIME_BUF_SIZE]
void rb_threadptr_pending_interrupt_clear(rb_thread_t *th)
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
#define TypedData_Get_Struct(obj, type, data_type, sval)
void rb_bug(const char *fmt,...)
#define RUBY_EVENT_THREAD_BEGIN
#define THREAD_LOCAL_STORAGE_INITIALISED_P(th)
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
RUBY_EXTERN void rb_write_error_str(VALUE mesg)
int rb_remove_event_hook(rb_event_hook_func_t func)
VALUE rb_sprintf(const char *format,...)
VALUE rb_thread_local_aset(VALUE thread, ID id, VALUE val)
#define THREAD_SHIELD_WAITING_MAX
VALUE rb_ary_join(VALUE ary, VALUE sep)
union rb_method_definition_struct::@0 body
void rb_fiber_close(rb_fiber_t *fiber)
#define threadptr_initialized(th)
@ RUBY_EVENT_HOOK_FLAG_SAFE
void rb_thread_sleep(int sec)
struct rb_unblock_callback unblock
VALUE rb_obj_alloc(VALUE)
Allocates an instance of klass.
char str[HTML_ESCAPE_MAX_LEN+1]
VALUE rb_ary_delete_at(VALUE ary, long pos)
VALUE pending_interrupt_mask_stack
void rb_thread_stop_timer_thread(void)
#define RARRAY_CONST_PTR(s)
#define RUBY_TYPED_FREE_IMMEDIATELY
int ruby_native_thread_p(void)
void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
#define MEMCPY(p1, p2, type, n)
VALUE rb_hash_aset(VALUE hash, VALUE key, VALUE val)
struct list_head living_threads
void rb_iseq_clear_event_flags(const rb_iseq_t *iseq, size_t pos, rb_event_flag_t reset)
#define RUBY_EVENT_THREAD_END
int clock_gettime(clockid_t, struct timespec *)
VALUE rb_ec_backtrace_str_ary(const rb_execution_context_t *ec, long lev, long n)
#define OBJ_FREEZE_RAW(x)
int rb_sigwait_fd_get(const rb_thread_t *)
#define STACK_DIR_UPPER(a, b)
rb_atomic_t interrupt_mask
@ VM_METHOD_TYPE_ISEQ
Ruby method.
double rb_num2dbl(VALUE)
Converts a Numeric object to double.
#define RB_NOGVL_UBF_ASYNC_SAFE
rb_code_position_t beg_pos
void rb_define_const(VALUE, const char *, VALUE)
const char ruby_digitmap[]
struct rb_thread_struct * th
VALUE rb_adjust_argv_kw_splat(int *, const VALUE **, int *)
int rb_get_next_signal(void)
int rb_signal_exec(rb_thread_t *th, int sig)
unsigned int report_on_exception
void rb_threadptr_signal_raise(rb_thread_t *th, int sig)
VALUE rb_exec_recursive_outer(VALUE(*func)(VALUE, VALUE, int), VALUE obj, VALUE arg)
#define COMPILER_WARNING_IGNORED(flag)
const rb_iseq_t * rb_proc_get_iseq(VALUE proc, int *is_proc)
void rb_gc_mark(VALUE ptr)
#define MJIT_FUNC_EXPORTED
int rb_thread_to_be_killed(VALUE thread)
VALUE rb_vm_thread_backtrace(int argc, const VALUE *argv, VALUE thval)
int rb_notify_fd_close(int fd, struct list_head *busy)
struct rb_method_definition_struct *const def
void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec)
VALUE rb_str_catf(VALUE str, const char *format,...)
void rb_thread_wait_fd(int fd)
VALUE rb_thread_shield_wait(VALUE self)
#define STACK_GROW_DIR_DETECTION
VALUE rb_thread_list(void)
VALUE rb_class_path(VALUE)
volatile int ubf_async_safe
int rb_keyword_given_p(void)
int rb_wait_for_single_fd(int fd, int events, struct timeval *tv)
struct rb_iseq_constant_body * body
VALUE rb_ary_dup(VALUE ary)
VALUE rb_ivar_set(VALUE, ID, VALUE)
VALUE rb_thread_wakeup_alive(VALUE thread)
void rb_sigwait_fd_put(const rb_thread_t *, int fd)
#define TIMET_MAX_PLUS_ONE
int rb_thread_check_trap_pending(void)
#define ATOMIC_CAS(var, oldval, newval)
VALUE rb_exec_recursive_paired_outer(VALUE(*func)(VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
rb_nativethread_lock_t interrupt_lock
VALUE rb_uninterruptible(VALUE(*b_proc)(VALUE), VALUE data)
VALUE pending_interrupt_queue
#define SAVE_ROOT_JMPBUF(th, stmt)
VALUE rb_catch_protect(VALUE t, rb_block_call_func *func, VALUE data, enum ruby_tag_type *stateptr)
#define RUBY_VM_SET_INTERRUPT(ec)
#define COVERAGE_TARGET_LINES
VALUE rb_ensure(VALUE(*b_proc)(VALUE), VALUE data1, VALUE(*e_proc)(VALUE), VALUE data2)
An equivalent to ensure clause.
#define RARRAY_CONST_PTR_TRANSIENT(a)
MJIT_STATIC VALUE ruby_vm_special_exception_copy(VALUE)
rb_code_position_t end_pos
struct rb_vm_struct::@10 default_params
struct rb_mutex_struct * keeping_mutexes
#define RUBY_VM_CHECK_INTS_BLOCKING(ec)
#define RUBY_EVENT_COVERAGE_BRANCH
#define rb_key_err_raise(mesg, recv, name)
rb_nativethread_lock_t workqueue_lock
int st_lookup(st_table *tab, st_data_t key, st_data_t *value)
#define RUBY_VM_SET_TRAP_INTERRUPT(ec)
#define RB_NOGVL_INTR_FAIL
void rb_nativethread_lock_destroy(rb_nativethread_lock_t *lock)
VALUE local_storage_recursive_hash
struct rb_thread_list_struct * next
VALUE rb_thread_run(VALUE thread)
NORETURN(static void async_bug_fd(const char *mesg, int errno_arg, int fd))
void rb_vm_gvl_destroy(rb_vm_t *vm)
void rb_thread_check_ints(void)
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
void rb_nativethread_lock_lock(rb_nativethread_lock_t *lock)
#define RUBY_THREAD_PRIORITY_MIN
VALUE rb_obj_is_kind_of(VALUE, VALUE)
Determines if obj is a kind of c.
void rb_define_alloc_func(VALUE, rb_alloc_func_t)
rb_unblock_function_t * func
unsigned int pending_interrupt_queue_checked
int ruby_thread_has_gvl_p(void)
int rb_ec_set_raised(rb_execution_context_t *ec)
VALUE rb_thread_current(void)
char rb_thread_id_string_t[sizeof(rb_nativethread_id_t) *2+3]
void rb_threadptr_pending_interrupt_enque(rb_thread_t *th, VALUE v)
rb_nativethread_lock_t waitpid_lock
VALUE rb_class_inherited_p(VALUE mod, VALUE arg)
Determines if mod inherits arg.
void rb_threadptr_root_fiber_terminate(rb_thread_t *th)
VALUE(* func)(VALUE, VALUE, int)
VALUE rb_block_proc(void)