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