Ruby 4.1.0dev (2026-09-07 revision b57404b461ba8bf34e802d86b0db78388216e182)
thread.c (b57404b461ba8bf34e802d86b0db78388216e182)
1/**********************************************************************
2
3 thread.c -
4
5 $Author$
6
7 Copyright (C) 2004-2007 Koichi Sasada
8
9**********************************************************************/
10
11/*
12 YARV Thread Design
13
14 model 1: Userlevel Thread
15 Same as traditional ruby thread.
16
17 model 2: Native Thread with Global VM lock
18 Using pthread (or Windows thread) and Ruby threads run concurrent.
19
20 model 3: Native Thread with fine grain lock
21 Using pthread and Ruby threads run concurrent or parallel.
22
23 model 4: M:N User:Native threads with Global VM lock
24 Combination of model 1 and 2
25
26 model 5: M:N User:Native thread with fine grain lock
27 Combination of model 1 and 3
28
29------------------------------------------------------------------------
30
31 model 2:
32 A thread has mutex (GVL: Global VM Lock or Giant VM Lock) can run.
33 When thread scheduling, running thread release GVL. If running thread
34 try blocking operation, this thread must release GVL and another
35 thread can continue this flow. After blocking operation, thread
36 must check interrupt (RUBY_VM_CHECK_INTS).
37
38 Every VM can run parallel.
39
40 Ruby threads are scheduled by OS thread scheduler.
41
42------------------------------------------------------------------------
43
44 model 3:
45 Every threads run concurrent or parallel and to access shared object
46 exclusive access control is needed. For example, to access String
47 object or Array object, fine grain lock must be locked every time.
48 */
49
50
51/*
52 * FD_SET, FD_CLR and FD_ISSET have a small sanity check when using glibc
53 * 2.15 or later and set _FORTIFY_SOURCE > 0.
54 * However, the implementation is wrong. Even though Linux's select(2)
55 * supports large fd size (>FD_SETSIZE), it wrongly assumes fd is always
56 * less than FD_SETSIZE (i.e. 1024). And then when enabling HAVE_RB_FD_INIT,
57 * it doesn't work correctly and makes program abort. Therefore we need to
58 * disable FORTIFY_SOURCE until glibc fixes it.
59 */
60#undef _FORTIFY_SOURCE
61#undef __USE_FORTIFY_LEVEL
62#define __USE_FORTIFY_LEVEL 0
63
64/* for model 2 */
65
66#include "ruby/internal/config.h"
67
68#ifdef __linux__
69// Normally, gcc(1) translates calls to alloca() with inlined code. This is not done when either the -ansi, -std=c89, -std=c99, or the -std=c11 option is given and the header <alloca.h> is not included.
70# include <alloca.h>
71#endif
72
73#define TH_SCHED(th) (&(th)->ractor->threads.sched)
74
75#include "eval_intern.h"
76#include "hrtime.h"
77#include "internal.h"
78#include "internal/class.h"
79#include "internal/cont.h"
80#include "internal/coverage.h"
81#include "internal/error.h"
82#include "internal/eval.h"
83#include "internal/gc.h"
84#include "internal/hash.h"
85#include "internal/io.h"
86#include "internal/object.h"
87#include "internal/proc.h"
89#include "internal/signal.h"
90#include "internal/thread.h"
91#include "internal/time.h"
92#include "internal/warnings.h"
93#include "iseq.h"
94#include "ruby/debug.h"
95#include "ruby/io.h"
96#include "ruby/thread.h"
97#include "ruby/thread_native.h"
98#include "timev.h"
99#include "vm_core.h"
100#include "ractor_core.h"
101#include "vm_debug.h"
102#include "vm_sync.h"
103#include "zjit.h"
104
105#include "ccan/list/list.h"
106
107#ifndef USE_NATIVE_THREAD_PRIORITY
108#define USE_NATIVE_THREAD_PRIORITY 0
109#define RUBY_THREAD_PRIORITY_MAX 3
110#define RUBY_THREAD_PRIORITY_MIN -3
111#endif
112
113static VALUE rb_cThreadShield;
114static VALUE cThGroup;
115
116static VALUE sym_immediate;
117static VALUE sym_on_blocking;
118static VALUE sym_never;
119
120static uint32_t thread_default_quantum_ms = 100;
121
122#define THREAD_LOCAL_STORAGE_INITIALISED FL_USER13
123#define THREAD_LOCAL_STORAGE_INITIALISED_P(th) RB_FL_TEST_RAW((th), THREAD_LOCAL_STORAGE_INITIALISED)
124
125static inline VALUE
126rb_thread_local_storage(VALUE thread)
127{
128 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
129 rb_ivar_set(thread, idLocals, rb_hash_new());
130 RB_FL_SET_RAW(thread, THREAD_LOCAL_STORAGE_INITIALISED);
131 }
132 return rb_ivar_get(thread, idLocals);
133}
134
135enum SLEEP_FLAGS {
136 SLEEP_DEADLOCKABLE = 0x01,
137 SLEEP_SPURIOUS_CHECK = 0x02,
138 SLEEP_ALLOW_SPURIOUS = 0x04,
139 SLEEP_NO_CHECKINTS = 0x08,
140};
141
142static void sleep_forever(rb_thread_t *th, unsigned int fl);
143static int sleep_hrtime(rb_thread_t *, rb_hrtime_t, unsigned int fl);
144
145static void rb_thread_sleep_deadly_allow_spurious_wakeup(VALUE blocker, VALUE timeout, rb_hrtime_t end);
146static int rb_threadptr_dead(rb_thread_t *th);
147static void rb_check_deadlock(rb_ractor_t *r);
148static int rb_threadptr_pending_interrupt_empty_p(const rb_thread_t *th);
149static const char *thread_status_name(rb_thread_t *th, int detail);
150static int hrtime_update_expire(rb_hrtime_t *, const rb_hrtime_t);
151NORETURN(static void async_bug_fd(const char *mesg, int errno_arg, int fd));
152MAYBE_UNUSED(static int consume_communication_pipe(int fd));
153
154static rb_atomic_t system_working = 1;
155static rb_internal_thread_specific_key_t specific_key_count;
156
157/********************************************************************************/
158
159#define THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
160
162 enum rb_thread_status prev_status;
163};
164
165static int unblock_function_set(rb_thread_t *th, rb_unblock_function_t *func, void *arg, int flags);
166static void unblock_function_clear(rb_thread_t *th);
167
168static inline int blocking_region_begin(rb_thread_t *th, struct rb_blocking_region_buffer *region,
169 rb_unblock_function_t *ubf, void *arg, int flags);
170static inline void blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region);
171
172#define THREAD_BLOCKING_BEGIN(th) do { \
173 struct rb_thread_sched * const sched = TH_SCHED(th); \
174 RB_VM_SAVE_MACHINE_CONTEXT(th); \
175 thread_sched_to_waiting((sched), (th), true);
176
177#define THREAD_BLOCKING_END(th) \
178 thread_sched_to_running((sched), (th)); \
179 rb_ractor_thread_switch(th->ractor, th, false); \
180} while(0)
181
182#ifdef __GNUC__
183#ifdef HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P
184#define only_if_constant(expr, notconst) __builtin_choose_expr(__builtin_constant_p(expr), (expr), (notconst))
185#else
186#define only_if_constant(expr, notconst) (__builtin_constant_p(expr) ? (expr) : (notconst))
187#endif
188#else
189#define only_if_constant(expr, notconst) notconst
190#endif
191#define RB_NOGVL_FAIL_FLAGS (RB_NOGVL_INTR_FAIL | RB_NOGVL_PENDING_INTR_FAIL)
192#define BLOCKING_REGION(th, exec, ubf, ubfarg, flags) do { \
193 struct rb_blocking_region_buffer __region; \
194 if (blocking_region_begin(th, &__region, (ubf), (ubfarg), flags) || \
195 /* always return true unless one of the fail flags is set */ \
196 !only_if_constant((flags) & RB_NOGVL_FAIL_FLAGS, TRUE)) { \
197 /* Important that this is inlined into the macro, and not part of \
198 * blocking_region_begin - see bug #20493 */ \
199 RB_VM_SAVE_MACHINE_CONTEXT(th); \
200 thread_sched_to_waiting(TH_SCHED(th), th, false); \
201 exec; \
202 blocking_region_end(th, &__region); \
203 }; \
204} while(0)
205
206/*
207 * returns true if this thread was spuriously interrupted, false otherwise
208 * (e.g. hit by Thread#run or ran a Ruby-level Signal.trap handler)
209 */
210#define RUBY_VM_CHECK_INTS_BLOCKING(ec) vm_check_ints_blocking(ec)
211static inline int
212vm_check_ints_blocking(rb_execution_context_t *ec)
213{
214#ifdef RUBY_ASSERT_CRITICAL_SECTION
215 VM_ASSERT(ruby_assert_critical_section_entered == 0);
216#endif
217
218 rb_thread_t *th = rb_ec_thread_ptr(ec);
219
220 if (LIKELY(rb_threadptr_pending_interrupt_empty_p(th))) {
221 if (LIKELY(!RUBY_VM_INTERRUPTED_ANY(ec))) return FALSE;
222 }
223 else {
224 th->pending_interrupt_queue_checked = 0;
225 RUBY_VM_SET_INTERRUPT(ec);
226 }
227
228 int result = rb_threadptr_execute_interrupts(th, 1);
229
230 // When a signal is received, we yield to the scheduler as soon as possible:
231 if (result || RUBY_VM_INTERRUPTED(ec)) {
233 if (scheduler != Qnil) {
234 rb_fiber_scheduler_yield(scheduler);
235 }
236 }
237
238 return result;
239}
240
241int
242rb_vm_check_ints_blocking(rb_execution_context_t *ec)
243{
244 return vm_check_ints_blocking(ec);
245}
246
247/*
248 * poll() is supported by many OSes, but so far Linux is the only
249 * one we know of that supports using poll() in all places select()
250 * would work.
251 */
252#if defined(HAVE_POLL)
253# if defined(__linux__)
254# define USE_POLL
255# endif
256# if defined(__FreeBSD_version) && __FreeBSD_version >= 1100000
257# define USE_POLL
258 /* FreeBSD does not set POLLOUT when POLLHUP happens */
259# define POLLERR_SET (POLLHUP | POLLERR)
260# endif
261#endif
262
263static void
264timeout_prepare(rb_hrtime_t **to, rb_hrtime_t *rel, rb_hrtime_t *end,
265 const struct timeval *timeout)
266{
267 if (timeout) {
268 *rel = rb_timeval2hrtime(timeout);
269 *end = rb_hrtime_add(rb_hrtime_now(), *rel);
270 *to = rel;
271 }
272 else {
273 *to = 0;
274 }
275}
276
277MAYBE_UNUSED(NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_start)));
278MAYBE_UNUSED(static bool th_has_dedicated_nt(const rb_thread_t *th));
279MAYBE_UNUSED(static int waitfd_to_waiting_flag(int wfd_event));
280
281#ifdef RB_THREAD_SCHED_NONE
282// The no-thread model is not a set of primitives under the common scheduler:
283// it replaces the scheduler with stubs, so it stands alone.
284# include THREAD_IMPL_SRC
285#else
286// The scheduler pulls in the platform implementation (THREAD_IMPL_SRC) itself:
287// the platform primitives come first, the scheduler is built on top of them.
288# include "thread_sched.c"
289#endif
290
291/*
292 * TODO: somebody with win32 knowledge should be able to get rid of
293 * timer-thread by busy-waiting on signals.
294 */
295#ifndef BUSY_WAIT_SIGNALS
296# define BUSY_WAIT_SIGNALS (0)
297#endif
298
299#ifndef USE_EVENTFD
300# define USE_EVENTFD (0)
301#endif
302
303#include "thread_sync.c"
304
305void
306rb_nativethread_lock_initialize(rb_nativethread_lock_t *lock)
307{
309}
310
311void
312rb_nativethread_lock_destroy(rb_nativethread_lock_t *lock)
313{
315}
316
317void
318rb_nativethread_lock_lock(rb_nativethread_lock_t *lock)
319{
321}
322
323void
324rb_nativethread_lock_unlock(rb_nativethread_lock_t *lock)
325{
327}
328
329static int
330unblock_function_set(rb_thread_t *th, rb_unblock_function_t *func, void *arg, int flags)
331{
332 do {
333 if (flags & RB_NOGVL_INTR_FAIL) {
334 if (RUBY_VM_INTERRUPTED_ANY(th->ec)) {
335 return FALSE;
336 }
337 }
338 else {
339 RUBY_VM_CHECK_INTS(th->ec);
340 }
341 if (flags & RB_NOGVL_PENDING_INTR_FAIL) {
342 if (!rb_threadptr_pending_interrupt_empty_p(th)) {
343 return FALSE;
344 }
345 }
346
347 rb_native_mutex_lock(&th->interrupt_lock);
348 } while (!th->ec->raised_flag && RUBY_VM_INTERRUPTED_ANY(th->ec) &&
349 (rb_native_mutex_unlock(&th->interrupt_lock), TRUE));
350
351 VM_ASSERT(th->unblock.func == NULL);
352
353 th->unblock.func = func;
354 th->unblock.arg = arg;
355 rb_native_mutex_unlock(&th->interrupt_lock);
356
357 return TRUE;
358}
359
360static void
361unblock_function_clear(rb_thread_t *th)
362{
363 rb_native_mutex_lock(&th->interrupt_lock);
364 th->unblock.func = 0;
365 rb_native_mutex_unlock(&th->interrupt_lock);
366}
367
368static void
369threadptr_set_interrupt_locked(rb_thread_t *th, bool trap)
370{
371 // th->interrupt_lock should be acquired here
372
373 RUBY_DEBUG_LOG("th:%u trap:%d", rb_th_serial(th), trap);
374
375 if (trap) {
376 RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
377 }
378 else {
379 RUBY_VM_SET_INTERRUPT(th->ec);
380 }
381
382 if (th->unblock.func != NULL) {
383 (th->unblock.func)(th->unblock.arg);
384 }
385 else {
386 /* none */
387 }
388}
389
390static void
391threadptr_set_interrupt(rb_thread_t *th, int trap)
392{
393 rb_native_mutex_lock(&th->interrupt_lock);
394 {
395 threadptr_set_interrupt_locked(th, trap);
396 }
397 rb_native_mutex_unlock(&th->interrupt_lock);
398}
399
400/* Set interrupt flag on another thread or current thread, and call its UBF if it has one set */
401void
402rb_threadptr_interrupt(rb_thread_t *th)
403{
404 RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
405 threadptr_set_interrupt(th, false);
406}
407
408static void
409threadptr_trap_interrupt(rb_thread_t *th)
410{
411 threadptr_set_interrupt(th, true);
412}
413
414static void
415terminate_all(rb_ractor_t *r, const rb_thread_t *main_thread)
416{
417 rb_thread_t *th = 0;
418
419 ccan_list_for_each(&r->threads.set, th, lt_node) {
420 if (th != main_thread) {
421 RUBY_DEBUG_LOG("terminate start th:%u status:%s", rb_th_serial(th), thread_status_name(th, TRUE));
422
423 rb_threadptr_pending_interrupt_enque(th, RUBY_FATAL_THREAD_TERMINATED);
424 rb_threadptr_interrupt(th);
425
426 RUBY_DEBUG_LOG("terminate done th:%u status:%s", rb_th_serial(th), thread_status_name(th, TRUE));
427 }
428 else {
429 RUBY_DEBUG_LOG("main thread th:%u", rb_th_serial(th));
430 }
431 }
432}
433
434static void
435rb_threadptr_join_list_wakeup(rb_thread_t *thread)
436{
437 while (thread->join_list) {
438 struct rb_waiting_list *join_list = thread->join_list;
439
440 // Consume the entry from the join list:
441 thread->join_list = join_list->next;
442
443 rb_thread_t *target_thread = join_list->thread;
444
445 if (target_thread->scheduler != Qnil && join_list->fiber) {
446 rb_fiber_scheduler_unblock(target_thread->scheduler, target_thread->self, rb_fiberptr_self(join_list->fiber));
447 }
448 else {
449 rb_threadptr_interrupt(target_thread);
450
451 switch (target_thread->status) {
452 case THREAD_STOPPED:
453 case THREAD_STOPPED_FOREVER:
454 target_thread->status = THREAD_RUNNABLE;
455 break;
456 default:
457 break;
458 }
459 }
460 }
461}
462
463void
464rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th)
465{
466 while (th->keeping_mutexes) {
467 rb_mutex_t *mutex = th->keeping_mutexes;
468 th->keeping_mutexes = mutex->next_mutex;
469
470 // rb_warn("mutex #<%p> was not unlocked by thread #<%p>", (void *)mutex, (void*)th);
471 VM_ASSERT(mutex->ec_serial);
472 const char *error_message = rb_mutex_unlock_th(mutex, th, 0);
473 if (error_message) rb_bug("invalid keeping_mutexes: %s", error_message);
474 }
475}
476
477void
478rb_thread_terminate_all(rb_thread_t *th)
479{
480 rb_ractor_t *cr = th->ractor;
481 rb_execution_context_t * volatile ec = th->ec;
482 volatile int sleeping = 0;
483
484 if (cr->threads.main != th) {
485 rb_bug("rb_thread_terminate_all: called by child thread (%p, %p)",
486 (void *)cr->threads.main, (void *)th);
487 }
488
489 /* unlock all locking mutexes */
490 rb_threadptr_unlock_all_locking_mutexes(th);
491
492 // tells the last sub-thread to wake this one out of the sleep below. Nothing
493 // clears it: no thread of this Ractor can run again once this returns.
494 cr->threads.terminating = true;
495
496 EC_PUSH_TAG(ec);
497 if (EC_EXEC_TAG() == TAG_NONE) {
498 retry:
499 RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
500
501 terminate_all(cr, th);
502
503 while (rb_ractor_living_thread_num(cr) > 1) {
504 rb_hrtime_t rel = RB_HRTIME_PER_SEC;
505 /*q
506 * Thread exiting routine in thread_start_func_2 notify
507 * me when the last sub-thread exit.
508 */
509 sleeping = 1;
510 native_sleep(th, &rel);
511 RUBY_VM_CHECK_INTS_BLOCKING(ec);
512 sleeping = 0;
513 }
514 }
515 else {
516 /*
517 * When caught an exception (e.g. Ctrl+C), let's broadcast
518 * kill request again to ensure killing all threads even
519 * if they are blocked on sleep, mutex, etc.
520 */
521 if (sleeping) {
522 sleeping = 0;
523 goto retry;
524 }
525 }
526 EC_POP_TAG();
527}
528
529void rb_threadptr_root_fiber_terminate(rb_thread_t *th);
530static void threadptr_interrupt_exec_cleanup(rb_thread_t *th);
531
532static void
533thread_cleanup_func_before_exec(void *th_ptr)
534{
535 rb_thread_t *th = th_ptr;
536 th->status = THREAD_KILLED;
537
538 // The thread stack doesn't exist in the forked process:
539 th->ec->machine.stack_start = th->ec->machine.stack_end = NULL;
540
541 threadptr_interrupt_exec_cleanup(th);
542 rb_threadptr_root_fiber_terminate(th);
543}
544
545static void
546thread_cleanup_func(void *th_ptr, int atfork)
547{
548 rb_thread_t *th = th_ptr;
549
550 th->locking_mutex = Qfalse;
551 thread_cleanup_func_before_exec(th_ptr);
552
553 if (atfork) {
554 native_thread_destroy_atfork(th->nt);
555 th->nt = NULL;
556 // The copied interrupt_lock may have been held at the moment of
557 // fork (interrupters run concurrently); reinitialize it so that
558 // thread_free's destroy is well-defined in the child.
559 rb_native_mutex_initialize(&th->interrupt_lock);
560 return;
561 }
562
563 // interrupt_lock is destroyed in thread_free: while th is in its
564 // Ractor's living set, anyone (terminate_all on the Ractor's main
565 // thread, Thread#kill/#raise) may lock it -- and the living set keeps
566 // the Thread object marked, so it cannot reach thread_free while
567 // listed. Destroying it anywhere during teardown leaves a window where
568 // a concurrent interrupter locks a destroyed mutex (EINVAL).
569}
570
571void
572rb_thread_free_native_thread(void *th_ptr)
573{
574 rb_thread_t *th = th_ptr;
575
576 native_thread_destroy_atfork(th->nt);
577 th->nt = NULL;
578}
579
580static VALUE rb_threadptr_raise(rb_thread_t *, int, VALUE *);
581static VALUE rb_thread_to_s(VALUE thread);
582
583void
584ruby_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame)
585{
586 native_thread_init_stack(th, local_in_parent_frame);
587}
588
589const VALUE *
590rb_vm_proc_local_ep(VALUE proc)
591{
592 const VALUE *ep = vm_proc_ep(proc);
593
594 if (ep) {
595 return rb_vm_ep_local_ep(ep);
596 }
597 else {
598 return NULL;
599 }
600}
601
602// for ractor, defined in vm.c
603VALUE rb_vm_invoke_proc_with_self(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
604 int argc, const VALUE *argv, int kw_splat, VALUE passed_block_handler,
605 const rb_cref_t *cref);
606
607static VALUE
608thread_do_start_proc(rb_thread_t *th)
609{
610 VALUE args = th->invoke_arg.proc.args;
611 const VALUE *args_ptr;
612 int args_len;
613 VALUE procval = th->invoke_arg.proc.proc;
614 rb_proc_t *proc;
615 GetProcPtr(procval, proc);
616 const rb_cref_t *cref = rb_proc_refinements_cref_for_call(procval);
617
618 th->ec->errinfo = Qnil;
619 th->ec->root_lep = rb_vm_proc_local_ep(procval);
620 th->ec->root_svar = Qfalse;
621
622 vm_check_ints_blocking(th->ec);
623
624 if (th->invoke_type == thread_invoke_type_ractor_proc) {
625 VALUE self = rb_ractor_self(th->ractor);
626 th->thgroup = th->ractor->thgroup_default = rb_obj_alloc(cThGroup);
627
628 VM_ASSERT(FIXNUM_P(args));
629 args_len = FIX2INT(args);
630 args_ptr = ALLOCA_N(VALUE, args_len);
631 rb_ractor_receive_parameters(th->ec, th->ractor, args_len, (VALUE *)args_ptr);
632 vm_check_ints_blocking(th->ec);
633
634 return rb_vm_invoke_proc_with_self(
635 th->ec, proc, self,
636 args_len, args_ptr,
637 th->invoke_arg.proc.kw_splat,
638 VM_BLOCK_HANDLER_NONE,
639 cref
640 );
641 }
642 else {
643 args_len = RARRAY_LENINT(args);
644 if (args_len < 8) {
645 /* free proc.args if the length is enough small */
646 args_ptr = ALLOCA_N(VALUE, args_len);
647 MEMCPY((VALUE *)args_ptr, RARRAY_CONST_PTR(args), VALUE, args_len);
648 th->invoke_arg.proc.args = Qnil;
649 }
650 else {
651 args_ptr = RARRAY_CONST_PTR(args);
652 }
653
654 vm_check_ints_blocking(th->ec);
655
656 return rb_vm_invoke_proc(
657 th->ec, proc,
658 args_len, args_ptr,
659 th->invoke_arg.proc.kw_splat,
660 VM_BLOCK_HANDLER_NONE,
661 cref
662 );
663 }
664}
665
666static VALUE
667thread_do_start(rb_thread_t *th)
668{
669 native_set_thread_name(th);
670 VALUE result = Qundef;
671
672 switch (th->invoke_type) {
673 case thread_invoke_type_proc:
674 result = thread_do_start_proc(th);
675 break;
676
677 case thread_invoke_type_ractor_proc:
678 result = thread_do_start_proc(th);
679 rb_ractor_atexit(th->ec, result);
680 break;
681
682 case thread_invoke_type_func:
683 result = (*th->invoke_arg.func.func)(th->invoke_arg.func.arg);
684 break;
685
686 case thread_invoke_type_none:
687 rb_bug("unreachable");
688 }
689
690 return result;
691}
692
693void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
694
695static int
696thread_start_func_2(rb_thread_t *th, VALUE *stack_start)
697{
698 RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
699 VM_ASSERT(th != th->vm->ractor.main_thread);
700
701 enum ruby_tag_type state;
702 VALUE errinfo = Qnil;
703 rb_thread_t *ractor_main_th = th->ractor->threads.main;
704
705 // setup ractor
706 if (rb_ractor_status_p(th->ractor, ractor_blocking)) {
707 RB_VM_LOCK();
708 {
709 rb_vm_ractor_blocking_cnt_dec(th->vm, th->ractor, __FILE__, __LINE__);
710
711 /* Left 0 at creation (building them then would put them in the parent's
712 * objspace), so build them here out of objects this Ractor owns. The mask
713 * stack starts empty: inheriting it would reference the parent's
714 * unshareable mask Hash. */
715 th->pending_interrupt_queue = rb_ary_hidden_new(0);
716 th->pending_interrupt_mask_stack = rb_ary_hidden_new(0);
717 }
718 RB_VM_UNLOCK();
719 }
720
721 // Ensure that we are not joinable.
722 VM_ASSERT(UNDEF_P(th->value));
723
724 volatile int fiber_scheduler_closed = 0, event_thread_end_hooked = 0;
725 VALUE result = Qundef;
726
727 EC_PUSH_TAG(th->ec);
728
729 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
730 EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_BEGIN, th->self, 0, 0, 0, Qundef);
731
732 result = thread_do_start(th);
733 }
734
735 if (!fiber_scheduler_closed) {
736 fiber_scheduler_closed = 1;
738 }
739
740 if (!event_thread_end_hooked) {
741 event_thread_end_hooked = 1;
742 EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
743 }
744
745 if (state == TAG_NONE) {
746 // This must be set AFTER doing all user-level code. At this point, the thread is effectively finished and calls to `Thread#join` will succeed.
747 th->value = result;
748 }
749 else {
750 errinfo = th->ec->errinfo;
751
752 VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
753 if (!NIL_P(exc)) errinfo = exc;
754
755 if (state == TAG_FATAL) {
756 if (th->invoke_type == thread_invoke_type_ractor_proc) {
757 rb_ractor_atexit(th->ec, Qnil);
758 }
759 /* fatal error within this thread, need to stop whole script */
760 }
761 else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
762 if (th->invoke_type == thread_invoke_type_ractor_proc) {
763 rb_ractor_atexit_exception(th->ec);
764 }
765
766 /* exit on main_thread. */
767 }
768 else {
769 if (th->report_on_exception) {
770 VALUE mesg = rb_thread_to_s(th->self);
771 rb_str_cat_cstr(mesg, " terminated with exception (report_on_exception is true):\n");
772 rb_write_error_str(mesg);
773 rb_ec_error_print(th->ec, errinfo);
774 }
775
776 if (th->invoke_type == thread_invoke_type_ractor_proc) {
777 rb_ractor_atexit_exception(th->ec);
778 }
779
780 if (th->vm->thread_abort_on_exception ||
781 th->abort_on_exception || RTEST(ruby_debug)) {
782 /* exit on main_thread */
783 }
784 else {
785 errinfo = Qnil;
786 }
787 }
788 th->value = Qnil;
789 }
790
791 // The thread is effectively finished and can be joined.
792 VM_ASSERT(!UNDEF_P(th->value));
793
794 rb_threadptr_join_list_wakeup(th);
795 rb_threadptr_unlock_all_locking_mutexes(th);
796
797 if (th->invoke_type == thread_invoke_type_ractor_proc) {
798 rb_thread_terminate_all(th);
799 rb_ractor_teardown(th->ec);
800 }
801
802 th->status = THREAD_KILLED;
803 RUBY_DEBUG_LOG("killed th:%u", rb_th_serial(th));
804
805 if (th->vm->ractor.main_thread == th) {
806 ruby_stop(0);
807 }
808
809 if (RB_TYPE_P(errinfo, T_OBJECT)) {
810 /* treat with normal error object */
811 rb_threadptr_raise(ractor_main_th, 1, &errinfo);
812 }
813
814 EC_POP_TAG();
815
816 rb_ec_clear_current_thread_trace_func(th->ec);
817
818 /* locking_mutex must be Qfalse */
819 if (th->locking_mutex != Qfalse) {
820 rb_bug("thread_start_func_2: locking_mutex must not be set (%p:%"PRIxVALUE")",
821 (void *)th, th->locking_mutex);
822 }
823
824 if (th->ractor->threads.terminating &&
825 th->ractor->threads.cnt <= 2 /* main thread and this thread */) {
826 /* I'm last thread. wake up main thread from rb_thread_terminate_all */
827 rb_threadptr_interrupt(ractor_main_th);
828 }
829
830 rb_check_deadlock(th->ractor);
831
832 rb_fiber_close(th->ec->fiber_ptr);
833
834 thread_cleanup_func(th, FALSE);
835 VM_ASSERT(th->ec->vm_stack == NULL);
836
837 // A dying Ractor collects its own objspace here, before the scheduler handoff
838 // below, and captures the structs it must free at its last step.
839 struct rb_ractor_postmortem_frees pf = { NULL, NULL };
840 if (th->invoke_type == thread_invoke_type_ractor_proc) {
841 rb_ractor_postmortem(th, &pf);
842 }
843
844#if defined(USE_MN_THREADS) && USE_MN_THREADS
845 if (th_has_coroutine(th)) {
846 // wait out any pending wake while th and its Ractor are still alive
847 rb_thread_wake_fence(th);
848
849 // Run the coroutine thread's epilogue here, while th is still valid;
850 // co_start then only makes the final transfer (see
851 // coroutine_thread_terminated in thread_sched_mn.c).
852 coroutine_thread_terminated(th);
853 rb_ractor_postmortem_free(&pf);
854 return 0;
855 }
856#endif
857
858 if (th->invoke_type == thread_invoke_type_ractor_proc) {
859 // The postmortem epilogue below runs after this Ractor is unlinked and no
860 // longer counted, with the GVL already released, and it frees through
861 // VM-global state (the jit_cont list and its mutex, the fiber pool, the
862 // main objspace's malloc accounting). Nothing else holds the main Ractor
863 // back at that point, so count it like a coroutine epilogue: then
864 // ruby_vm_destruct waits for it (rb_thread_sched_wait_winding) instead of
865 // tearing that state down underneath. th is freed by the epilogue, so
866 // keep the VM pointer.
867 rb_vm_t *const vm = th->vm;
868 rb_thread_sched_winding_begin(vm);
869
870 // after rb_ractor_living_threads_remove()
871 // GC will happen anytime and this ractor can be collected (and destroy GVL).
872 // So gvl_release() should be before it.
873 thread_sched_to_dead(TH_SCHED(th), th);
874 rb_ractor_living_threads_remove(th->ractor, th);
875 rb_ractor_postmortem_free(&pf);
876
877 rb_thread_sched_winding_end(vm);
878 }
879 else {
880 rb_ractor_living_threads_remove(th->ractor, th);
881 thread_sched_to_dead(TH_SCHED(th), th);
882 }
883
884 return 0;
885}
888 enum thread_invoke_type type;
889
890 // for normal proc thread
891 VALUE args;
892 VALUE proc;
893
894 // for ractor
895 rb_ractor_t *g;
896
897 // for func
898 VALUE (*fn)(void *);
899};
900
901static void thread_specific_storage_alloc(rb_thread_t *th);
902
903static VALUE
904thread_create_core(VALUE thval, struct thread_create_params *params)
905{
906 rb_execution_context_t *ec = GET_EC();
907 rb_thread_t *th = rb_thread_ptr(thval), *current_th = rb_ec_thread_ptr(ec);
908 int err;
909
910 thread_specific_storage_alloc(th);
911
912 if (OBJ_FROZEN(current_th->thgroup)) {
913 rb_raise(rb_eThreadError,
914 "can't start a new thread (frozen ThreadGroup)");
915 }
916
917 /* A new Ractor must not inherit the creating thread's fiber storage: its
918 * entries may be objects owned by the creating Ractor. Only threads created
919 * within the same Ractor inherit it. */
920 if (params->type != thread_invoke_type_ractor_proc) {
921 rb_fiber_inherit_storage(ec, th->ec->fiber_ptr);
922 }
923
924 switch (params->type) {
925 case thread_invoke_type_proc:
926 th->invoke_type = thread_invoke_type_proc;
927 th->invoke_arg.proc.args = params->args;
928 th->invoke_arg.proc.proc = params->proc;
929 th->invoke_arg.proc.kw_splat = rb_keyword_given_p();
930 break;
931
932 case thread_invoke_type_ractor_proc:
933 th->invoke_type = thread_invoke_type_ractor_proc;
934 th->ractor = params->g;
935 th->ec->ractor_id = rb_ractor_id(th->ractor);
936 th->ractor->threads.main = th;
937 th->invoke_arg.proc.proc = rb_proc_isolate_bang(params->proc, Qnil);
938 th->invoke_arg.proc.args = INT2FIX(RARRAY_LENINT(params->args));
939 th->invoke_arg.proc.kw_splat = rb_keyword_given_p();
940 break;
941
942 case thread_invoke_type_func:
943 th->invoke_type = thread_invoke_type_func;
944 th->invoke_arg.func.func = params->fn;
945 th->invoke_arg.func.arg = (void *)params->args;
946 break;
947
948 default:
949 rb_bug("unreachable");
950 }
951
952 th->priority = current_th->priority;
953 th->thgroup = current_th->thgroup;
954
955 if (th->invoke_type == thread_invoke_type_ractor_proc) {
956 /* Left 0: the child's main thread builds this in its own objspace at start
957 * (thread_start_func_2). Built here it would sit rootless in the parent's
958 * objspace, freed by the parent's local GC before the child starts. */
959 th->pending_interrupt_queue = 0;
960 th->pending_interrupt_mask_stack = 0;
961 th->pending_interrupt_queue_checked = 0;
962 /* Same for the thread group: the parent's lives in the parent's objspace, and
963 * keeping it would point the child's Thread wrapper at a foreign unshareable
964 * object with no shref. Left 0 until thread_do_start_proc builds it. */
965 th->thgroup = 0;
966 }
967 else {
968 th->pending_interrupt_queue = rb_ary_hidden_new(0);
969 th->pending_interrupt_queue_checked = 0;
970 th->pending_interrupt_mask_stack = rb_ary_dup(current_th->pending_interrupt_mask_stack);
971 RBASIC_CLEAR_CLASS(th->pending_interrupt_mask_stack);
972 }
973
974 RUBY_DEBUG_LOG("r:%u th:%u", rb_ractor_id(th->ractor), rb_th_serial(th));
975
976 rb_ractor_living_threads_insert(th->ractor, th);
977
978 if (th->invoke_type == thread_invoke_type_ractor_proc) {
979 /* Create the default port and send the arguments only after the child joined
980 * vm->ractor.set, so a global GC in between still marks the port in its root
981 * scan. If either raises (an uncopyable argument, NoMemoryError), undo the
982 * membership: left in place it would make terminate_all wait forever. */
983 enum ruby_tag_type state;
984 EC_PUSH_TAG(ec);
985 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
986 rb_ractor_setup_default_port(params->g);
987 rb_ractor_send_parameters(ec, params->g, params->args);
988 }
989 EC_POP_TAG();
990 if (state != TAG_NONE) {
991 th->status = THREAD_KILLED;
992 rb_ractor_cancel_creation(params->g, th);
993 EC_JUMP_TAG(ec, state);
994 }
995 }
996
997 /* kick thread */
998 err = native_thread_create(th);
999 if (err) {
1000 th->status = THREAD_KILLED;
1001 if (th->invoke_type == thread_invoke_type_ractor_proc) {
1002 /* A child Ractor's main thread: the creator runs this, so the ordinary
1003 * removal (which assumes the current Ractor and would run the Ractor exit
1004 * protocol) does not apply. Undo the creation like the send-failure path. */
1005 rb_ractor_cancel_creation(th->ractor, th);
1006 }
1007 else {
1008 rb_ractor_living_threads_remove(th->ractor, th);
1009 }
1010 rb_raise(rb_eThreadError, "can't create Thread: %s", strerror(err));
1011 }
1012 return thval;
1013}
1014
1015#define threadptr_initialized(th) ((th)->invoke_type != thread_invoke_type_none)
1016
1017/*
1018 * call-seq:
1019 * Thread.new { ... } -> thread
1020 * Thread.new(*args, &proc) -> thread
1021 * Thread.new(*args) { |args| ... } -> thread
1022 *
1023 * Creates a new thread executing the given block.
1024 *
1025 * Any +args+ given to ::new will be passed to the block:
1026 *
1027 * arr = []
1028 * a, b, c = 1, 2, 3
1029 * Thread.new(a,b,c) { |d,e,f| arr << d << e << f }.join
1030 * arr #=> [1, 2, 3]
1031 *
1032 * A ThreadError exception is raised if ::new is called without a block.
1033 *
1034 * If you're going to subclass Thread, be sure to call super in your
1035 * +initialize+ method, otherwise a ThreadError will be raised.
1036 */
1037static VALUE
1038thread_s_new(int argc, VALUE *argv, VALUE klass)
1039{
1040 rb_thread_t *th;
1041 VALUE thread = rb_thread_alloc(klass);
1042
1043 if (GET_RACTOR()->threads.main->status == THREAD_KILLED) {
1044 rb_raise(rb_eThreadError, "can't alloc thread");
1045 }
1046
1047 rb_obj_call_init_kw(thread, argc, argv, RB_PASS_CALLED_KEYWORDS);
1048 th = rb_thread_ptr(thread);
1049 if (!threadptr_initialized(th)) {
1050 rb_raise(rb_eThreadError, "uninitialized thread - check '%"PRIsVALUE"#initialize'",
1051 klass);
1052 }
1053 return thread;
1054}
1055
1056/*
1057 * call-seq:
1058 * Thread.start([args]*) {|args| block } -> thread
1059 * Thread.fork([args]*) {|args| block } -> thread
1060 *
1061 * Basically the same as ::new. However, if class Thread is subclassed, then
1062 * calling +start+ in that subclass will not invoke the subclass's
1063 * +initialize+ method.
1064 */
1065
1066static VALUE
1067thread_start(VALUE klass, VALUE args)
1068{
1069 struct thread_create_params params = {
1070 .type = thread_invoke_type_proc,
1071 .args = args,
1072 .proc = rb_block_proc(),
1073 };
1074 return thread_create_core(rb_thread_alloc(klass), &params);
1075}
1076
1077static VALUE
1078threadptr_invoke_proc_location(rb_thread_t *th)
1079{
1080 if (th->invoke_type == thread_invoke_type_proc) {
1081 return rb_proc_location(th->invoke_arg.proc.proc);
1082 }
1083 else {
1084 return Qnil;
1085 }
1086}
1087
1088/* :nodoc: */
1089static VALUE
1090thread_initialize(VALUE thread, VALUE args)
1091{
1092 rb_thread_t *th = rb_thread_ptr(thread);
1093
1094 if (!rb_block_given_p()) {
1095 rb_raise(rb_eThreadError, "must be called with a block");
1096 }
1097 else if (th->invoke_type != thread_invoke_type_none) {
1098 VALUE loc = threadptr_invoke_proc_location(th);
1099 if (!NIL_P(loc)) {
1100 rb_raise(rb_eThreadError,
1101 "already initialized thread - %"PRIsVALUE":%"PRIsVALUE,
1102 RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
1103 }
1104 else {
1105 rb_raise(rb_eThreadError, "already initialized thread");
1106 }
1107 }
1108 else {
1109 struct thread_create_params params = {
1110 .type = thread_invoke_type_proc,
1111 .args = args,
1112 .proc = rb_block_proc(),
1113 };
1114 return thread_create_core(thread, &params);
1115 }
1116}
1117
1118VALUE
1119rb_thread_create(VALUE (*fn)(void *), void *arg)
1120{
1121 struct thread_create_params params = {
1122 .type = thread_invoke_type_func,
1123 .fn = fn,
1124 .args = (VALUE)arg,
1125 };
1126 return thread_create_core(rb_thread_alloc(rb_cThread), &params);
1127}
1128
1129static VALUE
1130create_ractor_alloc_thread(rb_ractor_t *r, rb_ractor_t *cr, rb_execution_context_t *ec)
1131{
1132 /* Allocate the child's main Thread and root Fiber wrappers directly in the child's
1133 * objspace, so the thread is built of objects it owns. Whole-VM walks read
1134 * cr->objspace: swap it under the VM lock, unobservable to others. */
1135 volatile VALUE thval = Qundef;
1136 const bool multi_objspace = rb_gc_multi_objspace_p();
1137 enum ruby_tag_type alloc_state = TAG_NONE;
1138 RB_VM_LOCKING() {
1139 void *const parent_objspace = cr->objspace;
1140 if (multi_objspace) cr->objspace = r->objspace;
1141 /* The wrapper allocations must not re-enter GC: while cr->objspace points at
1142 * the child, the creator's own objspace is invisible to every walk, so a global
1143 * GC would skip it and leave stale mark bits (a UAF). Single allocations;
1144 * suppressing GC costs only a little growth. */
1145 VALUE gc_was_disabled = rb_gc_local_disable_no_rest();
1146 /* The alloc can raise NoMemoryError; a longjmp here would skip both the unlock
1147 * of RB_VM_LOCKING and the objspace restore, so catch and rethrow outside. */
1148 EC_PUSH_TAG(ec);
1149 if ((alloc_state = EC_EXEC_TAG()) == TAG_NONE) {
1150 thval = rb_thread_alloc(rb_cThread);
1151 }
1152 EC_POP_TAG();
1153 if (gc_was_disabled == Qfalse) rb_gc_local_enable();
1154 if (multi_objspace) cr->objspace = parent_objspace;
1155 /* The child's objspace holds the wrappers but is not in vm->ractor.set yet:
1156 * keep it enumerable until vm_insert_ractor clears this under the VM lock. One
1157 * slot suffices: the GVL is never released between set and clear and one
1158 * Ractor creates children serially, so no overwrite (asserted: releasing the
1159 * GVL here in the future would break it). */
1160 if (alloc_state == TAG_NONE && multi_objspace) {
1161 RUBY_ASSERT(cr->creating_child_objspace == NULL);
1162 cr->creating_child_objspace = r->objspace;
1163 }
1164 }
1165 if (alloc_state != TAG_NONE) {
1166 /* No cover was set; park the child objspace for the orphan merge and re-raise. */
1167 RB_VM_LOCKING() {
1168 if (r->objspace) {
1169 rb_gc_objspace_disown(r->objspace);
1170 r->objspace = NULL;
1171 }
1172 }
1173 EC_JUMP_TAG(ec, alloc_state);
1174 }
1175 return thval;
1176}
1177
1178VALUE
1179rb_thread_create_ractor(rb_ractor_t *r, VALUE args, VALUE proc)
1180{
1181 struct thread_create_params params = {
1182 .type = thread_invoke_type_ractor_proc,
1183 .g = r,
1184 .args = args,
1185 .proc = proc,
1186 };
1187
1188 rb_ractor_t *cr = GET_RACTOR();
1189 rb_execution_context_t *ec = GET_EC();
1190
1191 VALUE thval = create_ractor_alloc_thread(r, cr, ec);
1192
1193 /* Creation can still fail before vm_insert_ractor (an IsolationError, say), and a
1194 * left-over cover would enumerate the dead child's objspace twice and dangle after
1195 * the merge: on failure hand the objspace to zombie_objspaces under the VM lock,
1196 * drop the cover, NULL r->objspace. */
1197 enum ruby_tag_type state;
1198 VALUE thret = Qundef;
1199 EC_PUSH_TAG(ec);
1200 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1201 thret = thread_create_core(thval, &params);
1202 }
1203 EC_POP_TAG();
1204 if (state != TAG_NONE) {
1205 RB_VM_LOCKING() {
1206 if (cr->creating_child_objspace == r->objspace) {
1207 cr->creating_child_objspace = NULL;
1208 }
1209 if (r->objspace) {
1210 rb_gc_objspace_disown(r->objspace);
1211 r->objspace = NULL;
1212 }
1213 }
1214 EC_JUMP_TAG(ec, state);
1215 }
1216 return thret;
1217}
1218
1220struct join_arg {
1221 struct rb_waiting_list *waiter;
1222 rb_thread_t *target;
1223 VALUE timeout;
1224 rb_hrtime_t *limit;
1225};
1226
1227static VALUE
1228remove_from_join_list(VALUE arg)
1229{
1230 struct join_arg *p = (struct join_arg *)arg;
1231 rb_thread_t *target_thread = p->target;
1232
1233 if (target_thread->status != THREAD_KILLED) {
1234 struct rb_waiting_list **join_list = &target_thread->join_list;
1235
1236 while (*join_list) {
1237 if (*join_list == p->waiter) {
1238 *join_list = (*join_list)->next;
1239 break;
1240 }
1241
1242 join_list = &(*join_list)->next;
1243 }
1244 }
1245
1246 return Qnil;
1247}
1248
1249static int
1250thread_finished(rb_thread_t *th)
1251{
1252 return th->status == THREAD_KILLED || !UNDEF_P(th->value);
1253}
1254
1255static VALUE
1256thread_join_sleep(VALUE arg)
1257{
1258 struct join_arg *p = (struct join_arg *)arg;
1259 rb_thread_t *target_th = p->target, *th = p->waiter->thread;
1260 rb_hrtime_t end = 0, *limit = p->limit;
1261
1262 if (limit) {
1263 end = rb_hrtime_add(*limit, rb_hrtime_now());
1264 }
1265
1266 while (!thread_finished(target_th)) {
1268
1269 if (!limit) {
1270 if (scheduler != Qnil) {
1271 rb_fiber_scheduler_block(scheduler, target_th->self, Qnil);
1272 }
1273 else {
1274 sleep_forever(th, SLEEP_DEADLOCKABLE | SLEEP_ALLOW_SPURIOUS | SLEEP_NO_CHECKINTS);
1275 }
1276 }
1277 else {
1278 if (hrtime_update_expire(limit, end)) {
1279 RUBY_DEBUG_LOG("timeout target_th:%u", rb_th_serial(target_th));
1280 return Qfalse;
1281 }
1282
1283 if (scheduler != Qnil) {
1284 VALUE timeout = rb_float_new(hrtime2double(*limit));
1285 rb_fiber_scheduler_block(scheduler, target_th->self, timeout);
1286 }
1287 else {
1288 th->status = THREAD_STOPPED;
1289 native_sleep(th, limit);
1290 }
1291 }
1292 RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1293 th->status = THREAD_RUNNABLE;
1294
1295 RUBY_DEBUG_LOG("interrupted target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1296 }
1297
1298 return Qtrue;
1299}
1300
1301static VALUE
1302thread_join(rb_thread_t *target_th, VALUE timeout, rb_hrtime_t *limit)
1303{
1304 rb_execution_context_t *ec = GET_EC();
1305 rb_thread_t *th = ec->thread_ptr;
1306 rb_fiber_t *fiber = ec->fiber_ptr;
1307
1308 if (th == target_th) {
1309 rb_raise(rb_eThreadError, "Target thread must not be current thread");
1310 }
1311
1312 if (th->ractor->threads.main == target_th) {
1313 rb_raise(rb_eThreadError, "Target thread must not be main thread");
1314 }
1315
1316 RUBY_DEBUG_LOG("target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1317
1318 if (target_th->status != THREAD_KILLED) {
1319 struct rb_waiting_list waiter;
1320 waiter.next = target_th->join_list;
1321 waiter.thread = th;
1322 waiter.fiber = rb_fiberptr_blocking(fiber) ? NULL : fiber;
1323 target_th->join_list = &waiter;
1324
1325 struct join_arg arg;
1326 arg.waiter = &waiter;
1327 arg.target = target_th;
1328 arg.timeout = timeout;
1329 arg.limit = limit;
1330
1331 if (!rb_ensure(thread_join_sleep, (VALUE)&arg, remove_from_join_list, (VALUE)&arg)) {
1332 return Qnil;
1333 }
1334 }
1335
1336 RUBY_DEBUG_LOG("success target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1337
1338 if (target_th->ec->errinfo != Qnil) {
1339 VALUE err = target_th->ec->errinfo;
1340
1341 if (FIXNUM_P(err)) {
1342 switch (err) {
1343 case INT2FIX(TAG_FATAL):
1344 RUBY_DEBUG_LOG("terminated target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1345
1346 /* OK. killed. */
1347 break;
1348 default:
1349 if (err == RUBY_FATAL_FIBER_KILLED) { // not integer constant so can't be a case expression
1350 // root fiber killed in non-main thread
1351 break;
1352 }
1353 rb_bug("thread_join: Fixnum (%d) should not reach here.", FIX2INT(err));
1354 }
1355 }
1356 else if (THROW_DATA_P(target_th->ec->errinfo)) {
1357 rb_bug("thread_join: THROW_DATA should not reach here.");
1358 }
1359 else {
1360 /* normal exception */
1361 rb_exc_raise(err);
1362 }
1363 }
1364 return target_th->self;
1365}
1366
1367/*
1368 * call-seq:
1369 * thr.join -> thr
1370 * thr.join(limit) -> thr
1371 *
1372 * The calling thread will suspend execution and run this +thr+.
1373 *
1374 * Does not return until +thr+ exits or until the given +limit+ seconds have
1375 * passed.
1376 *
1377 * If the time limit expires, +nil+ will be returned, otherwise +thr+ is
1378 * returned.
1379 *
1380 * Any threads not joined will be killed when the main program exits.
1381 *
1382 * If +thr+ had previously raised an exception and the ::abort_on_exception or
1383 * $DEBUG flags are not set, (so the exception has not yet been processed), it
1384 * will be processed at this time.
1385 *
1386 * a = Thread.new { print "a"; sleep(10); print "b"; print "c" }
1387 * x = Thread.new { print "x"; Thread.pass; print "y"; print "z" }
1388 * x.join # Let thread x finish, thread a will be killed on exit.
1389 * #=> "axyz"
1390 *
1391 * The following example illustrates the +limit+ parameter.
1392 *
1393 * y = Thread.new { 4.times { sleep 0.1; puts 'tick... ' }}
1394 * puts "Waiting" until y.join(0.15)
1395 *
1396 * This will produce:
1397 *
1398 * tick...
1399 * Waiting
1400 * tick...
1401 * Waiting
1402 * tick...
1403 * tick...
1404 */
1405
1406static VALUE
1407thread_join_m(int argc, VALUE *argv, VALUE self)
1408{
1409 VALUE timeout = Qnil;
1410 rb_hrtime_t rel = 0, *limit = 0;
1411
1412 if (rb_check_arity(argc, 0, 1)) {
1413 timeout = argv[0];
1414 }
1415
1416 // Convert the timeout eagerly, so it's always converted and deterministic
1417 /*
1418 * This supports INFINITY and negative values, so we can't use
1419 * rb_time_interval right now...
1420 */
1421 if (NIL_P(timeout)) {
1422 /* unlimited */
1423 }
1424 else if (FIXNUM_P(timeout)) {
1425 rel = rb_sec2hrtime(NUM2TIMET(timeout));
1426 limit = &rel;
1427 }
1428 else {
1429 limit = double2hrtime(&rel, rb_num2dbl(timeout));
1430 }
1431
1432 return thread_join(rb_thread_ptr(self), timeout, limit);
1433}
1434
1435/*
1436 * call-seq:
1437 * thr.value -> obj
1438 *
1439 * Waits for +thr+ to complete, using #join, and returns its value or raises
1440 * the exception which terminated the thread.
1441 *
1442 * a = Thread.new { 2 + 2 }
1443 * a.value #=> 4
1444 *
1445 * b = Thread.new { raise 'something went wrong' }
1446 * b.value #=> RuntimeError: something went wrong
1447 */
1448
1449static VALUE
1450thread_value(VALUE self)
1451{
1452 rb_thread_t *th = rb_thread_ptr(self);
1453 thread_join(th, Qnil, 0);
1454 if (UNDEF_P(th->value)) {
1455 // If the thread is dead because we forked th->value is still Qundef.
1456 return Qnil;
1457 }
1458 return th->value;
1459}
1460
1461/*
1462 * Thread Scheduling
1463 */
1464
1465static void
1466getclockofday(struct timespec *ts)
1467{
1468#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
1469 if (clock_gettime(CLOCK_MONOTONIC, ts) == 0)
1470 return;
1471#endif
1472 rb_timespec_now(ts);
1473}
1474
1475/*
1476 * Don't inline this, since library call is already time consuming
1477 * and we don't want "struct timespec" on stack too long for GC
1478 */
1479NOINLINE(rb_hrtime_t rb_hrtime_now(void));
1480rb_hrtime_t
1481rb_hrtime_now(void)
1482{
1483 struct timespec ts;
1484
1485 getclockofday(&ts);
1486 return rb_timespec2hrtime(&ts);
1487}
1488
1489/*
1490 * at least gcc 7.2 and 7.3 complains about "rb_hrtime_t end"
1491 * being uninitialized, maybe other versions, too.
1492 */
1493COMPILER_WARNING_PUSH
1494#if defined(__GNUC__) && __GNUC__ == 7 && __GNUC_MINOR__ <= 3
1495COMPILER_WARNING_IGNORED(-Wmaybe-uninitialized)
1496#endif
1497#ifndef PRIu64
1498#define PRIu64 PRI_64_PREFIX "u"
1499#endif
1500/*
1501 * @end is the absolute time when @ts is set to expire
1502 * Returns true if @end has past
1503 * Updates @ts and returns false otherwise
1504 */
1505static int
1506hrtime_update_expire(rb_hrtime_t *timeout, const rb_hrtime_t end)
1507{
1508 rb_hrtime_t now = rb_hrtime_now();
1509
1510 if (now > end) return 1;
1511
1512 RUBY_DEBUG_LOG("%"PRIu64" > %"PRIu64"", (uint64_t)end, (uint64_t)now);
1513
1514 *timeout = end - now;
1515 return 0;
1516}
1517COMPILER_WARNING_POP
1518
1519static int sleep_hrtime_until(rb_thread_t *th, rb_hrtime_t end, unsigned int fl);
1520
1521static int
1522sleep_hrtime(rb_thread_t *th, rb_hrtime_t rel, unsigned int fl)
1523{
1524 return sleep_hrtime_until(th, rb_hrtime_add(rb_hrtime_now(), rel), fl);
1525}
1526
1527static int
1528sleep_hrtime_until(rb_thread_t *th, rb_hrtime_t end, unsigned int fl)
1529{
1530 enum rb_thread_status prev_status = th->status;
1531 int woke;
1532 rb_hrtime_t rel = rb_hrtime_sub(end, rb_hrtime_now());
1533
1534 th->status = THREAD_STOPPED;
1535 RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1536 while (th->status == THREAD_STOPPED) {
1537 native_sleep(th, &rel);
1538 woke = vm_check_ints_blocking(th->ec);
1539 if (woke && !(fl & SLEEP_SPURIOUS_CHECK))
1540 break;
1541 if (hrtime_update_expire(&rel, end))
1542 break;
1543 woke = 1;
1544 }
1545 th->status = prev_status;
1546 return woke;
1547}
1548
1549static void
1550sleep_forever(rb_thread_t *th, unsigned int fl)
1551{
1552 enum rb_thread_status prev_status = th->status;
1553 enum rb_thread_status status;
1554 int woke;
1555
1556 status = fl & SLEEP_DEADLOCKABLE ? THREAD_STOPPED_FOREVER : THREAD_STOPPED;
1557 th->status = status;
1558
1559 if (!(fl & SLEEP_NO_CHECKINTS)) RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1560
1561 while (th->status == status) {
1562 if (fl & SLEEP_DEADLOCKABLE) {
1563 rb_ractor_sleeper_threads_inc(th->ractor);
1564 rb_check_deadlock(th->ractor);
1565 }
1566 {
1567 native_sleep(th, 0);
1568 }
1569 if (fl & SLEEP_DEADLOCKABLE) {
1570 rb_ractor_sleeper_threads_dec(th->ractor);
1571 }
1572 if (fl & SLEEP_ALLOW_SPURIOUS) {
1573 break;
1574 }
1575
1576 woke = vm_check_ints_blocking(th->ec);
1577
1578 if (woke && !(fl & SLEEP_SPURIOUS_CHECK)) {
1579 break;
1580 }
1581 }
1582 th->status = prev_status;
1583}
1584
1585void
1587{
1588 RUBY_DEBUG_LOG("forever");
1589 sleep_forever(GET_THREAD(), SLEEP_SPURIOUS_CHECK);
1590}
1591
1592void
1594{
1595 RUBY_DEBUG_LOG("deadly");
1596 sleep_forever(GET_THREAD(), SLEEP_DEADLOCKABLE|SLEEP_SPURIOUS_CHECK);
1597}
1598
1599static void
1600rb_thread_sleep_deadly_allow_spurious_wakeup(VALUE blocker, VALUE timeout, rb_hrtime_t end)
1601{
1602 rb_thread_t *th = GET_THREAD();
1604 if (scheduler != Qnil) {
1605 rb_fiber_scheduler_block(scheduler, blocker, timeout);
1606 }
1607 else {
1608 RUBY_DEBUG_LOG("...");
1609 if (end) {
1610 sleep_hrtime_until(th, end, SLEEP_SPURIOUS_CHECK);
1611 }
1612 else {
1613 sleep_forever(th, SLEEP_DEADLOCKABLE);
1614 }
1615 }
1616}
1617
1618void
1619rb_thread_wait_for(struct timeval time)
1620{
1621 rb_thread_t *th = GET_THREAD();
1622
1623 sleep_hrtime(th, rb_timeval2hrtime(&time), SLEEP_SPURIOUS_CHECK);
1624}
1625
1626void
1627rb_ec_check_ints(rb_execution_context_t *ec)
1628{
1629 RUBY_VM_CHECK_INTS_BLOCKING(ec);
1630}
1631
1632/*
1633 * CAUTION: This function causes thread switching.
1634 * rb_thread_check_ints() check ruby's interrupts.
1635 * some interrupt needs thread switching/invoke handlers,
1636 * and so on.
1637 */
1638
1639void
1641{
1642 rb_ec_check_ints(GET_EC());
1643}
1644
1645/*
1646 * Hidden API for tcl/tk wrapper.
1647 * There is no guarantee to perpetuate it.
1648 */
1649int
1650rb_thread_check_trap_pending(void)
1651{
1652 return rb_signal_buff_size() != 0;
1653}
1654
1655/* This function can be called in blocking region. */
1658{
1659 return (int)RUBY_VM_INTERRUPTED(rb_thread_ptr(thval)->ec);
1660}
1661
1662void
1663rb_thread_sleep(int sec)
1664{
1666}
1667
1668static void
1669rb_thread_schedule_limits(uint32_t limits_us)
1670{
1671 if (!rb_thread_alone()) {
1672 rb_thread_t *th = GET_THREAD();
1673 RUBY_DEBUG_LOG("us:%u", (unsigned int)limits_us);
1674
1675 if (th->running_time_us >= limits_us) {
1676 RUBY_DEBUG_LOG("switch %s", "start");
1677
1678 RB_VM_SAVE_MACHINE_CONTEXT(th);
1679 thread_sched_yield(TH_SCHED(th), th);
1680 rb_ractor_thread_switch(th->ractor, th, true);
1681
1682 RUBY_DEBUG_LOG("switch %s", "done");
1683 }
1684 }
1685}
1686
1687void
1689{
1690 rb_thread_schedule_limits(0);
1691 RUBY_VM_CHECK_INTS(GET_EC());
1692}
1693
1694/* blocking region */
1695
1696static inline int
1697blocking_region_begin(rb_thread_t *th, struct rb_blocking_region_buffer *region,
1698 rb_unblock_function_t *ubf, void *arg, int flags)
1699{
1700#ifdef RUBY_ASSERT_CRITICAL_SECTION
1701 VM_ASSERT(ruby_assert_critical_section_entered == 0);
1702#endif
1703 VM_ASSERT(th == GET_THREAD());
1704
1705 region->prev_status = th->status;
1706 if (unblock_function_set(th, ubf, arg, flags)) {
1707 th->blocking_region_buffer = region;
1708 th->status = THREAD_STOPPED;
1709 rb_ractor_blocking_threads_inc(th->ractor, __FILE__, __LINE__);
1710
1711 RUBY_DEBUG_LOG("thread_id:%p", (void *)th->nt->thread_id);
1712 return TRUE;
1713 }
1714 else {
1715 return FALSE;
1716 }
1717}
1718
1719static inline void
1720blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region)
1721{
1722 /* entry to ubf_list still permitted at this point, make it impossible: */
1723 unblock_function_clear(th);
1724 /* entry to ubf_list impossible at this point, so unregister is safe: */
1725 unregister_ubf_list(th);
1726
1727 thread_sched_to_running(TH_SCHED(th), th);
1728 rb_ractor_thread_switch(th->ractor, th, false);
1729
1730 th->blocking_region_buffer = 0;
1731 rb_ractor_blocking_threads_dec(th->ractor, __FILE__, __LINE__);
1732 if (th->status == THREAD_STOPPED) {
1733 th->status = region->prev_status;
1734 }
1735
1736 RUBY_DEBUG_LOG("end");
1737
1738#ifndef _WIN32
1739 // GET_THREAD() clears WSAGetLastError()
1740 VM_ASSERT(th == GET_THREAD());
1741#endif
1742}
1743
1744/*
1745 * Resolve sentinel unblock function values to their actual function pointers
1746 * and appropriate data2 values. This centralizes the logic for handling
1747 * RUBY_UBF_IO and RUBY_UBF_PROCESS sentinel values.
1748 *
1749 * @param unblock_function Pointer to unblock function pointer (modified in place)
1750 * @param data2 Pointer to data2 pointer (modified in place)
1751 * @param thread Thread context for resolving data2 when needed
1752 * @return true if sentinel values were resolved, false otherwise
1753 */
1754bool
1755rb_thread_resolve_unblock_function(rb_unblock_function_t **unblock_function, void **data2, struct rb_thread_struct *thread)
1756{
1757 rb_unblock_function_t *ubf = *unblock_function;
1758
1759 if ((ubf == RUBY_UBF_IO) || (ubf == RUBY_UBF_PROCESS)) {
1760 *unblock_function = ubf_select;
1761 *data2 = thread;
1762 return true;
1763 }
1764 return false;
1765}
1766
1767void *
1768rb_nogvl(void *(*func)(void *), void *data1,
1769 rb_unblock_function_t *ubf, void *data2,
1770 int flags)
1771{
1772 rb_execution_context_t *ec = GET_EC();
1773 rb_thread_t *th = rb_ec_thread_ptr(ec);
1774
1775 if (
1776 (flags & RB_NOGVL_PENDING_INTR_FAIL) &&
1777 !rb_threadptr_pending_interrupt_empty_p(th)
1778 ) {
1779 /* Match the in-region skip path, which leaves errno at saved_errno (0)
1780 * because the function was never called. */
1781 rb_errno_set(0);
1782 return 0;
1783 }
1784
1785 if (flags & RB_NOGVL_OFFLOAD_SAFE) {
1786 VALUE scheduler = rb_fiber_scheduler_current();
1787 if (scheduler != Qnil) {
1789
1790 VALUE result = rb_fiber_scheduler_blocking_operation_wait(scheduler, func, data1, ubf, data2, flags, &state);
1791
1792 if (!UNDEF_P(result)) {
1793 rb_errno_set(state.saved_errno);
1794 return state.result;
1795 }
1796 }
1797 }
1798
1799 void *val = 0;
1800 rb_vm_t *vm = rb_ec_vm_ptr(ec);
1801 bool is_main_thread = vm->ractor.main_thread == th;
1802 int saved_errno = 0;
1803
1804 bool sentinel_ubf = rb_thread_resolve_unblock_function(&ubf, &data2, th);
1805
1806 if (ubf && rb_ractor_living_thread_num(th->ractor) == 1 && is_main_thread) {
1807 // ubf_select, which the sentinel ubfs resolve to, takes ubf_list_lock
1808 // and the ractor scheduler lock: not async-signal-safe, whatever the
1809 // caller claims.
1810 if ((flags & RB_NOGVL_UBF_ASYNC_SAFE) && !sentinel_ubf) {
1811 vm->ubf_async_safe = 1;
1812 }
1813 }
1814
1815 rb_vm_t *volatile saved_vm = vm;
1816 BLOCKING_REGION(th, {
1817 val = func(data1);
1818 saved_errno = rb_errno();
1819 }, ubf, data2, flags);
1820 vm = saved_vm;
1821
1822 if (is_main_thread) vm->ubf_async_safe = 0;
1823
1824 if ((flags & RB_NOGVL_INTR_FAIL) == 0) {
1825 RUBY_VM_CHECK_INTS_BLOCKING(ec);
1826 }
1827
1828 rb_errno_set(saved_errno);
1829
1830 return val;
1831}
1832
1833/*
1834 * rb_thread_call_without_gvl - permit concurrent/parallel execution.
1835 * rb_thread_call_without_gvl2 - permit concurrent/parallel execution
1836 * without interrupt process.
1837 *
1838 * rb_thread_call_without_gvl() does:
1839 * (1) Check interrupts.
1840 * (2) release GVL.
1841 * Other Ruby threads may run in parallel.
1842 * (3) call func with data1
1843 * (4) acquire GVL.
1844 * Other Ruby threads can not run in parallel any more.
1845 * (5) Check interrupts.
1846 *
1847 * rb_thread_call_without_gvl2() does:
1848 * (1) Check interrupt and return if interrupted.
1849 * (2) release GVL.
1850 * (3) call func with data1 and a pointer to the flags.
1851 * (4) acquire GVL.
1852 *
1853 * If another thread interrupts this thread (Thread#kill, signal delivery,
1854 * VM-shutdown request, and so on), `ubf()' is called (`ubf()' means
1855 * "un-blocking function"). `ubf()' should interrupt `func()' execution by
1856 * toggling a cancellation flag, canceling the invocation of a call inside
1857 * `func()' or similar. Note that `ubf()' may not be called with the GVL.
1858 *
1859 * There are built-in ubfs and you can specify these ubfs:
1860 *
1861 * * RUBY_UBF_IO: ubf for IO operation
1862 * * RUBY_UBF_PROCESS: ubf for process operation
1863 *
1864 * However, we can not guarantee our built-in ubfs interrupt your `func()'
1865 * correctly. Be careful to use rb_thread_call_without_gvl(). If you don't
1866 * provide proper ubf(), your program will not stop for Control+C or other
1867 * shutdown events.
1868 *
1869 * "Check interrupts" on above list means checking asynchronous
1870 * interrupt events (such as Thread#kill, signal delivery, VM-shutdown
1871 * request, and so on) and calling corresponding procedures
1872 * (such as `trap' for signals, raise an exception for Thread#raise).
1873 * If `func()' finished and received interrupts, you may skip interrupt
1874 * checking. For example, assume the following func() it reads data from file.
1875 *
1876 * read_func(...) {
1877 * // (a) before read
1878 * read(buffer); // (b) reading
1879 * // (c) after read
1880 * }
1881 *
1882 * If an interrupt occurs at (a) or (b), then `ubf()' cancels this
1883 * `read_func()' and interrupts are checked. However, if an interrupt occurs
1884 * at (c), after *read* operation is completed, checking interrupts is harmful
1885 * because it causes irrevocable side-effect, the read data will vanish. To
1886 * avoid such problem, the `read_func()' should be used with
1887 * `rb_thread_call_without_gvl2()'.
1888 *
1889 * If `rb_thread_call_without_gvl2()' detects interrupt, it returns
1890 * immediately. This function does not show when the execution was interrupted.
1891 * For example, there are 4 possible timing (a), (b), (c) and before calling
1892 * read_func(). You need to record progress of a read_func() and check
1893 * the progress after `rb_thread_call_without_gvl2()'. You may need to call
1894 * `rb_thread_check_ints()' correctly or your program can not process proper
1895 * process such as `trap' and so on.
1896 *
1897 * NOTE: You can not execute most of Ruby C API and touch Ruby
1898 * objects in `func()' and `ubf()', including raising an
1899 * exception, because current thread doesn't acquire GVL
1900 * (it causes synchronization problems). If you need to
1901 * call ruby functions either use rb_thread_call_with_gvl()
1902 * or read source code of C APIs and confirm safety by
1903 * yourself.
1904 *
1905 * NOTE: In short, this API is difficult to use safely. I recommend you
1906 * use other ways if you have. We lack experiences to use this API.
1907 * Please report your problem related on it.
1908 *
1909 * NOTE: Releasing GVL and re-acquiring GVL may be expensive operations
1910 * for a short running `func()'. Be sure to benchmark and use this
1911 * mechanism when `func()' consumes enough time.
1912 *
1913 * Safe C API:
1914 * * rb_thread_interrupted() - check interrupt flag
1915 * * ruby_xmalloc(), ruby_xrealloc(), ruby_xfree() -
1916 * they will work without GVL, and may acquire GVL when GC is needed.
1917 */
1918void *
1919rb_thread_call_without_gvl2(void *(*func)(void *), void *data1,
1920 rb_unblock_function_t *ubf, void *data2)
1921{
1922 return rb_nogvl(func, data1, ubf, data2, RB_NOGVL_INTR_FAIL);
1923}
1924
1925void *
1926rb_thread_call_without_gvl(void *(*func)(void *data), void *data1,
1927 rb_unblock_function_t *ubf, void *data2)
1928{
1929 return rb_nogvl(func, data1, ubf, data2, 0);
1930}
1931
1932static int
1933waitfd_to_waiting_flag(int wfd_event)
1934{
1935 return wfd_event << 1;
1936}
1937
1938static struct ccan_list_head *
1939rb_io_blocking_operations(struct rb_io *io)
1940{
1941 rb_serial_t fork_generation = GET_VM()->fork_gen;
1942
1943 // On fork, all existing entries in this list (which are stack allocated) become invalid.
1944 // Therefore, we re-initialize the list which clears it.
1945 if (io->fork_generation != fork_generation) {
1946 ccan_list_head_init(&io->blocking_operations);
1947 io->fork_generation = fork_generation;
1948 }
1949
1950 return &io->blocking_operations;
1951}
1952
1953/*
1954 * Registers a blocking operation for an IO object. This is used to track all threads and fibers
1955 * that are currently blocked on this IO for reading, writing or other operations.
1956 *
1957 * When the IO is closed, all blocking operations will be notified via rb_fiber_scheduler_fiber_interrupt
1958 * for fibers with a scheduler, or via rb_threadptr_interrupt for threads without a scheduler.
1959 *
1960 * @parameter io The IO object on which the operation will block
1961 * @parameter blocking_operation The operation details including the execution context that will be blocked
1962 */
1963static void
1964rb_io_blocking_operation_enter(struct rb_io *io, struct rb_io_blocking_operation *blocking_operation)
1965{
1966 ccan_list_add(rb_io_blocking_operations(io), &blocking_operation->list);
1967}
1968
1969static void
1970rb_io_blocking_operation_pop(struct rb_io *io, struct rb_io_blocking_operation *blocking_operation)
1971{
1972 ccan_list_del(&blocking_operation->list);
1973}
1976 struct rb_io *io;
1977 struct rb_io_blocking_operation *blocking_operation;
1978};
1979
1980static VALUE
1981io_blocking_operation_exit(VALUE _arguments)
1982{
1983 struct io_blocking_operation_arguments *arguments = (void*)_arguments;
1984 struct rb_io_blocking_operation *blocking_operation = arguments->blocking_operation;
1985
1986 rb_io_blocking_operation_pop(arguments->io, blocking_operation);
1987
1988 rb_io_t *io = arguments->io;
1989 rb_thread_t *thread = io->closing_ec->thread_ptr;
1990 rb_fiber_t *fiber = io->closing_ec->fiber_ptr;
1991
1992 if (thread->scheduler != Qnil) {
1993 // This can cause spurious wakeups...
1994 rb_fiber_scheduler_unblock(thread->scheduler, io->self, rb_fiberptr_self(fiber));
1995 }
1996 else {
1997 rb_thread_wakeup(thread->self);
1998 }
1999
2000 return Qnil;
2001}
2002
2003/*
2004 * Called when a blocking operation completes or is interrupted. Removes the operation from
2005 * the IO's blocking_operations list and wakes up any waiting threads/fibers.
2006 *
2007 * If there's a wakeup_mutex (meaning an IO close is in progress), synchronizes the cleanup
2008 * through that mutex to ensure proper coordination with the closing thread.
2009 *
2010 * @parameter io The IO object the operation was performed on
2011 * @parameter blocking_operation The completed operation to clean up
2012 */
2013static void
2014rb_io_blocking_operation_exit(struct rb_io *io, struct rb_io_blocking_operation *blocking_operation)
2015{
2016 VALUE wakeup_mutex = io->wakeup_mutex;
2017
2018 // Indicate that the blocking operation is no longer active:
2019 blocking_operation->ec = NULL;
2020
2021 if (RB_TEST(wakeup_mutex)) {
2022 struct io_blocking_operation_arguments arguments = {
2023 .io = io,
2024 .blocking_operation = blocking_operation
2025 };
2026
2027 rb_mutex_synchronize(wakeup_mutex, io_blocking_operation_exit, (VALUE)&arguments);
2028 }
2029 else {
2030 // If there's no wakeup_mutex, we can safely remove the operation directly:
2031 rb_io_blocking_operation_pop(io, blocking_operation);
2032 }
2033}
2034
2035static VALUE
2036rb_thread_io_blocking_operation_ensure(VALUE _argument)
2037{
2038 struct io_blocking_operation_arguments *arguments = (void*)_argument;
2039
2040 rb_io_blocking_operation_exit(arguments->io, arguments->blocking_operation);
2041
2042 return Qnil;
2043}
2044
2045/*
2046 * Executes a function that performs a blocking IO operation, while properly tracking
2047 * the operation in the IO's blocking_operations list. This ensures proper cleanup
2048 * and interruption handling if the IO is closed while blocked.
2049 *
2050 * The operation is automatically removed from the blocking_operations list when the function
2051 * returns, whether normally or due to an exception.
2052 *
2053 * @parameter self The IO object
2054 * @parameter function The function to execute that will perform the blocking operation
2055 * @parameter argument The argument to pass to the function
2056 * @returns The result of the blocking operation function
2057 */
2058VALUE
2059rb_thread_io_blocking_operation(VALUE self, VALUE(*function)(VALUE), VALUE argument)
2060{
2061 struct rb_io *io;
2062 RB_IO_POINTER(self, io);
2063
2064 rb_execution_context_t *ec = GET_EC();
2065 struct rb_io_blocking_operation blocking_operation = {
2066 .ec = ec,
2067 };
2068 rb_io_blocking_operation_enter(io, &blocking_operation);
2069
2071 .io = io,
2072 .blocking_operation = &blocking_operation
2073 };
2074
2075 return rb_ensure(function, argument, rb_thread_io_blocking_operation_ensure, (VALUE)&io_blocking_operation_arguments);
2076}
2077
2078static bool
2079thread_io_mn_schedulable(rb_thread_t *th, int events, const struct timeval *timeout)
2080{
2081#if defined(USE_MN_THREADS) && USE_MN_THREADS
2082 // RB_WAITFD_PRI has no thread_sched_waiting_* event: the scheduler would
2083 // register nothing and park the thread forever. POLLPRI works on the
2084 // blocking path.
2085 return !th_has_dedicated_nt(th) && (events || timeout) && th->blocking &&
2086 !(events & ~(RB_WAITFD_IN | RB_WAITFD_OUT));
2087#else
2088 return false;
2089#endif
2090}
2091
2092enum io_wait_result {
2093 io_wait_ready, // the MN scheduler waited and the fd is ready
2094 io_wait_timed_out, // the MN scheduler waited until the timeout expired
2095 io_wait_unhandled, // the MN scheduler did not wait; use the blocking path
2096};
2097
2098// Wait for `fd` on the MN scheduler, if it can take this wait at all.
2099// `known_not_ready`: the caller just saw EAGAIN, so probing the fd would only
2100// repeat an answer we have. Callers with no preceding operation need the probe.
2101static enum io_wait_result
2102thread_io_wait_events(rb_thread_t *th, int fd, int events, const struct timeval *timeout,
2103 bool known_not_ready)
2104{
2105#if defined(USE_MN_THREADS) && USE_MN_THREADS
2106 if (thread_io_mn_schedulable(th, events, timeout)) {
2107 rb_hrtime_t rel, *prel;
2108
2109 if (timeout) {
2110 rel = rb_timeval2hrtime(timeout);
2111 prel = &rel;
2112 }
2113 else {
2114 prel = NULL;
2115 }
2116
2117 VM_ASSERT(prel || (events & (RB_WAITFD_IN | RB_WAITFD_OUT)));
2118
2119 enum thread_sched_waiting_flag flags = waitfd_to_waiting_flag(events);
2120 if (known_not_ready) flags |= thread_sched_waiting_io_force;
2121
2122 switch (thread_sched_wait_events(TH_SCHED(th), th, fd, flags, prel)) {
2123 case thread_sched_wait_event:
2124 return io_wait_ready;
2125 case thread_sched_wait_timeout:
2126 return io_wait_timed_out;
2127 case thread_sched_wait_unavailable:
2128 // Never waited: reporting "ready" here would fabricate readiness and
2129 // spin, so hand the wait back to the caller's blocking path.
2130 return io_wait_unhandled;
2131 }
2132 }
2133#endif // defined(USE_MN_THREADS) && USE_MN_THREADS
2134 return io_wait_unhandled;
2135}
2136
2137// assume read/write
2138static bool
2139blocking_call_retryable_p(int r, int eno)
2140{
2141 if (r != -1) return false;
2142
2143 switch (eno) {
2144 case EAGAIN:
2145#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
2146 case EWOULDBLOCK:
2147#endif
2148 return true;
2149 default:
2150 return false;
2151 }
2152}
2153
2154bool
2155rb_thread_mn_schedulable(VALUE thval)
2156{
2157 rb_thread_t *th = rb_thread_ptr(thval);
2158 return th->mn_schedulable;
2159}
2160
2161VALUE
2162rb_thread_io_blocking_call(struct rb_io* io, rb_blocking_function_t *func, void *data1, int events)
2163{
2164 rb_execution_context_t * volatile ec = GET_EC();
2165 rb_thread_t * volatile th = rb_ec_thread_ptr(ec);
2166
2167 RUBY_DEBUG_LOG("th:%u fd:%d ev:%d", rb_th_serial(th), io->fd, events);
2168
2169 volatile VALUE val = Qundef; /* shouldn't be used */
2170 volatile int saved_errno = 0;
2171 enum ruby_tag_type state;
2172 volatile bool prev_mn_schedulable = th->mn_schedulable;
2173 th->mn_schedulable = thread_io_mn_schedulable(th, events, NULL);
2174
2175 int fd = io->fd;
2176
2177 // `errno` is only valid when there is an actual error - but we can't
2178 // extract that from the return value of `func` alone, so we clear any
2179 // prior `errno` value here so that we can later check if it was set by
2180 // `func` or not (as opposed to some previously set value).
2181 errno = 0;
2182
2183 struct rb_io_blocking_operation blocking_operation = {
2184 .ec = ec,
2185 };
2186 rb_io_blocking_operation_enter(io, &blocking_operation);
2187
2188 {
2189 EC_PUSH_TAG(ec);
2190 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
2191 volatile enum ruby_tag_type saved_state = state; /* for BLOCKING_REGION */
2192 retry:
2193 BLOCKING_REGION(th, {
2194 val = func(data1);
2195 saved_errno = errno;
2196 }, ubf_select, th, FALSE);
2197
2198 RUBY_ASSERT(th == rb_ec_thread_ptr(ec));
2199 if (events && blocking_call_retryable_p((int)val, saved_errno)) {
2200 // `func` just returned EAGAIN, so the fd is known not to be ready.
2201 if (thread_io_wait_events(th, fd, events, NULL, true) == io_wait_ready) {
2202 RUBY_VM_CHECK_INTS_BLOCKING(ec);
2203 goto retry;
2204 }
2205 else if (th->mn_schedulable) {
2206 // Retrying now would spin and returning would leak EAGAIN to
2207 // Ruby, so wait the ordinary blocking way, then retry.
2208 rb_thread_wait_for_single_fd(th, fd, events, NULL);
2209 RUBY_VM_CHECK_INTS_BLOCKING(ec);
2210 goto retry;
2211 }
2212 }
2213
2214 RUBY_VM_CHECK_INTS_BLOCKING(ec);
2215
2216 state = saved_state;
2217 }
2218 EC_POP_TAG();
2219
2220 th = rb_ec_thread_ptr(ec);
2221 th->mn_schedulable = prev_mn_schedulable;
2222 }
2223
2224 rb_io_blocking_operation_exit(io, &blocking_operation);
2225
2226 if (state) {
2227 EC_JUMP_TAG(ec, state);
2228 }
2229
2230 // If the error was a timeout, we raise a specific exception for that:
2231 if (saved_errno == ETIMEDOUT) {
2232 rb_raise(rb_eIOTimeoutError, "Blocking operation timed out!");
2233 }
2234
2235 errno = saved_errno;
2236
2237 return val;
2238}
2239
2240VALUE
2241rb_thread_io_blocking_region(struct rb_io *io, rb_blocking_function_t *func, void *data1)
2242{
2243 return rb_thread_io_blocking_call(io, func, data1, 0);
2244}
2245
2246/*
2247 * rb_thread_call_with_gvl - re-enter the Ruby world after GVL release.
2248 *
2249 * After releasing GVL using
2250 * rb_thread_call_without_gvl() you can not access Ruby values or invoke
2251 * methods. If you need to access Ruby you must use this function
2252 * rb_thread_call_with_gvl().
2253 *
2254 * This function rb_thread_call_with_gvl() does:
2255 * (1) acquire GVL.
2256 * (2) call passed function `func'.
2257 * (3) release GVL.
2258 * (4) return a value which is returned at (2).
2259 *
2260 * NOTE: You should not return Ruby object at (2) because such Object
2261 * will not be marked.
2262 *
2263 * NOTE: If an exception is raised in `func', this function DOES NOT
2264 * protect (catch) the exception. If you have any resources
2265 * which should free before throwing exception, you need use
2266 * rb_protect() in `func' and return a value which represents
2267 * exception was raised.
2268 *
2269 * NOTE: This function should not be called by a thread which was not
2270 * created as Ruby thread (created by Thread.new or so). In other
2271 * words, this function *DOES NOT* associate or convert a NON-Ruby
2272 * thread to a Ruby thread.
2273 *
2274 * NOTE: If this thread has already acquired the GVL, then the method call
2275 * is performed without acquiring or releasing the GVL (from Ruby 4.0).
2276 */
2277void *
2278rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
2279{
2280 rb_thread_t *th = ruby_thread_from_native();
2281 struct rb_blocking_region_buffer *brb;
2282 struct rb_unblock_callback prev_unblock;
2283 void *r;
2284
2285 if (th == 0) {
2286 /* Error has occurred, but we can't use rb_bug()
2287 * because this thread is not Ruby's thread.
2288 * What should we do?
2289 */
2290 bp();
2291 fprintf(stderr, "[BUG] rb_thread_call_with_gvl() is called by non-ruby thread\n");
2292 exit(EXIT_FAILURE);
2293 }
2294
2295 brb = (struct rb_blocking_region_buffer *)th->blocking_region_buffer;
2296 prev_unblock = th->unblock;
2297
2298 if (brb == 0) {
2299 /* the GVL is already acquired, call method directly */
2300 return (*func)(data1);
2301 }
2302
2303 blocking_region_end(th, brb);
2304 /* enter to Ruby world: You can access Ruby values, methods and so on. */
2305 r = (*func)(data1);
2306 /* leave from Ruby world: You can not access Ruby values, etc. */
2307 int released = blocking_region_begin(th, brb, prev_unblock.func, prev_unblock.arg, FALSE);
2308 RUBY_ASSERT_ALWAYS(released);
2309 RB_VM_SAVE_MACHINE_CONTEXT(th);
2310 thread_sched_to_waiting(TH_SCHED(th), th, true);
2311 return r;
2312}
2313
2314/*
2315 * ruby_thread_has_gvl_p - check if current native thread has GVL.
2316 */
2317
2319ruby_thread_has_gvl_p(void)
2320{
2321 rb_thread_t *th = ruby_thread_from_native();
2322
2323 if (th && th->blocking_region_buffer == 0) {
2324 return 1;
2325 }
2326 else {
2327 return 0;
2328 }
2329}
2330
2331/*
2332 * call-seq:
2333 * Thread.pass -> nil
2334 *
2335 * Give the thread scheduler a hint to pass execution to another thread.
2336 * A running thread may or may not switch, it depends on OS and processor.
2337 */
2338
2339static VALUE
2340thread_s_pass(VALUE klass)
2341{
2343 return Qnil;
2344}
2345
2346/*****************************************************/
2347
2348/*
2349 * rb_threadptr_pending_interrupt_* - manage asynchronous error queue
2350 *
2351 * Async events such as an exception thrown by Thread#raise,
2352 * Thread#kill and thread termination (after main thread termination)
2353 * will be queued to th->pending_interrupt_queue.
2354 * - clear: clear the queue.
2355 * - enque: enqueue err object into queue.
2356 * - deque: dequeue err object from queue.
2357 * - active_p: return 1 if the queue should be checked.
2358 *
2359 * All rb_threadptr_pending_interrupt_* functions are called by
2360 * a GVL acquired thread, of course.
2361 * Note that all "rb_" prefix APIs need GVL to call.
2362 */
2363
2364void
2365rb_threadptr_pending_interrupt_clear(rb_thread_t *th)
2366{
2367 rb_ary_clear(th->pending_interrupt_queue);
2368}
2369
2370void
2371rb_threadptr_pending_interrupt_enque(rb_thread_t *th, VALUE v)
2372{
2373 rb_ary_push(th->pending_interrupt_queue, v);
2374 th->pending_interrupt_queue_checked = 0;
2375}
2376
2377static void
2378threadptr_check_pending_interrupt_queue(rb_thread_t *th)
2379{
2380 if (!th->pending_interrupt_queue) {
2381 rb_raise(rb_eThreadError, "uninitialized thread");
2382 }
2383}
2384
2385enum handle_interrupt_timing {
2386 INTERRUPT_NONE,
2387 INTERRUPT_IMMEDIATE,
2388 INTERRUPT_ON_BLOCKING,
2389 INTERRUPT_NEVER
2390};
2391
2392static enum handle_interrupt_timing
2393rb_threadptr_pending_interrupt_from_symbol(rb_thread_t *th, VALUE sym)
2394{
2395 if (sym == sym_immediate) {
2396 return INTERRUPT_IMMEDIATE;
2397 }
2398 else if (sym == sym_on_blocking) {
2399 return INTERRUPT_ON_BLOCKING;
2400 }
2401 else if (sym == sym_never) {
2402 return INTERRUPT_NEVER;
2403 }
2404 else {
2405 rb_raise(rb_eThreadError, "unknown mask signature");
2406 }
2407}
2408
2409static enum handle_interrupt_timing
2410rb_threadptr_pending_interrupt_check_mask(rb_thread_t *th, VALUE err)
2411{
2412 VALUE mask;
2413 long mask_stack_len = RARRAY_LEN(th->pending_interrupt_mask_stack);
2414 const VALUE *mask_stack = RARRAY_CONST_PTR(th->pending_interrupt_mask_stack);
2415 VALUE mod;
2416 long i;
2417
2418 for (i=0; i<mask_stack_len; i++) {
2419 mask = mask_stack[mask_stack_len-(i+1)];
2420
2421 if (SYMBOL_P(mask)) {
2422 /* do not match RUBY_FATAL_THREAD_KILLED etc */
2423 if (err != rb_cInteger) {
2424 return rb_threadptr_pending_interrupt_from_symbol(th, mask);
2425 }
2426 else {
2427 continue;
2428 }
2429 }
2430
2431 for (mod = err; mod; mod = RCLASS_SUPER(mod)) {
2432 VALUE klass = mod;
2433 VALUE sym;
2434
2435 if (BUILTIN_TYPE(mod) == T_ICLASS) {
2436 klass = RBASIC(mod)->klass;
2437 }
2438 else if (mod != RCLASS_ORIGIN(mod)) {
2439 continue;
2440 }
2441
2442 if ((sym = rb_hash_aref(mask, klass)) != Qnil) {
2443 return rb_threadptr_pending_interrupt_from_symbol(th, sym);
2444 }
2445 }
2446 /* try to next mask */
2447 }
2448 return INTERRUPT_NONE;
2449}
2450
2451static int
2452rb_threadptr_pending_interrupt_empty_p(const rb_thread_t *th)
2453{
2454 return RARRAY_LEN(th->pending_interrupt_queue) == 0;
2455}
2456
2457static int
2458rb_threadptr_pending_interrupt_include_p(rb_thread_t *th, VALUE err)
2459{
2460 int i;
2461 for (i=0; i<RARRAY_LEN(th->pending_interrupt_queue); i++) {
2462 VALUE e = RARRAY_AREF(th->pending_interrupt_queue, i);
2463 if (rb_obj_is_kind_of(e, err)) {
2464 return TRUE;
2465 }
2466 }
2467 return FALSE;
2468}
2469
2470static VALUE
2471rb_threadptr_pending_interrupt_deque(rb_thread_t *th, enum handle_interrupt_timing timing)
2472{
2473#if 1 /* 1 to enable Thread#handle_interrupt, 0 to ignore it */
2474 int i;
2475
2476 for (i=0; i<RARRAY_LEN(th->pending_interrupt_queue); i++) {
2477 VALUE err = RARRAY_AREF(th->pending_interrupt_queue, i);
2478
2479 enum handle_interrupt_timing mask_timing = rb_threadptr_pending_interrupt_check_mask(th, CLASS_OF(err));
2480
2481 switch (mask_timing) {
2482 case INTERRUPT_ON_BLOCKING:
2483 if (timing != INTERRUPT_ON_BLOCKING) {
2484 break;
2485 }
2486 /* fall through */
2487 case INTERRUPT_NONE: /* default: IMMEDIATE */
2488 case INTERRUPT_IMMEDIATE:
2489 rb_ary_delete_at(th->pending_interrupt_queue, i);
2490 return err;
2491 case INTERRUPT_NEVER:
2492 break;
2493 }
2494 }
2495
2496 th->pending_interrupt_queue_checked = 1;
2497 return Qundef;
2498#else
2499 VALUE err = rb_ary_shift(th->pending_interrupt_queue);
2500 if (rb_threadptr_pending_interrupt_empty_p(th)) {
2501 th->pending_interrupt_queue_checked = 1;
2502 }
2503 return err;
2504#endif
2505}
2506
2507static int
2508threadptr_pending_interrupt_active_p(rb_thread_t *th)
2509{
2510 /*
2511 * For optimization, we don't check async errinfo queue
2512 * if the queue and the thread interrupt mask were not changed
2513 * since last check.
2514 */
2515 if (th->pending_interrupt_queue_checked) {
2516 return 0;
2517 }
2518
2519 if (rb_threadptr_pending_interrupt_empty_p(th)) {
2520 return 0;
2521 }
2522
2523 return 1;
2524}
2525
2526static int
2527handle_interrupt_arg_check_i(VALUE key, VALUE val, VALUE args)
2528{
2529 VALUE *maskp = (VALUE *)args;
2530
2531 if (val != sym_immediate && val != sym_on_blocking && val != sym_never) {
2532 rb_raise(rb_eArgError, "unknown mask signature");
2533 }
2534
2535 if (key == rb_eException && (UNDEF_P(*maskp) || NIL_P(*maskp))) {
2536 *maskp = val;
2537 return ST_CONTINUE;
2538 }
2539
2540 if (RTEST(*maskp)) {
2541 if (!RB_TYPE_P(*maskp, T_HASH)) {
2542 VALUE prev = *maskp;
2543 *maskp = rb_ident_hash_new();
2544 if (SYMBOL_P(prev)) {
2545 rb_hash_aset(*maskp, rb_eException, prev);
2546 }
2547 }
2548 rb_hash_aset(*maskp, key, val);
2549 }
2550 else {
2551 *maskp = Qfalse;
2552 }
2553
2554 return ST_CONTINUE;
2555}
2556
2557/*
2558 * call-seq:
2559 * Thread.handle_interrupt(hash) { ... } -> result of the block
2560 *
2561 * Changes asynchronous interrupt timing.
2562 *
2563 * _interrupt_ means asynchronous event and corresponding procedure
2564 * by Thread#raise, Thread#kill, signal trap (not supported yet)
2565 * and main thread termination (if main thread terminates, then all
2566 * other thread will be killed).
2567 *
2568 * The given +hash+ has pairs like <code>ExceptionClass =>
2569 * :TimingSymbol</code>. Where the ExceptionClass is the interrupt handled by
2570 * the given block. The TimingSymbol can be one of the following symbols:
2571 *
2572 * [+:immediate+] Invoke interrupts immediately.
2573 * [+:on_blocking+] Invoke interrupts while _BlockingOperation_.
2574 * [+:never+] Never invoke all interrupts.
2575 *
2576 * _BlockingOperation_ means that the operation will block the calling thread,
2577 * such as read and write. On CRuby implementation, _BlockingOperation_ is any
2578 * operation executed without GVL.
2579 *
2580 * Masked asynchronous interrupts are delayed until they are enabled.
2581 * This method is similar to sigprocmask(3).
2582 *
2583 * === NOTE
2584 *
2585 * Asynchronous interrupts are difficult to use.
2586 *
2587 * If you need to communicate between threads, please consider to use another way such as Queue.
2588 *
2589 * Or use them with deep understanding about this method.
2590 *
2591 * === Usage
2592 *
2593 * In this example, we can guard from Thread#raise exceptions.
2594 *
2595 * Using the +:never+ TimingSymbol the RuntimeError exception will always be
2596 * ignored in the first block of the main thread. In the second
2597 * ::handle_interrupt block we can purposefully handle RuntimeError exceptions.
2598 *
2599 * th = Thread.new do
2600 * Thread.handle_interrupt(RuntimeError => :never) {
2601 * begin
2602 * # You can write resource allocation code safely.
2603 * Thread.handle_interrupt(RuntimeError => :immediate) {
2604 * # ...
2605 * }
2606 * ensure
2607 * # You can write resource deallocation code safely.
2608 * end
2609 * }
2610 * end
2611 * Thread.pass
2612 * # ...
2613 * th.raise "stop"
2614 *
2615 * While we are ignoring the RuntimeError exception, it's safe to write our
2616 * resource allocation code. Then, the ensure block is where we can safely
2617 * deallocate your resources.
2618 *
2619 * ==== Stack control settings
2620 *
2621 * It's possible to stack multiple levels of ::handle_interrupt blocks in order
2622 * to control more than one ExceptionClass and TimingSymbol at a time.
2623 *
2624 * Thread.handle_interrupt(FooError => :never) {
2625 * Thread.handle_interrupt(BarError => :never) {
2626 * # FooError and BarError are prohibited.
2627 * }
2628 * }
2629 *
2630 * ==== Inheritance with ExceptionClass
2631 *
2632 * All exceptions inherited from the ExceptionClass parameter will be considered.
2633 *
2634 * Thread.handle_interrupt(Exception => :never) {
2635 * # all exceptions inherited from Exception are prohibited.
2636 * }
2637 *
2638 * For handling all interrupts, use +Object+ and not +Exception+
2639 * as the ExceptionClass, as kill/terminate interrupts are not handled by +Exception+.
2640 */
2641static VALUE
2642rb_thread_s_handle_interrupt(VALUE self, VALUE mask_arg)
2643{
2644 VALUE mask = Qundef;
2645 rb_execution_context_t * volatile ec = GET_EC();
2646 rb_thread_t * volatile th = rb_ec_thread_ptr(ec);
2647 volatile VALUE r = Qnil;
2648 enum ruby_tag_type state;
2649
2650 if (!rb_block_given_p()) {
2651 rb_raise(rb_eArgError, "block is needed.");
2652 }
2653
2654 mask_arg = rb_to_hash_type(mask_arg);
2655
2656 if (OBJ_FROZEN(mask_arg) && rb_hash_compare_by_id_p(mask_arg)) {
2657 mask = Qnil;
2658 }
2659
2660 rb_hash_foreach(mask_arg, handle_interrupt_arg_check_i, (VALUE)&mask);
2661
2662 if (UNDEF_P(mask)) {
2663 return rb_yield(Qnil);
2664 }
2665
2666 if (!RTEST(mask)) {
2667 mask = mask_arg;
2668 }
2669 else if (RB_TYPE_P(mask, T_HASH)) {
2670 OBJ_FREEZE(mask);
2671 }
2672
2673 rb_ary_push(th->pending_interrupt_mask_stack, mask);
2674 if (!rb_threadptr_pending_interrupt_empty_p(th)) {
2675 th->pending_interrupt_queue_checked = 0;
2676 RUBY_VM_SET_INTERRUPT(th->ec);
2677 }
2678
2679 EC_PUSH_TAG(th->ec);
2680 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
2681 r = rb_yield(Qnil);
2682 }
2683 EC_POP_TAG();
2684
2685 rb_ary_pop(th->pending_interrupt_mask_stack);
2686 if (!rb_threadptr_pending_interrupt_empty_p(th)) {
2687 th->pending_interrupt_queue_checked = 0;
2688 RUBY_VM_SET_INTERRUPT(th->ec);
2689 }
2690
2691 RUBY_VM_CHECK_INTS(th->ec);
2692
2693 if (state) {
2694 EC_JUMP_TAG(th->ec, state);
2695 }
2696
2697 return r;
2698}
2699
2700/*
2701 * call-seq:
2702 * target_thread.pending_interrupt?(error = nil) -> true/false
2703 *
2704 * Returns whether or not the asynchronous queue is empty for the target thread.
2705 *
2706 * If +error+ is given, then check only for +error+ type deferred events.
2707 *
2708 * See ::pending_interrupt? for more information.
2709 */
2710static VALUE
2711rb_thread_pending_interrupt_p(int argc, VALUE *argv, VALUE target_thread)
2712{
2713 rb_thread_t *target_th = rb_thread_ptr(target_thread);
2714
2715 if (!target_th->pending_interrupt_queue) {
2716 return Qfalse;
2717 }
2718 if (rb_threadptr_pending_interrupt_empty_p(target_th)) {
2719 return Qfalse;
2720 }
2721 if (rb_check_arity(argc, 0, 1)) {
2722 VALUE err = argv[0];
2723 if (!rb_obj_is_kind_of(err, rb_cModule)) {
2724 rb_raise(rb_eTypeError, "class or module required for rescue clause");
2725 }
2726 return RBOOL(rb_threadptr_pending_interrupt_include_p(target_th, err));
2727 }
2728 else {
2729 return Qtrue;
2730 }
2731}
2732
2733/*
2734 * call-seq:
2735 * Thread.pending_interrupt?(error = nil) -> true/false
2736 *
2737 * Returns whether or not the asynchronous queue is empty.
2738 *
2739 * Since Thread::handle_interrupt can be used to defer asynchronous events,
2740 * this method can be used to determine if there are any deferred events.
2741 *
2742 * If you find this method returns true, then you may finish +:never+ blocks.
2743 *
2744 * For example, the following method processes deferred asynchronous events
2745 * immediately.
2746 *
2747 * def Thread.kick_interrupt_immediately
2748 * Thread.handle_interrupt(Object => :immediate) {
2749 * Thread.pass
2750 * }
2751 * end
2752 *
2753 * If +error+ is given, then check only for +error+ type deferred events.
2754 *
2755 * === Usage
2756 *
2757 * th = Thread.new{
2758 * Thread.handle_interrupt(RuntimeError => :on_blocking){
2759 * while true
2760 * ...
2761 * # reach safe point to invoke interrupt
2762 * if Thread.pending_interrupt?
2763 * Thread.handle_interrupt(Object => :immediate){}
2764 * end
2765 * ...
2766 * end
2767 * }
2768 * }
2769 * ...
2770 * th.raise # stop thread
2771 *
2772 * This example can also be written as the following, which you should use to
2773 * avoid asynchronous interrupts.
2774 *
2775 * flag = true
2776 * th = Thread.new{
2777 * Thread.handle_interrupt(RuntimeError => :on_blocking){
2778 * while true
2779 * ...
2780 * # reach safe point to invoke interrupt
2781 * break if flag == false
2782 * ...
2783 * end
2784 * }
2785 * }
2786 * ...
2787 * flag = false # stop thread
2788 */
2789
2790static VALUE
2791rb_thread_s_pending_interrupt_p(int argc, VALUE *argv, VALUE self)
2792{
2793 return rb_thread_pending_interrupt_p(argc, argv, GET_THREAD()->self);
2794}
2795
2796NORETURN(static void rb_threadptr_to_kill(rb_thread_t *th));
2797
2798static void
2799rb_threadptr_to_kill(rb_thread_t *th)
2800{
2801 VM_ASSERT(GET_THREAD() == th);
2802 rb_threadptr_pending_interrupt_clear(th);
2803 th->status = THREAD_RUNNABLE;
2804 th->to_kill = 1;
2805 th->ec->errinfo = INT2FIX(TAG_FATAL);
2806 EC_JUMP_TAG(th->ec, TAG_FATAL);
2807}
2808
2809static inline rb_atomic_t
2810threadptr_get_interrupts(rb_thread_t *th)
2811{
2812 rb_execution_context_t *ec = th->ec;
2813 rb_atomic_t interrupt;
2814 rb_atomic_t old;
2815
2816 old = ATOMIC_LOAD_RELAXED(ec->interrupt_flag);
2817 do {
2818 interrupt = old;
2819 old = ATOMIC_CAS(ec->interrupt_flag, interrupt, interrupt & ec->interrupt_mask);
2820 } while (old != interrupt);
2821 return interrupt & (rb_atomic_t)~ec->interrupt_mask;
2822}
2823
2824static void threadptr_interrupt_exec_exec(rb_thread_t *th);
2825
2826// Execute interrupts on currently running thread
2827// In certain situations, calling this function will raise an exception. Some examples are:
2828// * during VM shutdown (`rb_ractor_terminate_all`)
2829// * Call to Thread#exit for current thread (`rb_thread_kill`)
2830// * Call to Thread#raise for current thread
2831int
2832rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
2833{
2834 rb_atomic_t interrupt;
2835 int postponed_job_interrupt = 0;
2836 int ret = FALSE;
2837
2838 VM_ASSERT(GET_THREAD() == th);
2839
2840 if (th->ec->raised_flag) return ret;
2841
2842 while ((interrupt = threadptr_get_interrupts(th)) != 0) {
2843 int sig;
2844 int timer_interrupt;
2845 int pending_interrupt;
2846 int trap_interrupt;
2847 int terminate_interrupt;
2848
2849 timer_interrupt = interrupt & TIMER_INTERRUPT_MASK;
2850 pending_interrupt = interrupt & PENDING_INTERRUPT_MASK;
2851 postponed_job_interrupt = interrupt & POSTPONED_JOB_INTERRUPT_MASK;
2852 trap_interrupt = interrupt & TRAP_INTERRUPT_MASK;
2853 terminate_interrupt = interrupt & TERMINATE_INTERRUPT_MASK; // request from other ractors
2854
2855 if (interrupt & VM_BARRIER_INTERRUPT_MASK) {
2856 RB_VM_LOCKING();
2857 }
2858
2859 if (postponed_job_interrupt) {
2860 rb_postponed_job_flush(th->vm);
2861 }
2862
2863 if (trap_interrupt) {
2864 /* signal handling */
2865 if (th == th->vm->ractor.main_thread) {
2866 enum rb_thread_status prev_status = th->status;
2867
2868 th->status = THREAD_RUNNABLE;
2869 {
2870 while ((sig = rb_get_next_signal()) != 0) {
2871 ret |= rb_signal_exec(th, sig);
2872 }
2873 }
2874 th->status = prev_status;
2875 }
2876
2877 if (!ccan_list_empty(&th->interrupt_exec_tasks)) {
2878 enum rb_thread_status prev_status = th->status;
2879
2880 th->status = THREAD_RUNNABLE;
2881 {
2882 threadptr_interrupt_exec_exec(th);
2883 }
2884 th->status = prev_status;
2885 }
2886 }
2887
2888 /* exception from another thread */
2889 if (pending_interrupt && threadptr_pending_interrupt_active_p(th)) {
2890 VALUE err = rb_threadptr_pending_interrupt_deque(th, blocking_timing ? INTERRUPT_ON_BLOCKING : INTERRUPT_NONE);
2891 RUBY_DEBUG_LOG("err:%"PRIdVALUE, err);
2892 ret = TRUE;
2893
2894 if (UNDEF_P(err)) {
2895 /* no error */
2896 }
2897 else if (err == RUBY_FATAL_THREAD_KILLED /* Thread#kill received */ ||
2898 err == RUBY_FATAL_THREAD_TERMINATED /* Terminate thread */ ||
2899 err == INT2FIX(TAG_FATAL) /* Thread.exit etc. */ ) {
2900 terminate_interrupt = 1;
2901 }
2902 else {
2903 if (err == th->vm->special_exceptions[ruby_error_stream_closed]) {
2904 /* the only special exception to be queued across thread */
2905 err = ruby_vm_special_exception_copy(err);
2906 }
2907 /* set runnable if th was slept. */
2908 if (th->status == THREAD_STOPPED ||
2909 th->status == THREAD_STOPPED_FOREVER)
2910 th->status = THREAD_RUNNABLE;
2911 rb_exc_raise(err);
2912 }
2913 }
2914
2915 if (terminate_interrupt) {
2916 rb_threadptr_to_kill(th);
2917 }
2918
2919 if (timer_interrupt) {
2920 uint32_t limits_us = thread_default_quantum_ms * 1000;
2921
2922 if (th->priority > 0)
2923 limits_us <<= th->priority;
2924 else
2925 limits_us >>= -th->priority;
2926
2927 if (th->status == THREAD_RUNNABLE)
2928 th->running_time_us += 10 * 1000; // 10ms = 10_000us // TODO: use macro
2929
2930 VM_ASSERT(th->ec->cfp);
2931 EXEC_EVENT_HOOK(th->ec, RUBY_INTERNAL_EVENT_SWITCH, th->ec->cfp->self,
2932 0, 0, 0, Qundef);
2933
2934 rb_thread_schedule_limits(limits_us);
2935 }
2936 }
2937 return ret;
2938}
2939
2940void
2941rb_thread_execute_interrupts(VALUE thval)
2942{
2943 rb_threadptr_execute_interrupts(rb_thread_ptr(thval), 1);
2944}
2945
2946static void
2947rb_threadptr_ready(rb_thread_t *th)
2948{
2949 rb_threadptr_interrupt(th);
2950}
2951
2952static VALUE
2953rb_threadptr_raise(rb_thread_t *target_th, int argc, VALUE *argv)
2954{
2955 VALUE exc;
2956
2957 if (rb_threadptr_dead(target_th)) {
2958 return Qnil;
2959 }
2960
2961 if (argc == 0) {
2962 exc = rb_exc_new(rb_eRuntimeError, 0, 0);
2963 }
2964 else {
2965 exc = rb_make_exception(argc, argv);
2966 }
2967
2968 /* making an exception object can switch thread,
2969 so we need to check thread deadness again */
2970 if (rb_threadptr_dead(target_th)) {
2971 return Qnil;
2972 }
2973
2974 rb_ec_setup_exception(GET_EC(), exc, Qundef);
2975 rb_threadptr_pending_interrupt_enque(target_th, exc);
2976 rb_threadptr_interrupt(target_th);
2977
2978 return Qnil;
2979}
2980
2981void
2982rb_threadptr_signal_raise(rb_thread_t *th, int sig)
2983{
2984 VALUE argv[2];
2985
2986 argv[0] = rb_eSignal;
2987 argv[1] = INT2FIX(sig);
2988 rb_threadptr_raise(th->vm->ractor.main_thread, 2, argv);
2989}
2990
2991void
2992rb_threadptr_interrupt_raise(rb_thread_t *th)
2993{
2994 rb_thread_t *target_th = th->vm->ractor.main_thread;
2995
2996 if (rb_threadptr_dead(target_th)) {
2997 return;
2998 }
2999
3000 /* Preserve the traditional no-message Interrupt from default SIGINT. */
3001 VALUE exc = rb_exc_new(rb_eInterrupt, 0, 0);
3002
3003 /* making an exception object can switch thread,
3004 so we need to check thread deadness again */
3005 if (rb_threadptr_dead(target_th)) {
3006 return;
3007 }
3008
3009 rb_ec_setup_exception(GET_EC(), exc, Qundef);
3010 rb_threadptr_pending_interrupt_enque(target_th, exc);
3011 rb_threadptr_interrupt(target_th);
3012}
3013
3014void
3015rb_threadptr_signal_exit(rb_thread_t *th)
3016{
3017 VALUE argv[2];
3018
3019 argv[0] = rb_eSystemExit;
3020 argv[1] = rb_str_new2("exit");
3021
3022 // TODO: check signal raise deliverly
3023 rb_threadptr_raise(th->vm->ractor.main_thread, 2, argv);
3024}
3025
3026int
3027rb_ec_set_raised(rb_execution_context_t *ec)
3028{
3029 if (ec->raised_flag & RAISED_EXCEPTION) {
3030 return 1;
3031 }
3032 ec->raised_flag |= RAISED_EXCEPTION;
3033 return 0;
3034}
3035
3036int
3037rb_ec_reset_raised(rb_execution_context_t *ec)
3038{
3039 if (!(ec->raised_flag & RAISED_EXCEPTION)) {
3040 return 0;
3041 }
3042 ec->raised_flag &= ~RAISED_EXCEPTION;
3043 return 1;
3044}
3045
3046/*
3047 * Thread-safe IO closing mechanism.
3048 *
3049 * When an IO is closed while other threads or fibers are blocked on it, we need to:
3050 * 1. Track and notify all blocking operations through io->blocking_operations
3051 * 2. Ensure only one thread can close at a time using io->closing_ec
3052 * 3. Synchronize cleanup using wakeup_mutex
3053 *
3054 * The close process works as follows:
3055 * - First check if any thread is already closing (io->closing_ec)
3056 * - Set up wakeup_mutex for synchronization
3057 * - Iterate through all blocking operations in io->blocking_operations
3058 * - For each blocked fiber with a scheduler:
3059 * - Notify via rb_fiber_scheduler_fiber_interrupt
3060 * - For each blocked thread without a scheduler:
3061 * - Enqueue IOError via rb_threadptr_pending_interrupt_enque
3062 * - Wake via rb_threadptr_interrupt
3063 * - Wait on wakeup_mutex until all operations are cleaned up
3064 * - Only then clear closing state and allow actual close to proceed
3065 */
3066static VALUE
3067thread_io_close_notify_all(VALUE _io)
3068{
3069 struct rb_io *io = (struct rb_io *)_io;
3070
3071 size_t count = 0;
3072 rb_vm_t *vm = io->closing_ec->thread_ptr->vm;
3073 VALUE error = vm->special_exceptions[ruby_error_stream_closed];
3074
3075 struct rb_io_blocking_operation *blocking_operation;
3076 ccan_list_for_each(rb_io_blocking_operations(io), blocking_operation, list) {
3077 rb_execution_context_t *ec = blocking_operation->ec;
3078
3079 // If the operation is in progress, we need to interrupt it:
3080 if (ec) {
3081 rb_thread_t *thread = ec->thread_ptr;
3082
3083 if (thread->scheduler != Qnil) {
3084 rb_fiber_scheduler_fiber_interrupt(thread->scheduler, rb_fiberptr_self(ec->fiber_ptr), error);
3085 }
3086 else {
3087 // If the thread is not the current thread, we need to enqueue an error:
3088 rb_threadptr_pending_interrupt_enque(thread, error);
3089 rb_threadptr_interrupt(thread);
3090 }
3091 }
3092
3093 count += 1;
3094 }
3095
3096 return (VALUE)count;
3097}
3098
3099size_t
3100rb_thread_io_close_interrupt(struct rb_io *io)
3101{
3102 // We guard this operation based on `io->closing_ec` -> only one thread will ever enter this function.
3103 if (io->closing_ec) {
3104 return 0;
3105 }
3106
3107 // If there are no blocking operations, we are done:
3108 if (ccan_list_empty(rb_io_blocking_operations(io))) {
3109 return 0;
3110 }
3111
3112 // Otherwise, we are now closing the IO:
3113 rb_execution_context_t *ec = GET_EC();
3114 io->closing_ec = ec;
3115
3116 // This is used to ensure the correct execution context is woken up after the blocking operation is interrupted:
3117 io->wakeup_mutex = rb_mutex_new();
3118 rb_mutex_allow_trap(io->wakeup_mutex, 1);
3119
3120 // We need to use a mutex here as entering the fiber scheduler may cause a context switch:
3121 VALUE result = rb_mutex_synchronize(io->wakeup_mutex, thread_io_close_notify_all, (VALUE)io);
3122
3123 return (size_t)result;
3124}
3125
3126void
3127rb_thread_io_close_wait(struct rb_io* io)
3128{
3129 VALUE wakeup_mutex = io->wakeup_mutex;
3130
3131 if (!RB_TEST(wakeup_mutex)) {
3132 // There was nobody else using this file when we closed it, so we never bothered to allocate a mutex:
3133 return;
3134 }
3135
3136 rb_mutex_lock(wakeup_mutex);
3137 while (!ccan_list_empty(rb_io_blocking_operations(io))) {
3138 rb_mutex_sleep(wakeup_mutex, Qnil);
3139 }
3140 rb_mutex_unlock(wakeup_mutex);
3141
3142 // We are done closing:
3143 io->wakeup_mutex = Qnil;
3144 io->closing_ec = NULL;
3145}
3146
3147void
3148rb_thread_fd_close(int fd)
3149{
3150 rb_warn("rb_thread_fd_close is deprecated (and is now a no-op).");
3151}
3152
3153/*
3154 * call-seq:
3155 * raise(exception, message = exception.to_s, backtrace = nil, cause: $!)
3156 * raise(message = nil, cause: $!)
3157 *
3158 * Raises an exception from the given thread. The caller does not have to be
3159 * +thr+. See Kernel#raise for more information on arguments.
3160 *
3161 * Thread.abort_on_exception = true
3162 * a = Thread.new { sleep(200) }
3163 * a.raise("Gotcha")
3164 *
3165 * This will produce:
3166 *
3167 * prog.rb:3: Gotcha (RuntimeError)
3168 * from prog.rb:2:in `initialize'
3169 * from prog.rb:2:in `new'
3170 * from prog.rb:2
3171 */
3172
3173static VALUE
3174thread_raise_m(int argc, VALUE *argv, VALUE self)
3175{
3176 rb_thread_t *target_th = rb_thread_ptr(self);
3177 const rb_thread_t *current_th = GET_THREAD();
3178
3179 threadptr_check_pending_interrupt_queue(target_th);
3180
3181 if (rb_threadptr_dead(target_th)) {
3182 return Qnil;
3183 }
3184
3185 VALUE exception = rb_exception_setup(argc, argv);
3186 rb_threadptr_pending_interrupt_enque(target_th, exception);
3187 rb_threadptr_interrupt(target_th);
3188
3189 /* To perform Thread.current.raise as Kernel.raise */
3190 if (current_th == target_th) {
3191 RUBY_VM_CHECK_INTS(target_th->ec);
3192 }
3193 return Qnil;
3194}
3195
3196
3197/*
3198 * call-seq:
3199 * thr.exit -> thr
3200 * thr.kill -> thr
3201 * thr.terminate -> thr
3202 *
3203 * Terminates +thr+ and schedules another thread to be run, returning
3204 * the terminated Thread. If this is the main thread, or the last
3205 * thread, exits the process. Note that the caller does not wait for
3206 * the thread to terminate if the receiver is different from the currently
3207 * running thread. The termination is asynchronous, and the thread can still
3208 * run a small amount of ruby code before exiting.
3209 */
3210
3212rb_thread_kill(VALUE thread)
3213{
3214 rb_thread_t *target_th = rb_thread_ptr(thread);
3215
3216 if (target_th->to_kill || target_th->status == THREAD_KILLED) {
3217 return thread;
3218 }
3219 if (target_th == target_th->vm->ractor.main_thread) {
3220 rb_exit(EXIT_SUCCESS);
3221 }
3222
3223 RUBY_DEBUG_LOG("target_th:%u", rb_th_serial(target_th));
3224
3225 if (target_th == GET_THREAD()) {
3226 /* kill myself immediately */
3227 rb_threadptr_to_kill(target_th);
3228 }
3229 else {
3230 threadptr_check_pending_interrupt_queue(target_th);
3231 rb_threadptr_pending_interrupt_enque(target_th, RUBY_FATAL_THREAD_KILLED);
3232 rb_threadptr_interrupt(target_th);
3233 }
3234
3235 return thread;
3236}
3237
3238int
3239rb_thread_to_be_killed(VALUE thread)
3240{
3241 rb_thread_t *target_th = rb_thread_ptr(thread);
3242
3243 if (target_th->to_kill || target_th->status == THREAD_KILLED) {
3244 return TRUE;
3245 }
3246 return FALSE;
3247}
3248
3249/*
3250 * call-seq:
3251 * Thread.kill(thread) -> thread
3252 *
3253 * Causes the given +thread+ to exit, see also Thread::exit.
3254 *
3255 * count = 0
3256 * a = Thread.new { loop { count += 1 } }
3257 * sleep(0.1) #=> 0
3258 * Thread.kill(a) #=> #<Thread:0x401b3d30 dead>
3259 * count #=> 93947
3260 * a.alive? #=> false
3261 */
3262
3263static VALUE
3264rb_thread_s_kill(VALUE obj, VALUE th)
3265{
3266 return rb_thread_kill(th);
3267}
3268
3269
3270/*
3271 * call-seq:
3272 * Thread.exit -> thread
3273 *
3274 * Terminates the currently running thread and schedules another thread to be
3275 * run.
3276 *
3277 * If this thread is already marked to be killed, ::exit returns the Thread.
3278 *
3279 * If this is the main thread, or the last thread, exit the process.
3280 */
3281
3282static VALUE
3283rb_thread_exit(VALUE _)
3284{
3285 rb_thread_t *th = GET_THREAD();
3286 return rb_thread_kill(th->self);
3287}
3288
3289
3290/*
3291 * call-seq:
3292 * thr.wakeup -> thr
3293 *
3294 * Marks a given thread as eligible for scheduling, however it may still
3295 * remain blocked on I/O.
3296 *
3297 * *Note:* This does not invoke the scheduler, see #run for more information.
3298 *
3299 * c = Thread.new { Thread.stop; puts "hey!" }
3300 * sleep 0.1 while c.status!='sleep'
3301 * c.wakeup
3302 * c.join
3303 * #=> "hey!"
3304 */
3305
3307rb_thread_wakeup(VALUE thread)
3308{
3309 if (!RTEST(rb_thread_wakeup_alive(thread))) {
3310 rb_raise(rb_eThreadError, "killed thread");
3311 }
3312 return thread;
3313}
3314
3317{
3318 rb_thread_t *target_th = rb_thread_ptr(thread);
3319 if (target_th->status == THREAD_KILLED) return Qnil;
3320
3321 rb_threadptr_ready(target_th);
3322
3323 if (target_th->status == THREAD_STOPPED ||
3324 target_th->status == THREAD_STOPPED_FOREVER) {
3325 target_th->status = THREAD_RUNNABLE;
3326 }
3327
3328 return thread;
3329}
3330
3331
3332/*
3333 * call-seq:
3334 * thr.run -> thr
3335 *
3336 * Wakes up +thr+, making it eligible for scheduling.
3337 *
3338 * a = Thread.new { puts "a"; Thread.stop; puts "c" }
3339 * sleep 0.1 while a.status!='sleep'
3340 * puts "Got here"
3341 * a.run
3342 * a.join
3343 *
3344 * This will produce:
3345 *
3346 * a
3347 * Got here
3348 * c
3349 *
3350 * See also the instance method #wakeup.
3351 */
3352
3354rb_thread_run(VALUE thread)
3355{
3356 rb_thread_wakeup(thread);
3358 return thread;
3359}
3360
3361
3363rb_thread_stop(void)
3364{
3365 if (rb_thread_alone()) {
3366 rb_raise(rb_eThreadError,
3367 "stopping only thread\n\tnote: use sleep to stop forever");
3368 }
3370 return Qnil;
3371}
3372
3373/*
3374 * call-seq:
3375 * Thread.stop -> nil
3376 *
3377 * Stops execution of the current thread, putting it into a ``sleep'' state,
3378 * and schedules execution of another thread.
3379 *
3380 * a = Thread.new { print "a"; Thread.stop; print "c" }
3381 * sleep 0.1 while a.status!='sleep'
3382 * print "b"
3383 * a.run
3384 * a.join
3385 * #=> "abc"
3386 */
3387
3388static VALUE
3389thread_stop(VALUE _)
3390{
3391 return rb_thread_stop();
3392}
3393
3394/********************************************************************/
3395
3396VALUE
3397rb_thread_list(void)
3398{
3399 // TODO
3400 return rb_ractor_thread_list();
3401}
3402
3403/*
3404 * call-seq:
3405 * Thread.list -> array
3406 *
3407 * Returns an array of Thread objects for all threads that are either runnable
3408 * or stopped.
3409 *
3410 * Thread.new { sleep(200) }
3411 * Thread.new { 1000000.times {|i| i*i } }
3412 * Thread.new { Thread.stop }
3413 * Thread.list.each {|t| p t}
3414 *
3415 * This will produce:
3416 *
3417 * #<Thread:0x401b3e84 sleep>
3418 * #<Thread:0x401b3f38 run>
3419 * #<Thread:0x401b3fb0 sleep>
3420 * #<Thread:0x401bdf4c run>
3421 */
3422
3423static VALUE
3424thread_list(VALUE _)
3425{
3426 return rb_thread_list();
3427}
3428
3431{
3432 return GET_THREAD()->self;
3433}
3434
3435/*
3436 * call-seq:
3437 * Thread.current -> thread
3438 *
3439 * Returns the currently executing thread.
3440 *
3441 * Thread.current #=> #<Thread:0x401bdf4c run>
3442 */
3443
3444static VALUE
3445thread_s_current(VALUE klass)
3446{
3447 return rb_thread_current();
3448}
3449
3451rb_thread_main(void)
3452{
3453 return GET_RACTOR()->threads.main->self;
3454}
3455
3456/*
3457 * call-seq:
3458 * Thread.main -> thread
3459 *
3460 * Returns the main thread.
3461 */
3462
3463static VALUE
3464rb_thread_s_main(VALUE klass)
3465{
3466 return rb_thread_main();
3467}
3468
3469
3470/*
3471 * call-seq:
3472 * Thread.abort_on_exception -> true or false
3473 *
3474 * Returns the status of the global ``abort on exception'' condition.
3475 *
3476 * The default is +false+.
3477 *
3478 * When set to +true+, if any thread is aborted by an exception, the
3479 * raised exception will be re-raised in the main thread.
3480 *
3481 * Can also be specified by the global $DEBUG flag or command line option
3482 * +-d+.
3483 *
3484 * See also ::abort_on_exception=.
3485 *
3486 * There is also an instance level method to set this for a specific thread,
3487 * see #abort_on_exception.
3488 */
3489
3490static VALUE
3491rb_thread_s_abort_exc(VALUE _)
3492{
3493 return RBOOL(GET_THREAD()->vm->thread_abort_on_exception);
3494}
3495
3496
3497/*
3498 * call-seq:
3499 * Thread.abort_on_exception= boolean -> true or false
3500 *
3501 * When set to +true+, if any thread is aborted by an exception, the
3502 * raised exception will be re-raised in the main thread.
3503 * Returns the new state.
3504 *
3505 * Thread.abort_on_exception = true
3506 * t1 = Thread.new do
3507 * puts "In new thread"
3508 * raise "Exception from thread"
3509 * end
3510 * sleep(1)
3511 * puts "not reached"
3512 *
3513 * This will produce:
3514 *
3515 * In new thread
3516 * prog.rb:4: Exception from thread (RuntimeError)
3517 * from prog.rb:2:in `initialize'
3518 * from prog.rb:2:in `new'
3519 * from prog.rb:2
3520 *
3521 * See also ::abort_on_exception.
3522 *
3523 * There is also an instance level method to set this for a specific thread,
3524 * see #abort_on_exception=.
3525 */
3526
3527static VALUE
3528rb_thread_s_abort_exc_set(VALUE self, VALUE val)
3529{
3530 GET_THREAD()->vm->thread_abort_on_exception = RTEST(val);
3531 return val;
3532}
3533
3534
3535/*
3536 * call-seq:
3537 * thr.abort_on_exception -> true or false
3538 *
3539 * Returns the status of the thread-local ``abort on exception'' condition for
3540 * this +thr+.
3541 *
3542 * The default is +false+.
3543 *
3544 * See also #abort_on_exception=.
3545 *
3546 * There is also a class level method to set this for all threads, see
3547 * ::abort_on_exception.
3548 */
3549
3550static VALUE
3551rb_thread_abort_exc(VALUE thread)
3552{
3553 return RBOOL(rb_thread_ptr(thread)->abort_on_exception);
3554}
3555
3556
3557/*
3558 * call-seq:
3559 * thr.abort_on_exception= boolean -> true or false
3560 *
3561 * When set to +true+, if this +thr+ is aborted by an exception, the
3562 * raised exception will be re-raised in the main thread.
3563 *
3564 * See also #abort_on_exception.
3565 *
3566 * There is also a class level method to set this for all threads, see
3567 * ::abort_on_exception=.
3568 */
3569
3570static VALUE
3571rb_thread_abort_exc_set(VALUE thread, VALUE val)
3572{
3573 rb_thread_ptr(thread)->abort_on_exception = RTEST(val);
3574 return val;
3575}
3576
3577
3578/*
3579 * call-seq:
3580 * Thread.report_on_exception -> true or false
3581 *
3582 * Returns the status of the global ``report on exception'' condition.
3583 *
3584 * The default is +true+ since Ruby 2.5.
3585 *
3586 * All threads created when this flag is true will report
3587 * a message on $stderr if an exception kills the thread.
3588 *
3589 * Thread.new { 1.times { raise } }
3590 *
3591 * will produce this output on $stderr:
3592 *
3593 * #<Thread:...> terminated with exception (report_on_exception is true):
3594 * Traceback (most recent call last):
3595 * 2: from -e:1:in `block in <main>'
3596 * 1: from -e:1:in `times'
3597 *
3598 * This is done to catch errors in threads early.
3599 * In some cases, you might not want this output.
3600 * There are multiple ways to avoid the extra output:
3601 *
3602 * * If the exception is not intended, the best is to fix the cause of
3603 * the exception so it does not happen anymore.
3604 * * If the exception is intended, it might be better to rescue it closer to
3605 * where it is raised rather then let it kill the Thread.
3606 * * If it is guaranteed the Thread will be joined with Thread#join or
3607 * Thread#value, then it is safe to disable this report with
3608 * <code>Thread.current.report_on_exception = false</code>
3609 * when starting the Thread.
3610 * However, this might handle the exception much later, or not at all
3611 * if the Thread is never joined due to the parent thread being blocked, etc.
3612 *
3613 * See also ::report_on_exception=.
3614 *
3615 * There is also an instance level method to set this for a specific thread,
3616 * see #report_on_exception=.
3617 *
3618 */
3619
3620static VALUE
3621rb_thread_s_report_exc(VALUE _)
3622{
3623 return RBOOL(GET_THREAD()->vm->thread_report_on_exception);
3624}
3625
3626
3627/*
3628 * call-seq:
3629 * Thread.report_on_exception= boolean -> true or false
3630 *
3631 * Returns the new state.
3632 * When set to +true+, all threads created afterwards will inherit the
3633 * condition and report a message on $stderr if an exception kills a thread:
3634 *
3635 * Thread.report_on_exception = true
3636 * t1 = Thread.new do
3637 * puts "In new thread"
3638 * raise "Exception from thread"
3639 * end
3640 * sleep(1)
3641 * puts "In the main thread"
3642 *
3643 * This will produce:
3644 *
3645 * In new thread
3646 * #<Thread:...prog.rb:2> terminated with exception (report_on_exception is true):
3647 * Traceback (most recent call last):
3648 * prog.rb:4:in `block in <main>': Exception from thread (RuntimeError)
3649 * In the main thread
3650 *
3651 * See also ::report_on_exception.
3652 *
3653 * There is also an instance level method to set this for a specific thread,
3654 * see #report_on_exception=.
3655 */
3656
3657static VALUE
3658rb_thread_s_report_exc_set(VALUE self, VALUE val)
3659{
3660 GET_THREAD()->vm->thread_report_on_exception = RTEST(val);
3661 return val;
3662}
3663
3664
3665/*
3666 * call-seq:
3667 * Thread.ignore_deadlock -> true or false
3668 *
3669 * Returns the status of the global ``ignore deadlock'' condition.
3670 * The default is +false+, so that deadlock conditions are not ignored.
3671 *
3672 * See also ::ignore_deadlock=.
3673 *
3674 */
3675
3676static VALUE
3677rb_thread_s_ignore_deadlock(VALUE _)
3678{
3679 return RBOOL(GET_THREAD()->vm->thread_ignore_deadlock);
3680}
3681
3682
3683/*
3684 * call-seq:
3685 * Thread.ignore_deadlock = boolean -> true or false
3686 *
3687 * Returns the new state.
3688 * When set to +true+, the VM will not check for deadlock conditions.
3689 * It is only useful to set this if your application can break a
3690 * deadlock condition via some other means, such as a signal.
3691 *
3692 * Thread.ignore_deadlock = true
3693 * queue = Thread::Queue.new
3694 *
3695 * trap(:SIGUSR1){queue.push "Received signal"}
3696 *
3697 * # raises fatal error unless ignoring deadlock
3698 * puts queue.pop
3699 *
3700 * See also ::ignore_deadlock.
3701 */
3702
3703static VALUE
3704rb_thread_s_ignore_deadlock_set(VALUE self, VALUE val)
3705{
3706 GET_THREAD()->vm->thread_ignore_deadlock = RTEST(val);
3707 return val;
3708}
3709
3710
3711/*
3712 * call-seq:
3713 * thr.report_on_exception -> true or false
3714 *
3715 * Returns the status of the thread-local ``report on exception'' condition for
3716 * this +thr+.
3717 *
3718 * The default value when creating a Thread is the value of
3719 * the global flag Thread.report_on_exception.
3720 *
3721 * See also #report_on_exception=.
3722 *
3723 * There is also a class level method to set this for all new threads, see
3724 * ::report_on_exception=.
3725 */
3726
3727static VALUE
3728rb_thread_report_exc(VALUE thread)
3729{
3730 return RBOOL(rb_thread_ptr(thread)->report_on_exception);
3731}
3732
3733
3734/*
3735 * call-seq:
3736 * thr.report_on_exception= boolean -> true or false
3737 *
3738 * When set to +true+, a message is printed on $stderr if an exception
3739 * kills this +thr+. See ::report_on_exception for details.
3740 *
3741 * See also #report_on_exception.
3742 *
3743 * There is also a class level method to set this for all new threads, see
3744 * ::report_on_exception=.
3745 */
3746
3747static VALUE
3748rb_thread_report_exc_set(VALUE thread, VALUE val)
3749{
3750 rb_thread_ptr(thread)->report_on_exception = RTEST(val);
3751 return val;
3752}
3753
3754
3755/*
3756 * call-seq:
3757 * thr.group -> thgrp or nil
3758 *
3759 * Returns the ThreadGroup which contains the given thread.
3760 *
3761 * Thread.main.group #=> #<ThreadGroup:0x4029d914>
3762 */
3763
3764VALUE
3765rb_thread_group(VALUE thread)
3766{
3767 return rb_thread_ptr(thread)->thgroup;
3768}
3769
3770static const char *
3771thread_status_name(rb_thread_t *th, int detail)
3772{
3773 switch (th->status) {
3774 case THREAD_RUNNABLE:
3775 return th->to_kill ? "aborting" : "run";
3776 case THREAD_STOPPED_FOREVER:
3777 if (detail) return "sleep_forever";
3778 case THREAD_STOPPED:
3779 return "sleep";
3780 case THREAD_KILLED:
3781 return "dead";
3782 default:
3783 return "unknown";
3784 }
3785}
3786
3787static int
3788rb_threadptr_dead(rb_thread_t *th)
3789{
3790 return th->status == THREAD_KILLED;
3791}
3792
3793
3794/*
3795 * call-seq:
3796 * thr.status -> string, false or nil
3797 *
3798 * Returns the status of +thr+.
3799 *
3800 * [<tt>"sleep"</tt>]
3801 * Returned if this thread is sleeping or waiting on I/O
3802 * [<tt>"run"</tt>]
3803 * When this thread is executing
3804 * [<tt>"aborting"</tt>]
3805 * If this thread is aborting
3806 * [+false+]
3807 * When this thread is terminated normally
3808 * [+nil+]
3809 * If terminated with an exception.
3810 *
3811 * a = Thread.new { raise("die now") }
3812 * b = Thread.new { Thread.stop }
3813 * c = Thread.new { Thread.exit }
3814 * d = Thread.new { sleep }
3815 * d.kill #=> #<Thread:0x401b3678 aborting>
3816 * a.status #=> nil
3817 * b.status #=> "sleep"
3818 * c.status #=> false
3819 * d.status #=> "aborting"
3820 * Thread.current.status #=> "run"
3821 *
3822 * See also the instance methods #alive? and #stop?
3823 */
3824
3825static VALUE
3826rb_thread_status(VALUE thread)
3827{
3828 rb_thread_t *target_th = rb_thread_ptr(thread);
3829
3830 if (rb_threadptr_dead(target_th)) {
3831 if (!NIL_P(target_th->ec->errinfo) &&
3832 !FIXNUM_P(target_th->ec->errinfo)) {
3833 return Qnil;
3834 }
3835 else {
3836 return Qfalse;
3837 }
3838 }
3839 else {
3840 return rb_str_new2(thread_status_name(target_th, FALSE));
3841 }
3842}
3843
3844
3845/*
3846 * call-seq:
3847 * thr.alive? -> true or false
3848 *
3849 * Returns +true+ if +thr+ is running or sleeping.
3850 *
3851 * thr = Thread.new { }
3852 * thr.join #=> #<Thread:0x401b3fb0 dead>
3853 * Thread.current.alive? #=> true
3854 * thr.alive? #=> false
3855 *
3856 * See also #stop? and #status.
3857 */
3858
3859static VALUE
3860rb_thread_alive_p(VALUE thread)
3861{
3862 return RBOOL(!thread_finished(rb_thread_ptr(thread)));
3863}
3864
3865/*
3866 * call-seq:
3867 * thr.stop? -> true or false
3868 *
3869 * Returns +true+ if +thr+ is dead or sleeping.
3870 *
3871 * a = Thread.new { Thread.stop }
3872 * b = Thread.current
3873 * a.stop? #=> true
3874 * b.stop? #=> false
3875 *
3876 * See also #alive? and #status.
3877 */
3878
3879static VALUE
3880rb_thread_stop_p(VALUE thread)
3881{
3882 rb_thread_t *th = rb_thread_ptr(thread);
3883
3884 if (rb_threadptr_dead(th)) {
3885 return Qtrue;
3886 }
3887 return RBOOL(th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER);
3888}
3889
3890/*
3891 * call-seq:
3892 * thr.name -> string
3893 *
3894 * show the name of the thread.
3895 */
3896
3897static VALUE
3898rb_thread_getname(VALUE thread)
3899{
3900 return rb_thread_ptr(thread)->name;
3901}
3902
3903/*
3904 * call-seq:
3905 * thr.name=(name) -> string
3906 *
3907 * set given name to the ruby thread.
3908 * On some platform, it may set the name to pthread and/or kernel.
3909 */
3910
3911static VALUE
3912rb_thread_setname(VALUE thread, VALUE name)
3913{
3914 rb_thread_t *target_th = rb_thread_ptr(thread);
3915
3916 if (!NIL_P(name)) {
3917 rb_encoding *enc;
3918 StringValueCStr(name);
3919 enc = rb_enc_get(name);
3920 if (!rb_enc_asciicompat(enc)) {
3921 rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
3922 rb_enc_name(enc));
3923 }
3924 name = rb_str_new_frozen(name);
3925 }
3926 target_th->name = name;
3927 if (threadptr_initialized(target_th) && target_th->has_dedicated_nt) {
3928 native_set_another_thread_name(target_th->nt->thread_id, name);
3929 }
3930 return name;
3931}
3932
3933#if USE_NATIVE_THREAD_NATIVE_THREAD_ID
3934/*
3935 * call-seq:
3936 * thr.native_thread_id -> integer
3937 *
3938 * Return the native thread ID which is used by the Ruby thread.
3939 *
3940 * The ID depends on the OS. (not POSIX thread ID returned by pthread_self(3))
3941 * * On Linux it is TID returned by gettid(2).
3942 * * On macOS it is the system-wide unique integral ID of thread returned
3943 * by pthread_threadid_np(3).
3944 * * On FreeBSD it is the unique integral ID of the thread returned by
3945 * pthread_getthreadid_np(3).
3946 * * On Windows it is the thread identifier returned by GetThreadId().
3947 * * On other platforms, it raises NotImplementedError.
3948 *
3949 * NOTE:
3950 * If the thread is not associated yet or already deassociated with a native
3951 * thread, it returns _nil_.
3952 * If the Ruby implementation uses M:N thread model, the ID may change
3953 * depending on the timing.
3954 */
3955
3956static VALUE
3957rb_thread_native_thread_id(VALUE thread)
3958{
3959 rb_thread_t *target_th = rb_thread_ptr(thread);
3960 if (rb_threadptr_dead(target_th)) return Qnil;
3961 return native_thread_native_thread_id(target_th);
3962}
3963#else
3964# define rb_thread_native_thread_id rb_f_notimplement
3965#endif
3966
3967/*
3968 * call-seq:
3969 * thr.to_s -> string
3970 *
3971 * Dump the name, id, and status of _thr_ to a string.
3972 */
3973
3974static VALUE
3975rb_thread_to_s(VALUE thread)
3976{
3977 VALUE cname = rb_class_path(rb_obj_class(thread));
3978 rb_thread_t *target_th = rb_thread_ptr(thread);
3979 const char *status;
3980 VALUE str, loc;
3981
3982 status = thread_status_name(target_th, TRUE);
3983 str = rb_sprintf("#<%"PRIsVALUE":%p", cname, (void *)thread);
3984 if (!NIL_P(target_th->name)) {
3985 rb_str_catf(str, "@%"PRIsVALUE, target_th->name);
3986 }
3987 if ((loc = threadptr_invoke_proc_location(target_th)) != Qnil) {
3988 rb_str_catf(str, " %"PRIsVALUE":%"PRIsVALUE,
3989 RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
3990 }
3991 rb_str_catf(str, " %s>", status);
3992
3993 return str;
3994}
3995
3996/* variables for recursive traversals */
3997#define recursive_key id__recursive_key__
3998
3999static VALUE
4000threadptr_local_aref(rb_thread_t *th, ID id)
4001{
4002 if (id == recursive_key) {
4003 return th->ec->local_storage_recursive_hash;
4004 }
4005 else {
4006 VALUE val;
4007 struct rb_id_table *local_storage = th->ec->local_storage;
4008
4009 if (local_storage != NULL && rb_id_table_lookup(local_storage, id, &val)) {
4010 return val;
4011 }
4012 else {
4013 return Qnil;
4014 }
4015 }
4016}
4017
4019rb_thread_local_aref(VALUE thread, ID id)
4020{
4021 return threadptr_local_aref(rb_thread_ptr(thread), id);
4022}
4023
4024/*
4025 * call-seq:
4026 * thr[sym] -> obj or nil
4027 *
4028 * Attribute Reference---Returns the value of a fiber-local variable (current thread's root fiber
4029 * if not explicitly inside a Fiber), using either a symbol or a string name.
4030 * If the specified variable does not exist, returns +nil+.
4031 *
4032 * [
4033 * Thread.new { Thread.current["name"] = "A" },
4034 * Thread.new { Thread.current[:name] = "B" },
4035 * Thread.new { Thread.current["name"] = "C" }
4036 * ].each do |th|
4037 * th.join
4038 * puts "#{th.inspect}: #{th[:name]}"
4039 * end
4040 *
4041 * This will produce:
4042 *
4043 * #<Thread:0x00000002a54220 dead>: A
4044 * #<Thread:0x00000002a541a8 dead>: B
4045 * #<Thread:0x00000002a54130 dead>: C
4046 *
4047 * Thread#[] and Thread#[]= are not thread-local but fiber-local.
4048 * This confusion did not exist in Ruby 1.8 because
4049 * fibers are only available since Ruby 1.9.
4050 * Ruby 1.9 chooses that the methods behaves fiber-local to save
4051 * following idiom for dynamic scope.
4052 *
4053 * def meth(newvalue)
4054 * begin
4055 * oldvalue = Thread.current[:name]
4056 * Thread.current[:name] = newvalue
4057 * yield
4058 * ensure
4059 * Thread.current[:name] = oldvalue
4060 * end
4061 * end
4062 *
4063 * The idiom may not work as dynamic scope if the methods are thread-local
4064 * and a given block switches fiber.
4065 *
4066 * f = Fiber.new {
4067 * meth(1) {
4068 * Fiber.yield
4069 * }
4070 * }
4071 * meth(2) {
4072 * f.resume
4073 * }
4074 * f.resume
4075 * p Thread.current[:name]
4076 * #=> nil if fiber-local
4077 * #=> 2 if thread-local (The value 2 is leaked to outside of meth method.)
4078 *
4079 * For thread-local variables, please see #thread_variable_get and
4080 * #thread_variable_set.
4081 *
4082 */
4083
4084static VALUE
4085rb_thread_aref(VALUE thread, VALUE key)
4086{
4087 ID id = rb_check_id(&key);
4088 if (!id) return Qnil;
4089 return rb_thread_local_aref(thread, id);
4090}
4091
4092/*
4093 * call-seq:
4094 * thr.fetch(sym) -> obj
4095 * thr.fetch(sym) { } -> obj
4096 * thr.fetch(sym, default) -> obj
4097 *
4098 * Returns a fiber-local for the given key. If the key can't be
4099 * found, there are several options: With no other arguments, it will
4100 * raise a KeyError exception; if <i>default</i> is given, then that
4101 * will be returned; if the optional code block is specified, then
4102 * that will be run and its result returned. See Thread#[] and
4103 * Hash#fetch.
4104 */
4105static VALUE
4106rb_thread_fetch(int argc, VALUE *argv, VALUE self)
4107{
4108 VALUE key, val;
4109 ID id;
4110 rb_thread_t *target_th = rb_thread_ptr(self);
4111 int block_given;
4112
4113 rb_check_arity(argc, 1, 2);
4114 key = argv[0];
4115
4116 block_given = rb_block_given_p();
4117 if (block_given && argc == 2) {
4118 rb_warn("block supersedes default value argument");
4119 }
4120
4121 id = rb_check_id(&key);
4122
4123 if (id == recursive_key) {
4124 return target_th->ec->local_storage_recursive_hash;
4125 }
4126 else if (id && target_th->ec->local_storage &&
4127 rb_id_table_lookup(target_th->ec->local_storage, id, &val)) {
4128 return val;
4129 }
4130 else if (block_given) {
4131 return rb_yield(key);
4132 }
4133 else if (argc == 1) {
4134 rb_key_err_raise(rb_sprintf("key not found: %+"PRIsVALUE, key), self, key);
4135 }
4136 else {
4137 return argv[1];
4138 }
4139}
4140
4141static VALUE
4142threadptr_local_aset(rb_thread_t *th, ID id, VALUE val)
4143{
4144 if (id == recursive_key) {
4145 th->ec->local_storage_recursive_hash = val;
4146 return val;
4147 }
4148 else {
4149 struct rb_id_table *local_storage = th->ec->local_storage;
4150
4151 if (NIL_P(val)) {
4152 if (!local_storage) return Qnil;
4153 rb_id_table_delete(local_storage, id);
4154 return Qnil;
4155 }
4156 else {
4157 if (local_storage == NULL) {
4158 th->ec->local_storage = local_storage = rb_id_table_create(0);
4159 }
4160 rb_id_table_insert(local_storage, id, val);
4161 return val;
4162 }
4163 }
4164}
4165
4167rb_thread_local_aset(VALUE thread, ID id, VALUE val)
4168{
4169 if (OBJ_FROZEN(thread)) {
4170 rb_frozen_error_raise(thread, "can't modify frozen thread locals");
4171 }
4172
4173 return threadptr_local_aset(rb_thread_ptr(thread), id, val);
4174}
4175
4176/*
4177 * call-seq:
4178 * thr[sym] = obj -> obj
4179 *
4180 * Attribute Assignment---Sets or creates the value of a fiber-local variable,
4181 * using either a symbol or a string.
4182 *
4183 * See also Thread#[].
4184 *
4185 * For thread-local variables, please see #thread_variable_set and
4186 * #thread_variable_get.
4187 */
4188
4189static VALUE
4190rb_thread_aset(VALUE self, VALUE id, VALUE val)
4191{
4192 return rb_thread_local_aset(self, rb_to_id(id), val);
4193}
4194
4195/*
4196 * call-seq:
4197 * thr.thread_variable_get(key) -> obj or nil
4198 *
4199 * Returns the value of a thread local variable that has been set. Note that
4200 * these are different than fiber local values. For fiber local values,
4201 * please see Thread#[] and Thread#[]=.
4202 *
4203 * Thread local values are carried along with threads, and do not respect
4204 * fibers. For example:
4205 *
4206 * Thread.new {
4207 * Thread.current.thread_variable_set("foo", "bar") # set a thread local
4208 * Thread.current["foo"] = "bar" # set a fiber local
4209 *
4210 * Fiber.new {
4211 * Fiber.yield [
4212 * Thread.current.thread_variable_get("foo"), # get the thread local
4213 * Thread.current["foo"], # get the fiber local
4214 * ]
4215 * }.resume
4216 * }.join.value # => ['bar', nil]
4217 *
4218 * The value "bar" is returned for the thread local, where nil is returned
4219 * for the fiber local. The fiber is executed in the same thread, so the
4220 * thread local values are available.
4221 */
4222
4223static VALUE
4224rb_thread_variable_get(VALUE thread, VALUE key)
4225{
4226 VALUE locals;
4227 VALUE symbol = rb_to_symbol(key);
4228
4229 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4230 return Qnil;
4231 }
4232 locals = rb_thread_local_storage(thread);
4233 return rb_hash_aref(locals, symbol);
4234}
4235
4236/*
4237 * call-seq:
4238 * thr.thread_variable_set(key, value)
4239 *
4240 * Sets a thread local with +key+ to +value+. Note that these are local to
4241 * threads, and not to fibers. Please see Thread#thread_variable_get and
4242 * Thread#[] for more information.
4243 */
4244
4245static VALUE
4246rb_thread_variable_set(VALUE thread, VALUE key, VALUE val)
4247{
4248 VALUE locals;
4249
4250 if (OBJ_FROZEN(thread)) {
4251 rb_frozen_error_raise(thread, "can't modify frozen thread locals");
4252 }
4253
4254 locals = rb_thread_local_storage(thread);
4255 return rb_hash_aset(locals, rb_to_symbol(key), val);
4256}
4257
4258/*
4259 * call-seq:
4260 * thr.key?(sym) -> true or false
4261 *
4262 * Returns +true+ if the given string (or symbol) exists as a fiber-local
4263 * variable.
4264 *
4265 * me = Thread.current
4266 * me[:oliver] = "a"
4267 * me.key?(:oliver) #=> true
4268 * me.key?(:stanley) #=> false
4269 */
4270
4271static VALUE
4272rb_thread_key_p(VALUE self, VALUE key)
4273{
4274 VALUE val;
4275 ID id = rb_check_id(&key);
4276 struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
4277
4278 if (!id || local_storage == NULL) {
4279 return Qfalse;
4280 }
4281 return RBOOL(rb_id_table_lookup(local_storage, id, &val));
4282}
4283
4284static enum rb_id_table_iterator_result
4285thread_keys_i(ID key, VALUE value, void *ary)
4286{
4287 rb_ary_push((VALUE)ary, ID2SYM(key));
4288 return ID_TABLE_CONTINUE;
4289}
4290
4292rb_thread_alone(void)
4293{
4294 // TODO
4295 return rb_ractor_living_thread_num(GET_RACTOR()) == 1;
4296}
4297
4298/*
4299 * call-seq:
4300 * thr.keys -> array
4301 *
4302 * Returns an array of the names of the fiber-local variables (as Symbols).
4303 *
4304 * thr = Thread.new do
4305 * Thread.current[:cat] = 'meow'
4306 * Thread.current["dog"] = 'woof'
4307 * end
4308 * thr.join #=> #<Thread:0x401b3f10 dead>
4309 * thr.keys #=> [:dog, :cat]
4310 */
4311
4312static VALUE
4313rb_thread_keys(VALUE self)
4314{
4315 struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
4316 VALUE ary = rb_ary_new();
4317
4318 if (local_storage) {
4319 rb_id_table_foreach(local_storage, thread_keys_i, (void *)ary);
4320 }
4321 return ary;
4322}
4323
4324static int
4325keys_i(VALUE key, VALUE value, VALUE ary)
4326{
4327 rb_ary_push(ary, key);
4328 return ST_CONTINUE;
4329}
4330
4331/*
4332 * call-seq:
4333 * thr.thread_variables -> array
4334 *
4335 * Returns an array of the names of the thread-local variables (as Symbols).
4336 *
4337 * thr = Thread.new do
4338 * Thread.current.thread_variable_set(:cat, 'meow')
4339 * Thread.current.thread_variable_set("dog", 'woof')
4340 * end
4341 * thr.join #=> #<Thread:0x401b3f10 dead>
4342 * thr.thread_variables #=> [:dog, :cat]
4343 *
4344 * Note that these are not fiber local variables. Please see Thread#[] and
4345 * Thread#thread_variable_get for more details.
4346 */
4347
4348static VALUE
4349rb_thread_variables(VALUE thread)
4350{
4351 VALUE locals;
4352 VALUE ary;
4353
4354 ary = rb_ary_new();
4355 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4356 return ary;
4357 }
4358 locals = rb_thread_local_storage(thread);
4359 rb_hash_foreach(locals, keys_i, ary);
4360
4361 return ary;
4362}
4363
4364/*
4365 * call-seq:
4366 * thr.thread_variable?(key) -> true or false
4367 *
4368 * Returns +true+ if the given string (or symbol) exists as a thread-local
4369 * variable.
4370 *
4371 * me = Thread.current
4372 * me.thread_variable_set(:oliver, "a")
4373 * me.thread_variable?(:oliver) #=> true
4374 * me.thread_variable?(:stanley) #=> false
4375 *
4376 * Note that these are not fiber local variables. Please see Thread#[] and
4377 * Thread#thread_variable_get for more details.
4378 */
4379
4380static VALUE
4381rb_thread_variable_p(VALUE thread, VALUE key)
4382{
4383 VALUE locals;
4384 VALUE symbol = rb_to_symbol(key);
4385
4386 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4387 return Qfalse;
4388 }
4389 locals = rb_thread_local_storage(thread);
4390
4391 return RBOOL(rb_hash_lookup(locals, symbol) != Qnil);
4392}
4393
4394/*
4395 * call-seq:
4396 * thr.priority -> integer
4397 *
4398 * Returns the priority of <i>thr</i>. Default is inherited from the
4399 * current thread which creating the new thread, or zero for the
4400 * initial main thread; higher-priority thread will run more frequently
4401 * than lower-priority threads (but lower-priority threads can also run).
4402 *
4403 * This is just hint for Ruby thread scheduler. It may be ignored on some
4404 * platform.
4405 *
4406 * Thread.current.priority #=> 0
4407 */
4408
4409static VALUE
4410rb_thread_priority(VALUE thread)
4411{
4412 return INT2NUM(rb_thread_ptr(thread)->priority);
4413}
4414
4415
4416/*
4417 * call-seq:
4418 * thr.priority= integer -> thr
4419 *
4420 * Sets the priority of <i>thr</i> to <i>integer</i>. Higher-priority threads
4421 * will run more frequently than lower-priority threads (but lower-priority
4422 * threads can also run).
4423 *
4424 * This is just hint for Ruby thread scheduler. It may be ignored on some
4425 * platform.
4426 *
4427 * count1 = count2 = 0
4428 * a = Thread.new do
4429 * loop { count1 += 1 }
4430 * end
4431 * a.priority = -1
4432 *
4433 * b = Thread.new do
4434 * loop { count2 += 1 }
4435 * end
4436 * b.priority = -2
4437 * sleep 1 #=> 1
4438 * count1 #=> 622504
4439 * count2 #=> 5832
4440 */
4441
4442static VALUE
4443rb_thread_priority_set(VALUE thread, VALUE prio)
4444{
4445 rb_thread_t *target_th = rb_thread_ptr(thread);
4446 int priority;
4447
4448#if USE_NATIVE_THREAD_PRIORITY
4449 target_th->priority = NUM2INT(prio);
4450 native_thread_apply_priority(th);
4451#else
4452 priority = NUM2INT(prio);
4453 if (priority > RUBY_THREAD_PRIORITY_MAX) {
4454 priority = RUBY_THREAD_PRIORITY_MAX;
4455 }
4456 else if (priority < RUBY_THREAD_PRIORITY_MIN) {
4457 priority = RUBY_THREAD_PRIORITY_MIN;
4458 }
4459 target_th->priority = (int8_t)priority;
4460#endif
4461 return INT2NUM(target_th->priority);
4462}
4463
4464/* for IO */
4465
4466#if defined(NFDBITS) && defined(HAVE_RB_FD_INIT)
4467
4468/*
4469 * several Unix platforms support file descriptors bigger than FD_SETSIZE
4470 * in select(2) system call.
4471 *
4472 * - Linux 2.2.12 (?)
4473 * - NetBSD 1.2 (src/sys/kern/sys_generic.c:1.25)
4474 * select(2) documents how to allocate fd_set dynamically.
4475 * http://netbsd.gw.com/cgi-bin/man-cgi?select++NetBSD-4.0
4476 * - FreeBSD 2.2 (src/sys/kern/sys_generic.c:1.19)
4477 * - OpenBSD 2.0 (src/sys/kern/sys_generic.c:1.4)
4478 * select(2) documents how to allocate fd_set dynamically.
4479 * http://www.openbsd.org/cgi-bin/man.cgi?query=select&manpath=OpenBSD+4.4
4480 * - Solaris 8 has select_large_fdset
4481 * - Mac OS X 10.7 (Lion)
4482 * select(2) returns EINVAL if nfds is greater than FD_SET_SIZE and
4483 * _DARWIN_UNLIMITED_SELECT (or _DARWIN_C_SOURCE) isn't defined.
4484 * https://developer.apple.com/library/archive/releasenotes/Darwin/SymbolVariantsRelNotes/index.html
4485 *
4486 * When fd_set is not big enough to hold big file descriptors,
4487 * it should be allocated dynamically.
4488 * Note that this assumes fd_set is structured as bitmap.
4489 *
4490 * rb_fd_init allocates the memory.
4491 * rb_fd_term free the memory.
4492 * rb_fd_set may re-allocates bitmap.
4493 *
4494 * So rb_fd_set doesn't reject file descriptors bigger than FD_SETSIZE.
4495 */
4496
4497void
4499{
4500 fds->maxfd = 0;
4501 fds->fdset = ALLOC(fd_set);
4502 FD_ZERO(fds->fdset);
4503}
4504
4505static inline size_t
4506fdset_memsize(int maxfd)
4507{
4508 size_t o = howmany(maxfd, NFDBITS) * sizeof(fd_mask);
4509 if (o < sizeof(fd_set)) {
4510 return sizeof(fd_set);
4511 }
4512 return o;
4513}
4514
4515void
4516rb_fd_init_copy(rb_fdset_t *dst, rb_fdset_t *src)
4517{
4518 size_t size = fdset_memsize(rb_fd_max(src));
4519 dst->maxfd = src->maxfd;
4520 dst->fdset = xmalloc(size);
4521 memcpy(dst->fdset, src->fdset, size);
4522}
4523
4524void
4526{
4527 ruby_xfree_sized(fds->fdset, fdset_memsize(fds->maxfd));
4528 fds->maxfd = 0;
4529 fds->fdset = 0;
4530}
4531
4532void
4534{
4535 if (fds->fdset)
4536 MEMZERO(fds->fdset, fd_mask, howmany(fds->maxfd, NFDBITS));
4537}
4538
4539static void
4540rb_fd_resize(int n, rb_fdset_t *fds)
4541{
4542 size_t m = fdset_memsize(n + 1);
4543 size_t o = fdset_memsize(fds->maxfd);
4544
4545 if (m > o) {
4546 fds->fdset = ruby_xrealloc_sized(fds->fdset, m, o);
4547 memset((char *)fds->fdset + o, 0, m - o);
4548 }
4549 if (n >= fds->maxfd) fds->maxfd = n + 1;
4550}
4551
4552void
4553rb_fd_set(int n, rb_fdset_t *fds)
4554{
4555 rb_fd_resize(n, fds);
4556 FD_SET(n, fds->fdset);
4557}
4558
4559void
4560rb_fd_clr(int n, rb_fdset_t *fds)
4561{
4562 if (n >= fds->maxfd) return;
4563 FD_CLR(n, fds->fdset);
4564}
4565
4566int
4567rb_fd_isset(int n, const rb_fdset_t *fds)
4568{
4569 if (n >= fds->maxfd) return 0;
4570 return FD_ISSET(n, fds->fdset) != 0; /* "!= 0" avoids FreeBSD PR 91421 */
4571}
4572
4573void
4574rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
4575{
4576 size_t size = fdset_memsize(max);
4577 dst->fdset = ruby_xrealloc_sized(dst->fdset, size, fdset_memsize(dst->maxfd));
4578 dst->maxfd = max;
4579 memcpy(dst->fdset, src, size);
4580}
4581
4582void
4583rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
4584{
4585 size_t size = fdset_memsize(rb_fd_max(src));
4586 dst->fdset = ruby_xrealloc_sized(dst->fdset, size, fdset_memsize(dst->maxfd));
4587 dst->maxfd = src->maxfd;
4588 memcpy(dst->fdset, src->fdset, size);
4589}
4590
4591int
4592rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout)
4593{
4594 fd_set *r = NULL, *w = NULL, *e = NULL;
4595 if (readfds) {
4596 rb_fd_resize(n - 1, readfds);
4597 r = rb_fd_ptr(readfds);
4598 }
4599 if (writefds) {
4600 rb_fd_resize(n - 1, writefds);
4601 w = rb_fd_ptr(writefds);
4602 }
4603 if (exceptfds) {
4604 rb_fd_resize(n - 1, exceptfds);
4605 e = rb_fd_ptr(exceptfds);
4606 }
4607 return select(n, r, w, e, timeout);
4608}
4609
4610#define rb_fd_no_init(fds) ((void)((fds)->fdset = 0), (void)((fds)->maxfd = 0))
4611
4612#undef FD_ZERO
4613#undef FD_SET
4614#undef FD_CLR
4615#undef FD_ISSET
4616
4617#define FD_ZERO(f) rb_fd_zero(f)
4618#define FD_SET(i, f) rb_fd_set((i), (f))
4619#define FD_CLR(i, f) rb_fd_clr((i), (f))
4620#define FD_ISSET(i, f) rb_fd_isset((i), (f))
4621
4622#elif defined(_WIN32)
4623
4624void
4626{
4627 set->capa = FD_SETSIZE;
4628 set->fdset = ALLOC(fd_set);
4629 FD_ZERO(set->fdset);
4630}
4631
4632void
4633rb_fd_init_copy(rb_fdset_t *dst, rb_fdset_t *src)
4634{
4635 rb_fd_init(dst);
4636 rb_fd_dup(dst, src);
4637}
4638
4639static inline size_t
4640fdset_memsize(int capa)
4641{
4642 if (capa == FD_SETSIZE) {
4643 return sizeof(fd_set);
4644 }
4645 return sizeof(unsigned int) + (capa * sizeof(SOCKET));
4646}
4647
4648void
4650{
4651 ruby_xfree_sized(set->fdset, fdset_memsize(set->capa));
4652 set->fdset = NULL;
4653 set->capa = 0;
4654}
4655
4656void
4657rb_fd_set(int fd, rb_fdset_t *set)
4658{
4659 unsigned int i;
4660 SOCKET s = rb_w32_get_osfhandle(fd);
4661
4662 for (i = 0; i < set->fdset->fd_count; i++) {
4663 if (set->fdset->fd_array[i] == s) {
4664 return;
4665 }
4666 }
4667 if (set->fdset->fd_count >= (unsigned)set->capa) {
4668 set->capa = (set->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
4669 set->fdset =
4670 rb_xrealloc_mul_add(
4671 set->fdset, set->capa, sizeof(SOCKET), sizeof(unsigned int));
4672 }
4673 set->fdset->fd_array[set->fdset->fd_count++] = s;
4674}
4675
4676#undef FD_ZERO
4677#undef FD_SET
4678#undef FD_CLR
4679#undef FD_ISSET
4680
4681#define FD_ZERO(f) rb_fd_zero(f)
4682#define FD_SET(i, f) rb_fd_set((i), (f))
4683#define FD_CLR(i, f) rb_fd_clr((i), (f))
4684#define FD_ISSET(i, f) rb_fd_isset((i), (f))
4685
4686#define rb_fd_no_init(fds) (void)((fds)->fdset = 0)
4687
4688#endif
4689
4690#ifndef rb_fd_no_init
4691#define rb_fd_no_init(fds) (void)(fds)
4692#endif
4693
4694static int
4695wait_retryable(volatile int *result, int errnum, rb_hrtime_t *rel, rb_hrtime_t end)
4696{
4697 int r = *result;
4698 if (r < 0) {
4699 switch (errnum) {
4700 case EINTR:
4701#ifdef ERESTART
4702 case ERESTART:
4703#endif
4704 *result = 0;
4705 if (rel && hrtime_update_expire(rel, end)) {
4706 *rel = 0;
4707 }
4708 return TRUE;
4709 }
4710 return FALSE;
4711 }
4712 else if (r == 0) {
4713 /* check for spurious wakeup */
4714 if (rel) {
4715 return !hrtime_update_expire(rel, end);
4716 }
4717 return TRUE;
4718 }
4719 return FALSE;
4720}
4722struct select_set {
4723 int max;
4724 rb_thread_t *th;
4725 rb_fdset_t *rset;
4726 rb_fdset_t *wset;
4727 rb_fdset_t *eset;
4728 rb_fdset_t orig_rset;
4729 rb_fdset_t orig_wset;
4730 rb_fdset_t orig_eset;
4731 struct timeval *timeout;
4732};
4733
4734static VALUE
4735select_set_free(VALUE p)
4736{
4737 struct select_set *set = (struct select_set *)p;
4738
4739 rb_fd_term(&set->orig_rset);
4740 rb_fd_term(&set->orig_wset);
4741 rb_fd_term(&set->orig_eset);
4742
4743 return Qfalse;
4744}
4745
4746static VALUE
4747do_select(VALUE p)
4748{
4749 struct select_set *set = (struct select_set *)p;
4750 volatile int result = 0;
4751 int lerrno;
4752 rb_hrtime_t *to, rel, end = 0;
4753
4754 timeout_prepare(&to, &rel, &end, set->timeout);
4755 volatile rb_hrtime_t endtime = end;
4756#define restore_fdset(dst, src) \
4757 ((dst) ? rb_fd_dup(dst, src) : (void)0)
4758#define do_select_update() \
4759 (restore_fdset(set->rset, &set->orig_rset), \
4760 restore_fdset(set->wset, &set->orig_wset), \
4761 restore_fdset(set->eset, &set->orig_eset), \
4762 TRUE)
4763
4764 do {
4765 lerrno = 0;
4766
4767 BLOCKING_REGION(set->th, {
4768 struct timeval tv;
4769
4770 if (!RUBY_VM_INTERRUPTED(set->th->ec)) {
4771 result = native_fd_select(set->max,
4772 set->rset, set->wset, set->eset,
4773 rb_hrtime2timeval(&tv, to), set->th);
4774 if (result < 0) lerrno = errno;
4775 }
4776 }, ubf_select, set->th, TRUE);
4777
4778 RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec); /* may raise */
4779 } while (wait_retryable(&result, lerrno, to, endtime) && do_select_update());
4780
4781 RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec);
4782
4783 if (result < 0) {
4784 errno = lerrno;
4785 }
4786
4787 return (VALUE)result;
4788}
4789
4791rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t * except,
4792 struct timeval *timeout)
4793{
4794 struct select_set set;
4795
4796 set.th = GET_THREAD();
4797 RUBY_VM_CHECK_INTS_BLOCKING(set.th->ec);
4798 set.max = max;
4799 set.rset = read;
4800 set.wset = write;
4801 set.eset = except;
4802 set.timeout = timeout;
4803
4804 if (!set.rset && !set.wset && !set.eset) {
4805 if (!timeout) {
4807 return 0;
4808 }
4809 rb_thread_wait_for(*timeout);
4810 return 0;
4811 }
4812
4813#define fd_init_copy(f) do { \
4814 if (set.f) { \
4815 rb_fd_resize(set.max - 1, set.f); \
4816 if (&set.orig_##f != set.f) { /* sigwait_fd */ \
4817 rb_fd_init_copy(&set.orig_##f, set.f); \
4818 } \
4819 } \
4820 else { \
4821 rb_fd_no_init(&set.orig_##f); \
4822 } \
4823 } while (0)
4824 fd_init_copy(rset);
4825 fd_init_copy(wset);
4826 fd_init_copy(eset);
4827#undef fd_init_copy
4828
4829 return (int)rb_ensure(do_select, (VALUE)&set, select_set_free, (VALUE)&set);
4830}
4831
4832#ifdef USE_POLL
4833
4834/* The same with linux kernel. TODO: make platform independent definition. */
4835#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
4836#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
4837#define POLLEX_SET (POLLPRI)
4838
4839#ifndef POLLERR_SET /* defined for FreeBSD for now */
4840# define POLLERR_SET (0)
4841#endif
4842
4843static int
4844wait_for_single_fd_blocking_region(rb_thread_t *th, struct pollfd *fds, nfds_t nfds,
4845 rb_hrtime_t *const to, volatile int *lerrno)
4846{
4847 struct timespec ts;
4848 volatile int result = 0;
4849
4850 *lerrno = 0;
4851 BLOCKING_REGION(th, {
4852 if (!RUBY_VM_INTERRUPTED(th->ec)) {
4853 result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, to), 0);
4854 if (result < 0) *lerrno = errno;
4855 }
4856 }, ubf_select, th, TRUE);
4857 return result;
4858}
4859
4860/*
4861 * returns a mask of events
4862 */
4863static int
4864thread_io_wait(rb_thread_t *th, struct rb_io *io, int fd, int events, struct timeval *timeout)
4865{
4866 struct pollfd fds[1] = {{
4867 .fd = fd,
4868 .events = (short)events,
4869 .revents = 0,
4870 }};
4871 volatile int result = 0;
4872 nfds_t nfds;
4873 struct rb_io_blocking_operation blocking_operation;
4874 enum ruby_tag_type state = TAG_NONE;
4875 volatile int lerrno;
4876
4877 RUBY_ASSERT(th);
4878 rb_execution_context_t *ec = th->ec;
4879
4880 if (io) {
4881 blocking_operation.ec = ec;
4882COMPILER_WARNING_PUSH
4883#if RBIMPL_COMPILER_SINCE(GCC, 12, 0, 0)
4884COMPILER_WARNING_IGNORED(-Wdangling-pointer)
4885#endif
4886 // rb_io_blocking_operation_exit() below unlinks it on every path.
4887 rb_io_blocking_operation_enter(io, &blocking_operation);
4888COMPILER_WARNING_POP
4889 }
4890
4891 // A zero timeout is a plain probe; ppoll answers it without parking.
4892 bool mn_wait = timeout == NULL || timeout->tv_sec != 0 || timeout->tv_usec != 0;
4893
4894 switch (mn_wait ? thread_io_wait_events(th, fd, events, timeout, false) : io_wait_unhandled) {
4895 case io_wait_ready:
4896 fds[0].revents = events;
4897 errno = 0;
4898 break;
4899 case io_wait_timed_out:
4900 // revents stays 0, so the result below becomes 0 as with ppoll's timeout.
4901 errno = 0;
4902 break;
4903 case io_wait_unhandled:
4904 EC_PUSH_TAG(ec);
4905 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
4906 rb_hrtime_t *to, rel, end = 0;
4907 RUBY_VM_CHECK_INTS_BLOCKING(ec);
4908 timeout_prepare(&to, &rel, &end, timeout);
4909 do {
4910 nfds = numberof(fds);
4911 result = wait_for_single_fd_blocking_region(th, fds, nfds, to, &lerrno);
4912
4913 RUBY_VM_CHECK_INTS_BLOCKING(ec);
4914 } while (wait_retryable(&result, lerrno, to, end));
4915
4916 RUBY_VM_CHECK_INTS_BLOCKING(ec);
4917 }
4918
4919 EC_POP_TAG();
4920 }
4921
4922 if (io) {
4923 rb_io_blocking_operation_exit(io, &blocking_operation);
4924 }
4925
4926 if (state) {
4927 EC_JUMP_TAG(ec, state);
4928 }
4929
4930 if (result < 0) {
4931 errno = lerrno;
4932 return -1;
4933 }
4934
4935 if (fds[0].revents & POLLNVAL) {
4936 errno = EBADF;
4937 return -1;
4938 }
4939
4940 /*
4941 * POLLIN, POLLOUT have a different meanings from select(2)'s read/write bit.
4942 * Therefore we need to fix it up.
4943 */
4944 result = 0;
4945 if (fds[0].revents & POLLIN_SET)
4946 result |= RB_WAITFD_IN;
4947 if (fds[0].revents & POLLOUT_SET)
4948 result |= RB_WAITFD_OUT;
4949 if (fds[0].revents & POLLEX_SET)
4950 result |= RB_WAITFD_PRI;
4951
4952 /* all requested events are ready if there is an error */
4953 if (fds[0].revents & POLLERR_SET)
4954 result |= events;
4955
4956 return result;
4957}
4958#else /* ! USE_POLL - implement rb_io_poll_fd() using select() */
4959struct select_args {
4960 struct rb_io *io;
4961 struct rb_io_blocking_operation *blocking_operation;
4962
4963 union {
4964 int fd;
4965 int error;
4966 } as;
4967 rb_fdset_t *read;
4968 rb_fdset_t *write;
4969 rb_fdset_t *except;
4970 struct timeval *tv;
4971};
4972
4973static VALUE
4974select_single(VALUE ptr)
4975{
4976 struct select_args *args = (struct select_args *)ptr;
4977 int r;
4978
4979 r = rb_thread_fd_select(args->as.fd + 1,
4980 args->read, args->write, args->except, args->tv);
4981 if (r == -1)
4982 args->as.error = errno;
4983 if (r > 0) {
4984 r = 0;
4985 if (args->read && rb_fd_isset(args->as.fd, args->read))
4986 r |= RB_WAITFD_IN;
4987 if (args->write && rb_fd_isset(args->as.fd, args->write))
4988 r |= RB_WAITFD_OUT;
4989 if (args->except && rb_fd_isset(args->as.fd, args->except))
4990 r |= RB_WAITFD_PRI;
4991 }
4992 return (VALUE)r;
4993}
4994
4995static VALUE
4996select_single_cleanup(VALUE ptr)
4997{
4998 struct select_args *args = (struct select_args *)ptr;
4999
5000 if (args->blocking_operation) {
5001 rb_io_blocking_operation_exit(args->io, args->blocking_operation);
5002 }
5003
5004 if (args->read) rb_fd_term(args->read);
5005 if (args->write) rb_fd_term(args->write);
5006 if (args->except) rb_fd_term(args->except);
5007
5008 return (VALUE)-1;
5009}
5010
5011static rb_fdset_t *
5012init_set_fd(int fd, rb_fdset_t *fds)
5013{
5014 if (fd < 0) {
5015 return 0;
5016 }
5017 rb_fd_init(fds);
5018 rb_fd_set(fd, fds);
5019
5020 return fds;
5021}
5022
5023static int
5024thread_io_wait(rb_thread_t *th, struct rb_io *io, int fd, int events, struct timeval *timeout)
5025{
5026 rb_fdset_t rfds, wfds, efds;
5027 struct select_args args;
5028 VALUE ptr = (VALUE)&args;
5029
5030 struct rb_io_blocking_operation blocking_operation;
5031 if (io) {
5032 args.io = io;
5033 blocking_operation.ec = th->ec;
5034 rb_io_blocking_operation_enter(io, &blocking_operation);
5035 args.blocking_operation = &blocking_operation;
5036 }
5037 else {
5038 args.io = NULL;
5039 blocking_operation.ec = NULL;
5040 args.blocking_operation = NULL;
5041 }
5042
5043 args.as.fd = fd;
5044 args.read = (events & RB_WAITFD_IN) ? init_set_fd(fd, &rfds) : NULL;
5045 args.write = (events & RB_WAITFD_OUT) ? init_set_fd(fd, &wfds) : NULL;
5046 args.except = (events & RB_WAITFD_PRI) ? init_set_fd(fd, &efds) : NULL;
5047 args.tv = timeout;
5048
5049 int result = (int)rb_ensure(select_single, ptr, select_single_cleanup, ptr);
5050 if (result == -1)
5051 errno = args.as.error;
5052
5053 return result;
5054}
5055#endif /* ! USE_POLL */
5056
5057int
5058rb_thread_wait_for_single_fd(rb_thread_t *th, int fd, int events, struct timeval *timeout)
5059{
5060 return thread_io_wait(th, NULL, fd, events, timeout);
5061}
5062
5063int
5064rb_thread_io_wait(rb_thread_t *th, struct rb_io *io, int events, struct timeval * timeout)
5065{
5066 return thread_io_wait(th, io, io->fd, events, timeout);
5067}
5068
5069/*
5070 * for GC
5071 */
5072
5073#ifdef USE_CONSERVATIVE_STACK_END
5074void
5075rb_gc_set_stack_end(VALUE **stack_end_p)
5076{
5077 VALUE stack_end;
5078COMPILER_WARNING_PUSH
5079#if RBIMPL_COMPILER_SINCE(GCC, 12, 0, 0)
5080COMPILER_WARNING_IGNORED(-Wdangling-pointer);
5081#endif
5082 *stack_end_p = &stack_end;
5083COMPILER_WARNING_POP
5084}
5085#endif
5086
5087/*
5088 *
5089 */
5090
5091void
5092rb_threadptr_check_signal(rb_thread_t *mth)
5093{
5094 /* mth must be main_thread */
5095 if (rb_signal_buff_size() > 0) {
5096 /* wakeup main thread */
5097 threadptr_trap_interrupt(mth);
5098 }
5099}
5100
5101static void
5102async_bug_fd(const char *mesg, int errno_arg, int fd)
5103{
5104 char buff[64];
5105 size_t n = strlcpy(buff, mesg, sizeof(buff));
5106 if (n < sizeof(buff)-3) {
5107 ruby_snprintf(buff+n, sizeof(buff)-n, "(%d)", fd);
5108 }
5109 rb_async_bug_errno(buff, errno_arg);
5110}
5111
5112/* VM-dependent API is not available for this function */
5113static int
5114consume_communication_pipe(int fd)
5115{
5116#if USE_EVENTFD
5117 uint64_t buff[1];
5118#else
5119 /* buffer can be shared because no one refers to them. */
5120 static char buff[1024];
5121#endif
5122 ssize_t result;
5123 int ret = FALSE; /* for rb_sigwait_sleep */
5124
5125 while (1) {
5126 result = read(fd, buff, sizeof(buff));
5127#if USE_EVENTFD
5128 RUBY_DEBUG_LOG("resultf:%d buff:%lu", (int)result, (unsigned long)buff[0]);
5129#else
5130 RUBY_DEBUG_LOG("result:%d", (int)result);
5131#endif
5132 if (result > 0) {
5133 ret = TRUE;
5134 if (USE_EVENTFD || result < (ssize_t)sizeof(buff)) {
5135 return ret;
5136 }
5137 }
5138 else if (result == 0) {
5139 return ret;
5140 }
5141 else if (result < 0) {
5142 int e = errno;
5143 switch (e) {
5144 case EINTR:
5145 continue; /* retry */
5146 case EAGAIN:
5147#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
5148 case EWOULDBLOCK:
5149#endif
5150 return ret;
5151 default:
5152 async_bug_fd("consume_communication_pipe: read", e, fd);
5153 }
5154 }
5155 }
5156}
5157
5158void
5159rb_thread_stop_timer_thread(void)
5160{
5161 if (TIMER_THREAD_CREATED_P() && native_stop_timer_thread()) {
5162 native_reset_timer_thread();
5163 }
5164}
5165
5166void
5167rb_thread_reset_timer_thread(void)
5168{
5169 native_reset_timer_thread();
5170}
5171
5172void
5173rb_thread_start_timer_thread(void)
5174{
5175 system_working = 1;
5176 rb_thread_create_timer_thread();
5177}
5178
5179static int
5180clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
5181{
5182 int i;
5183 VALUE coverage = (VALUE)val;
5184 VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
5185 VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
5186
5187 if (lines) {
5188 if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
5189 rb_ary_clear(lines);
5190 }
5191 else {
5192 int i;
5193 for (i = 0; i < RARRAY_LEN(lines); i++) {
5194 if (RARRAY_AREF(lines, i) != Qnil)
5195 RARRAY_ASET(lines, i, INT2FIX(0));
5196 }
5197 }
5198 }
5199 if (branches) {
5200 VALUE counters = RARRAY_AREF(branches, 1);
5201 for (i = 0; i < RARRAY_LEN(counters); i++) {
5202 RARRAY_ASET(counters, i, INT2FIX(0));
5203 }
5204 }
5205
5206 return ST_CONTINUE;
5207}
5208
5209void
5210rb_clear_coverages(void)
5211{
5212 VALUE coverages = rb_get_coverages();
5213 if (RTEST(coverages)) {
5214 rb_hash_foreach(coverages, clear_coverage_i, 0);
5215 }
5216}
5217
5218#if defined(HAVE_WORKING_FORK)
5219
5220static void
5221rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const rb_thread_t *))
5222{
5223 rb_thread_t *i = 0;
5224 rb_vm_t *vm = th->vm;
5225 rb_ractor_t *r = th->ractor;
5226 vm->ractor.main_ractor = r;
5227 vm->ractor.main_thread = th;
5228 r->threads.main = th;
5229 r->status_ = ractor_created;
5230
5231 thread_sched_atfork(TH_SCHED(th));
5232 ubf_list_atfork();
5233 rb_signal_atfork();
5234
5235 // OK. Only this thread accesses:
5236 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
5237 if (r != vm->ractor.main_ractor) {
5238 rb_ractor_terminate_atfork(vm, r);
5239 }
5240 ccan_list_for_each(&r->threads.set, i, lt_node) {
5241 atfork(i, th);
5242 }
5243 }
5244 rb_vm_living_threads_init(vm);
5245
5246 rb_ractor_atfork(vm, th);
5247 rb_vm_postponed_job_atfork();
5248
5249 /* may be held by any thread in parent */
5250 rb_native_mutex_initialize(&th->interrupt_lock);
5251 rb_native_mutex_initialize(&vm->once_lock);
5252 rb_native_cond_initialize(&vm->once_cond);
5253 rb_gc_zombie_objspaces_atfork();
5254 rb_gc_atfork_global_locks();
5255 rb_generic_fields_lock_atfork();
5256 ccan_list_head_init(&th->interrupt_exec_tasks);
5257
5258 vm->fork_gen++;
5259 rb_ractor_sleeper_threads_clear(th->ractor);
5260 rb_clear_coverages();
5261
5262 // restart timer thread (timer threads access to `vm->waitpid_lock` and so on.
5263 rb_thread_reset_timer_thread();
5264 rb_thread_start_timer_thread();
5265
5266 VM_ASSERT(vm->ractor.blocking_cnt == 0);
5267 VM_ASSERT(vm->ractor.cnt == 1);
5268}
5269
5270static void
5271terminate_atfork_i(rb_thread_t *th, const rb_thread_t *current_th)
5272{
5273 if (th != current_th) {
5274 // Clear the scheduler as it is no longer operational:
5275 th->scheduler = Qnil;
5276
5277 rb_native_mutex_initialize(&th->interrupt_lock);
5278 rb_mutex_abandon_keeping_mutexes(th);
5279 rb_mutex_abandon_locking_mutex(th);
5280 thread_cleanup_func(th, TRUE);
5281 }
5282}
5283
5284void rb_fiber_atfork(rb_thread_t *);
5285void
5286rb_thread_atfork(void)
5287{
5288 rb_thread_t *th = GET_THREAD();
5289 rb_threadptr_pending_interrupt_clear(th);
5290 rb_thread_atfork_internal(th, terminate_atfork_i);
5291 th->join_list = NULL;
5292 th->scheduler = Qnil;
5293 rb_fiber_atfork(th);
5294
5295 /* We don't want reproduce CVE-2003-0900. */
5297}
5298
5299static void
5300terminate_atfork_before_exec_i(rb_thread_t *th, const rb_thread_t *current_th)
5301{
5302 if (th != current_th) {
5303 thread_cleanup_func_before_exec(th);
5304 }
5305}
5306
5307void
5309{
5310 rb_thread_t *th = GET_THREAD();
5311 rb_thread_atfork_internal(th, terminate_atfork_before_exec_i);
5312}
5313#else
5314void
5315rb_thread_atfork(void)
5316{
5317}
5318
5319void
5321{
5322}
5323#endif
5325struct thgroup {
5326 int enclosed;
5327};
5328
5329static const rb_data_type_t thgroup_data_type = {
5330 "thgroup",
5331 {
5332 0,
5334 NULL, // No external memory to report
5335 },
5336 0, 0, RUBY_TYPED_THREAD_SAFE_FREE | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
5337};
5338
5339/*
5340 * Document-class: ThreadGroup
5341 *
5342 * ThreadGroup provides a means of keeping track of a number of threads as a
5343 * group.
5344 *
5345 * A given Thread object can only belong to one ThreadGroup at a time; adding
5346 * a thread to a new group will remove it from any previous group.
5347 *
5348 * Newly created threads belong to the same group as the thread from which they
5349 * were created.
5350 */
5351
5352/*
5353 * Document-const: Default
5354 *
5355 * The default ThreadGroup created when Ruby starts; all Threads belong to it
5356 * by default.
5357 */
5358static VALUE
5359thgroup_s_alloc(VALUE klass)
5360{
5361 VALUE group;
5362 struct thgroup *data;
5363
5364 group = TypedData_Make_Struct(klass, struct thgroup, &thgroup_data_type, data);
5365 data->enclosed = 0;
5366
5367 return group;
5368}
5369
5370/*
5371 * call-seq:
5372 * thgrp.list -> array
5373 *
5374 * Returns an array of all existing Thread objects that belong to this group.
5375 *
5376 * ThreadGroup::Default.list #=> [#<Thread:0x401bdf4c run>]
5377 */
5378
5379static VALUE
5380thgroup_list(VALUE group)
5381{
5382 VALUE ary = rb_ary_new();
5383 rb_thread_t *th = 0;
5384 rb_ractor_t *r = GET_RACTOR();
5385
5386 ccan_list_for_each(&r->threads.set, th, lt_node) {
5387 if (th->thgroup == group) {
5388 rb_ary_push(ary, th->self);
5389 }
5390 }
5391 return ary;
5392}
5393
5394
5395/*
5396 * call-seq:
5397 * thgrp.enclose -> thgrp
5398 *
5399 * Prevents threads from being added to or removed from the receiving
5400 * ThreadGroup.
5401 *
5402 * New threads can still be started in an enclosed ThreadGroup.
5403 *
5404 * ThreadGroup::Default.enclose #=> #<ThreadGroup:0x4029d914>
5405 * thr = Thread.new { Thread.stop } #=> #<Thread:0x402a7210 sleep>
5406 * tg = ThreadGroup.new #=> #<ThreadGroup:0x402752d4>
5407 * tg.add thr
5408 * #=> ThreadError: can't move from the enclosed thread group
5409 */
5410
5411static VALUE
5412thgroup_enclose(VALUE group)
5413{
5414 struct thgroup *data;
5415
5416 TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
5417 data->enclosed = 1;
5418
5419 return group;
5420}
5421
5422
5423/*
5424 * call-seq:
5425 * thgrp.enclosed? -> true or false
5426 *
5427 * Returns +true+ if the +thgrp+ is enclosed. See also ThreadGroup#enclose.
5428 */
5429
5430static VALUE
5431thgroup_enclosed_p(VALUE group)
5432{
5433 struct thgroup *data;
5434
5435 TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
5436 return RBOOL(data->enclosed);
5437}
5438
5439
5440/*
5441 * call-seq:
5442 * thgrp.add(thread) -> thgrp
5443 *
5444 * Adds the given +thread+ to this group, removing it from any other
5445 * group to which it may have previously been a member.
5446 *
5447 * puts "Initial group is #{ThreadGroup::Default.list}"
5448 * tg = ThreadGroup.new
5449 * t1 = Thread.new { sleep }
5450 * t2 = Thread.new { sleep }
5451 * puts "t1 is #{t1}"
5452 * puts "t2 is #{t2}"
5453 * tg.add(t1)
5454 * puts "Initial group now #{ThreadGroup::Default.list}"
5455 * puts "tg group now #{tg.list}"
5456 *
5457 * This will produce:
5458 *
5459 * Initial group is #<Thread:0x401bdf4c>
5460 * t1 is #<Thread:0x401b3c90>
5461 * t2 is #<Thread:0x401b3c18>
5462 * Initial group now #<Thread:0x401b3c18>#<Thread:0x401bdf4c>
5463 * tg group now #<Thread:0x401b3c90>
5464 */
5465
5466static VALUE
5467thgroup_add(VALUE group, VALUE thread)
5468{
5469 rb_thread_t *target_th = rb_thread_ptr(thread);
5470 struct thgroup *data;
5471
5472 if (OBJ_FROZEN(group)) {
5473 rb_raise(rb_eThreadError, "can't move to the frozen thread group");
5474 }
5475 TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
5476 if (data->enclosed) {
5477 rb_raise(rb_eThreadError, "can't move to the enclosed thread group");
5478 }
5479
5480 if (OBJ_FROZEN(target_th->thgroup)) {
5481 rb_raise(rb_eThreadError, "can't move from the frozen thread group");
5482 }
5483 TypedData_Get_Struct(target_th->thgroup, struct thgroup, &thgroup_data_type, data);
5484 if (data->enclosed) {
5485 rb_raise(rb_eThreadError,
5486 "can't move from the enclosed thread group");
5487 }
5488
5489 target_th->thgroup = group;
5490 return group;
5491}
5492
5493/*
5494 * Document-class: ThreadShield
5495 */
5496static void
5497thread_shield_mark(void *ptr)
5498{
5499 rb_gc_mark((VALUE)ptr);
5500}
5501
5502static const rb_data_type_t thread_shield_data_type = {
5503 "thread_shield",
5504 {thread_shield_mark, 0, 0,},
5505 0, 0, RUBY_TYPED_THREAD_SAFE_FREE
5506};
5507
5508static VALUE
5509thread_shield_alloc(VALUE klass)
5510{
5511 return TypedData_Wrap_Struct(klass, &thread_shield_data_type, (void *)mutex_alloc(0));
5512}
5513
5514#define GetThreadShieldPtr(obj) ((VALUE)rb_check_typeddata((obj), &thread_shield_data_type))
5515#define THREAD_SHIELD_WAITING_MASK (((FL_USER19-1)&~(FL_USER0-1))|FL_USER19)
5516#define THREAD_SHIELD_WAITING_SHIFT (FL_USHIFT)
5517#define THREAD_SHIELD_WAITING_MAX (THREAD_SHIELD_WAITING_MASK>>THREAD_SHIELD_WAITING_SHIFT)
5518STATIC_ASSERT(THREAD_SHIELD_WAITING_MAX, THREAD_SHIELD_WAITING_MAX <= UINT_MAX);
5519static inline unsigned int
5520rb_thread_shield_waiting(VALUE b)
5521{
5522 return ((RBASIC(b)->flags&THREAD_SHIELD_WAITING_MASK)>>THREAD_SHIELD_WAITING_SHIFT);
5523}
5524
5525static inline void
5526rb_thread_shield_waiting_inc(VALUE b)
5527{
5528 unsigned int w = rb_thread_shield_waiting(b);
5529 w++;
5530 if (w > THREAD_SHIELD_WAITING_MAX)
5531 rb_raise(rb_eRuntimeError, "waiting count overflow");
5532 RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
5533 RBASIC(b)->flags |= ((VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
5534}
5535
5536static inline void
5537rb_thread_shield_waiting_dec(VALUE b)
5538{
5539 unsigned int w = rb_thread_shield_waiting(b);
5540 if (!w) rb_raise(rb_eRuntimeError, "waiting count underflow");
5541 w--;
5542 RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
5543 RBASIC(b)->flags |= ((VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
5544}
5545
5546VALUE
5547rb_thread_shield_new(void)
5548{
5549 VALUE thread_shield = thread_shield_alloc(rb_cThreadShield);
5550 rb_mutex_lock((VALUE)DATA_PTR(thread_shield));
5551 return thread_shield;
5552}
5553
5554bool
5555rb_thread_shield_owned(VALUE self)
5556{
5557 VALUE mutex = GetThreadShieldPtr(self);
5558 if (!mutex) return false;
5559
5560 rb_mutex_t *m = mutex_ptr(mutex);
5561
5562 return m->ec_serial == rb_ec_serial(GET_EC());
5563}
5564
5565/*
5566 * Wait a thread shield.
5567 *
5568 * Returns
5569 * true: acquired the thread shield
5570 * false: the thread shield was destroyed and no other threads waiting
5571 * nil: the thread shield was destroyed but still in use
5572 */
5573VALUE
5574rb_thread_shield_wait(VALUE self)
5575{
5576 VALUE mutex = GetThreadShieldPtr(self);
5577 rb_mutex_t *m;
5578
5579 if (!mutex) return Qfalse;
5580 m = mutex_ptr(mutex);
5581 if (m->ec_serial == rb_ec_serial(GET_EC())) return Qnil;
5582 rb_thread_shield_waiting_inc(self);
5583 rb_mutex_lock(mutex);
5584 rb_thread_shield_waiting_dec(self);
5585 if (DATA_PTR(self)) return Qtrue;
5586 rb_mutex_unlock(mutex);
5587 return rb_thread_shield_waiting(self) > 0 ? Qnil : Qfalse;
5588}
5589
5590static VALUE
5591thread_shield_get_mutex(VALUE self)
5592{
5593 VALUE mutex = GetThreadShieldPtr(self);
5594 if (!mutex)
5595 rb_raise(rb_eThreadError, "destroyed thread shield - %p", (void *)self);
5596 return mutex;
5597}
5598
5599/*
5600 * Release a thread shield, and return true if it has waiting threads.
5601 */
5602VALUE
5603rb_thread_shield_release(VALUE self)
5604{
5605 VALUE mutex = thread_shield_get_mutex(self);
5606 rb_mutex_unlock(mutex);
5607 return RBOOL(rb_thread_shield_waiting(self) > 0);
5608}
5609
5610/*
5611 * Release and destroy a thread shield, and return true if it has waiting threads.
5612 */
5613VALUE
5614rb_thread_shield_destroy(VALUE self)
5615{
5616 VALUE mutex = thread_shield_get_mutex(self);
5617 DATA_PTR(self) = 0;
5618 rb_mutex_unlock(mutex);
5619 return RBOOL(rb_thread_shield_waiting(self) > 0);
5620}
5621
5622static VALUE
5623threadptr_recursive_hash(rb_thread_t *th)
5624{
5625 return th->ec->local_storage_recursive_hash;
5626}
5627
5628static void
5629threadptr_recursive_hash_set(rb_thread_t *th, VALUE hash)
5630{
5631 th->ec->local_storage_recursive_hash = hash;
5632}
5633
5635
5636/*
5637 * Returns the current "recursive list" used to detect recursion.
5638 * This list is a hash table, unique for the current thread and for
5639 * the current __callee__.
5640 */
5641
5642static VALUE
5643recursive_list_access(VALUE sym)
5644{
5645 rb_thread_t *th = GET_THREAD();
5646 VALUE hash = threadptr_recursive_hash(th);
5647 VALUE list;
5648 if (NIL_P(hash) || !RB_TYPE_P(hash, T_HASH)) {
5649 hash = rb_ident_hash_new();
5650 threadptr_recursive_hash_set(th, hash);
5651 list = Qnil;
5652 }
5653 else {
5654 list = rb_hash_aref(hash, sym);
5655 }
5656 if (NIL_P(list) || !RB_TYPE_P(list, T_HASH)) {
5657 list = rb_ident_hash_new();
5658 rb_hash_aset(hash, sym, list);
5659 }
5660 return list;
5661}
5662
5663/*
5664 * Returns true if and only if obj (or the pair <obj, paired_obj>) is already
5665 * in the recursion list.
5666 * Assumes the recursion list is valid.
5667 */
5668
5669static bool
5670recursive_check(VALUE list, VALUE obj, VALUE paired_obj_id)
5671{
5672#if SIZEOF_LONG == SIZEOF_VOIDP
5673 #define OBJ_ID_EQL(obj_id, other) ((obj_id) == (other))
5674#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
5675 #define OBJ_ID_EQL(obj_id, other) (RB_BIGNUM_TYPE_P((obj_id)) ? \
5676 rb_big_eql((obj_id), (other)) : ((obj_id) == (other)))
5677#endif
5678
5679 VALUE pair_list = rb_hash_lookup2(list, obj, Qundef);
5680 if (UNDEF_P(pair_list))
5681 return false;
5682 if (paired_obj_id) {
5683 if (!RB_TYPE_P(pair_list, T_HASH)) {
5684 if (!OBJ_ID_EQL(paired_obj_id, pair_list))
5685 return false;
5686 }
5687 else {
5688 if (NIL_P(rb_hash_lookup(pair_list, paired_obj_id)))
5689 return false;
5690 }
5691 }
5692 return true;
5693}
5694
5695/*
5696 * Pushes obj (or the pair <obj, paired_obj>) in the recursion list.
5697 * For a single obj, it sets list[obj] to Qtrue.
5698 * For a pair, it sets list[obj] to paired_obj_id if possible,
5699 * otherwise list[obj] becomes a hash like:
5700 * {paired_obj_id_1 => true, paired_obj_id_2 => true, ... }
5701 * Assumes the recursion list is valid.
5702 */
5703
5704static void
5705recursive_push(VALUE list, VALUE obj, VALUE paired_obj)
5706{
5707 VALUE pair_list;
5708
5709 if (!paired_obj) {
5710 rb_hash_aset(list, obj, Qtrue);
5711 }
5712 else if (UNDEF_P(pair_list = rb_hash_lookup2(list, obj, Qundef))) {
5713 rb_hash_aset(list, obj, paired_obj);
5714 }
5715 else {
5716 if (!RB_TYPE_P(pair_list, T_HASH)){
5717 VALUE other_paired_obj = pair_list;
5718 pair_list = rb_hash_new();
5719 rb_hash_aset(pair_list, other_paired_obj, Qtrue);
5720 rb_hash_aset(list, obj, pair_list);
5721 }
5722 rb_hash_aset(pair_list, paired_obj, Qtrue);
5723 }
5724}
5725
5726/*
5727 * Pops obj (or the pair <obj, paired_obj>) from the recursion list.
5728 * For a pair, if list[obj] is a hash, then paired_obj_id is
5729 * removed from the hash and no attempt is made to simplify
5730 * list[obj] from {only_one_paired_id => true} to only_one_paired_id
5731 * Assumes the recursion list is valid.
5732 */
5733
5734static int
5735recursive_pop(VALUE list, VALUE obj, VALUE paired_obj)
5736{
5737 if (paired_obj) {
5738 VALUE pair_list = rb_hash_lookup2(list, obj, Qundef);
5739 if (UNDEF_P(pair_list)) {
5740 return 0;
5741 }
5742 if (RB_TYPE_P(pair_list, T_HASH)) {
5743 rb_hash_delete_entry(pair_list, paired_obj);
5744 if (!RHASH_EMPTY_P(pair_list)) {
5745 return 1; /* keep hash until is empty */
5746 }
5747 }
5748 }
5749 rb_hash_delete_entry(list, obj);
5750 return 1;
5751}
5753struct exec_recursive_params {
5754 VALUE (*func) (VALUE, VALUE, int);
5755 VALUE list;
5756 VALUE obj;
5757 VALUE pairid;
5758 VALUE arg;
5759};
5760
5761static VALUE
5762exec_recursive_i(RB_BLOCK_CALL_FUNC_ARGLIST(tag, data))
5763{
5764 struct exec_recursive_params *p = (void *)data;
5765 return (*p->func)(p->obj, p->arg, FALSE);
5766}
5767
5768/*
5769 * Calls func(obj, arg, recursive), where recursive is non-zero if the
5770 * current method is called recursively on obj, or on the pair <obj, pairid>
5771 * If outer is 0, then the innermost func will be called with recursive set
5772 * to true, otherwise the outermost func will be called. In the latter case,
5773 * all inner func are short-circuited by throw.
5774 * Implementation details: the value thrown is the recursive list which is
5775 * proper to the current method and unlikely to be caught anywhere else.
5776 * list[recursive_key] is used as a flag for the outermost call.
5777 */
5778
5779static VALUE
5780exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE pairid, VALUE arg, int outer, ID mid)
5781{
5782 VALUE result = Qundef;
5783 const VALUE sym = mid ? ID2SYM(mid) : ID2SYM(idNULL);
5784 struct exec_recursive_params p;
5785 int outermost;
5786 p.list = recursive_list_access(sym);
5787 p.obj = obj;
5788 p.pairid = pairid;
5789 p.arg = arg;
5790 outermost = outer && !recursive_check(p.list, ID2SYM(recursive_key), 0);
5791
5792 if (recursive_check(p.list, p.obj, pairid)) {
5793 if (outer && !outermost) {
5794 rb_throw_obj(p.list, p.list);
5795 }
5796 return (*func)(obj, arg, TRUE);
5797 }
5798 else {
5799 enum ruby_tag_type state;
5800
5801 p.func = func;
5802
5803 if (outermost) {
5804 recursive_push(p.list, ID2SYM(recursive_key), 0);
5805 recursive_push(p.list, p.obj, p.pairid);
5806 result = rb_catch_protect(p.list, exec_recursive_i, (VALUE)&p, &state);
5807 if (!recursive_pop(p.list, p.obj, p.pairid)) goto invalid;
5808 if (!recursive_pop(p.list, ID2SYM(recursive_key), 0)) goto invalid;
5809 if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
5810 if (result == p.list) {
5811 result = (*func)(obj, arg, TRUE);
5812 }
5813 }
5814 else {
5815 volatile VALUE ret = Qundef;
5816 recursive_push(p.list, p.obj, p.pairid);
5817 EC_PUSH_TAG(GET_EC());
5818 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
5819 ret = (*func)(obj, arg, FALSE);
5820 }
5821 EC_POP_TAG();
5822 if (!recursive_pop(p.list, p.obj, p.pairid)) {
5823 goto invalid;
5824 }
5825 if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
5826 result = ret;
5827 }
5828 }
5829 *(volatile struct exec_recursive_params *)&p;
5830 return result;
5831
5832 invalid:
5833 rb_raise(rb_eTypeError, "invalid inspect_tbl pair_list "
5834 "for %+"PRIsVALUE" in %+"PRIsVALUE,
5835 sym, rb_thread_current());
5837}
5838
5839/*
5840 * Calls func(obj, arg, recursive), where recursive is non-zero if the
5841 * current method is called recursively on obj
5842 */
5843
5844VALUE
5845rb_exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
5846{
5847 return exec_recursive(func, obj, 0, arg, 0, rb_frame_last_func());
5848}
5849
5850/*
5851 * Calls func(obj, arg, recursive), where recursive is non-zero if the
5852 * current method is called recursively on the ordered pair <obj, paired_obj>
5853 */
5854
5855VALUE
5856rb_exec_recursive_paired(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
5857{
5858 return exec_recursive(func, obj, rb_memory_id(paired_obj), arg, 0, rb_frame_last_func());
5859}
5860
5861/*
5862 * If recursion is detected on the current method and obj, the outermost
5863 * func will be called with (obj, arg, true). All inner func will be
5864 * short-circuited using throw.
5865 */
5866
5867VALUE
5868rb_exec_recursive_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
5869{
5870 return exec_recursive(func, obj, 0, arg, 1, rb_frame_last_func());
5871}
5872
5873VALUE
5874rb_exec_recursive_outer_mid(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg, ID mid)
5875{
5876 return exec_recursive(func, obj, 0, arg, 1, mid);
5877}
5878
5879/*
5880 * If recursion is detected on the current method, obj and paired_obj,
5881 * the outermost func will be called with (obj, arg, true). All inner
5882 * func will be short-circuited using throw.
5883 */
5884
5885VALUE
5886rb_exec_recursive_paired_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
5887{
5888 return exec_recursive(func, obj, rb_memory_id(paired_obj), arg, 1, rb_frame_last_func());
5889}
5890
5891/*
5892 * call-seq:
5893 * thread.backtrace -> array or nil
5894 *
5895 * Returns the current backtrace of the target thread.
5896 *
5897 */
5898
5899static VALUE
5900rb_thread_backtrace_m(int argc, VALUE *argv, VALUE thval)
5901{
5902 return rb_vm_thread_backtrace(argc, argv, thval);
5903}
5904
5905/* call-seq:
5906 * thread.backtrace_locations(*args) -> array or nil
5907 *
5908 * Returns the execution stack for the target thread---an array containing
5909 * backtrace location objects.
5910 *
5911 * See Thread::Backtrace::Location for more information.
5912 *
5913 * This method behaves similarly to Kernel#caller_locations except it applies
5914 * to a specific thread.
5915 */
5916static VALUE
5917rb_thread_backtrace_locations_m(int argc, VALUE *argv, VALUE thval)
5918{
5919 return rb_vm_thread_backtrace_locations(argc, argv, thval);
5920}
5921
5922void
5923Init_Thread_Mutex(void)
5924{
5925 rb_thread_t *th = GET_THREAD();
5926
5927 rb_native_mutex_initialize(&th->vm->workqueue_lock);
5928 rb_native_mutex_initialize(&th->vm->once_lock);
5929 rb_native_cond_initialize(&th->vm->once_cond);
5930 rb_native_mutex_initialize(&th->interrupt_lock);
5931}
5932
5933/*
5934 * Document-class: ThreadError
5935 *
5936 * Raised when an invalid operation is attempted on a thread.
5937 *
5938 * For example, when no other thread has been started:
5939 *
5940 * Thread.stop
5941 *
5942 * This will raises the following exception:
5943 *
5944 * ThreadError: stopping only thread
5945 * note: use sleep to stop forever
5946 */
5947
5948void
5949Init_Thread(void)
5950{
5951 rb_thread_t *th = GET_THREAD();
5952
5953 sym_never = ID2SYM(rb_intern_const("never"));
5954 sym_immediate = ID2SYM(rb_intern_const("immediate"));
5955 sym_on_blocking = ID2SYM(rb_intern_const("on_blocking"));
5956
5957 rb_define_singleton_method(rb_cThread, "new", thread_s_new, -1);
5958 rb_define_singleton_method(rb_cThread, "start", thread_start, -2);
5959 rb_define_singleton_method(rb_cThread, "fork", thread_start, -2);
5960 rb_define_singleton_method(rb_cThread, "main", rb_thread_s_main, 0);
5961 rb_define_singleton_method(rb_cThread, "current", thread_s_current, 0);
5962 rb_define_singleton_method(rb_cThread, "stop", thread_stop, 0);
5963 rb_define_singleton_method(rb_cThread, "kill", rb_thread_s_kill, 1);
5964 rb_define_singleton_method(rb_cThread, "exit", rb_thread_exit, 0);
5965 rb_define_singleton_method(rb_cThread, "pass", thread_s_pass, 0);
5966 rb_define_singleton_method(rb_cThread, "list", thread_list, 0);
5967 rb_define_singleton_method(rb_cThread, "abort_on_exception", rb_thread_s_abort_exc, 0);
5968 rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1);
5969 rb_define_singleton_method(rb_cThread, "report_on_exception", rb_thread_s_report_exc, 0);
5970 rb_define_singleton_method(rb_cThread, "report_on_exception=", rb_thread_s_report_exc_set, 1);
5971 rb_define_singleton_method(rb_cThread, "ignore_deadlock", rb_thread_s_ignore_deadlock, 0);
5972 rb_define_singleton_method(rb_cThread, "ignore_deadlock=", rb_thread_s_ignore_deadlock_set, 1);
5973 rb_define_singleton_method(rb_cThread, "handle_interrupt", rb_thread_s_handle_interrupt, 1);
5974 rb_define_singleton_method(rb_cThread, "pending_interrupt?", rb_thread_s_pending_interrupt_p, -1);
5975 rb_define_method(rb_cThread, "pending_interrupt?", rb_thread_pending_interrupt_p, -1);
5976
5977 rb_define_method(rb_cThread, "initialize", thread_initialize, -2);
5978 rb_define_method(rb_cThread, "raise", thread_raise_m, -1);
5979 rb_define_method(rb_cThread, "join", thread_join_m, -1);
5980 rb_define_method(rb_cThread, "value", thread_value, 0);
5981 rb_define_method(rb_cThread, "kill", rb_thread_kill, 0);
5982 rb_define_method(rb_cThread, "terminate", rb_thread_kill, 0);
5983 rb_define_method(rb_cThread, "exit", rb_thread_kill, 0);
5984 rb_define_method(rb_cThread, "run", rb_thread_run, 0);
5985 rb_define_method(rb_cThread, "wakeup", rb_thread_wakeup, 0);
5986 rb_define_method(rb_cThread, "[]", rb_thread_aref, 1);
5987 rb_define_method(rb_cThread, "[]=", rb_thread_aset, 2);
5988 rb_define_method(rb_cThread, "fetch", rb_thread_fetch, -1);
5989 rb_define_method(rb_cThread, "key?", rb_thread_key_p, 1);
5990 rb_define_method(rb_cThread, "keys", rb_thread_keys, 0);
5991 rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
5992 rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
5993 rb_define_method(rb_cThread, "status", rb_thread_status, 0);
5994 rb_define_method(rb_cThread, "thread_variable_get", rb_thread_variable_get, 1);
5995 rb_define_method(rb_cThread, "thread_variable_set", rb_thread_variable_set, 2);
5996 rb_define_method(rb_cThread, "thread_variables", rb_thread_variables, 0);
5997 rb_define_method(rb_cThread, "thread_variable?", rb_thread_variable_p, 1);
5998 rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
5999 rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
6000 rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
6001 rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1);
6002 rb_define_method(rb_cThread, "report_on_exception", rb_thread_report_exc, 0);
6003 rb_define_method(rb_cThread, "report_on_exception=", rb_thread_report_exc_set, 1);
6004 rb_define_method(rb_cThread, "group", rb_thread_group, 0);
6005 rb_define_method(rb_cThread, "backtrace", rb_thread_backtrace_m, -1);
6006 rb_define_method(rb_cThread, "backtrace_locations", rb_thread_backtrace_locations_m, -1);
6007
6008 rb_define_method(rb_cThread, "name", rb_thread_getname, 0);
6009 rb_define_method(rb_cThread, "name=", rb_thread_setname, 1);
6010 rb_define_method(rb_cThread, "native_thread_id", rb_thread_native_thread_id, 0);
6011 rb_define_method(rb_cThread, "to_s", rb_thread_to_s, 0);
6012 rb_define_alias(rb_cThread, "inspect", "to_s");
6013
6014 rb_vm_register_special_exception(ruby_error_stream_closed, rb_eIOError,
6015 "stream closed in another thread");
6016
6017 cThGroup = rb_define_class("ThreadGroup", rb_cObject);
6018 rb_define_alloc_func(cThGroup, thgroup_s_alloc);
6019 rb_define_method(cThGroup, "list", thgroup_list, 0);
6020 rb_define_method(cThGroup, "enclose", thgroup_enclose, 0);
6021 rb_define_method(cThGroup, "enclosed?", thgroup_enclosed_p, 0);
6022 rb_define_method(cThGroup, "add", thgroup_add, 1);
6023
6024 const char * ptr = getenv("RUBY_THREAD_TIMESLICE");
6025
6026 if (ptr) {
6027 long quantum = strtol(ptr, NULL, 0);
6028 if (quantum > 0 && !(SIZEOF_LONG > 4 && quantum > UINT32_MAX)) {
6029 thread_default_quantum_ms = (uint32_t)quantum;
6030 }
6031 else if (0) {
6032 fprintf(stderr, "Ignored RUBY_THREAD_TIMESLICE=%s\n", ptr);
6033 }
6034 }
6035
6036 {
6037 th->thgroup = th->ractor->thgroup_default = rb_obj_alloc(cThGroup);
6038 rb_define_const(cThGroup, "Default", th->thgroup);
6039 }
6040
6041 rb_eThreadError = rb_define_class("ThreadError", rb_eStandardError);
6042
6043 /* init thread core */
6044 {
6045 /* main thread setting */
6046 {
6047 /* acquire global vm lock */
6048#ifdef HAVE_PTHREAD_NP_H
6049 VM_ASSERT(TH_SCHED(th)->running == th);
6050#endif
6051 // thread_sched_to_running() should not be called because
6052 // it assumes blocked by thread_sched_to_waiting().
6053 // thread_sched_to_running(sched, th);
6054
6055 th->pending_interrupt_queue = rb_ary_hidden_new(0);
6056 th->pending_interrupt_queue_checked = 0;
6057 th->pending_interrupt_mask_stack = rb_ary_hidden_new(0);
6058 }
6059 }
6060
6061 rb_thread_create_timer_thread();
6062
6063 Init_thread_sync();
6064
6065 // TODO: Suppress unused function warning for now
6066 // if (0) rb_thread_sched_destroy(NULL);
6067}
6068
6071{
6072 rb_thread_t *th = ruby_thread_from_native();
6073
6074 return th != 0;
6075}
6076
6077#ifdef NON_SCALAR_THREAD_ID
6078 #define thread_id_str(th) (NULL)
6079#else
6080 #define thread_id_str(th) ((void *)(uintptr_t)(th)->nt->thread_id)
6081#endif
6082
6083static void
6084debug_deadlock_check(rb_ractor_t *r, VALUE msg)
6085{
6086 rb_thread_t *th = 0;
6087 VALUE sep = rb_str_new_cstr("\n ");
6088
6089 rb_str_catf(msg, "\n%d threads, %d sleeps current:%p main thread:%p\n",
6090 rb_ractor_living_thread_num(r), rb_ractor_sleeper_thread_num(r),
6091 (void *)GET_THREAD(), (void *)r->threads.main);
6092
6093 ccan_list_for_each(&r->threads.set, th, lt_node) {
6094 rb_str_catf(msg, "* %+"PRIsVALUE"\n rb_thread_t:%p "
6095 "native:%p int:%u",
6096 th->self, (void *)th, th->nt ? thread_id_str(th) : "N/A", th->ec->interrupt_flag);
6097
6098 if (th->locking_mutex) {
6099 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
6100 rb_str_catf(msg, " mutex:%llu cond:%"PRIuSIZE,
6101 (unsigned long long)mutex->ec_serial, rb_mutex_num_waiting(mutex));
6102 }
6103
6104 {
6105 struct rb_waiting_list *list = th->join_list;
6106 while (list) {
6107 rb_str_catf(msg, "\n depended by: tb_thread_id:%p", (void *)list->thread);
6108 list = list->next;
6109 }
6110 }
6111 rb_str_catf(msg, "\n ");
6112 rb_str_concat(msg, rb_ary_join(rb_ec_backtrace_str_ary(th->ec, RUBY_BACKTRACE_START, RUBY_ALL_BACKTRACE_LINES), sep));
6113 rb_str_catf(msg, "\n");
6114 }
6115}
6116
6117static void
6118rb_check_deadlock(rb_ractor_t *r)
6119{
6120 if (GET_THREAD()->vm->thread_ignore_deadlock) return;
6121
6122 if (r->threads.sched.readyq_cnt > 0) return;
6123
6124 int sleeper_num = rb_ractor_sleeper_thread_num(r);
6125 int ltnum = rb_ractor_living_thread_num(r);
6126
6127 if (ltnum > sleeper_num) return;
6128 if (ltnum < sleeper_num) rb_bug("sleeper must not be more than vm_living_thread_num(vm)");
6129
6130 int found = 0;
6131 rb_thread_t *th = NULL;
6132
6133 ccan_list_for_each(&r->threads.set, th, lt_node) {
6134 if (th->status != THREAD_STOPPED_FOREVER || RUBY_VM_INTERRUPTED(th->ec)) {
6135 found = 1;
6136 }
6137 else if (th->locking_mutex) {
6138 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
6139 if (mutex->ec_serial == rb_ec_serial(th->ec) || (!mutex->ec_serial && !ccan_list_empty(&mutex->waitq))) {
6140 found = 1;
6141 }
6142 }
6143 if (found)
6144 break;
6145 }
6146
6147 if (!found) {
6148 VALUE argv[2];
6149 argv[0] = rb_eFatal;
6150 argv[1] = rb_str_new2("No live threads left. Deadlock?");
6151 debug_deadlock_check(r, argv[1]);
6152 rb_ractor_sleeper_threads_dec(GET_RACTOR());
6153 rb_threadptr_raise(r->threads.main, 2, argv);
6154 }
6155}
6156
6157static void
6158update_line_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
6159{
6160 const rb_control_frame_t *cfp = GET_EC()->cfp;
6161 VALUE coverage = rb_iseq_coverage(CFP_ISEQ(cfp));
6162 if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
6163 VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
6164 if (lines) {
6165 long line = rb_sourceline() - 1;
6166 VM_ASSERT(line >= 0);
6167 long count;
6168 VALUE num;
6169 void rb_iseq_clear_event_flags(const rb_iseq_t *iseq, size_t pos, rb_event_flag_t reset);
6170 if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
6171 rb_iseq_clear_event_flags(CFP_ISEQ(cfp), CFP_PC(cfp) - ISEQ_BODY(CFP_ISEQ(cfp))->iseq_encoded - 1, RUBY_EVENT_COVERAGE_LINE);
6172 rb_ary_push(lines, LONG2FIX(line + 1));
6173 return;
6174 }
6175 if (line >= RARRAY_LEN(lines)) { /* no longer tracked */
6176 return;
6177 }
6178 num = RARRAY_AREF(lines, line);
6179 if (!FIXNUM_P(num)) return;
6180 count = FIX2LONG(num) + 1;
6181 if (POSFIXABLE(count)) {
6182 RARRAY_ASET(lines, line, LONG2FIX(count));
6183 }
6184 }
6185 }
6186}
6187
6188static void
6189update_branch_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
6190{
6191 const rb_control_frame_t *cfp = GET_EC()->cfp;
6192 VALUE coverage = rb_iseq_coverage(CFP_ISEQ(cfp));
6193 if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
6194 VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
6195 if (branches) {
6196 long pc = CFP_PC(cfp) - ISEQ_BODY(CFP_ISEQ(cfp))->iseq_encoded - 1;
6197 long idx = FIX2INT(RARRAY_AREF(ISEQ_PC2BRANCHINDEX(CFP_ISEQ(cfp)), pc)), count;
6198 VALUE counters = RARRAY_AREF(branches, 1);
6199 VALUE num = RARRAY_AREF(counters, idx);
6200 count = FIX2LONG(num) + 1;
6201 if (POSFIXABLE(count)) {
6202 RARRAY_ASET(counters, idx, LONG2FIX(count));
6203 }
6204 }
6205 }
6206}
6207
6208const rb_method_entry_t *
6209rb_resolve_me_location(const rb_method_entry_t *me, VALUE resolved_location[5])
6210{
6211 VALUE path, beg_pos_lineno, beg_pos_column, end_pos_lineno, end_pos_column;
6212
6213 if (!me->def) return NULL; // negative cme
6214
6215 retry:
6216 switch (me->def->type) {
6217 case VM_METHOD_TYPE_ISEQ: {
6218 const rb_iseq_t *iseq = me->def->body.iseq.iseqptr;
6219 rb_iseq_location_t *loc = &ISEQ_BODY(iseq)->location;
6220 path = rb_iseq_path(iseq);
6221 beg_pos_lineno = INT2FIX(loc->code_location.beg_pos.lineno);
6222 beg_pos_column = INT2FIX(loc->code_location.beg_pos.column);
6223 end_pos_lineno = INT2FIX(loc->code_location.end_pos.lineno);
6224 end_pos_column = INT2FIX(loc->code_location.end_pos.column);
6225 break;
6226 }
6227 case VM_METHOD_TYPE_BMETHOD: {
6228 const rb_iseq_t *iseq = rb_proc_get_iseq(me->def->body.bmethod.proc, 0);
6229 if (iseq) {
6230 rb_iseq_location_t *loc;
6231 rb_iseq_check(iseq);
6232 path = rb_iseq_path(iseq);
6233 loc = &ISEQ_BODY(iseq)->location;
6234 beg_pos_lineno = INT2FIX(loc->code_location.beg_pos.lineno);
6235 beg_pos_column = INT2FIX(loc->code_location.beg_pos.column);
6236 end_pos_lineno = INT2FIX(loc->code_location.end_pos.lineno);
6237 end_pos_column = INT2FIX(loc->code_location.end_pos.column);
6238 break;
6239 }
6240 return NULL;
6241 }
6242 case VM_METHOD_TYPE_ALIAS:
6243 me = me->def->body.alias.original_me;
6244 goto retry;
6245 case VM_METHOD_TYPE_REFINED:
6246 me = me->def->body.refined.orig_me;
6247 if (!me) return NULL;
6248 goto retry;
6249 default:
6250 return NULL;
6251 }
6252
6253 /* found */
6254 if (RB_TYPE_P(path, T_ARRAY)) {
6255 path = rb_ary_entry(path, 1);
6256 if (!RB_TYPE_P(path, T_STRING)) return NULL; /* just for the case... */
6257 }
6258 if (resolved_location) {
6259 resolved_location[0] = path;
6260 resolved_location[1] = beg_pos_lineno;
6261 resolved_location[2] = beg_pos_column;
6262 resolved_location[3] = end_pos_lineno;
6263 resolved_location[4] = end_pos_column;
6264 }
6265 return me;
6266}
6268struct method_coverage_arg {
6269 rb_coverage_method_callback *callback;
6270 void *data;
6271};
6272
6273/* Fills *out for the method entry `me_v` and returns true, or returns false
6274 * if the method entry is not a subject of method coverage (aliases,
6275 * complemented entries, and methods without a source location). */
6276bool
6277rb_coverage_method_data_of(VALUE me_v, VALUE count, struct rb_coverage_method_data *out)
6278{
6279 const rb_method_entry_t *me = (const rb_method_entry_t *)me_v;
6280 VALUE location[5];
6281 const rb_method_entry_t *resolved_me = rb_resolve_me_location(me, location);
6282
6283 if (me != resolved_me || RB_TYPE_P(me->owner, T_ICLASS) ||
6284 FIX2LONG(location[1]) <= 0) return false;
6285
6286 out->owner = me->owner;
6287 out->method_id = ID2SYM(me->def->original_id);
6288 out->path = location[0];
6289 out->first_lineno = location[1];
6290 out->first_column = location[2];
6291 out->last_lineno = location[3];
6292 out->last_column = location[4];
6293 out->count = count;
6294 return true;
6295}
6296
6297static void
6298method_coverage_call(const rb_method_entry_t *me, VALUE count,
6299 struct method_coverage_arg *arg)
6300{
6301 struct rb_coverage_method_data method;
6302 if (rb_coverage_method_data_of((VALUE)me, count, &method)) {
6303 arg->callback(&method, arg->data);
6304 }
6305}
6306
6307static int
6308method_coverage_me_i(VALUE me, VALUE value, VALUE data)
6309{
6310 method_coverage_call((const rb_method_entry_t *)me, INT2FIX(0),
6311 (struct method_coverage_arg *)data);
6312 return ST_CONTINUE;
6313}
6314
6315static int
6316method_coverage_count_i(VALUE me, VALUE count, VALUE data)
6317{
6318 if (!FIXNUM_P(count)) count = INT2FIX(0);
6319 method_coverage_call((const rb_method_entry_t *)me, count,
6320 (struct method_coverage_arg *)data);
6321 return ST_CONTINUE;
6322}
6323
6324void
6325rb_coverage_each_method(rb_coverage_method_callback callback, void *data)
6326{
6327 struct method_coverage_arg arg = {callback, data};
6328 VALUE me_set = GET_VM()->me_set;
6329 VALUE cme2counter = GET_VM()->cme2counter;
6330
6331 if (RTEST(me_set)) {
6332 rb_hash_foreach(me_set, method_coverage_me_i, (VALUE)&arg);
6333 }
6334 if (RTEST(cme2counter)) {
6335 rb_hash_foreach(cme2counter, method_coverage_count_i, (VALUE)&arg);
6336 }
6337}
6338
6339static void
6340update_method_coverage(VALUE cme2counter, rb_trace_arg_t *trace_arg)
6341{
6342 const rb_control_frame_t *cfp = GET_EC()->cfp;
6343 const rb_callable_method_entry_t *cme = rb_vm_frame_method_entry(cfp);
6344 const rb_method_entry_t *me = (const rb_method_entry_t *)cme;
6345 VALUE rcount;
6346 long count;
6347
6348 me = rb_resolve_me_location(me, 0);
6349 if (!me) return;
6350
6351 rcount = rb_hash_aref(cme2counter, (VALUE) me);
6352 count = FIXNUM_P(rcount) ? FIX2LONG(rcount) + 1 : 1;
6353 if (POSFIXABLE(count)) {
6354 rb_hash_aset(cme2counter, (VALUE) me, LONG2FIX(count));
6355 }
6356}
6357
6358/* [Bug #22179] Record every method entry as it is defined (via method_added)
6359 * into me_set, so that method coverage no longer needs to reconstruct the set
6360 * of defined methods by walking the heap. This keeps shadowed/removed method
6361 * entries discoverable (me_set holds them as keys, so GC cannot reclaim them)
6362 * and makes the result independent of GC timing. Only entries that resolve to
6363 * themselves (i.e. methods defined by `def` or Module#define_method) are
6364 * recorded. */
6365void
6366rb_vm_coverage_record_me(const rb_method_entry_t *me)
6367{
6368 if (!RTEST(GET_VM()->coverages)) return;
6369 if (!(GET_VM()->coverage_mode & COVERAGE_TARGET_METHODS)) return;
6370
6371 VALUE me_set = GET_VM()->me_set;
6372 if (!RTEST(me_set)) return;
6373
6374 if (rb_resolve_me_location(me, 0) == me) {
6375 rb_hash_aset(me_set, (VALUE)me, Qtrue);
6376 }
6377}
6378
6379VALUE
6380rb_get_coverages(void)
6381{
6382 return GET_VM()->coverages;
6383}
6384
6385int
6386rb_get_coverage_mode(void)
6387{
6388 return GET_VM()->coverage_mode;
6389}
6390
6391void
6392rb_set_coverages(VALUE coverages, int mode, VALUE cme2counter, VALUE me_set)
6393{
6394 GET_VM()->coverages = coverages;
6395 GET_VM()->cme2counter = cme2counter;
6396 GET_VM()->me_set = me_set;
6397 GET_VM()->coverage_mode = mode;
6398}
6399
6400void
6401rb_resume_coverages(void)
6402{
6403 int mode = GET_VM()->coverage_mode;
6404 VALUE cme2counter = GET_VM()->cme2counter;
6405 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);
6406 if (mode & COVERAGE_TARGET_BRANCHES) {
6407 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);
6408 }
6409 if (mode & COVERAGE_TARGET_METHODS) {
6410 rb_add_event_hook2((rb_event_hook_func_t) update_method_coverage, RUBY_EVENT_CALL, cme2counter, RUBY_EVENT_HOOK_FLAG_SAFE | RUBY_EVENT_HOOK_FLAG_RAW_ARG);
6411 }
6412}
6413
6414void
6415rb_suspend_coverages(void)
6416{
6417 rb_remove_event_hook((rb_event_hook_func_t) update_line_coverage);
6418 if (GET_VM()->coverage_mode & COVERAGE_TARGET_BRANCHES) {
6419 rb_remove_event_hook((rb_event_hook_func_t) update_branch_coverage);
6420 }
6421 if (GET_VM()->coverage_mode & COVERAGE_TARGET_METHODS) {
6422 rb_remove_event_hook((rb_event_hook_func_t) update_method_coverage);
6423 }
6424}
6425
6426/* Make coverage arrays empty so old covered files are no longer tracked. */
6427void
6428rb_reset_coverages(void)
6429{
6430 rb_clear_coverages();
6431 rb_iseq_remove_coverage_all();
6432 GET_VM()->coverages = Qfalse;
6433 GET_VM()->cme2counter = Qnil;
6434 GET_VM()->me_set = Qnil;
6435}
6436
6437VALUE
6438rb_default_coverage(int n)
6439{
6440 VALUE coverage = rb_ary_hidden_new_fill(3);
6441 VALUE lines = Qfalse, branches = Qfalse;
6442 int mode = GET_VM()->coverage_mode;
6443
6444 if (mode & COVERAGE_TARGET_LINES) {
6445 lines = n > 0 ? rb_ary_hidden_new_fill(n) : rb_ary_hidden_new(0);
6446 }
6447 RARRAY_ASET(coverage, COVERAGE_INDEX_LINES, lines);
6448
6449 if (mode & COVERAGE_TARGET_BRANCHES) {
6450 branches = rb_ary_hidden_new_fill(2);
6451 /* internal data structures for branch coverage:
6452 *
6453 * { branch base key (see decl_branch_base) =>
6454 * [base_type, base_first_lineno, base_first_column, base_last_lineno, base_last_column, {
6455 * branch target id =>
6456 * [target_type, target_first_lineno, target_first_column, target_last_lineno, target_last_column, target_counter_index],
6457 * ...
6458 * }],
6459 * ...
6460 * }
6461 *
6462 * Example:
6463 * { [source_hash, node_id, lineno] =>
6464 * [1, 0, 4, 3, {
6465 * 0 => [2, 8, 2, 9, 0],
6466 * 1 => [3, 8, 3, 9, 1],
6467 * ...
6468 * }],
6469 * ...
6470 * }
6471 */
6472 VALUE structure = rb_hash_new();
6473 rb_obj_hide(structure);
6474 RARRAY_ASET(branches, 0, structure);
6475 /* branch execution counters */
6476 RARRAY_ASET(branches, 1, rb_ary_hidden_new(0));
6477 }
6478 RARRAY_ASET(coverage, COVERAGE_INDEX_BRANCHES, branches);
6479
6480 return coverage;
6481}
6482
6483static VALUE
6484uninterruptible_exit(VALUE v)
6485{
6486 rb_thread_t *cur_th = GET_THREAD();
6487 rb_ary_pop(cur_th->pending_interrupt_mask_stack);
6488
6489 cur_th->pending_interrupt_queue_checked = 0;
6490 if (!rb_threadptr_pending_interrupt_empty_p(cur_th)) {
6491 RUBY_VM_SET_INTERRUPT(cur_th->ec);
6492 }
6493 return Qnil;
6494}
6495
6496VALUE
6497rb_uninterruptible(VALUE (*b_proc)(VALUE), VALUE data)
6498{
6499 VALUE interrupt_mask = rb_ident_hash_new();
6500 rb_thread_t *cur_th = GET_THREAD();
6501
6502 rb_hash_aset(interrupt_mask, rb_cObject, sym_never);
6503 OBJ_FREEZE(interrupt_mask);
6504 rb_ary_push(cur_th->pending_interrupt_mask_stack, interrupt_mask);
6505
6506 VALUE ret = rb_ensure(b_proc, data, uninterruptible_exit, Qnil);
6507
6508 RUBY_VM_CHECK_INTS(cur_th->ec);
6509 return ret;
6510}
6511
6512static void
6513thread_specific_storage_alloc(rb_thread_t *th)
6514{
6515 VM_ASSERT(th->specific_storage == NULL);
6516
6517 if (UNLIKELY(specific_key_count > 0)) {
6518 th->specific_storage = ZALLOC_N(void *, RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6519 }
6520}
6521
6522rb_internal_thread_specific_key_t
6524{
6525 rb_vm_t *vm = GET_VM();
6526
6527 if (specific_key_count == 0 && vm->ractor.cnt > 1) {
6528 rb_raise(rb_eThreadError, "The first rb_internal_thread_specific_key_create() is called with multiple ractors");
6529 }
6530 else if (specific_key_count > RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX) {
6531 rb_raise(rb_eThreadError, "rb_internal_thread_specific_key_create() is called more than %d times", RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6532 }
6533 else {
6534 rb_internal_thread_specific_key_t key = specific_key_count++;
6535
6536 if (key == 0) {
6537 // allocate
6538 rb_ractor_t *cr = GET_RACTOR();
6539 rb_thread_t *th;
6540
6541 ccan_list_for_each(&cr->threads.set, th, lt_node) {
6542 thread_specific_storage_alloc(th);
6543 }
6544 }
6545 return key;
6546 }
6547}
6548
6549// async and native thread safe.
6550void *
6551rb_internal_thread_specific_get(VALUE thread_val, rb_internal_thread_specific_key_t key)
6552{
6553 rb_thread_t *th = DATA_PTR(thread_val);
6554
6555 VM_ASSERT(rb_thread_ptr(thread_val) == th);
6556 VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6557 VM_ASSERT(th->specific_storage);
6558
6559 return th->specific_storage[key];
6560}
6561
6562// async and native thread safe.
6563void
6564rb_internal_thread_specific_set(VALUE thread_val, rb_internal_thread_specific_key_t key, void *data)
6565{
6566 rb_thread_t *th = DATA_PTR(thread_val);
6567
6568 VM_ASSERT(rb_thread_ptr(thread_val) == th);
6569 VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6570 VM_ASSERT(th->specific_storage);
6571
6572 th->specific_storage[key] = data;
6573}
6574
6575// interrupt_exec
6578 struct ccan_list_node node;
6579
6580 rb_interrupt_exec_func_t *func;
6581 void *data;
6582 enum rb_interrupt_exec_flag flags;
6583};
6584
6585void
6586rb_threadptr_interrupt_exec_task_mark(rb_thread_t *th)
6587{
6588 struct rb_interrupt_exec_task *task;
6589
6590 ccan_list_for_each(&th->interrupt_exec_tasks, task, node) {
6591 if (task->flags & rb_interrupt_exec_flag_value_data) {
6592 rb_gc_mark((VALUE)task->data);
6593 }
6594 }
6595}
6596
6597// native thread safe
6598// th should be available
6599void
6600rb_threadptr_interrupt_exec(rb_thread_t *th, rb_interrupt_exec_func_t *func, void *data, enum rb_interrupt_exec_flag flags)
6601{
6602 // should not use ALLOC
6604 *task = (struct rb_interrupt_exec_task) {
6605 .flags = flags,
6606 .func = func,
6607 .data = data,
6608 };
6609
6610 rb_native_mutex_lock(&th->interrupt_lock);
6611 {
6612 ccan_list_add_tail(&th->interrupt_exec_tasks, &task->node);
6613 threadptr_set_interrupt_locked(th, true);
6614 }
6615 rb_native_mutex_unlock(&th->interrupt_lock);
6616}
6617
6618static void
6619threadptr_interrupt_exec_exec(rb_thread_t *th)
6620{
6621 while (1) {
6622 struct rb_interrupt_exec_task *task;
6623
6624 rb_native_mutex_lock(&th->interrupt_lock);
6625 {
6626 task = ccan_list_pop(&th->interrupt_exec_tasks, struct rb_interrupt_exec_task, node);
6627 }
6628 rb_native_mutex_unlock(&th->interrupt_lock);
6629
6630 RUBY_DEBUG_LOG("task:%p", task);
6631
6632 if (task) {
6633 if (task->flags & rb_interrupt_exec_flag_new_thread) {
6634 rb_thread_create(task->func, task->data);
6635 }
6636 else {
6637 (*task->func)(task->data);
6638 }
6639 SIZED_FREE(task);
6640 }
6641 else {
6642 break;
6643 }
6644 }
6645}
6646
6647static void
6648threadptr_interrupt_exec_cleanup(rb_thread_t *th)
6649{
6650 rb_native_mutex_lock(&th->interrupt_lock);
6651 {
6652 struct rb_interrupt_exec_task *task;
6653
6654 while ((task = ccan_list_pop(&th->interrupt_exec_tasks, struct rb_interrupt_exec_task, node)) != NULL) {
6655 SIZED_FREE(task);
6656 }
6657 }
6658 rb_native_mutex_unlock(&th->interrupt_lock);
6659}
6660
6661// native thread safe
6662// func/data should be native thread safe
6663void
6664rb_ractor_interrupt_exec(struct rb_ractor_struct *target_r,
6665 rb_interrupt_exec_func_t *func, void *data, enum rb_interrupt_exec_flag flags)
6666{
6667 RUBY_DEBUG_LOG("flags:%d", (int)flags);
6668
6669 rb_thread_t *main_th = target_r->threads.main;
6670 rb_threadptr_interrupt_exec(main_th, func, data, flags | rb_interrupt_exec_flag_new_thread);
6671}
#define RUBY_ASSERT_ALWAYS(expr,...)
A variant of RUBY_ASSERT that does not interface with RUBY_DEBUG.
Definition assert.h:199
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
Definition atomic.h:69
#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.
Definition event.h:90
int rb_remove_event_hook(rb_event_hook_func_t func)
Removes the passed function from the list of event hooks.
Definition vm_trace.c:439
#define RUBY_EVENT_THREAD_BEGIN
Encountered a new thread.
Definition event.h:57
void(* rb_event_hook_func_t)(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
Type of event hooks.
Definition event.h:120
uint32_t rb_event_flag_t
Represents event(s).
Definition event.h:108
#define RUBY_EVENT_CALL
A method, written in Ruby, is called.
Definition event.h:41
#define RUBY_EVENT_THREAD_END
Encountered an end of a thread.
Definition event.h:58
static void RB_FL_SET_RAW(VALUE obj, VALUE flags)
This is an implementation detail of RB_FL_SET().
Definition fl_type.h:541
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition class.c:2913
ID rb_frame_last_func(void)
Returns the ID of the last method in the call stack.
Definition eval.c:1267
int rb_keyword_given_p(void)
Determines if the current method is given a keyword argument.
Definition eval.c:1046
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:1033
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1676
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
Definition fl_type.h:133
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:131
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
Definition assume.h:29
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:205
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define FIX2INT
Old name of RB_FIX2INT.
Definition int.h:41
#define ZALLOC_N
Old name of RB_ZALLOC_N.
Definition memory.h:401
#define T_ICLASS
Old name of RUBY_T_ICLASS.
Definition value_type.h:66
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define FIX2LONG
Old name of RB_FIX2LONG.
Definition long.h:46
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define T_OBJECT
Old name of RUBY_T_OBJECT.
Definition value_type.h:75
#define NIL_P
Old name of RB_NIL_P.
#define POSFIXABLE
Old name of RB_POSFIXABLE.
Definition fixnum.h:29
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition value_type.h:85
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define SYMBOL_P
Old name of RB_SYMBOL_P.
Definition value_type.h:88
void ruby_stop(int ex)
Calls ruby_cleanup() and exits the process.
Definition eval.c:306
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
Definition error.h:487
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:676
VALUE rb_eSystemExit
SystemExit exception.
Definition error.c:1424
VALUE rb_eIOError
IOError exception.
Definition io.c:189
VALUE rb_eStandardError
StandardError exception.
Definition error.c:1428
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1431
void rb_frozen_error_raise(VALUE frozen_obj, const char *fmt,...)
Raises an instance of rb_eFrozenError.
Definition error.c:4254
VALUE rb_eFatal
fatal exception.
Definition error.c:1427
VALUE rb_eInterrupt
Interrupt exception.
Definition error.c:1425
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1429
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:468
VALUE rb_exc_new(VALUE etype, const char *ptr, long len)
Creates an instance of the passed exception class.
Definition error.c:1469
VALUE rb_eException
Mother of all exceptions.
Definition error.c:1423
VALUE rb_eThreadError
ThreadError exception.
Definition eval.c:1051
void rb_exit(int status)
Terminates the current execution context.
Definition process.c:4362
VALUE rb_eSignal
SignalException exception.
Definition error.c:1426
VALUE rb_cObject
Object class.
Definition object.c:60
VALUE rb_obj_alloc(VALUE klass)
Allocates an instance of the given class.
Definition object.c:2250
VALUE rb_cInteger
Module class.
Definition numeric.c:202
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:94
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:234
VALUE rb_cThread
Thread class.
Definition vm.c:697
VALUE rb_cModule
Module class.
Definition object.c:61
double rb_num2dbl(VALUE num)
Converts an instance of rb_cNumeric into C's double.
Definition object.c:3834
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.
Definition object.c:905
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.
Definition error.h:284
VALUE rb_block_proc(void)
Constructs a Proc object from implicitly passed components.
Definition proc.c:1575
void rb_reset_random_seed(void)
Resets the RNG behind rb_genrand_int32()/rb_genrand_real().
Definition random.c:1757
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition string.c:1555
VALUE rb_str_concat(VALUE dst, VALUE src)
Identical to rb_str_append(), except it also accepts an integer as a codepoint.
Definition string.c:4135
#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.
Definition string.h:1657
#define rb_str_new_cstr(str)
Identical to rb_str_new, except it assumes the passed pointer is a pointer to a C string.
Definition string.h:1515
int rb_thread_interrupted(VALUE thval)
Checks if the thread's execution was recently interrupted.
Definition thread.c:1656
VALUE rb_thread_local_aref(VALUE thread, ID key)
This badly named function reads from a Fiber local storage.
Definition thread.c:4018
VALUE rb_mutex_new(void)
Creates a mutex.
VALUE rb_thread_kill(VALUE thread)
Terminates the given thread.
Definition thread.c:3211
#define RUBY_UBF_IO
A special UBF for blocking IO operations.
Definition thread.h:382
VALUE rb_thread_main(void)
Obtains the "main" thread.
Definition thread.c:3450
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.
Definition thread.c:1585
void rb_thread_fd_close(int fd)
This function is now a no-op.
Definition thread.c:3147
void rb_thread_wait_for(struct timeval time)
Identical to rb_thread_sleep(), except it takes struct timeval instead.
Definition thread.c:1618
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.
Definition thread.c:3362
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.
Definition thread.h:336
void rb_thread_atfork_before_exec(void)
:FIXME: situation of this function is unclear.
Definition thread.c:5319
void rb_thread_check_ints(void)
Checks for interrupts.
Definition thread.c:1639
VALUE rb_thread_run(VALUE thread)
This is a rb_thread_wakeup() + rb_thread_schedule() combo.
Definition thread.c:3353
VALUE rb_thread_wakeup(VALUE thread)
Marks a given thread as eligible for scheduling.
Definition thread.c:3306
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" ...
Definition thread.c:1592
void rb_thread_atfork(void)
A pthread_atfork(3posix)-like API.
Definition thread.c:5314
VALUE rb_thread_current(void)
Obtains the "current" thread.
Definition thread.c:3429
int rb_thread_alone(void)
Checks if the thread this function is running is the only thread that is currently alive.
Definition thread.c:4291
VALUE rb_thread_local_aset(VALUE thread, ID key, VALUE val)
This badly named function writes to a Fiber local storage.
Definition thread.c:4166
void rb_thread_schedule(void)
Tries to switch to another thread.
Definition thread.c:1687
#define RUBY_UBF_PROCESS
A special UBF for blocking process operations.
Definition thread.h:389
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.
Definition thread.c:3315
VALUE rb_mutex_lock(VALUE mutex)
Attempts to lock the mutex.
void rb_thread_sleep(int sec)
Blocks for the given period of time.
Definition thread.c:1662
void rb_timespec_now(struct timespec *ts)
Fills the current time into the given struct.
Definition time.c:2021
struct timeval rb_time_timeval(VALUE time)
Converts an instance of rb_cTime to a struct timeval that represents the identical point of time.
Definition time.c:2976
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.
Definition variable.c:2060
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.
Definition variable.c:1579
VALUE rb_class_path(VALUE mod)
Identical to rb_mod_name(), except it returns #<Class: ...> style inspection for anonymous modules.
Definition variable.c:395
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__.
Definition vm.c:2158
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:285
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition symbol.c:1288
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
Definition string.c:13740
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...
Definition string.c:13730
int capa
Designed capacity of the buffer.
Definition io.h:11
#define RB_IO_POINTER(obj, fp)
Queries the underlying IO pointer.
Definition io.h:436
VALUE rb_eIOTimeoutError
Indicates that a timeout has occurred while performing an IO operation.
Definition io.c:190
#define RB_NOGVL_UBF_ASYNC_SAFE
Passing this flag to rb_nogvl() indicates that the passed UBF is async-signal-safe.
Definition thread.h:71
void * rb_internal_thread_specific_get(VALUE thread_val, rb_internal_thread_specific_key_t key)
Get thread and tool specific data.
Definition thread.c:6550
#define RB_NOGVL_INTR_FAIL
Passing this flag to rb_nogvl() prevents it from processing interrupts after the given function retur...
Definition thread.h:50
void rb_internal_thread_specific_set(VALUE thread_val, rb_internal_thread_specific_key_t key, void *data)
Set thread and tool specific data.
Definition thread.c:6563
rb_internal_thread_specific_key_t rb_internal_thread_specific_key_create(void)
Create a key to store thread specific data.
Definition thread.c:6522
#define RB_NOGVL_PENDING_INTR_FAIL
Passing this flag to rb_nogvl() prevents it from entering the blocking region if the current thread h...
Definition thread.h:59
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...
Definition thread.c:1767
void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
(Re-)acquires the GVL.
Definition thread.c:2277
#define RB_NOGVL_OFFLOAD_SAFE
Passing this flag to rb_nogvl() indicates that the passed function is safe to offload to a background...
Definition thread.h:84
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.
Definition thread.c:1918
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.
Definition iterator.h:58
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1378
void rb_throw_obj(VALUE tag, VALUE val)
Identical to rb_throw(), except it allows arbitrary Ruby object to become a tag.
Definition vm_eval.c:2605
static int rb_fd_max(const rb_fdset_t *f)
It seems this function has no use.
Definition largesize.h:209
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.
Definition largesize.h:195
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:372
#define ALLOCA_N(type, n)
Definition memory.h:292
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition memory.h:360
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.
Definition posix.h:60
#define rb_fd_select
Waits for multiple file descriptors at once.
Definition posix.h:66
#define rb_fd_init
Initialises the :given :rb_fdset_t.
Definition posix.h:63
#define rb_fd_set
Sets the given fd to the rb_fdset_t.
Definition posix.h:54
#define rb_fd_zero
Clears the given rb_fdset_t.
Definition posix.h:51
#define rb_fd_clr
Unsets the given fd from the rb_fdset_t.
Definition posix.h:57
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:50
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:280
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:385
#define RARRAY_AREF(a, i)
Definition rarray.h:402
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
Definition rarray.h:51
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
Definition rbasic.h:166
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
#define RCLASS_SUPER
Just another name of rb_class_get_superclass.
Definition rclass.h:44
#define RHASH_EMPTY_P(h)
Checks if the hash is empty.
Definition rhash.h:79
#define StringValueCStr(v)
Identical to StringValuePtr, except it additionally checks for the contents for viability as a C stri...
Definition rstring.h:89
#define RUBY_TYPED_DEFAULT_FREE
This is a value you can set to rb_data_type_struct::dfree.
Definition rtypeddata.h:81
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:773
#define DATA_PTR(obj)
Convenient casting macro for backward compatibility.
Definition rtypeddata.h:435
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:557
#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...
Definition rtypeddata.h:604
#define errno
Ractor-aware version of errno.
Definition ruby.h:388
int ruby_native_thread_p(void)
Queries if the thread which calls this function is a ruby's thread.
Definition thread.c:6069
int ruby_snprintf(char *str, size_t n, char const *fmt,...)
Our own locale-insensitive version of snprintf(3).
Definition sprintf.c:1048
#define RB_PASS_CALLED_KEYWORDS
Pass keywords if current method is called with keywords, useful for argument delegation.
Definition scan_args.h:78
Scheduler APIs.
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.
Definition scheduler.c:1062
VALUE rb_fiber_scheduler_current(void)
Identical to rb_fiber_scheduler_get(), except it also returns RUBY_Qnil in case of a blocking fiber.
Definition scheduler.c:459
VALUE rb_fiber_scheduler_fiber_interrupt(VALUE scheduler, VALUE fiber, VALUE exception)
Interrupt a fiber by raising an exception.
Definition scheduler.c:1105
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....
Definition scheduler.c:648
VALUE rb_fiber_scheduler_yield(VALUE scheduler)
Yield to the scheduler, to be resumed on the next scheduling cycle.
Definition scheduler.c:549
VALUE rb_fiber_scheduler_set(VALUE scheduler)
Destructively assigns the passed scheduler to that of the current thread that is calling this functio...
Definition scheduler.c:421
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...
Definition scheduler.c:472
VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber)
Wakes up a fiber previously blocked using rb_fiber_scheduler_block().
Definition scheduler.c:667
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.
Definition thread.c:4790
#define rb_fd_resize(n, f)
Does nothing (defined for compatibility).
Definition select.h:43
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.
Definition stdarg.h:35
Definition method.h:63
CREF (Class REFerence)
Definition method.h:45
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:238
The data structure which wraps the fd_set bitmap used by select(2).
Definition largesize.h:71
int maxfd
Maximum allowed number of FDs.
Definition largesize.h:72
fd_set * fdset
File descriptors buffer.
Definition largesize.h:73
int capa
Maximum allowed number of FDs.
Definition win32.h:50
Ruby's IO, metadata and buffers.
Definition io.h:295
VALUE self
The IO's Ruby level counterpart.
Definition io.h:298
int fd
file descriptor.
Definition io.h:306
struct ccan_list_head blocking_operations
Threads that are performing a blocking operation without the GVL using this IO.
Definition io.h:131
Definition method.h:55
const rb_iseq_t * iseqptr
iseq pointer, should be separated from iseqval
Definition method.h:143
void rb_nativethread_lock_lock(rb_nativethread_lock_t *lock)
Blocks until the current thread obtains a lock.
Definition thread.c:317
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.
Definition thread.c:323
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.
Definition thread.c:305
void rb_nativethread_lock_destroy(rb_nativethread_lock_t *lock)
Destroys the passed mutex.
Definition thread.c:311
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376