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
95static VALUE rb_cThreadShield;
97static VALUE sym_immediate;
98static VALUE sym_on_blocking;
99static 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)
110rb_thread_local_storage(
VALUE thread)
120static void sleep_forever(
rb_thread_t *th,
unsigned int fl);
121static void rb_thread_sleep_deadly_allow_spurious_wakeup(
void);
123static void rb_check_deadlock(
rb_vm_t *vm);
124static int rb_threadptr_pending_interrupt_empty_p(
const rb_thread_t *th);
125static const char *thread_status_name(
rb_thread_t *th,
int detail);
127NORETURN(
static void async_bug_fd(
const char *mesg,
int errno_arg,
int fd));
128static int consume_communication_pipe(
int fd);
129static int check_signals_nogvl(
rb_thread_t *,
int sigwait_fd);
132#define eKillSignal INT2FIX(0)
133#define eTerminateSignal INT2FIX(1)
134static volatile int system_working = 1;
150#define THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
157static 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))) {
219vm_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
256void 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
261void rb_thread_debug(
const char *
fmt, ...);
262#define thread_debug rb_thread_debug
263#define POSITION_FORMAT
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++) {
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"
296static int rb_thread_debug_enabled;
307rb_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"
341static void timer_thread_function(
void);
345ubf_sigwait(
void *ignore)
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)
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)
387static 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;
455 if (fail_if_interrupted) {
486rb_threadptr_interrupt_common(
rb_thread_t *th,
int trap)
507 rb_threadptr_interrupt_common(th, 0);
513 rb_threadptr_interrupt_common(th, 1);
522 if (th != main_thread) {
531 thread_debug(
"terminate_all: main thread (%p)\n", (
void *)th);
548 err = rb_mutex_unlock_th(mutex, th);
559 volatile int sleeping = 0;
562 rb_bug(
"rb_thread_terminate_all: called by child thread (%p, %p)",
572 thread_debug(
"rb_thread_terminate_all (main thread: %p)\n", (
void *)th);
573 terminate_all(vm, th);
575 while (vm_living_thread_num(vm) > 1) {
582 native_sleep(th, &rel);
604thread_cleanup_func_before_exec(
void *th_ptr)
615thread_cleanup_func(
void *th_ptr,
int atfork)
620 thread_cleanup_func_before_exec(th_ptr);
635 native_thread_destroy(th);
644 native_thread_init_stack(th);
650 const VALUE *ep = vm_proc_ep(proc);
663 native_set_thread_name(th);
669 const VALUE *args_ptr;
679 vm_check_ints_blocking(th->
ec);
717 rb_bug(
"thread_start_func_2 must not be used for main thread");
726 gvl_acquire(th->
vm, th);
732 ruby_thread_set_native(th);
735 thread_debug(
"thread start (get lock): %p\n", (
void *)th);
736 rb_thread_set_current(th);
757 rb_str_cat_cstr(mesg,
" terminated with exception (report_on_exception is true):\n");
781 rb_threadptr_raise(main_th, 1, &errinfo);
789 rb_bug(
"thread_start_func_2: locking_mutex must not be set (%p:%"PRIxVALUE")",
790 (
void *)th, th->locking_mutex);
794 rb_vm_living_threads_remove(th->
vm, th);
809 join_list = join_list->
next;
813 rb_check_deadlock(th->
vm);
818 thread_cleanup_func(th,
FALSE);
834 "can't start a new thread (frozen ThreadGroup)");
852 th->
priority = current_th->priority;
853 th->
thgroup = current_th->thgroup;
863 err = native_thread_create(th);
868 rb_vm_living_threads_insert(th->
vm, th);
872#define threadptr_initialized(th) ((th)->invoke_type != thread_invoke_type_none)
904 th = rb_thread_ptr(thread);
948 else if (th->
invoke_type != thread_invoke_type_none) {
949 VALUE loc = threadptr_invoke_proc_location(th);
960 return thread_create_core(thread, args,
NULL);
986 if ((*p)->th == th) {
1012 rb_check_deadlock(th->
vm);
1013 native_sleep(th, 0);
1017 if (hrtime_update_expire(p->
limit, end)) {
1023 native_sleep(th, p->
limit);
1039 if (th == target_th) {
1042 if (
GET_VM()->main_thread == target_th) {
1046 arg.target = target_th;
1059 remove_from_join_list, (
VALUE)&
arg)) {
1083 rb_bug(
"thread_join: THROW_DATA should not reach here.");
1090 return target_th->
self;
1155 return thread_join(rb_thread_ptr(
self), to);
1173thread_value(
VALUE self)
1191#define TIMESPEC_SEC_MAX TIMET_MAX
1192#define TIMESPEC_SEC_MIN TIMET_MIN
1200 if (TIMESPEC_SEC_MAX_PLUS_ONE <= d) {
1215#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
1233 return rb_timespec2hrtime(&ts);
1246 while (th->
status == status) {
1249 rb_check_deadlock(th->
vm);
1251 native_sleep(th, 0);
1255 woke = vm_check_ints_blocking(th->
ec);
1259 th->
status = prev_status;
1267#if defined(__GNUC__) && __GNUC__ == 7 && __GNUC_MINOR__ <= 3
1271#define PRIu64 PRI_64_PREFIX "u"
1283 if (now > end)
return 1;
1287 *timeout = end - now;
1302 native_sleep(th, &rel);
1303 woke = vm_check_ints_blocking(th->
ec);
1306 if (hrtime_update_expire(&rel, end))
1309 th->
status = prev_status;
1333 native_sleep(th, 0);
1335 th->
status = prev_status;
1339rb_thread_sleep_deadly_allow_spurious_wakeup(
void)
1341 thread_debug(
"rb_thread_sleep_deadly_allow_spurious_wakeup\n");
1390rb_thread_schedule_limits(
uint32_t limits_us)
1399 gvl_yield(th->
vm, th);
1400 rb_thread_set_current(th);
1409 rb_thread_schedule_limits(0);
1420 if (unblock_function_set(th, ubf,
arg, fail_if_interrupted)) {
1423 thread_debug(
"enter blocking region (%p)\n", (
void *)th);
1425 gvl_release(th->
vm);
1437 unblock_function_clear(th);
1439 unregister_ubf_list(th);
1441 gvl_acquire(th->
vm, th);
1442 rb_thread_set_current(th);
1443 thread_debug(
"leave blocking region (%p)\n", (
void *)th);
1458 int saved_errno = 0;
1465 else if (ubf && vm_living_thread_num(th->
vm) == 1) {
1470 ubf_th = rb_thread_start_unblock_thread();
1476 saved_errno =
errno;
1489 errno = saved_errno;
1590 return rb_nogvl(func, data1, ubf, data2, 0);
1598 volatile int saved_errno = 0;
1603 wfd.
th = rb_ec_thread_ptr(ec);
1610 saved_errno = errno;
1611 }, ubf_select, wfd.
th,
FALSE);
1627 errno = saved_errno;
1674 fprintf(
stderr,
"[BUG] rb_thread_call_with_gvl() is called by non-ruby thread\n");
1682 rb_bug(
"rb_thread_call_with_gvl: called by a thread which has GVL.");
1685 blocking_region_end(th, brb);
1689 int released = blocking_region_begin(th, brb, prev_unblock.
func, prev_unblock.
arg,
FALSE);
1763threadptr_check_pending_interrupt_queue(
rb_thread_t *th)
1786 for (
i=0;
i<mask_stack_len;
i++) {
1787 mask = mask_stack[mask_stack_len-(
i+1)];
1801 if (
sym == sym_immediate) {
1804 else if (
sym == sym_on_blocking) {
1807 else if (
sym == sym_never) {
1821rb_threadptr_pending_interrupt_empty_p(
const rb_thread_t *th)
1850 switch (mask_timing) {
1869 if (rb_threadptr_pending_interrupt_empty_p(th)) {
1877threadptr_pending_interrupt_active_p(
rb_thread_t *th)
1888 if (rb_threadptr_pending_interrupt_empty_p(th)) {
1900 if (val != sym_immediate && val != sym_on_blocking && val != sym_never) {
2019rb_thread_s_handle_interrupt(
VALUE self,
VALUE mask_arg)
2039 if (!rb_threadptr_pending_interrupt_empty_p(th)) {
2051 if (!rb_threadptr_pending_interrupt_empty_p(th)) {
2078 rb_thread_t *target_th = rb_thread_ptr(target_thread);
2083 if (rb_threadptr_pending_interrupt_empty_p(target_th)) {
2091 if (rb_threadptr_pending_interrupt_include_p(target_th,
err)) {
2188 }
while (
old != interrupt);
2196 int postponed_job_interrupt = 0;
2201 while ((interrupt = threadptr_get_interrupts(th)) != 0) {
2203 int timer_interrupt;
2204 int pending_interrupt;
2212 if (postponed_job_interrupt) {
2221 if (sigwait_fd >= 0) {
2222 (
void)consume_communication_pipe(sigwait_fd);
2235 if (pending_interrupt && threadptr_pending_interrupt_active_p(th)) {
2246 rb_threadptr_to_kill(th);
2261 if (timer_interrupt) {
2262 uint32_t limits_us = TIME_QUANTUM_USEC;
2276 rb_thread_schedule_limits(limits_us);
2299 if (rb_threadptr_dead(target_th)) {
2312 if (rb_threadptr_dead(target_th)) {
2369 if (wfd->
fd ==
fd) {
2389 list_head_init(&busy);
2422 threadptr_check_pending_interrupt_queue(target_th);
2423 rb_threadptr_raise(target_th,
argc,
argv);
2426 if (current_th == target_th) {
2460 rb_threadptr_to_kill(th);
2463 threadptr_check_pending_interrupt_queue(th);
2553 rb_threadptr_ready(target_th);
2599 "stopping only thread\n\tnote: use sleep to stop forever");
2736rb_thread_s_abort_exc(
VALUE _)
2773rb_thread_s_abort_exc_set(
VALUE self,
VALUE val)
2796rb_thread_abort_exc(
VALUE thread)
2798 return rb_thread_ptr(thread)->abort_on_exception ?
Qtrue :
Qfalse;
2816rb_thread_abort_exc_set(
VALUE thread,
VALUE val)
2818 rb_thread_ptr(thread)->abort_on_exception =
RTEST(val);
2866rb_thread_s_report_exc(
VALUE _)
2903rb_thread_s_report_exc_set(
VALUE self,
VALUE val)
2927rb_thread_report_exc(
VALUE thread)
2929 return rb_thread_ptr(thread)->report_on_exception ?
Qtrue :
Qfalse;
2947rb_thread_report_exc_set(
VALUE thread,
VALUE val)
2949 rb_thread_ptr(thread)->report_on_exception =
RTEST(val);
2967 VALUE group = rb_thread_ptr(thread)->thgroup;
2968 return group == 0 ?
Qnil : group;
2976 return th->
to_kill ?
"aborting" :
"run";
2978 if (detail)
return "sleep_forever";
3031 if (rb_threadptr_dead(target_th)) {
3061rb_thread_alive_p(
VALUE thread)
3063 if (rb_threadptr_dead(rb_thread_ptr(thread))) {
3086rb_thread_stop_p(
VALUE thread)
3090 if (rb_threadptr_dead(th)) {
3113rb_thread_safe_level(
VALUE thread)
3115 rb_warn(
"Thread#safe_level will be removed in Ruby 3.0");
3127rb_thread_getname(
VALUE thread)
3129 return rb_thread_ptr(thread)->name;
3170rb_thread_to_s(
VALUE thread)
3177 status = thread_status_name(target_th,
TRUE);
3182 if ((loc = threadptr_invoke_proc_location(target_th)) !=
Qnil) {
3193static ID recursive_key;
3198 if (
id == recursive_key) {
3205 if (local_storage !=
NULL &&
st_lookup(local_storage,
id, &val)) {
3217 return threadptr_local_aref(rb_thread_ptr(thread),
id);
3284 if (!
id)
return Qnil;
3313 if (block_given &&
argc == 2) {
3314 rb_warn(
"block supersedes default value argument");
3319 if (
id == recursive_key) {
3326 else if (block_given) {
3329 else if (
argc == 1) {
3340 if (
id == recursive_key) {
3348 if (!local_storage)
return Qnil;
3349 st_delete_wrap(local_storage,
id);
3353 if (local_storage ==
NULL) {
3369 return threadptr_local_aset(rb_thread_ptr(thread),
id, val);
3427 locals = rb_thread_local_storage(thread);
3449 locals = rb_thread_local_storage(thread);
3470 st_table *local_storage = rb_thread_ptr(
self)->ec->local_storage;
3472 if (!
id || local_storage ==
NULL) {
3493 return vm_living_thread_num(
GET_VM()) == 1;
3511rb_thread_keys(
VALUE self)
3513 st_table *local_storage = rb_thread_ptr(
self)->ec->local_storage;
3516 if (local_storage) {
3517 st_foreach(local_storage, thread_keys_i, ary);
3547rb_thread_variables(
VALUE thread)
3556 locals = rb_thread_local_storage(thread);
3589 locals = rb_thread_local_storage(thread);
3617rb_thread_priority(
VALUE thread)
3619 return INT2NUM(rb_thread_ptr(thread)->priority);
3650rb_thread_priority_set(
VALUE thread,
VALUE prio)
3655#if USE_NATIVE_THREAD_PRIORITY
3657 native_thread_apply_priority(th);
3673#if defined(NFDBITS) && defined(HAVE_RB_FD_INIT)
3767 if (
n >= fds->
maxfd)
return;
3774 if (
n >= fds->
maxfd)
return 0;
3817 return select(
n, r, w, e, timeout);
3820#define rb_fd_no_init(fds) ((void)((fds)->fdset = 0), (void)((fds)->maxfd = 0))
3827#define FD_ZERO(f) rb_fd_zero(f)
3828#define FD_SET(i, f) rb_fd_set((i), (f))
3829#define FD_CLR(i, f) rb_fd_clr((i), (f))
3830#define FD_ISSET(i, f) rb_fd_isset((i), (f))
3832#elif defined(_WIN32)
3863 for (
i = 0;
i < set->
fdset->fd_count;
i++) {
3864 if (set->
fdset->fd_array[
i] == s) {
3868 if (set->
fdset->fd_count >= (
unsigned)set->capa) {
3872 set->
fdset, set->capa,
sizeof(SOCKET),
sizeof(
unsigned int));
3874 set->
fdset->fd_array[set->
fdset->fd_count++] = s;
3882#define FD_ZERO(f) rb_fd_zero(f)
3883#define FD_SET(i, f) rb_fd_set((i), (f))
3884#define FD_CLR(i, f) rb_fd_clr((i), (f))
3885#define FD_ISSET(i, f) rb_fd_isset((i), (f))
3887#define rb_fd_no_init(fds) (void)((fds)->fdset = 0)
3891#ifndef rb_fd_no_init
3892#define rb_fd_no_init(fds) (void)(fds)
3905 if (rel && hrtime_update_expire(rel, end)) {
3912 else if (*result == 0) {
3915 return !hrtime_update_expire(rel, end);
3936select_set_free(
VALUE p)
3956 static const rb_hrtime_t quantum = TIME_QUANTUM_USEC * 1000;
3960 if (!orig || *orig > quantum)
3975 timeout_prepare(&to, &rel, &end, set->
timeout);
3976#define restore_fdset(dst, src) \
3977 ((dst) ? rb_fd_dup(dst, src) : (void)0)
3978#define do_select_update() \
3979 (restore_fdset(set->rset, &set->orig_rset), \
3980 restore_fdset(set->wset, &set->orig_wset), \
3981 restore_fdset(set->eset, &set->orig_eset), \
3989 const rb_hrtime_t *sto;
3992 sto = sigwait_timeout(set->th, set->sigwait_fd, to, &drained);
3993 if (!RUBY_VM_INTERRUPTED(set->th->ec)) {
3994 result = native_fd_select(set->max, set->rset, set->wset,
3996 rb_hrtime2timeval(&tv, sto), set->th);
3997 if (result < 0) lerrno = errno;
4006 (
void)check_signals_nogvl(set->
th, -1);
4017 return (
VALUE)result;
4021rb_thread_wait_fd_rw(
int fd,
int read)
4026 thread_debug(
"rb_thread_wait_fd_rw(%d, %s)\n", fd,
read ?
"read" :
"write");
4037 thread_debug(
"rb_thread_wait_fd_rw(%d, %s): done\n", fd,
read ?
"read" :
"write");
4043 rb_thread_wait_fd_rw(fd, 1);
4049 rb_thread_wait_fd_rw(fd, 0);
4098#define fd_init_copy(f) do { \
4100 rb_fd_resize(set.max - 1, set.f); \
4101 if (&set.orig_##f != set.f) { \
4102 rb_fd_init_copy(&set.orig_##f, set.f); \
4106 rb_fd_no_init(&set.orig_##f); \
4120#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
4121#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
4122#define POLLEX_SET (POLLPRI)
4125# define POLLERR_SET (0)
4134 struct pollfd fds[2];
4135 int result = 0, lerrno;
4145 list_add(&wfd.th->vm->waiting_fds, &wfd.wfd_node);
4149 timeout_prepare(&to, &rel, &end, timeout);
4151 fds[0].events = (
short)events;
4156 if (fds[1].
fd >= 0) {
4157 fds[1].events = POLLIN;
4169 const rb_hrtime_t *sto;
4172 sto = sigwait_timeout(wfd.th, fds[1].fd, to, &drained);
4173 if (!RUBY_VM_INTERRUPTED(wfd.th->ec)) {
4174 result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, sto), 0);
4175 if (result < 0) lerrno = errno;
4177 }, ubf, wfd.th,
TRUE);
4179 if (fds[1].
fd >= 0) {
4180 if (result > 0 && fds[1].revents) {
4182 (
void)check_signals_nogvl(wfd.th, fds[1].fd);
4184 (
void)check_signals_nogvl(wfd.th, -1);
4190 }
while (wait_retryable(&result, lerrno, to, end));
4203 if (fds[0].revents & POLLNVAL) {
4213 if (fds[0].revents & POLLIN_SET)
4215 if (fds[0].revents & POLLOUT_SET)
4217 if (fds[0].revents & POLLEX_SET)
4221 if (fds[0].revents & POLLERR_SET)
4303#ifdef USE_CONSERVATIVE_STACK_END
4308 *stack_end_p = &stack_end;
4322 threadptr_trap_interrupt(mth);
4327timer_thread_function(
void)
4338async_bug_fd(
const char *mesg,
int errno_arg,
int fd)
4341 size_t n =
strlcpy(buff, mesg,
sizeof(buff));
4342 if (
n <
sizeof(buff)-3) {
4350consume_communication_pipe(
int fd)
4356 static char buff[1024];
4370 result =
read(
fd, buff,
sizeof(buff));
4377 else if (result == 0) {
4380 else if (result < 0) {
4386#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
4391 async_bug_fd(
"consume_communication_pipe: read", e,
fd);
4398check_signals_nogvl(
rb_thread_t *th,
int sigwait_fd)
4401 int ret = sigwait_fd >= 0 ? consume_communication_pipe(sigwait_fd) :
FALSE;
4402 ubf_wakeup_all_threads();
4418 if (TIMER_THREAD_CREATED_P() && native_stop_timer_thread()) {
4419 native_reset_timer_thread();
4426 native_reset_timer_thread();
4433 rb_thread_create_timer_thread();
4470 if (
RTEST(coverages)) {
4475#if defined(HAVE_WORKING_FORK)
4489 rb_vm_living_threads_init(vm);
4490 rb_vm_living_threads_insert(vm, th);
4508 if (th != current_th) {
4509 rb_mutex_abandon_keeping_mutexes(th);
4510 rb_mutex_abandon_locking_mutex(th);
4511 thread_cleanup_func(th,
TRUE);
4520 rb_thread_atfork_internal(th, terminate_atfork_i);
4522 rb_fiber_atfork(th);
4534 if (th != current_th) {
4535 thread_cleanup_func_before_exec(th);
4543 rb_thread_atfork_internal(th, terminate_atfork_before_exec_i);
4563thgroup_memsize(
const void *
ptr)
4565 return sizeof(
struct thgroup);
4728 "can't move from the enclosed thread group");
4739thread_shield_mark(
void *
ptr)
4746 {thread_shield_mark, 0, 0,},
4756#define GetThreadShieldPtr(obj) ((VALUE)rb_check_typeddata((obj), &thread_shield_data_type))
4757#define THREAD_SHIELD_WAITING_MASK (((FL_USER19-1)&~(FL_USER0-1))|FL_USER19)
4758#define THREAD_SHIELD_WAITING_SHIFT (FL_USHIFT)
4759#define THREAD_SHIELD_WAITING_MAX (THREAD_SHIELD_WAITING_MASK>>THREAD_SHIELD_WAITING_SHIFT)
4761static inline unsigned int
4762rb_thread_shield_waiting(
VALUE b)
4768rb_thread_shield_waiting_inc(
VALUE b)
4770 unsigned int w = rb_thread_shield_waiting(b);
4774 RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
4779rb_thread_shield_waiting_dec(
VALUE b)
4781 unsigned int w = rb_thread_shield_waiting(b);
4784 RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
4791 VALUE thread_shield = thread_shield_alloc(rb_cThreadShield);
4793 return thread_shield;
4810 if (!mutex)
return Qfalse;
4811 m = mutex_ptr(mutex);
4813 rb_thread_shield_waiting_inc(
self);
4815 rb_thread_shield_waiting_dec(
self);
4818 return rb_thread_shield_waiting(
self) > 0 ?
Qnil :
Qfalse;
4822thread_shield_get_mutex(
VALUE self)
4836 VALUE mutex = thread_shield_get_mutex(
self);
4838 return rb_thread_shield_waiting(
self) > 0 ?
Qtrue :
Qfalse;
4847 VALUE mutex = thread_shield_get_mutex(
self);
4850 return rb_thread_shield_waiting(
self) > 0 ?
Qtrue :
Qfalse;
4877 VALUE hash = threadptr_recursive_hash(th);
4881 threadptr_recursive_hash_set(th, hash);
4903#if SIZEOF_LONG == SIZEOF_VOIDP
4904 #define OBJ_ID_EQL(obj_id, other) ((obj_id) == (other))
4905#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
4906 #define OBJ_ID_EQL(obj_id, other) (RB_TYPE_P((obj_id), T_BIGNUM) ? \
4907 rb_big_eql((obj_id), (other)) : ((obj_id) == (other)))
4913 if (paired_obj_id) {
4948 VALUE other_paired_obj = pair_list;
4970 if (pair_list ==
Qundef) {
5018 p.
list = recursive_list_access(
sym);
5022 outermost = outer && !recursive_check(p.list,
ID2SYM(recursive_key), 0);
5024 if (recursive_check(p.list, p.obj,
pairid)) {
5025 if (outer && !outermost) {
5036 recursive_push(p.list,
ID2SYM(recursive_key), 0);
5037 recursive_push(p.list, p.obj, p.pairid);
5039 if (!recursive_pop(p.list, p.obj, p.pairid))
goto invalid;
5040 if (!recursive_pop(p.list,
ID2SYM(recursive_key), 0))
goto invalid;
5042 if (result == p.list) {
5048 recursive_push(p.list, p.obj, p.pairid);
5054 if (!recursive_pop(p.list, p.obj, p.pairid)) {
5164#define rb_intern(str) rb_intern_const(str)
5233 "stream closed in another thread");
5247 recursive_key =
rb_intern(
"__recursive_key__");
5256 gvl_acquire(th->
vm, th);
5267 rb_thread_create_timer_thread();
5270 (
void)native_mutex_trylock;
5289 rb_str_catf(msg,
"\n%d threads, %d sleeps current:%p main thread:%p\n",
5295 if (th->locking_mutex) {
5296 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
5298 (
void *)mutex->
th, rb_mutex_num_waiting(mutex));
5303 rb_str_catf(msg,
"\n depended by: tb_thread_id:%p", (
void *)
list->th);
5319 if (vm_living_thread_num(vm) > vm->
sleeper)
return;
5320 if (vm_living_thread_num(vm) < vm->
sleeper)
rb_bug(
"sleeper must not be more than vm_living_thread_num(vm)");
5321 if (patrol_thread && patrol_thread !=
GET_THREAD())
return;
5342 debug_deadlock_check(vm,
argv[1]);
5401 VALUE path, beg_pos_lineno, beg_pos_column, end_pos_lineno, end_pos_column;
5419 rb_iseq_check(
iseq);
5446 if (resolved_location) {
5447 resolved_location[0] =
path;
5448 resolved_location[1] = beg_pos_lineno;
5449 resolved_location[2] = beg_pos_column;
5450 resolved_location[3] = end_pos_lineno;
5451 resolved_location[4] = end_pos_column;
5478 return GET_VM()->coverages;
5484 return GET_VM()->coverage_mode;
5490 GET_VM()->coverages = coverages;
5491 GET_VM()->coverage_mode = mode;
5522 int mode =
GET_VM()->coverage_mode;
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)
@ RUBY_EVENT_HOOK_FLAG_SAFE
@ RUBY_EVENT_HOOK_FLAG_RAW_ARG
struct rb_encoding_entry * list
rb_encoding * rb_enc_get(VALUE obj)
#define rb_enc_asciicompat(enc)
char str[HTML_ESCAPE_MAX_LEN+1]
void ruby_stop(int ex)
Calls ruby_cleanup() and exits the process.
VALUE rb_define_class(const char *, VALUE)
Defines a top-level class.
ID rb_frame_last_func(void)
Returns the ID of the last method in the call stack.
void rb_define_alias(VALUE, const char *, const char *)
Defines an alias of a method.
int rb_block_given_p(void)
Determines if the current method is given a block.
VALUE rb_cObject
Object class.
VALUE rb_cModule
Module class.
int ruby_native_thread_p(void)
void rb_raise(VALUE exc, const char *fmt,...)
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
void rb_bug(const char *fmt,...)
void rb_frozen_error_raise(VALUE frozen_obj, const char *fmt,...)
VALUE rb_make_exception(int, const VALUE *)
Make an Exception object from the list of arguments in a manner similar to Kernel#raise.
void rb_warn(const char *fmt,...)
VALUE rb_exc_new(VALUE, const char *, long)
VALUE rb_ensure(VALUE(*)(VALUE), VALUE, VALUE(*)(VALUE), VALUE)
An equivalent to ensure clause.
void rb_async_bug_errno(const char *mesg, int errno_arg)
void rb_sys_fail(const char *mesg)
VALUE rb_obj_alloc(VALUE)
Allocates an instance of klass.
VALUE rb_obj_class(VALUE)
Equivalent to Object#class in Ruby.
VALUE rb_class_inherited_p(VALUE mod, VALUE arg)
Determines if mod inherits arg.
double rb_num2dbl(VALUE)
Converts a Numeric object to double.
VALUE rb_obj_is_kind_of(VALUE, VALUE)
Determines if obj is a kind of c.
#define RB_HRTIME_PER_SEC
#define rb_fd_init_copy(d, s)
#define rb_fd_resize(n, f)
MJIT_STATIC VALUE ruby_vm_special_exception_copy(VALUE)
void rb_iseq_clear_event_flags(const rb_iseq_t *iseq, size_t pos, rb_event_flag_t reset)
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
void rb_sigwait_fd_put(const rb_thread_t *, int fd)
int rb_sigwait_fd_get(const rb_thread_t *)
int st_delete(st_table *tab, st_data_t *key, st_data_t *value)
st_table * st_init_numtable(void)
int st_insert(st_table *tab, st_data_t key, st_data_t value)
int st_lookup(st_table *tab, st_data_t key, st_data_t *value)
int st_foreach(st_table *tab, st_foreach_callback_func *func, st_data_t arg)
VALUE(* func)(VALUE, VALUE, int)
enum rb_thread_status prev_status
struct rb_method_definition_struct *const def
rb_code_position_t beg_pos
rb_code_position_t end_pos
struct rb_execution_context_struct::@55 machine
rb_atomic_t interrupt_flag
rb_atomic_t interrupt_mask
VALUE local_storage_recursive_hash
rb_iseq_location_t location
rb_code_location_t code_location
struct rb_iseq_constant_body * body
struct rb_method_entry_struct * original_me
rb_method_bmethod_t bmethod
union rb_method_definition_struct::@41 body
rb_method_refined_t refined
rb_iseq_t * iseqptr
iseq pointer, should be separated from iseqval
struct rb_method_entry_struct * orig_me
struct rb_mutex_struct * next_mutex
struct rb_thread_list_struct * next
struct rb_thread_struct * th
rb_execution_context_t * ec
struct rb_unblock_callback unblock
union rb_thread_struct::@56 invoke_arg
unsigned int pending_interrupt_queue_checked
enum rb_thread_status status
unsigned int abort_on_exception
rb_nativethread_id_t thread_id
enum rb_thread_struct::@57 invoke_type
VALUE pending_interrupt_mask_stack
rb_nativethread_lock_t interrupt_lock
unsigned int report_on_exception
struct rb_mutex_struct * keeping_mutexes
VALUE pending_interrupt_queue
void * blocking_region_buffer
rb_thread_list_t * join_list
rb_unblock_function_t * func
rb_nativethread_lock_t waitpid_lock
struct rb_thread_struct * main_thread
struct list_head waiting_fds
volatile int ubf_async_safe
size_t thread_vm_stack_size
const VALUE special_exceptions[ruby_special_error_count]
struct rb_vm_struct::@52 default_params
rb_nativethread_lock_t workqueue_lock
unsigned int thread_abort_on_exception
struct list_head living_threads
union select_args::@222 as
struct list_node wfd_node
int rb_ec_set_raised(rb_execution_context_t *ec)
int rb_thread_check_trap_pending(void)
MJIT_FUNC_EXPORTED int rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
VALUE rb_get_coverages(void)
int rb_thread_interrupted(VALUE thval)
#define threadptr_initialized(th)
void rb_vm_gvl_destroy(rb_vm_t *vm)
void rb_threadptr_check_signal(rb_thread_t *mth)
void ruby_thread_init_stack(rb_thread_t *th)
int ruby_thread_has_gvl_p(void)
const rb_method_entry_t * rb_resolve_me_location(const rb_method_entry_t *me, VALUE resolved_location[5])
int rb_thread_fd_writable(int fd)
VALUE rb_thread_group(VALUE thread)
void rb_nativethread_lock_lock(rb_nativethread_lock_t *lock)
#define THREAD_SHIELD_WAITING_MAX
VALUE rb_thread_local_aref(VALUE thread, ID id)
VALUE rb_default_coverage(int n)
VALUE rb_thread_create(VALUE(*fn)(void *), void *arg)
#define THREAD_LOCAL_STORAGE_INITIALISED
void rb_clear_coverages(void)
VALUE rb_thread_kill(VALUE thread)
NORETURN(static void async_bug_fd(const char *mesg, int errno_arg, int fd))
#define GetThreadShieldPtr(obj)
int rb_thread_to_be_killed(VALUE thread)
VALUE rb_thread_main(void)
void rb_thread_sleep_forever(void)
int rb_thread_fd_select(int max, rb_fdset_t *read, rb_fdset_t *write, rb_fdset_t *except, struct timeval *timeout)
#define RUBY_THREAD_PRIORITY_MAX
void * rb_nogvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2, int flags)
#define THREAD_SHIELD_WAITING_MASK
int rb_wait_for_single_fd(int fd, int events, struct timeval *tv)
void rb_thread_fd_close(int fd)
#define BLOCKING_REGION(th, exec, ubf, ubfarg, fail_if_interrupted)
void rb_sigwait_fd_migrate(rb_vm_t *)
VALUE rb_thread_shield_new(void)
void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
void rb_threadptr_pending_interrupt_enque(rb_thread_t *th, VALUE v)
void rb_thread_wait_for(struct timeval time)
void rb_threadptr_pending_interrupt_clear(rb_thread_t *th)
VALUE rb_thread_shield_destroy(VALUE self)
VALUE rb_thread_stop(void)
void rb_thread_wait_fd(int fd)
void * rb_thread_call_without_gvl(void *(*func)(void *data), void *data1, rb_unblock_function_t *ubf, void *data2)
VALUE rb_uninterruptible(VALUE(*b_proc)(VALUE), VALUE data)
VALUE rb_exec_recursive_outer(VALUE(*func)(VALUE, VALUE, int), VALUE obj, VALUE arg)
void rb_threadptr_signal_raise(rb_thread_t *th, int sig)
void rb_set_coverages(VALUE coverages, int mode, VALUE me2counter)
#define thread_id_str(th)
#define do_select_update()
VALUE rb_exec_recursive_paired_outer(VALUE(*func)(VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
#define RB_GC_SAVE_MACHINE_CONTEXT(th)
void rb_thread_terminate_all(void)
STATIC_ASSERT(THREAD_SHIELD_WAITING_MAX, THREAD_SHIELD_WAITING_MAX<=UINT_MAX)
void rb_thread_atfork_before_exec(void)
VALUE rb_thread_shield_wait(VALUE self)
void rb_thread_sleep_interruptible(void)
void rb_thread_check_ints(void)
void * rb_thread_call_without_gvl2(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
void rb_thread_reset_timer_thread(void)
#define OBJ_ID_EQL(obj_id, other)
VALUE rb_thread_run(VALUE thread)
int rb_notify_fd_close(int fd, struct list_head *busy)
VALUE rb_thread_wakeup(VALUE thread)
void rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th)
rb_hrtime_t rb_hrtime_now(void)
void ruby_sigchld_handler(rb_vm_t *)
void rb_thread_sleep_deadly(void)
void rb_thread_stop_timer_thread(void)
VALUE rb_thread_shield_release(VALUE self)
void rb_threadptr_signal_exit(rb_thread_t *th)
void rb_gc_set_stack_end(VALUE **stack_end_p)
void rb_thread_atfork(void)
VALUE rb_exec_recursive(VALUE(*func)(VALUE, VALUE, int), VALUE obj, VALUE arg)
void rb_nativethread_lock_unlock(rb_nativethread_lock_t *lock)
VALUE rb_thread_current(void)
void rb_threadptr_interrupt(rb_thread_t *th)
VALUE rb_exec_recursive_paired(VALUE(*func)(VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
int rb_thread_alone(void)
void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec)
int rb_ec_reset_raised(rb_execution_context_t *ec)
const VALUE * rb_vm_proc_local_ep(VALUE proc)
void rb_thread_schedule(void)
#define THREAD_SHIELD_WAITING_SHIFT
#define RUBY_THREAD_PRIORITY_MIN
void rb_nativethread_lock_initialize(rb_nativethread_lock_t *lock)
int rb_get_coverage_mode(void)
#define BUSY_WAIT_SIGNALS
VALUE rb_thread_local_aset(VALUE thread, ID id, VALUE val)
void rb_threadptr_root_fiber_terminate(rb_thread_t *th)
#define THREAD_LOCAL_STORAGE_INITIALISED_P(th)
void rb_thread_execute_interrupts(VALUE thval)
#define RUBY_VM_CHECK_INTS_BLOCKING(ec)
NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_start))
VALUE rb_thread_io_blocking_region(rb_blocking_function_t *func, void *data1, int fd)
VALUE rb_thread_wakeup_alive(VALUE thread)
void rb_nativethread_lock_destroy(rb_nativethread_lock_t *lock)
void rb_reset_coverages(void)
void rb_thread_sleep(int sec)
void rb_thread_start_timer_thread(void)
VALUE rb_thread_list(void)
#define RB_NOGVL_UBF_ASYNC_SAFE
#define RB_NOGVL_INTR_FAIL
MJIT_STATIC const rb_callable_method_entry_t * rb_vm_frame_method_entry(const rb_control_frame_t *cfp)
void rb_throw_obj(VALUE tag, VALUE value)
SOCKET rb_w32_get_osfhandle(int)