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