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