Ruby 4.1.0dev (2026-03-01 revision d68e4be1873e364c5ee24ed112bce4bc86e3a406)
thread.c (d68e4be1873e364c5ee24ed112bce4bc86e3a406)
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));
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); \
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}
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
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
1445rb_thread_wait_for(struct timeval time)
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. */
1484{
1485 return (int)RUBY_VM_INTERRUPTED(rb_thread_ptr(thval)->ec);
1486}
1487
1488void
1489rb_thread_sleep(int sec)
1490{
1492}
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}
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);
2096 return r;
2097}
2098
2099/*
2100 * ruby_thread_has_gvl_p - check if current native thread has GVL.
2101 */
2102
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 while ((interrupt = threadptr_get_interrupts(th)) != 0) {
2628 int sig;
2629 int timer_interrupt;
2630 int pending_interrupt;
2631 int trap_interrupt;
2632 int terminate_interrupt;
2633
2634 timer_interrupt = interrupt & TIMER_INTERRUPT_MASK;
2635 pending_interrupt = interrupt & PENDING_INTERRUPT_MASK;
2636 postponed_job_interrupt = interrupt & POSTPONED_JOB_INTERRUPT_MASK;
2637 trap_interrupt = interrupt & TRAP_INTERRUPT_MASK;
2638 terminate_interrupt = interrupt & TERMINATE_INTERRUPT_MASK; // request from other ractors
2639
2640 if (interrupt & VM_BARRIER_INTERRUPT_MASK) {
2641 RB_VM_LOCKING();
2642 }
2643
2644 if (postponed_job_interrupt) {
2645 rb_postponed_job_flush(th->vm);
2646 }
2647
2648 if (trap_interrupt) {
2649 /* signal handling */
2650 if (th == th->vm->ractor.main_thread) {
2651 enum rb_thread_status prev_status = th->status;
2652
2653 th->status = THREAD_RUNNABLE;
2654 {
2655 while ((sig = rb_get_next_signal()) != 0) {
2656 ret |= rb_signal_exec(th, sig);
2657 }
2658 }
2659 th->status = prev_status;
2660 }
2661
2662 if (!ccan_list_empty(&th->interrupt_exec_tasks)) {
2663 enum rb_thread_status prev_status = th->status;
2664
2665 th->status = THREAD_RUNNABLE;
2666 {
2667 threadptr_interrupt_exec_exec(th);
2668 }
2669 th->status = prev_status;
2670 }
2671 }
2672
2673 /* exception from another thread */
2674 if (pending_interrupt && threadptr_pending_interrupt_active_p(th)) {
2675 VALUE err = rb_threadptr_pending_interrupt_deque(th, blocking_timing ? INTERRUPT_ON_BLOCKING : INTERRUPT_NONE);
2676 RUBY_DEBUG_LOG("err:%"PRIdVALUE, err);
2677 ret = TRUE;
2678
2679 if (UNDEF_P(err)) {
2680 /* no error */
2681 }
2682 else if (err == RUBY_FATAL_THREAD_KILLED /* Thread#kill received */ ||
2683 err == RUBY_FATAL_THREAD_TERMINATED /* Terminate thread */ ||
2684 err == INT2FIX(TAG_FATAL) /* Thread.exit etc. */ ) {
2685 terminate_interrupt = 1;
2686 }
2687 else {
2688 if (err == th->vm->special_exceptions[ruby_error_stream_closed]) {
2689 /* the only special exception to be queued across thread */
2690 err = ruby_vm_special_exception_copy(err);
2691 }
2692 /* set runnable if th was slept. */
2693 if (th->status == THREAD_STOPPED ||
2694 th->status == THREAD_STOPPED_FOREVER)
2695 th->status = THREAD_RUNNABLE;
2696 rb_exc_raise(err);
2697 }
2698 }
2699
2700 if (terminate_interrupt) {
2701 rb_threadptr_to_kill(th);
2702 }
2703
2704 if (timer_interrupt) {
2705 uint32_t limits_us = thread_default_quantum_ms * 1000;
2706
2707 if (th->priority > 0)
2708 limits_us <<= th->priority;
2709 else
2710 limits_us >>= -th->priority;
2711
2712 if (th->status == THREAD_RUNNABLE)
2713 th->running_time_us += 10 * 1000; // 10ms = 10_000us // TODO: use macro
2714
2715 VM_ASSERT(th->ec->cfp);
2716 EXEC_EVENT_HOOK(th->ec, RUBY_INTERNAL_EVENT_SWITCH, th->ec->cfp->self,
2717 0, 0, 0, Qundef);
2718
2719 rb_thread_schedule_limits(limits_us);
2720 }
2721 }
2722 return ret;
2723}
2724
2725void
2726rb_thread_execute_interrupts(VALUE thval)
2727{
2728 rb_threadptr_execute_interrupts(rb_thread_ptr(thval), 1);
2729}
2730
2731static void
2732rb_threadptr_ready(rb_thread_t *th)
2733{
2734 rb_threadptr_interrupt(th);
2735}
2736
2737static VALUE
2738rb_threadptr_raise(rb_thread_t *target_th, int argc, VALUE *argv)
2739{
2740 VALUE exc;
2741
2742 if (rb_threadptr_dead(target_th)) {
2743 return Qnil;
2744 }
2745
2746 if (argc == 0) {
2747 exc = rb_exc_new(rb_eRuntimeError, 0, 0);
2748 }
2749 else {
2750 exc = rb_make_exception(argc, argv);
2751 }
2752
2753 /* making an exception object can switch thread,
2754 so we need to check thread deadness again */
2755 if (rb_threadptr_dead(target_th)) {
2756 return Qnil;
2757 }
2758
2759 rb_ec_setup_exception(GET_EC(), exc, Qundef);
2760 rb_threadptr_pending_interrupt_enque(target_th, exc);
2761 rb_threadptr_interrupt(target_th);
2762
2763 return Qnil;
2764}
2765
2766void
2767rb_threadptr_signal_raise(rb_thread_t *th, int sig)
2768{
2769 VALUE argv[2];
2770
2771 argv[0] = rb_eSignal;
2772 argv[1] = INT2FIX(sig);
2773 rb_threadptr_raise(th->vm->ractor.main_thread, 2, argv);
2774}
2775
2776void
2777rb_threadptr_signal_exit(rb_thread_t *th)
2778{
2779 VALUE argv[2];
2780
2781 argv[0] = rb_eSystemExit;
2782 argv[1] = rb_str_new2("exit");
2783
2784 // TODO: check signal raise deliverly
2785 rb_threadptr_raise(th->vm->ractor.main_thread, 2, argv);
2786}
2787
2788int
2789rb_ec_set_raised(rb_execution_context_t *ec)
2790{
2791 if (ec->raised_flag & RAISED_EXCEPTION) {
2792 return 1;
2793 }
2794 ec->raised_flag |= RAISED_EXCEPTION;
2795 return 0;
2796}
2797
2798int
2799rb_ec_reset_raised(rb_execution_context_t *ec)
2800{
2801 if (!(ec->raised_flag & RAISED_EXCEPTION)) {
2802 return 0;
2803 }
2804 ec->raised_flag &= ~RAISED_EXCEPTION;
2805 return 1;
2806}
2807
2808/*
2809 * Thread-safe IO closing mechanism.
2810 *
2811 * When an IO is closed while other threads or fibers are blocked on it, we need to:
2812 * 1. Track and notify all blocking operations through io->blocking_operations
2813 * 2. Ensure only one thread can close at a time using io->closing_ec
2814 * 3. Synchronize cleanup using wakeup_mutex
2815 *
2816 * The close process works as follows:
2817 * - First check if any thread is already closing (io->closing_ec)
2818 * - Set up wakeup_mutex for synchronization
2819 * - Iterate through all blocking operations in io->blocking_operations
2820 * - For each blocked fiber with a scheduler:
2821 * - Notify via rb_fiber_scheduler_fiber_interrupt
2822 * - For each blocked thread without a scheduler:
2823 * - Enqueue IOError via rb_threadptr_pending_interrupt_enque
2824 * - Wake via rb_threadptr_interrupt
2825 * - Wait on wakeup_mutex until all operations are cleaned up
2826 * - Only then clear closing state and allow actual close to proceed
2827 */
2828static VALUE
2829thread_io_close_notify_all(VALUE _io)
2830{
2831 struct rb_io *io = (struct rb_io *)_io;
2832
2833 size_t count = 0;
2834 rb_vm_t *vm = io->closing_ec->thread_ptr->vm;
2835 VALUE error = vm->special_exceptions[ruby_error_stream_closed];
2836
2837 struct rb_io_blocking_operation *blocking_operation;
2838 ccan_list_for_each(rb_io_blocking_operations(io), blocking_operation, list) {
2839 rb_execution_context_t *ec = blocking_operation->ec;
2840
2841 // If the operation is in progress, we need to interrupt it:
2842 if (ec) {
2843 rb_thread_t *thread = ec->thread_ptr;
2844
2845 VALUE result = RUBY_Qundef;
2846 if (thread->scheduler != Qnil) {
2847 result = rb_fiber_scheduler_fiber_interrupt(thread->scheduler, rb_fiberptr_self(ec->fiber_ptr), error);
2848 }
2849
2850 if (result == RUBY_Qundef) {
2851 // If the thread is not the current thread, we need to enqueue an error:
2852 rb_threadptr_pending_interrupt_enque(thread, error);
2853 rb_threadptr_interrupt(thread);
2854 }
2855 }
2856
2857 count += 1;
2858 }
2859
2860 return (VALUE)count;
2861}
2862
2863size_t
2864rb_thread_io_close_interrupt(struct rb_io *io)
2865{
2866 // We guard this operation based on `io->closing_ec` -> only one thread will ever enter this function.
2867 if (io->closing_ec) {
2868 return 0;
2869 }
2870
2871 // If there are no blocking operations, we are done:
2872 if (ccan_list_empty(rb_io_blocking_operations(io))) {
2873 return 0;
2874 }
2875
2876 // Otherwise, we are now closing the IO:
2877 rb_execution_context_t *ec = GET_EC();
2878 io->closing_ec = ec;
2879
2880 // This is used to ensure the correct execution context is woken up after the blocking operation is interrupted:
2881 io->wakeup_mutex = rb_mutex_new();
2882 rb_mutex_allow_trap(io->wakeup_mutex, 1);
2883
2884 // We need to use a mutex here as entering the fiber scheduler may cause a context switch:
2885 VALUE result = rb_mutex_synchronize(io->wakeup_mutex, thread_io_close_notify_all, (VALUE)io);
2886
2887 return (size_t)result;
2888}
2889
2890void
2891rb_thread_io_close_wait(struct rb_io* io)
2892{
2893 VALUE wakeup_mutex = io->wakeup_mutex;
2894
2895 if (!RB_TEST(wakeup_mutex)) {
2896 // There was nobody else using this file when we closed it, so we never bothered to allocate a mutex:
2897 return;
2898 }
2899
2900 rb_mutex_lock(wakeup_mutex);
2901 while (!ccan_list_empty(rb_io_blocking_operations(io))) {
2902 rb_mutex_sleep(wakeup_mutex, Qnil);
2903 }
2904 rb_mutex_unlock(wakeup_mutex);
2905
2906 // We are done closing:
2907 io->wakeup_mutex = Qnil;
2908 io->closing_ec = NULL;
2909}
2910
2911void
2912rb_thread_fd_close(int fd)
2913{
2914 rb_warn("rb_thread_fd_close is deprecated (and is now a no-op).");
2915}
2916
2917/*
2918 * call-seq:
2919 * raise(exception, message = exception.to_s, backtrace = nil, cause: $!)
2920 * raise(message = nil, cause: $!)
2921 *
2922 * Raises an exception from the given thread. The caller does not have to be
2923 * +thr+. See Kernel#raise for more information on arguments.
2924 *
2925 * Thread.abort_on_exception = true
2926 * a = Thread.new { sleep(200) }
2927 * a.raise("Gotcha")
2928 *
2929 * This will produce:
2930 *
2931 * prog.rb:3: Gotcha (RuntimeError)
2932 * from prog.rb:2:in `initialize'
2933 * from prog.rb:2:in `new'
2934 * from prog.rb:2
2935 */
2936
2937static VALUE
2938thread_raise_m(int argc, VALUE *argv, VALUE self)
2939{
2940 rb_thread_t *target_th = rb_thread_ptr(self);
2941 const rb_thread_t *current_th = GET_THREAD();
2942
2943 threadptr_check_pending_interrupt_queue(target_th);
2944
2945 if (rb_threadptr_dead(target_th)) {
2946 return Qnil;
2947 }
2948
2949 VALUE exception = rb_exception_setup(argc, argv);
2950 rb_threadptr_pending_interrupt_enque(target_th, exception);
2951 rb_threadptr_interrupt(target_th);
2952
2953 /* To perform Thread.current.raise as Kernel.raise */
2954 if (current_th == target_th) {
2955 RUBY_VM_CHECK_INTS(target_th->ec);
2956 }
2957 return Qnil;
2958}
2959
2960
2961/*
2962 * call-seq:
2963 * thr.exit -> thr
2964 * thr.kill -> thr
2965 * thr.terminate -> thr
2966 *
2967 * Terminates +thr+ and schedules another thread to be run, returning
2968 * the terminated Thread. If this is the main thread, or the last
2969 * thread, exits the process. Note that the caller does not wait for
2970 * the thread to terminate if the receiver is different from the currently
2971 * running thread. The termination is asynchronous, and the thread can still
2972 * run a small amount of ruby code before exiting.
2973 */
2974
2976rb_thread_kill(VALUE thread)
2977{
2978 rb_thread_t *target_th = rb_thread_ptr(thread);
2979
2980 if (target_th->to_kill || target_th->status == THREAD_KILLED) {
2981 return thread;
2982 }
2983 if (target_th == target_th->vm->ractor.main_thread) {
2984 rb_exit(EXIT_SUCCESS);
2985 }
2986
2987 RUBY_DEBUG_LOG("target_th:%u", rb_th_serial(target_th));
2988
2989 if (target_th == GET_THREAD()) {
2990 /* kill myself immediately */
2991 rb_threadptr_to_kill(target_th);
2992 }
2993 else {
2994 threadptr_check_pending_interrupt_queue(target_th);
2995 rb_threadptr_pending_interrupt_enque(target_th, RUBY_FATAL_THREAD_KILLED);
2996 rb_threadptr_interrupt(target_th);
2997 }
2998
2999 return thread;
3000}
3001
3002int
3003rb_thread_to_be_killed(VALUE thread)
3004{
3005 rb_thread_t *target_th = rb_thread_ptr(thread);
3006
3007 if (target_th->to_kill || target_th->status == THREAD_KILLED) {
3008 return TRUE;
3009 }
3010 return FALSE;
3011}
3012
3013/*
3014 * call-seq:
3015 * Thread.kill(thread) -> thread
3016 *
3017 * Causes the given +thread+ to exit, see also Thread::exit.
3018 *
3019 * count = 0
3020 * a = Thread.new { loop { count += 1 } }
3021 * sleep(0.1) #=> 0
3022 * Thread.kill(a) #=> #<Thread:0x401b3d30 dead>
3023 * count #=> 93947
3024 * a.alive? #=> false
3025 */
3026
3027static VALUE
3028rb_thread_s_kill(VALUE obj, VALUE th)
3029{
3030 return rb_thread_kill(th);
3031}
3032
3033
3034/*
3035 * call-seq:
3036 * Thread.exit -> thread
3037 *
3038 * Terminates the currently running thread and schedules another thread to be
3039 * run.
3040 *
3041 * If this thread is already marked to be killed, ::exit returns the Thread.
3042 *
3043 * If this is the main thread, or the last thread, exit the process.
3044 */
3045
3046static VALUE
3047rb_thread_exit(VALUE _)
3048{
3049 rb_thread_t *th = GET_THREAD();
3050 return rb_thread_kill(th->self);
3051}
3052
3053
3054/*
3055 * call-seq:
3056 * thr.wakeup -> thr
3057 *
3058 * Marks a given thread as eligible for scheduling, however it may still
3059 * remain blocked on I/O.
3060 *
3061 * *Note:* This does not invoke the scheduler, see #run for more information.
3062 *
3063 * c = Thread.new { Thread.stop; puts "hey!" }
3064 * sleep 0.1 while c.status!='sleep'
3065 * c.wakeup
3066 * c.join
3067 * #=> "hey!"
3068 */
3069
3071rb_thread_wakeup(VALUE thread)
3072{
3073 if (!RTEST(rb_thread_wakeup_alive(thread))) {
3074 rb_raise(rb_eThreadError, "killed thread");
3075 }
3076 return thread;
3077}
3078
3081{
3082 rb_thread_t *target_th = rb_thread_ptr(thread);
3083 if (target_th->status == THREAD_KILLED) return Qnil;
3084
3085 rb_threadptr_ready(target_th);
3086
3087 if (target_th->status == THREAD_STOPPED ||
3088 target_th->status == THREAD_STOPPED_FOREVER) {
3089 target_th->status = THREAD_RUNNABLE;
3090 }
3091
3092 return thread;
3093}
3094
3095
3096/*
3097 * call-seq:
3098 * thr.run -> thr
3099 *
3100 * Wakes up +thr+, making it eligible for scheduling.
3101 *
3102 * a = Thread.new { puts "a"; Thread.stop; puts "c" }
3103 * sleep 0.1 while a.status!='sleep'
3104 * puts "Got here"
3105 * a.run
3106 * a.join
3107 *
3108 * This will produce:
3109 *
3110 * a
3111 * Got here
3112 * c
3113 *
3114 * See also the instance method #wakeup.
3115 */
3116
3118rb_thread_run(VALUE thread)
3119{
3120 rb_thread_wakeup(thread);
3122 return thread;
3123}
3124
3125
3127rb_thread_stop(void)
3128{
3129 if (rb_thread_alone()) {
3130 rb_raise(rb_eThreadError,
3131 "stopping only thread\n\tnote: use sleep to stop forever");
3132 }
3134 return Qnil;
3135}
3136
3137/*
3138 * call-seq:
3139 * Thread.stop -> nil
3140 *
3141 * Stops execution of the current thread, putting it into a ``sleep'' state,
3142 * and schedules execution of another thread.
3143 *
3144 * a = Thread.new { print "a"; Thread.stop; print "c" }
3145 * sleep 0.1 while a.status!='sleep'
3146 * print "b"
3147 * a.run
3148 * a.join
3149 * #=> "abc"
3150 */
3151
3152static VALUE
3153thread_stop(VALUE _)
3154{
3155 return rb_thread_stop();
3156}
3157
3158/********************************************************************/
3159
3160VALUE
3161rb_thread_list(void)
3162{
3163 // TODO
3164 return rb_ractor_thread_list();
3165}
3166
3167/*
3168 * call-seq:
3169 * Thread.list -> array
3170 *
3171 * Returns an array of Thread objects for all threads that are either runnable
3172 * or stopped.
3173 *
3174 * Thread.new { sleep(200) }
3175 * Thread.new { 1000000.times {|i| i*i } }
3176 * Thread.new { Thread.stop }
3177 * Thread.list.each {|t| p t}
3178 *
3179 * This will produce:
3180 *
3181 * #<Thread:0x401b3e84 sleep>
3182 * #<Thread:0x401b3f38 run>
3183 * #<Thread:0x401b3fb0 sleep>
3184 * #<Thread:0x401bdf4c run>
3185 */
3186
3187static VALUE
3188thread_list(VALUE _)
3189{
3190 return rb_thread_list();
3191}
3192
3195{
3196 return GET_THREAD()->self;
3197}
3198
3199/*
3200 * call-seq:
3201 * Thread.current -> thread
3202 *
3203 * Returns the currently executing thread.
3204 *
3205 * Thread.current #=> #<Thread:0x401bdf4c run>
3206 */
3207
3208static VALUE
3209thread_s_current(VALUE klass)
3210{
3211 return rb_thread_current();
3212}
3213
3215rb_thread_main(void)
3216{
3217 return GET_RACTOR()->threads.main->self;
3218}
3219
3220/*
3221 * call-seq:
3222 * Thread.main -> thread
3223 *
3224 * Returns the main thread.
3225 */
3226
3227static VALUE
3228rb_thread_s_main(VALUE klass)
3229{
3230 return rb_thread_main();
3231}
3232
3233
3234/*
3235 * call-seq:
3236 * Thread.abort_on_exception -> true or false
3237 *
3238 * Returns the status of the global ``abort on exception'' condition.
3239 *
3240 * The default is +false+.
3241 *
3242 * When set to +true+, if any thread is aborted by an exception, the
3243 * raised exception will be re-raised in the main thread.
3244 *
3245 * Can also be specified by the global $DEBUG flag or command line option
3246 * +-d+.
3247 *
3248 * See also ::abort_on_exception=.
3249 *
3250 * There is also an instance level method to set this for a specific thread,
3251 * see #abort_on_exception.
3252 */
3253
3254static VALUE
3255rb_thread_s_abort_exc(VALUE _)
3256{
3257 return RBOOL(GET_THREAD()->vm->thread_abort_on_exception);
3258}
3259
3260
3261/*
3262 * call-seq:
3263 * Thread.abort_on_exception= boolean -> true or false
3264 *
3265 * When set to +true+, if any thread is aborted by an exception, the
3266 * raised exception will be re-raised in the main thread.
3267 * Returns the new state.
3268 *
3269 * Thread.abort_on_exception = true
3270 * t1 = Thread.new do
3271 * puts "In new thread"
3272 * raise "Exception from thread"
3273 * end
3274 * sleep(1)
3275 * puts "not reached"
3276 *
3277 * This will produce:
3278 *
3279 * In new thread
3280 * prog.rb:4: Exception from thread (RuntimeError)
3281 * from prog.rb:2:in `initialize'
3282 * from prog.rb:2:in `new'
3283 * from prog.rb:2
3284 *
3285 * See also ::abort_on_exception.
3286 *
3287 * There is also an instance level method to set this for a specific thread,
3288 * see #abort_on_exception=.
3289 */
3290
3291static VALUE
3292rb_thread_s_abort_exc_set(VALUE self, VALUE val)
3293{
3294 GET_THREAD()->vm->thread_abort_on_exception = RTEST(val);
3295 return val;
3296}
3297
3298
3299/*
3300 * call-seq:
3301 * thr.abort_on_exception -> true or false
3302 *
3303 * Returns the status of the thread-local ``abort on exception'' condition for
3304 * this +thr+.
3305 *
3306 * The default is +false+.
3307 *
3308 * See also #abort_on_exception=.
3309 *
3310 * There is also a class level method to set this for all threads, see
3311 * ::abort_on_exception.
3312 */
3313
3314static VALUE
3315rb_thread_abort_exc(VALUE thread)
3316{
3317 return RBOOL(rb_thread_ptr(thread)->abort_on_exception);
3318}
3319
3320
3321/*
3322 * call-seq:
3323 * thr.abort_on_exception= boolean -> true or false
3324 *
3325 * When set to +true+, if this +thr+ is aborted by an exception, the
3326 * raised exception will be re-raised in the main thread.
3327 *
3328 * See also #abort_on_exception.
3329 *
3330 * There is also a class level method to set this for all threads, see
3331 * ::abort_on_exception=.
3332 */
3333
3334static VALUE
3335rb_thread_abort_exc_set(VALUE thread, VALUE val)
3336{
3337 rb_thread_ptr(thread)->abort_on_exception = RTEST(val);
3338 return val;
3339}
3340
3341
3342/*
3343 * call-seq:
3344 * Thread.report_on_exception -> true or false
3345 *
3346 * Returns the status of the global ``report on exception'' condition.
3347 *
3348 * The default is +true+ since Ruby 2.5.
3349 *
3350 * All threads created when this flag is true will report
3351 * a message on $stderr if an exception kills the thread.
3352 *
3353 * Thread.new { 1.times { raise } }
3354 *
3355 * will produce this output on $stderr:
3356 *
3357 * #<Thread:...> terminated with exception (report_on_exception is true):
3358 * Traceback (most recent call last):
3359 * 2: from -e:1:in `block in <main>'
3360 * 1: from -e:1:in `times'
3361 *
3362 * This is done to catch errors in threads early.
3363 * In some cases, you might not want this output.
3364 * There are multiple ways to avoid the extra output:
3365 *
3366 * * If the exception is not intended, the best is to fix the cause of
3367 * the exception so it does not happen anymore.
3368 * * If the exception is intended, it might be better to rescue it closer to
3369 * where it is raised rather then let it kill the Thread.
3370 * * If it is guaranteed the Thread will be joined with Thread#join or
3371 * Thread#value, then it is safe to disable this report with
3372 * <code>Thread.current.report_on_exception = false</code>
3373 * when starting the Thread.
3374 * However, this might handle the exception much later, or not at all
3375 * if the Thread is never joined due to the parent thread being blocked, etc.
3376 *
3377 * See also ::report_on_exception=.
3378 *
3379 * There is also an instance level method to set this for a specific thread,
3380 * see #report_on_exception=.
3381 *
3382 */
3383
3384static VALUE
3385rb_thread_s_report_exc(VALUE _)
3386{
3387 return RBOOL(GET_THREAD()->vm->thread_report_on_exception);
3388}
3389
3390
3391/*
3392 * call-seq:
3393 * Thread.report_on_exception= boolean -> true or false
3394 *
3395 * Returns the new state.
3396 * When set to +true+, all threads created afterwards will inherit the
3397 * condition and report a message on $stderr if an exception kills a thread:
3398 *
3399 * Thread.report_on_exception = true
3400 * t1 = Thread.new do
3401 * puts "In new thread"
3402 * raise "Exception from thread"
3403 * end
3404 * sleep(1)
3405 * puts "In the main thread"
3406 *
3407 * This will produce:
3408 *
3409 * In new thread
3410 * #<Thread:...prog.rb:2> terminated with exception (report_on_exception is true):
3411 * Traceback (most recent call last):
3412 * prog.rb:4:in `block in <main>': Exception from thread (RuntimeError)
3413 * In the main thread
3414 *
3415 * See also ::report_on_exception.
3416 *
3417 * There is also an instance level method to set this for a specific thread,
3418 * see #report_on_exception=.
3419 */
3420
3421static VALUE
3422rb_thread_s_report_exc_set(VALUE self, VALUE val)
3423{
3424 GET_THREAD()->vm->thread_report_on_exception = RTEST(val);
3425 return val;
3426}
3427
3428
3429/*
3430 * call-seq:
3431 * Thread.ignore_deadlock -> true or false
3432 *
3433 * Returns the status of the global ``ignore deadlock'' condition.
3434 * The default is +false+, so that deadlock conditions are not ignored.
3435 *
3436 * See also ::ignore_deadlock=.
3437 *
3438 */
3439
3440static VALUE
3441rb_thread_s_ignore_deadlock(VALUE _)
3442{
3443 return RBOOL(GET_THREAD()->vm->thread_ignore_deadlock);
3444}
3445
3446
3447/*
3448 * call-seq:
3449 * Thread.ignore_deadlock = boolean -> true or false
3450 *
3451 * Returns the new state.
3452 * When set to +true+, the VM will not check for deadlock conditions.
3453 * It is only useful to set this if your application can break a
3454 * deadlock condition via some other means, such as a signal.
3455 *
3456 * Thread.ignore_deadlock = true
3457 * queue = Thread::Queue.new
3458 *
3459 * trap(:SIGUSR1){queue.push "Received signal"}
3460 *
3461 * # raises fatal error unless ignoring deadlock
3462 * puts queue.pop
3463 *
3464 * See also ::ignore_deadlock.
3465 */
3466
3467static VALUE
3468rb_thread_s_ignore_deadlock_set(VALUE self, VALUE val)
3469{
3470 GET_THREAD()->vm->thread_ignore_deadlock = RTEST(val);
3471 return val;
3472}
3473
3474
3475/*
3476 * call-seq:
3477 * thr.report_on_exception -> true or false
3478 *
3479 * Returns the status of the thread-local ``report on exception'' condition for
3480 * this +thr+.
3481 *
3482 * The default value when creating a Thread is the value of
3483 * the global flag Thread.report_on_exception.
3484 *
3485 * See also #report_on_exception=.
3486 *
3487 * There is also a class level method to set this for all new threads, see
3488 * ::report_on_exception=.
3489 */
3490
3491static VALUE
3492rb_thread_report_exc(VALUE thread)
3493{
3494 return RBOOL(rb_thread_ptr(thread)->report_on_exception);
3495}
3496
3497
3498/*
3499 * call-seq:
3500 * thr.report_on_exception= boolean -> true or false
3501 *
3502 * When set to +true+, a message is printed on $stderr if an exception
3503 * kills this +thr+. See ::report_on_exception for details.
3504 *
3505 * See also #report_on_exception.
3506 *
3507 * There is also a class level method to set this for all new threads, see
3508 * ::report_on_exception=.
3509 */
3510
3511static VALUE
3512rb_thread_report_exc_set(VALUE thread, VALUE val)
3513{
3514 rb_thread_ptr(thread)->report_on_exception = RTEST(val);
3515 return val;
3516}
3517
3518
3519/*
3520 * call-seq:
3521 * thr.group -> thgrp or nil
3522 *
3523 * Returns the ThreadGroup which contains the given thread.
3524 *
3525 * Thread.main.group #=> #<ThreadGroup:0x4029d914>
3526 */
3527
3528VALUE
3529rb_thread_group(VALUE thread)
3530{
3531 return rb_thread_ptr(thread)->thgroup;
3532}
3533
3534static const char *
3535thread_status_name(rb_thread_t *th, int detail)
3536{
3537 switch (th->status) {
3538 case THREAD_RUNNABLE:
3539 return th->to_kill ? "aborting" : "run";
3540 case THREAD_STOPPED_FOREVER:
3541 if (detail) return "sleep_forever";
3542 case THREAD_STOPPED:
3543 return "sleep";
3544 case THREAD_KILLED:
3545 return "dead";
3546 default:
3547 return "unknown";
3548 }
3549}
3550
3551static int
3552rb_threadptr_dead(rb_thread_t *th)
3553{
3554 return th->status == THREAD_KILLED;
3555}
3556
3557
3558/*
3559 * call-seq:
3560 * thr.status -> string, false or nil
3561 *
3562 * Returns the status of +thr+.
3563 *
3564 * [<tt>"sleep"</tt>]
3565 * Returned if this thread is sleeping or waiting on I/O
3566 * [<tt>"run"</tt>]
3567 * When this thread is executing
3568 * [<tt>"aborting"</tt>]
3569 * If this thread is aborting
3570 * [+false+]
3571 * When this thread is terminated normally
3572 * [+nil+]
3573 * If terminated with an exception.
3574 *
3575 * a = Thread.new { raise("die now") }
3576 * b = Thread.new { Thread.stop }
3577 * c = Thread.new { Thread.exit }
3578 * d = Thread.new { sleep }
3579 * d.kill #=> #<Thread:0x401b3678 aborting>
3580 * a.status #=> nil
3581 * b.status #=> "sleep"
3582 * c.status #=> false
3583 * d.status #=> "aborting"
3584 * Thread.current.status #=> "run"
3585 *
3586 * See also the instance methods #alive? and #stop?
3587 */
3588
3589static VALUE
3590rb_thread_status(VALUE thread)
3591{
3592 rb_thread_t *target_th = rb_thread_ptr(thread);
3593
3594 if (rb_threadptr_dead(target_th)) {
3595 if (!NIL_P(target_th->ec->errinfo) &&
3596 !FIXNUM_P(target_th->ec->errinfo)) {
3597 return Qnil;
3598 }
3599 else {
3600 return Qfalse;
3601 }
3602 }
3603 else {
3604 return rb_str_new2(thread_status_name(target_th, FALSE));
3605 }
3606}
3607
3608
3609/*
3610 * call-seq:
3611 * thr.alive? -> true or false
3612 *
3613 * Returns +true+ if +thr+ is running or sleeping.
3614 *
3615 * thr = Thread.new { }
3616 * thr.join #=> #<Thread:0x401b3fb0 dead>
3617 * Thread.current.alive? #=> true
3618 * thr.alive? #=> false
3619 *
3620 * See also #stop? and #status.
3621 */
3622
3623static VALUE
3624rb_thread_alive_p(VALUE thread)
3625{
3626 return RBOOL(!thread_finished(rb_thread_ptr(thread)));
3627}
3628
3629/*
3630 * call-seq:
3631 * thr.stop? -> true or false
3632 *
3633 * Returns +true+ if +thr+ is dead or sleeping.
3634 *
3635 * a = Thread.new { Thread.stop }
3636 * b = Thread.current
3637 * a.stop? #=> true
3638 * b.stop? #=> false
3639 *
3640 * See also #alive? and #status.
3641 */
3642
3643static VALUE
3644rb_thread_stop_p(VALUE thread)
3645{
3646 rb_thread_t *th = rb_thread_ptr(thread);
3647
3648 if (rb_threadptr_dead(th)) {
3649 return Qtrue;
3650 }
3651 return RBOOL(th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER);
3652}
3653
3654/*
3655 * call-seq:
3656 * thr.name -> string
3657 *
3658 * show the name of the thread.
3659 */
3660
3661static VALUE
3662rb_thread_getname(VALUE thread)
3663{
3664 return rb_thread_ptr(thread)->name;
3665}
3666
3667/*
3668 * call-seq:
3669 * thr.name=(name) -> string
3670 *
3671 * set given name to the ruby thread.
3672 * On some platform, it may set the name to pthread and/or kernel.
3673 */
3674
3675static VALUE
3676rb_thread_setname(VALUE thread, VALUE name)
3677{
3678 rb_thread_t *target_th = rb_thread_ptr(thread);
3679
3680 if (!NIL_P(name)) {
3681 rb_encoding *enc;
3682 StringValueCStr(name);
3683 enc = rb_enc_get(name);
3684 if (!rb_enc_asciicompat(enc)) {
3685 rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
3686 rb_enc_name(enc));
3687 }
3688 name = rb_str_new_frozen(name);
3689 }
3690 target_th->name = name;
3691 if (threadptr_initialized(target_th) && target_th->has_dedicated_nt) {
3692 native_set_another_thread_name(target_th->nt->thread_id, name);
3693 }
3694 return name;
3695}
3696
3697#if USE_NATIVE_THREAD_NATIVE_THREAD_ID
3698/*
3699 * call-seq:
3700 * thr.native_thread_id -> integer
3701 *
3702 * Return the native thread ID which is used by the Ruby thread.
3703 *
3704 * The ID depends on the OS. (not POSIX thread ID returned by pthread_self(3))
3705 * * On Linux it is TID returned by gettid(2).
3706 * * On macOS it is the system-wide unique integral ID of thread returned
3707 * by pthread_threadid_np(3).
3708 * * On FreeBSD it is the unique integral ID of the thread returned by
3709 * pthread_getthreadid_np(3).
3710 * * On Windows it is the thread identifier returned by GetThreadId().
3711 * * On other platforms, it raises NotImplementedError.
3712 *
3713 * NOTE:
3714 * If the thread is not associated yet or already deassociated with a native
3715 * thread, it returns _nil_.
3716 * If the Ruby implementation uses M:N thread model, the ID may change
3717 * depending on the timing.
3718 */
3719
3720static VALUE
3721rb_thread_native_thread_id(VALUE thread)
3722{
3723 rb_thread_t *target_th = rb_thread_ptr(thread);
3724 if (rb_threadptr_dead(target_th)) return Qnil;
3725 return native_thread_native_thread_id(target_th);
3726}
3727#else
3728# define rb_thread_native_thread_id rb_f_notimplement
3729#endif
3730
3731/*
3732 * call-seq:
3733 * thr.to_s -> string
3734 *
3735 * Dump the name, id, and status of _thr_ to a string.
3736 */
3737
3738static VALUE
3739rb_thread_to_s(VALUE thread)
3740{
3741 VALUE cname = rb_class_path(rb_obj_class(thread));
3742 rb_thread_t *target_th = rb_thread_ptr(thread);
3743 const char *status;
3744 VALUE str, loc;
3745
3746 status = thread_status_name(target_th, TRUE);
3747 str = rb_sprintf("#<%"PRIsVALUE":%p", cname, (void *)thread);
3748 if (!NIL_P(target_th->name)) {
3749 rb_str_catf(str, "@%"PRIsVALUE, target_th->name);
3750 }
3751 if ((loc = threadptr_invoke_proc_location(target_th)) != Qnil) {
3752 rb_str_catf(str, " %"PRIsVALUE":%"PRIsVALUE,
3753 RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
3754 }
3755 rb_str_catf(str, " %s>", status);
3756
3757 return str;
3758}
3759
3760/* variables for recursive traversals */
3761#define recursive_key id__recursive_key__
3762
3763static VALUE
3764threadptr_local_aref(rb_thread_t *th, ID id)
3765{
3766 if (id == recursive_key) {
3767 return th->ec->local_storage_recursive_hash;
3768 }
3769 else {
3770 VALUE val;
3771 struct rb_id_table *local_storage = th->ec->local_storage;
3772
3773 if (local_storage != NULL && rb_id_table_lookup(local_storage, id, &val)) {
3774 return val;
3775 }
3776 else {
3777 return Qnil;
3778 }
3779 }
3780}
3781
3783rb_thread_local_aref(VALUE thread, ID id)
3784{
3785 return threadptr_local_aref(rb_thread_ptr(thread), id);
3786}
3787
3788/*
3789 * call-seq:
3790 * thr[sym] -> obj or nil
3791 *
3792 * Attribute Reference---Returns the value of a fiber-local variable (current thread's root fiber
3793 * if not explicitly inside a Fiber), using either a symbol or a string name.
3794 * If the specified variable does not exist, returns +nil+.
3795 *
3796 * [
3797 * Thread.new { Thread.current["name"] = "A" },
3798 * Thread.new { Thread.current[:name] = "B" },
3799 * Thread.new { Thread.current["name"] = "C" }
3800 * ].each do |th|
3801 * th.join
3802 * puts "#{th.inspect}: #{th[:name]}"
3803 * end
3804 *
3805 * This will produce:
3806 *
3807 * #<Thread:0x00000002a54220 dead>: A
3808 * #<Thread:0x00000002a541a8 dead>: B
3809 * #<Thread:0x00000002a54130 dead>: C
3810 *
3811 * Thread#[] and Thread#[]= are not thread-local but fiber-local.
3812 * This confusion did not exist in Ruby 1.8 because
3813 * fibers are only available since Ruby 1.9.
3814 * Ruby 1.9 chooses that the methods behaves fiber-local to save
3815 * following idiom for dynamic scope.
3816 *
3817 * def meth(newvalue)
3818 * begin
3819 * oldvalue = Thread.current[:name]
3820 * Thread.current[:name] = newvalue
3821 * yield
3822 * ensure
3823 * Thread.current[:name] = oldvalue
3824 * end
3825 * end
3826 *
3827 * The idiom may not work as dynamic scope if the methods are thread-local
3828 * and a given block switches fiber.
3829 *
3830 * f = Fiber.new {
3831 * meth(1) {
3832 * Fiber.yield
3833 * }
3834 * }
3835 * meth(2) {
3836 * f.resume
3837 * }
3838 * f.resume
3839 * p Thread.current[:name]
3840 * #=> nil if fiber-local
3841 * #=> 2 if thread-local (The value 2 is leaked to outside of meth method.)
3842 *
3843 * For thread-local variables, please see #thread_variable_get and
3844 * #thread_variable_set.
3845 *
3846 */
3847
3848static VALUE
3849rb_thread_aref(VALUE thread, VALUE key)
3850{
3851 ID id = rb_check_id(&key);
3852 if (!id) return Qnil;
3853 return rb_thread_local_aref(thread, id);
3854}
3855
3856/*
3857 * call-seq:
3858 * thr.fetch(sym) -> obj
3859 * thr.fetch(sym) { } -> obj
3860 * thr.fetch(sym, default) -> obj
3861 *
3862 * Returns a fiber-local for the given key. If the key can't be
3863 * found, there are several options: With no other arguments, it will
3864 * raise a KeyError exception; if <i>default</i> is given, then that
3865 * will be returned; if the optional code block is specified, then
3866 * that will be run and its result returned. See Thread#[] and
3867 * Hash#fetch.
3868 */
3869static VALUE
3870rb_thread_fetch(int argc, VALUE *argv, VALUE self)
3871{
3872 VALUE key, val;
3873 ID id;
3874 rb_thread_t *target_th = rb_thread_ptr(self);
3875 int block_given;
3876
3877 rb_check_arity(argc, 1, 2);
3878 key = argv[0];
3879
3880 block_given = rb_block_given_p();
3881 if (block_given && argc == 2) {
3882 rb_warn("block supersedes default value argument");
3883 }
3884
3885 id = rb_check_id(&key);
3886
3887 if (id == recursive_key) {
3888 return target_th->ec->local_storage_recursive_hash;
3889 }
3890 else if (id && target_th->ec->local_storage &&
3891 rb_id_table_lookup(target_th->ec->local_storage, id, &val)) {
3892 return val;
3893 }
3894 else if (block_given) {
3895 return rb_yield(key);
3896 }
3897 else if (argc == 1) {
3898 rb_key_err_raise(rb_sprintf("key not found: %+"PRIsVALUE, key), self, key);
3899 }
3900 else {
3901 return argv[1];
3902 }
3903}
3904
3905static VALUE
3906threadptr_local_aset(rb_thread_t *th, ID id, VALUE val)
3907{
3908 if (id == recursive_key) {
3909 th->ec->local_storage_recursive_hash = val;
3910 return val;
3911 }
3912 else {
3913 struct rb_id_table *local_storage = th->ec->local_storage;
3914
3915 if (NIL_P(val)) {
3916 if (!local_storage) return Qnil;
3917 rb_id_table_delete(local_storage, id);
3918 return Qnil;
3919 }
3920 else {
3921 if (local_storage == NULL) {
3922 th->ec->local_storage = local_storage = rb_id_table_create(0);
3923 }
3924 rb_id_table_insert(local_storage, id, val);
3925 return val;
3926 }
3927 }
3928}
3929
3931rb_thread_local_aset(VALUE thread, ID id, VALUE val)
3932{
3933 if (OBJ_FROZEN(thread)) {
3934 rb_frozen_error_raise(thread, "can't modify frozen thread locals");
3935 }
3936
3937 return threadptr_local_aset(rb_thread_ptr(thread), id, val);
3938}
3939
3940/*
3941 * call-seq:
3942 * thr[sym] = obj -> obj
3943 *
3944 * Attribute Assignment---Sets or creates the value of a fiber-local variable,
3945 * using either a symbol or a string.
3946 *
3947 * See also Thread#[].
3948 *
3949 * For thread-local variables, please see #thread_variable_set and
3950 * #thread_variable_get.
3951 */
3952
3953static VALUE
3954rb_thread_aset(VALUE self, VALUE id, VALUE val)
3955{
3956 return rb_thread_local_aset(self, rb_to_id(id), val);
3957}
3958
3959/*
3960 * call-seq:
3961 * thr.thread_variable_get(key) -> obj or nil
3962 *
3963 * Returns the value of a thread local variable that has been set. Note that
3964 * these are different than fiber local values. For fiber local values,
3965 * please see Thread#[] and Thread#[]=.
3966 *
3967 * Thread local values are carried along with threads, and do not respect
3968 * fibers. For example:
3969 *
3970 * Thread.new {
3971 * Thread.current.thread_variable_set("foo", "bar") # set a thread local
3972 * Thread.current["foo"] = "bar" # set a fiber local
3973 *
3974 * Fiber.new {
3975 * Fiber.yield [
3976 * Thread.current.thread_variable_get("foo"), # get the thread local
3977 * Thread.current["foo"], # get the fiber local
3978 * ]
3979 * }.resume
3980 * }.join.value # => ['bar', nil]
3981 *
3982 * The value "bar" is returned for the thread local, where nil is returned
3983 * for the fiber local. The fiber is executed in the same thread, so the
3984 * thread local values are available.
3985 */
3986
3987static VALUE
3988rb_thread_variable_get(VALUE thread, VALUE key)
3989{
3990 VALUE locals;
3991 VALUE symbol = rb_to_symbol(key);
3992
3993 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
3994 return Qnil;
3995 }
3996 locals = rb_thread_local_storage(thread);
3997 return rb_hash_aref(locals, symbol);
3998}
3999
4000/*
4001 * call-seq:
4002 * thr.thread_variable_set(key, value)
4003 *
4004 * Sets a thread local with +key+ to +value+. Note that these are local to
4005 * threads, and not to fibers. Please see Thread#thread_variable_get and
4006 * Thread#[] for more information.
4007 */
4008
4009static VALUE
4010rb_thread_variable_set(VALUE thread, VALUE key, VALUE val)
4011{
4012 VALUE locals;
4013
4014 if (OBJ_FROZEN(thread)) {
4015 rb_frozen_error_raise(thread, "can't modify frozen thread locals");
4016 }
4017
4018 locals = rb_thread_local_storage(thread);
4019 return rb_hash_aset(locals, rb_to_symbol(key), val);
4020}
4021
4022/*
4023 * call-seq:
4024 * thr.key?(sym) -> true or false
4025 *
4026 * Returns +true+ if the given string (or symbol) exists as a fiber-local
4027 * variable.
4028 *
4029 * me = Thread.current
4030 * me[:oliver] = "a"
4031 * me.key?(:oliver) #=> true
4032 * me.key?(:stanley) #=> false
4033 */
4034
4035static VALUE
4036rb_thread_key_p(VALUE self, VALUE key)
4037{
4038 VALUE val;
4039 ID id = rb_check_id(&key);
4040 struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
4041
4042 if (!id || local_storage == NULL) {
4043 return Qfalse;
4044 }
4045 return RBOOL(rb_id_table_lookup(local_storage, id, &val));
4046}
4047
4048static enum rb_id_table_iterator_result
4049thread_keys_i(ID key, VALUE value, void *ary)
4050{
4051 rb_ary_push((VALUE)ary, ID2SYM(key));
4052 return ID_TABLE_CONTINUE;
4053}
4054
4056rb_thread_alone(void)
4057{
4058 // TODO
4059 return rb_ractor_living_thread_num(GET_RACTOR()) == 1;
4060}
4061
4062/*
4063 * call-seq:
4064 * thr.keys -> array
4065 *
4066 * Returns an array of the names of the fiber-local variables (as Symbols).
4067 *
4068 * thr = Thread.new do
4069 * Thread.current[:cat] = 'meow'
4070 * Thread.current["dog"] = 'woof'
4071 * end
4072 * thr.join #=> #<Thread:0x401b3f10 dead>
4073 * thr.keys #=> [:dog, :cat]
4074 */
4075
4076static VALUE
4077rb_thread_keys(VALUE self)
4078{
4079 struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
4080 VALUE ary = rb_ary_new();
4081
4082 if (local_storage) {
4083 rb_id_table_foreach(local_storage, thread_keys_i, (void *)ary);
4084 }
4085 return ary;
4086}
4087
4088static int
4089keys_i(VALUE key, VALUE value, VALUE ary)
4090{
4091 rb_ary_push(ary, key);
4092 return ST_CONTINUE;
4093}
4094
4095/*
4096 * call-seq:
4097 * thr.thread_variables -> array
4098 *
4099 * Returns an array of the names of the thread-local variables (as Symbols).
4100 *
4101 * thr = Thread.new do
4102 * Thread.current.thread_variable_set(:cat, 'meow')
4103 * Thread.current.thread_variable_set("dog", 'woof')
4104 * end
4105 * thr.join #=> #<Thread:0x401b3f10 dead>
4106 * thr.thread_variables #=> [:dog, :cat]
4107 *
4108 * Note that these are not fiber local variables. Please see Thread#[] and
4109 * Thread#thread_variable_get for more details.
4110 */
4111
4112static VALUE
4113rb_thread_variables(VALUE thread)
4114{
4115 VALUE locals;
4116 VALUE ary;
4117
4118 ary = rb_ary_new();
4119 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4120 return ary;
4121 }
4122 locals = rb_thread_local_storage(thread);
4123 rb_hash_foreach(locals, keys_i, ary);
4124
4125 return ary;
4126}
4127
4128/*
4129 * call-seq:
4130 * thr.thread_variable?(key) -> true or false
4131 *
4132 * Returns +true+ if the given string (or symbol) exists as a thread-local
4133 * variable.
4134 *
4135 * me = Thread.current
4136 * me.thread_variable_set(:oliver, "a")
4137 * me.thread_variable?(:oliver) #=> true
4138 * me.thread_variable?(:stanley) #=> false
4139 *
4140 * Note that these are not fiber local variables. Please see Thread#[] and
4141 * Thread#thread_variable_get for more details.
4142 */
4143
4144static VALUE
4145rb_thread_variable_p(VALUE thread, VALUE key)
4146{
4147 VALUE locals;
4148 VALUE symbol = rb_to_symbol(key);
4149
4150 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4151 return Qfalse;
4152 }
4153 locals = rb_thread_local_storage(thread);
4154
4155 return RBOOL(rb_hash_lookup(locals, symbol) != Qnil);
4156}
4157
4158/*
4159 * call-seq:
4160 * thr.priority -> integer
4161 *
4162 * Returns the priority of <i>thr</i>. Default is inherited from the
4163 * current thread which creating the new thread, or zero for the
4164 * initial main thread; higher-priority thread will run more frequently
4165 * than lower-priority threads (but lower-priority threads can also run).
4166 *
4167 * This is just hint for Ruby thread scheduler. It may be ignored on some
4168 * platform.
4169 *
4170 * Thread.current.priority #=> 0
4171 */
4172
4173static VALUE
4174rb_thread_priority(VALUE thread)
4175{
4176 return INT2NUM(rb_thread_ptr(thread)->priority);
4177}
4178
4179
4180/*
4181 * call-seq:
4182 * thr.priority= integer -> thr
4183 *
4184 * Sets the priority of <i>thr</i> to <i>integer</i>. Higher-priority threads
4185 * will run more frequently than lower-priority threads (but lower-priority
4186 * threads can also run).
4187 *
4188 * This is just hint for Ruby thread scheduler. It may be ignored on some
4189 * platform.
4190 *
4191 * count1 = count2 = 0
4192 * a = Thread.new do
4193 * loop { count1 += 1 }
4194 * end
4195 * a.priority = -1
4196 *
4197 * b = Thread.new do
4198 * loop { count2 += 1 }
4199 * end
4200 * b.priority = -2
4201 * sleep 1 #=> 1
4202 * count1 #=> 622504
4203 * count2 #=> 5832
4204 */
4205
4206static VALUE
4207rb_thread_priority_set(VALUE thread, VALUE prio)
4208{
4209 rb_thread_t *target_th = rb_thread_ptr(thread);
4210 int priority;
4211
4212#if USE_NATIVE_THREAD_PRIORITY
4213 target_th->priority = NUM2INT(prio);
4214 native_thread_apply_priority(th);
4215#else
4216 priority = NUM2INT(prio);
4217 if (priority > RUBY_THREAD_PRIORITY_MAX) {
4218 priority = RUBY_THREAD_PRIORITY_MAX;
4219 }
4220 else if (priority < RUBY_THREAD_PRIORITY_MIN) {
4221 priority = RUBY_THREAD_PRIORITY_MIN;
4222 }
4223 target_th->priority = (int8_t)priority;
4224#endif
4225 return INT2NUM(target_th->priority);
4226}
4227
4228/* for IO */
4229
4230#if defined(NFDBITS) && defined(HAVE_RB_FD_INIT)
4231
4232/*
4233 * several Unix platforms support file descriptors bigger than FD_SETSIZE
4234 * in select(2) system call.
4235 *
4236 * - Linux 2.2.12 (?)
4237 * - NetBSD 1.2 (src/sys/kern/sys_generic.c:1.25)
4238 * select(2) documents how to allocate fd_set dynamically.
4239 * http://netbsd.gw.com/cgi-bin/man-cgi?select++NetBSD-4.0
4240 * - FreeBSD 2.2 (src/sys/kern/sys_generic.c:1.19)
4241 * - OpenBSD 2.0 (src/sys/kern/sys_generic.c:1.4)
4242 * select(2) documents how to allocate fd_set dynamically.
4243 * http://www.openbsd.org/cgi-bin/man.cgi?query=select&manpath=OpenBSD+4.4
4244 * - Solaris 8 has select_large_fdset
4245 * - Mac OS X 10.7 (Lion)
4246 * select(2) returns EINVAL if nfds is greater than FD_SET_SIZE and
4247 * _DARWIN_UNLIMITED_SELECT (or _DARWIN_C_SOURCE) isn't defined.
4248 * https://developer.apple.com/library/archive/releasenotes/Darwin/SymbolVariantsRelNotes/index.html
4249 *
4250 * When fd_set is not big enough to hold big file descriptors,
4251 * it should be allocated dynamically.
4252 * Note that this assumes fd_set is structured as bitmap.
4253 *
4254 * rb_fd_init allocates the memory.
4255 * rb_fd_term free the memory.
4256 * rb_fd_set may re-allocates bitmap.
4257 *
4258 * So rb_fd_set doesn't reject file descriptors bigger than FD_SETSIZE.
4259 */
4260
4261void
4263{
4264 fds->maxfd = 0;
4265 fds->fdset = ALLOC(fd_set);
4266 FD_ZERO(fds->fdset);
4267}
4268
4269static inline size_t
4270fdset_memsize(int maxfd)
4271{
4272 size_t o = howmany(maxfd, NFDBITS) * sizeof(fd_mask);
4273 if (o < sizeof(fd_set)) {
4274 return sizeof(fd_set);
4275 }
4276 return o;
4277}
4278
4279void
4280rb_fd_init_copy(rb_fdset_t *dst, rb_fdset_t *src)
4281{
4282 size_t size = fdset_memsize(rb_fd_max(src));
4283 dst->maxfd = src->maxfd;
4284 dst->fdset = xmalloc(size);
4285 memcpy(dst->fdset, src->fdset, size);
4286}
4287
4288void
4290{
4291 ruby_sized_xfree(fds->fdset, fdset_memsize(fds->maxfd));
4292 fds->maxfd = 0;
4293 fds->fdset = 0;
4294}
4295
4296void
4298{
4299 if (fds->fdset)
4300 MEMZERO(fds->fdset, fd_mask, howmany(fds->maxfd, NFDBITS));
4301}
4302
4303static void
4304rb_fd_resize(int n, rb_fdset_t *fds)
4305{
4306 size_t m = fdset_memsize(n + 1);
4307 size_t o = fdset_memsize(fds->maxfd);
4308
4309 if (m > o) {
4310 fds->fdset = ruby_sized_xrealloc(fds->fdset, m, o);
4311 memset((char *)fds->fdset + o, 0, m - o);
4312 }
4313 if (n >= fds->maxfd) fds->maxfd = n + 1;
4314}
4315
4316void
4317rb_fd_set(int n, rb_fdset_t *fds)
4318{
4319 rb_fd_resize(n, fds);
4320 FD_SET(n, fds->fdset);
4321}
4322
4323void
4324rb_fd_clr(int n, rb_fdset_t *fds)
4325{
4326 if (n >= fds->maxfd) return;
4327 FD_CLR(n, fds->fdset);
4328}
4329
4330int
4331rb_fd_isset(int n, const rb_fdset_t *fds)
4332{
4333 if (n >= fds->maxfd) return 0;
4334 return FD_ISSET(n, fds->fdset) != 0; /* "!= 0" avoids FreeBSD PR 91421 */
4335}
4336
4337void
4338rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
4339{
4340 size_t size = fdset_memsize(max);
4341 dst->fdset = ruby_sized_xrealloc(dst->fdset, size, fdset_memsize(dst->maxfd));
4342 dst->maxfd = max;
4343 memcpy(dst->fdset, src, size);
4344}
4345
4346void
4347rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
4348{
4349 size_t size = fdset_memsize(rb_fd_max(src));
4350 dst->fdset = ruby_sized_xrealloc(dst->fdset, size, fdset_memsize(dst->maxfd));
4351 dst->maxfd = src->maxfd;
4352 memcpy(dst->fdset, src->fdset, size);
4353}
4354
4355int
4356rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout)
4357{
4358 fd_set *r = NULL, *w = NULL, *e = NULL;
4359 if (readfds) {
4360 rb_fd_resize(n - 1, readfds);
4361 r = rb_fd_ptr(readfds);
4362 }
4363 if (writefds) {
4364 rb_fd_resize(n - 1, writefds);
4365 w = rb_fd_ptr(writefds);
4366 }
4367 if (exceptfds) {
4368 rb_fd_resize(n - 1, exceptfds);
4369 e = rb_fd_ptr(exceptfds);
4370 }
4371 return select(n, r, w, e, timeout);
4372}
4373
4374#define rb_fd_no_init(fds) ((void)((fds)->fdset = 0), (void)((fds)->maxfd = 0))
4375
4376#undef FD_ZERO
4377#undef FD_SET
4378#undef FD_CLR
4379#undef FD_ISSET
4380
4381#define FD_ZERO(f) rb_fd_zero(f)
4382#define FD_SET(i, f) rb_fd_set((i), (f))
4383#define FD_CLR(i, f) rb_fd_clr((i), (f))
4384#define FD_ISSET(i, f) rb_fd_isset((i), (f))
4385
4386#elif defined(_WIN32)
4387
4388void
4390{
4391 set->capa = FD_SETSIZE;
4392 set->fdset = ALLOC(fd_set);
4393 FD_ZERO(set->fdset);
4394}
4395
4396void
4397rb_fd_init_copy(rb_fdset_t *dst, rb_fdset_t *src)
4398{
4399 rb_fd_init(dst);
4400 rb_fd_dup(dst, src);
4401}
4402
4403static inline size_t
4404fdset_memsize(int capa)
4405{
4406 if (capa == FD_SETSIZE) {
4407 return sizeof(fd_set);
4408 }
4409 return sizeof(unsigned int) + (capa * sizeof(SOCKET));
4410}
4411
4412void
4414{
4415 ruby_sized_xfree(set->fdset, fdset_memsize(set->capa));
4416 set->fdset = NULL;
4417 set->capa = 0;
4418}
4419
4420void
4421rb_fd_set(int fd, rb_fdset_t *set)
4422{
4423 unsigned int i;
4424 SOCKET s = rb_w32_get_osfhandle(fd);
4425
4426 for (i = 0; i < set->fdset->fd_count; i++) {
4427 if (set->fdset->fd_array[i] == s) {
4428 return;
4429 }
4430 }
4431 if (set->fdset->fd_count >= (unsigned)set->capa) {
4432 set->capa = (set->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
4433 set->fdset =
4434 rb_xrealloc_mul_add(
4435 set->fdset, set->capa, sizeof(SOCKET), sizeof(unsigned int));
4436 }
4437 set->fdset->fd_array[set->fdset->fd_count++] = s;
4438}
4439
4440#undef FD_ZERO
4441#undef FD_SET
4442#undef FD_CLR
4443#undef FD_ISSET
4444
4445#define FD_ZERO(f) rb_fd_zero(f)
4446#define FD_SET(i, f) rb_fd_set((i), (f))
4447#define FD_CLR(i, f) rb_fd_clr((i), (f))
4448#define FD_ISSET(i, f) rb_fd_isset((i), (f))
4449
4450#define rb_fd_no_init(fds) (void)((fds)->fdset = 0)
4451
4452#endif
4453
4454#ifndef rb_fd_no_init
4455#define rb_fd_no_init(fds) (void)(fds)
4456#endif
4457
4458static int
4459wait_retryable(volatile int *result, int errnum, rb_hrtime_t *rel, rb_hrtime_t end)
4460{
4461 int r = *result;
4462 if (r < 0) {
4463 switch (errnum) {
4464 case EINTR:
4465#ifdef ERESTART
4466 case ERESTART:
4467#endif
4468 *result = 0;
4469 if (rel && hrtime_update_expire(rel, end)) {
4470 *rel = 0;
4471 }
4472 return TRUE;
4473 }
4474 return FALSE;
4475 }
4476 else if (r == 0) {
4477 /* check for spurious wakeup */
4478 if (rel) {
4479 return !hrtime_update_expire(rel, end);
4480 }
4481 return TRUE;
4482 }
4483 return FALSE;
4484}
4486struct select_set {
4487 int max;
4488 rb_thread_t *th;
4489 rb_fdset_t *rset;
4490 rb_fdset_t *wset;
4491 rb_fdset_t *eset;
4492 rb_fdset_t orig_rset;
4493 rb_fdset_t orig_wset;
4494 rb_fdset_t orig_eset;
4495 struct timeval *timeout;
4496};
4497
4498static VALUE
4499select_set_free(VALUE p)
4500{
4501 struct select_set *set = (struct select_set *)p;
4502
4503 rb_fd_term(&set->orig_rset);
4504 rb_fd_term(&set->orig_wset);
4505 rb_fd_term(&set->orig_eset);
4506
4507 return Qfalse;
4508}
4509
4510static VALUE
4511do_select(VALUE p)
4512{
4513 struct select_set *set = (struct select_set *)p;
4514 volatile int result = 0;
4515 int lerrno;
4516 rb_hrtime_t *to, rel, end = 0;
4517
4518 timeout_prepare(&to, &rel, &end, set->timeout);
4519 volatile rb_hrtime_t endtime = end;
4520#define restore_fdset(dst, src) \
4521 ((dst) ? rb_fd_dup(dst, src) : (void)0)
4522#define do_select_update() \
4523 (restore_fdset(set->rset, &set->orig_rset), \
4524 restore_fdset(set->wset, &set->orig_wset), \
4525 restore_fdset(set->eset, &set->orig_eset), \
4526 TRUE)
4527
4528 do {
4529 lerrno = 0;
4530
4531 BLOCKING_REGION(set->th, {
4532 struct timeval tv;
4533
4534 if (!RUBY_VM_INTERRUPTED(set->th->ec)) {
4535 result = native_fd_select(set->max,
4536 set->rset, set->wset, set->eset,
4537 rb_hrtime2timeval(&tv, to), set->th);
4538 if (result < 0) lerrno = errno;
4539 }
4540 }, ubf_select, set->th, TRUE);
4541
4542 RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec); /* may raise */
4543 } while (wait_retryable(&result, lerrno, to, endtime) && do_select_update());
4544
4545 RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec);
4546
4547 if (result < 0) {
4548 errno = lerrno;
4549 }
4550
4551 return (VALUE)result;
4552}
4553
4555rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t * except,
4556 struct timeval *timeout)
4557{
4558 struct select_set set;
4559
4560 set.th = GET_THREAD();
4561 RUBY_VM_CHECK_INTS_BLOCKING(set.th->ec);
4562 set.max = max;
4563 set.rset = read;
4564 set.wset = write;
4565 set.eset = except;
4566 set.timeout = timeout;
4567
4568 if (!set.rset && !set.wset && !set.eset) {
4569 if (!timeout) {
4571 return 0;
4572 }
4573 rb_thread_wait_for(*timeout);
4574 return 0;
4575 }
4576
4577#define fd_init_copy(f) do { \
4578 if (set.f) { \
4579 rb_fd_resize(set.max - 1, set.f); \
4580 if (&set.orig_##f != set.f) { /* sigwait_fd */ \
4581 rb_fd_init_copy(&set.orig_##f, set.f); \
4582 } \
4583 } \
4584 else { \
4585 rb_fd_no_init(&set.orig_##f); \
4586 } \
4587 } while (0)
4588 fd_init_copy(rset);
4589 fd_init_copy(wset);
4590 fd_init_copy(eset);
4591#undef fd_init_copy
4592
4593 return (int)rb_ensure(do_select, (VALUE)&set, select_set_free, (VALUE)&set);
4594}
4595
4596#ifdef USE_POLL
4597
4598/* The same with linux kernel. TODO: make platform independent definition. */
4599#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
4600#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
4601#define POLLEX_SET (POLLPRI)
4602
4603#ifndef POLLERR_SET /* defined for FreeBSD for now */
4604# define POLLERR_SET (0)
4605#endif
4606
4607static int
4608wait_for_single_fd_blocking_region(rb_thread_t *th, struct pollfd *fds, nfds_t nfds,
4609 rb_hrtime_t *const to, volatile int *lerrno)
4610{
4611 struct timespec ts;
4612 volatile int result = 0;
4613
4614 *lerrno = 0;
4615 BLOCKING_REGION(th, {
4616 if (!RUBY_VM_INTERRUPTED(th->ec)) {
4617 result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, to), 0);
4618 if (result < 0) *lerrno = errno;
4619 }
4620 }, ubf_select, th, TRUE);
4621 return result;
4622}
4623
4624/*
4625 * returns a mask of events
4626 */
4627static int
4628thread_io_wait(rb_thread_t *th, struct rb_io *io, int fd, int events, struct timeval *timeout)
4629{
4630 struct pollfd fds[1] = {{
4631 .fd = fd,
4632 .events = (short)events,
4633 .revents = 0,
4634 }};
4635 volatile int result = 0;
4636 nfds_t nfds;
4637 struct rb_io_blocking_operation blocking_operation;
4638 enum ruby_tag_type state;
4639 volatile int lerrno;
4640
4641 RUBY_ASSERT(th);
4642 rb_execution_context_t *ec = th->ec;
4643
4644 if (io) {
4645 blocking_operation.ec = ec;
4646 rb_io_blocking_operation_enter(io, &blocking_operation);
4647 }
4648
4649 if (timeout == NULL && thread_io_wait_events(th, fd, events, NULL)) {
4650 // fd is readable
4651 state = 0;
4652 fds[0].revents = events;
4653 errno = 0;
4654 }
4655 else {
4656 EC_PUSH_TAG(ec);
4657 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
4658 rb_hrtime_t *to, rel, end = 0;
4659 RUBY_VM_CHECK_INTS_BLOCKING(ec);
4660 timeout_prepare(&to, &rel, &end, timeout);
4661 do {
4662 nfds = numberof(fds);
4663 result = wait_for_single_fd_blocking_region(th, fds, nfds, to, &lerrno);
4664
4665 RUBY_VM_CHECK_INTS_BLOCKING(ec);
4666 } while (wait_retryable(&result, lerrno, to, end));
4667
4668 RUBY_VM_CHECK_INTS_BLOCKING(ec);
4669 }
4670
4671 EC_POP_TAG();
4672 }
4673
4674 if (io) {
4675 rb_io_blocking_operation_exit(io, &blocking_operation);
4676 }
4677
4678 if (state) {
4679 EC_JUMP_TAG(ec, state);
4680 }
4681
4682 if (result < 0) {
4683 errno = lerrno;
4684 return -1;
4685 }
4686
4687 if (fds[0].revents & POLLNVAL) {
4688 errno = EBADF;
4689 return -1;
4690 }
4691
4692 /*
4693 * POLLIN, POLLOUT have a different meanings from select(2)'s read/write bit.
4694 * Therefore we need to fix it up.
4695 */
4696 result = 0;
4697 if (fds[0].revents & POLLIN_SET)
4698 result |= RB_WAITFD_IN;
4699 if (fds[0].revents & POLLOUT_SET)
4700 result |= RB_WAITFD_OUT;
4701 if (fds[0].revents & POLLEX_SET)
4702 result |= RB_WAITFD_PRI;
4703
4704 /* all requested events are ready if there is an error */
4705 if (fds[0].revents & POLLERR_SET)
4706 result |= events;
4707
4708 return result;
4709}
4710#else /* ! USE_POLL - implement rb_io_poll_fd() using select() */
4711struct select_args {
4712 struct rb_io *io;
4713 struct rb_io_blocking_operation *blocking_operation;
4714
4715 union {
4716 int fd;
4717 int error;
4718 } as;
4719 rb_fdset_t *read;
4720 rb_fdset_t *write;
4721 rb_fdset_t *except;
4722 struct timeval *tv;
4723};
4724
4725static VALUE
4726select_single(VALUE ptr)
4727{
4728 struct select_args *args = (struct select_args *)ptr;
4729 int r;
4730
4731 r = rb_thread_fd_select(args->as.fd + 1,
4732 args->read, args->write, args->except, args->tv);
4733 if (r == -1)
4734 args->as.error = errno;
4735 if (r > 0) {
4736 r = 0;
4737 if (args->read && rb_fd_isset(args->as.fd, args->read))
4738 r |= RB_WAITFD_IN;
4739 if (args->write && rb_fd_isset(args->as.fd, args->write))
4740 r |= RB_WAITFD_OUT;
4741 if (args->except && rb_fd_isset(args->as.fd, args->except))
4742 r |= RB_WAITFD_PRI;
4743 }
4744 return (VALUE)r;
4745}
4746
4747static VALUE
4748select_single_cleanup(VALUE ptr)
4749{
4750 struct select_args *args = (struct select_args *)ptr;
4751
4752 if (args->blocking_operation) {
4753 rb_io_blocking_operation_exit(args->io, args->blocking_operation);
4754 }
4755
4756 if (args->read) rb_fd_term(args->read);
4757 if (args->write) rb_fd_term(args->write);
4758 if (args->except) rb_fd_term(args->except);
4759
4760 return (VALUE)-1;
4761}
4762
4763static rb_fdset_t *
4764init_set_fd(int fd, rb_fdset_t *fds)
4765{
4766 if (fd < 0) {
4767 return 0;
4768 }
4769 rb_fd_init(fds);
4770 rb_fd_set(fd, fds);
4771
4772 return fds;
4773}
4774
4775static int
4776thread_io_wait(rb_thread_t *th, struct rb_io *io, int fd, int events, struct timeval *timeout)
4777{
4778 rb_fdset_t rfds, wfds, efds;
4779 struct select_args args;
4780 VALUE ptr = (VALUE)&args;
4781
4782 struct rb_io_blocking_operation blocking_operation;
4783 if (io) {
4784 args.io = io;
4785 blocking_operation.ec = th->ec;
4786 rb_io_blocking_operation_enter(io, &blocking_operation);
4787 args.blocking_operation = &blocking_operation;
4788 }
4789 else {
4790 args.io = NULL;
4791 blocking_operation.ec = NULL;
4792 args.blocking_operation = NULL;
4793 }
4794
4795 args.as.fd = fd;
4796 args.read = (events & RB_WAITFD_IN) ? init_set_fd(fd, &rfds) : NULL;
4797 args.write = (events & RB_WAITFD_OUT) ? init_set_fd(fd, &wfds) : NULL;
4798 args.except = (events & RB_WAITFD_PRI) ? init_set_fd(fd, &efds) : NULL;
4799 args.tv = timeout;
4800
4801 int result = (int)rb_ensure(select_single, ptr, select_single_cleanup, ptr);
4802 if (result == -1)
4803 errno = args.as.error;
4804
4805 return result;
4806}
4807#endif /* ! USE_POLL */
4808
4809int
4810rb_thread_wait_for_single_fd(rb_thread_t *th, int fd, int events, struct timeval *timeout)
4811{
4812 return thread_io_wait(th, NULL, fd, events, timeout);
4813}
4814
4815int
4816rb_thread_io_wait(rb_thread_t *th, struct rb_io *io, int events, struct timeval * timeout)
4817{
4818 return thread_io_wait(th, io, io->fd, events, timeout);
4819}
4820
4821/*
4822 * for GC
4823 */
4824
4825#ifdef USE_CONSERVATIVE_STACK_END
4826void
4827rb_gc_set_stack_end(VALUE **stack_end_p)
4828{
4829 VALUE stack_end;
4830COMPILER_WARNING_PUSH
4831#if RBIMPL_COMPILER_IS(GCC)
4832COMPILER_WARNING_IGNORED(-Wdangling-pointer);
4833#endif
4834 *stack_end_p = &stack_end;
4835COMPILER_WARNING_POP
4836}
4837#endif
4838
4839/*
4840 *
4841 */
4842
4843void
4844rb_threadptr_check_signal(rb_thread_t *mth)
4845{
4846 /* mth must be main_thread */
4847 if (rb_signal_buff_size() > 0) {
4848 /* wakeup main thread */
4849 threadptr_trap_interrupt(mth);
4850 }
4851}
4852
4853static void
4854async_bug_fd(const char *mesg, int errno_arg, int fd)
4855{
4856 char buff[64];
4857 size_t n = strlcpy(buff, mesg, sizeof(buff));
4858 if (n < sizeof(buff)-3) {
4859 ruby_snprintf(buff+n, sizeof(buff)-n, "(%d)", fd);
4860 }
4861 rb_async_bug_errno(buff, errno_arg);
4862}
4863
4864/* VM-dependent API is not available for this function */
4865static int
4866consume_communication_pipe(int fd)
4867{
4868#if USE_EVENTFD
4869 uint64_t buff[1];
4870#else
4871 /* buffer can be shared because no one refers to them. */
4872 static char buff[1024];
4873#endif
4874 ssize_t result;
4875 int ret = FALSE; /* for rb_sigwait_sleep */
4876
4877 while (1) {
4878 result = read(fd, buff, sizeof(buff));
4879#if USE_EVENTFD
4880 RUBY_DEBUG_LOG("resultf:%d buff:%lu", (int)result, (unsigned long)buff[0]);
4881#else
4882 RUBY_DEBUG_LOG("result:%d", (int)result);
4883#endif
4884 if (result > 0) {
4885 ret = TRUE;
4886 if (USE_EVENTFD || result < (ssize_t)sizeof(buff)) {
4887 return ret;
4888 }
4889 }
4890 else if (result == 0) {
4891 return ret;
4892 }
4893 else if (result < 0) {
4894 int e = errno;
4895 switch (e) {
4896 case EINTR:
4897 continue; /* retry */
4898 case EAGAIN:
4899#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
4900 case EWOULDBLOCK:
4901#endif
4902 return ret;
4903 default:
4904 async_bug_fd("consume_communication_pipe: read", e, fd);
4905 }
4906 }
4907 }
4908}
4909
4910void
4911rb_thread_stop_timer_thread(void)
4912{
4913 if (TIMER_THREAD_CREATED_P() && native_stop_timer_thread()) {
4914 native_reset_timer_thread();
4915 }
4916}
4917
4918void
4919rb_thread_reset_timer_thread(void)
4920{
4921 native_reset_timer_thread();
4922}
4923
4924void
4925rb_thread_start_timer_thread(void)
4926{
4927 system_working = 1;
4928 rb_thread_create_timer_thread();
4929}
4930
4931static int
4932clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
4933{
4934 int i;
4935 VALUE coverage = (VALUE)val;
4936 VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
4937 VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
4938
4939 if (lines) {
4940 if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
4941 rb_ary_clear(lines);
4942 }
4943 else {
4944 int i;
4945 for (i = 0; i < RARRAY_LEN(lines); i++) {
4946 if (RARRAY_AREF(lines, i) != Qnil)
4947 RARRAY_ASET(lines, i, INT2FIX(0));
4948 }
4949 }
4950 }
4951 if (branches) {
4952 VALUE counters = RARRAY_AREF(branches, 1);
4953 for (i = 0; i < RARRAY_LEN(counters); i++) {
4954 RARRAY_ASET(counters, i, INT2FIX(0));
4955 }
4956 }
4957
4958 return ST_CONTINUE;
4959}
4960
4961void
4962rb_clear_coverages(void)
4963{
4964 VALUE coverages = rb_get_coverages();
4965 if (RTEST(coverages)) {
4966 rb_hash_foreach(coverages, clear_coverage_i, 0);
4967 }
4968}
4969
4970#if defined(HAVE_WORKING_FORK)
4971
4972static void
4973rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const rb_thread_t *))
4974{
4975 rb_thread_t *i = 0;
4976 rb_vm_t *vm = th->vm;
4977 rb_ractor_t *r = th->ractor;
4978 vm->ractor.main_ractor = r;
4979 vm->ractor.main_thread = th;
4980 r->threads.main = th;
4981 r->status_ = ractor_created;
4982
4983 thread_sched_atfork(TH_SCHED(th));
4984 ubf_list_atfork();
4985 rb_signal_atfork();
4986
4987 // OK. Only this thread accesses:
4988 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
4989 if (r != vm->ractor.main_ractor) {
4990 rb_ractor_terminate_atfork(vm, r);
4991 }
4992 ccan_list_for_each(&r->threads.set, i, lt_node) {
4993 atfork(i, th);
4994 }
4995 }
4996 rb_vm_living_threads_init(vm);
4997
4998 rb_ractor_atfork(vm, th);
4999 rb_vm_postponed_job_atfork();
5000
5001 /* may be held by any thread in parent */
5002 rb_native_mutex_initialize(&th->interrupt_lock);
5003 ccan_list_head_init(&th->interrupt_exec_tasks);
5004
5005 vm->fork_gen++;
5006 rb_ractor_sleeper_threads_clear(th->ractor);
5007 rb_clear_coverages();
5008
5009 // restart timer thread (timer threads access to `vm->waitpid_lock` and so on.
5010 rb_thread_reset_timer_thread();
5011 rb_thread_start_timer_thread();
5012
5013 VM_ASSERT(vm->ractor.blocking_cnt == 0);
5014 VM_ASSERT(vm->ractor.cnt == 1);
5015}
5016
5017static void
5018terminate_atfork_i(rb_thread_t *th, const rb_thread_t *current_th)
5019{
5020 if (th != current_th) {
5021 // Clear the scheduler as it is no longer operational:
5022 th->scheduler = Qnil;
5023
5024 rb_native_mutex_initialize(&th->interrupt_lock);
5025 rb_mutex_abandon_keeping_mutexes(th);
5026 rb_mutex_abandon_locking_mutex(th);
5027 thread_cleanup_func(th, TRUE);
5028 }
5029}
5030
5031void rb_fiber_atfork(rb_thread_t *);
5032void
5033rb_thread_atfork(void)
5034{
5035 rb_thread_t *th = GET_THREAD();
5036 rb_threadptr_pending_interrupt_clear(th);
5037 rb_thread_atfork_internal(th, terminate_atfork_i);
5038 th->join_list = NULL;
5039 th->scheduler = Qnil;
5040 rb_fiber_atfork(th);
5041
5042 /* We don't want reproduce CVE-2003-0900. */
5044}
5045
5046static void
5047terminate_atfork_before_exec_i(rb_thread_t *th, const rb_thread_t *current_th)
5048{
5049 if (th != current_th) {
5050 thread_cleanup_func_before_exec(th);
5051 }
5052}
5053
5054void
5056{
5057 rb_thread_t *th = GET_THREAD();
5058 rb_thread_atfork_internal(th, terminate_atfork_before_exec_i);
5059}
5060#else
5061void
5062rb_thread_atfork(void)
5063{
5064}
5065
5066void
5068{
5069}
5070#endif
5072struct thgroup {
5073 int enclosed;
5074};
5075
5076static const rb_data_type_t thgroup_data_type = {
5077 "thgroup",
5078 {
5079 0,
5081 NULL, // No external memory to report
5082 },
5083 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
5084};
5085
5086/*
5087 * Document-class: ThreadGroup
5088 *
5089 * ThreadGroup provides a means of keeping track of a number of threads as a
5090 * group.
5091 *
5092 * A given Thread object can only belong to one ThreadGroup at a time; adding
5093 * a thread to a new group will remove it from any previous group.
5094 *
5095 * Newly created threads belong to the same group as the thread from which they
5096 * were created.
5097 */
5098
5099/*
5100 * Document-const: Default
5101 *
5102 * The default ThreadGroup created when Ruby starts; all Threads belong to it
5103 * by default.
5104 */
5105static VALUE
5106thgroup_s_alloc(VALUE klass)
5107{
5108 VALUE group;
5109 struct thgroup *data;
5110
5111 group = TypedData_Make_Struct(klass, struct thgroup, &thgroup_data_type, data);
5112 data->enclosed = 0;
5113
5114 return group;
5115}
5116
5117/*
5118 * call-seq:
5119 * thgrp.list -> array
5120 *
5121 * Returns an array of all existing Thread objects that belong to this group.
5122 *
5123 * ThreadGroup::Default.list #=> [#<Thread:0x401bdf4c run>]
5124 */
5125
5126static VALUE
5127thgroup_list(VALUE group)
5128{
5129 VALUE ary = rb_ary_new();
5130 rb_thread_t *th = 0;
5131 rb_ractor_t *r = GET_RACTOR();
5132
5133 ccan_list_for_each(&r->threads.set, th, lt_node) {
5134 if (th->thgroup == group) {
5135 rb_ary_push(ary, th->self);
5136 }
5137 }
5138 return ary;
5139}
5140
5141
5142/*
5143 * call-seq:
5144 * thgrp.enclose -> thgrp
5145 *
5146 * Prevents threads from being added to or removed from the receiving
5147 * ThreadGroup.
5148 *
5149 * New threads can still be started in an enclosed ThreadGroup.
5150 *
5151 * ThreadGroup::Default.enclose #=> #<ThreadGroup:0x4029d914>
5152 * thr = Thread.new { Thread.stop } #=> #<Thread:0x402a7210 sleep>
5153 * tg = ThreadGroup.new #=> #<ThreadGroup:0x402752d4>
5154 * tg.add thr
5155 * #=> ThreadError: can't move from the enclosed thread group
5156 */
5157
5158static VALUE
5159thgroup_enclose(VALUE group)
5160{
5161 struct thgroup *data;
5162
5163 TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
5164 data->enclosed = 1;
5165
5166 return group;
5167}
5168
5169
5170/*
5171 * call-seq:
5172 * thgrp.enclosed? -> true or false
5173 *
5174 * Returns +true+ if the +thgrp+ is enclosed. See also ThreadGroup#enclose.
5175 */
5176
5177static VALUE
5178thgroup_enclosed_p(VALUE group)
5179{
5180 struct thgroup *data;
5181
5182 TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
5183 return RBOOL(data->enclosed);
5184}
5185
5186
5187/*
5188 * call-seq:
5189 * thgrp.add(thread) -> thgrp
5190 *
5191 * Adds the given +thread+ to this group, removing it from any other
5192 * group to which it may have previously been a member.
5193 *
5194 * puts "Initial group is #{ThreadGroup::Default.list}"
5195 * tg = ThreadGroup.new
5196 * t1 = Thread.new { sleep }
5197 * t2 = Thread.new { sleep }
5198 * puts "t1 is #{t1}"
5199 * puts "t2 is #{t2}"
5200 * tg.add(t1)
5201 * puts "Initial group now #{ThreadGroup::Default.list}"
5202 * puts "tg group now #{tg.list}"
5203 *
5204 * This will produce:
5205 *
5206 * Initial group is #<Thread:0x401bdf4c>
5207 * t1 is #<Thread:0x401b3c90>
5208 * t2 is #<Thread:0x401b3c18>
5209 * Initial group now #<Thread:0x401b3c18>#<Thread:0x401bdf4c>
5210 * tg group now #<Thread:0x401b3c90>
5211 */
5212
5213static VALUE
5214thgroup_add(VALUE group, VALUE thread)
5215{
5216 rb_thread_t *target_th = rb_thread_ptr(thread);
5217 struct thgroup *data;
5218
5219 if (OBJ_FROZEN(group)) {
5220 rb_raise(rb_eThreadError, "can't move to the frozen thread group");
5221 }
5222 TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
5223 if (data->enclosed) {
5224 rb_raise(rb_eThreadError, "can't move to the enclosed thread group");
5225 }
5226
5227 if (OBJ_FROZEN(target_th->thgroup)) {
5228 rb_raise(rb_eThreadError, "can't move from the frozen thread group");
5229 }
5230 TypedData_Get_Struct(target_th->thgroup, struct thgroup, &thgroup_data_type, data);
5231 if (data->enclosed) {
5232 rb_raise(rb_eThreadError,
5233 "can't move from the enclosed thread group");
5234 }
5235
5236 target_th->thgroup = group;
5237 return group;
5238}
5239
5240/*
5241 * Document-class: ThreadShield
5242 */
5243static void
5244thread_shield_mark(void *ptr)
5245{
5246 rb_gc_mark((VALUE)ptr);
5247}
5248
5249static const rb_data_type_t thread_shield_data_type = {
5250 "thread_shield",
5251 {thread_shield_mark, 0, 0,},
5253};
5254
5255static VALUE
5256thread_shield_alloc(VALUE klass)
5257{
5258 return TypedData_Wrap_Struct(klass, &thread_shield_data_type, (void *)mutex_alloc(0));
5259}
5260
5261#define GetThreadShieldPtr(obj) ((VALUE)rb_check_typeddata((obj), &thread_shield_data_type))
5262#define THREAD_SHIELD_WAITING_MASK (((FL_USER19-1)&~(FL_USER0-1))|FL_USER19)
5263#define THREAD_SHIELD_WAITING_SHIFT (FL_USHIFT)
5264#define THREAD_SHIELD_WAITING_MAX (THREAD_SHIELD_WAITING_MASK>>THREAD_SHIELD_WAITING_SHIFT)
5265STATIC_ASSERT(THREAD_SHIELD_WAITING_MAX, THREAD_SHIELD_WAITING_MAX <= UINT_MAX);
5266static inline unsigned int
5267rb_thread_shield_waiting(VALUE b)
5268{
5269 return ((RBASIC(b)->flags&THREAD_SHIELD_WAITING_MASK)>>THREAD_SHIELD_WAITING_SHIFT);
5270}
5271
5272static inline void
5273rb_thread_shield_waiting_inc(VALUE b)
5274{
5275 unsigned int w = rb_thread_shield_waiting(b);
5276 w++;
5277 if (w > THREAD_SHIELD_WAITING_MAX)
5278 rb_raise(rb_eRuntimeError, "waiting count overflow");
5279 RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
5280 RBASIC(b)->flags |= ((VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
5281}
5282
5283static inline void
5284rb_thread_shield_waiting_dec(VALUE b)
5285{
5286 unsigned int w = rb_thread_shield_waiting(b);
5287 if (!w) rb_raise(rb_eRuntimeError, "waiting count underflow");
5288 w--;
5289 RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
5290 RBASIC(b)->flags |= ((VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
5291}
5292
5293VALUE
5294rb_thread_shield_new(void)
5295{
5296 VALUE thread_shield = thread_shield_alloc(rb_cThreadShield);
5297 rb_mutex_lock((VALUE)DATA_PTR(thread_shield));
5298 return thread_shield;
5299}
5300
5301bool
5302rb_thread_shield_owned(VALUE self)
5303{
5304 VALUE mutex = GetThreadShieldPtr(self);
5305 if (!mutex) return false;
5306
5307 rb_mutex_t *m = mutex_ptr(mutex);
5308
5309 return m->ec_serial == rb_ec_serial(GET_EC());
5310}
5311
5312/*
5313 * Wait a thread shield.
5314 *
5315 * Returns
5316 * true: acquired the thread shield
5317 * false: the thread shield was destroyed and no other threads waiting
5318 * nil: the thread shield was destroyed but still in use
5319 */
5320VALUE
5321rb_thread_shield_wait(VALUE self)
5322{
5323 VALUE mutex = GetThreadShieldPtr(self);
5324 rb_mutex_t *m;
5325
5326 if (!mutex) return Qfalse;
5327 m = mutex_ptr(mutex);
5328 if (m->ec_serial == rb_ec_serial(GET_EC())) return Qnil;
5329 rb_thread_shield_waiting_inc(self);
5330 rb_mutex_lock(mutex);
5331 rb_thread_shield_waiting_dec(self);
5332 if (DATA_PTR(self)) return Qtrue;
5333 rb_mutex_unlock(mutex);
5334 return rb_thread_shield_waiting(self) > 0 ? Qnil : Qfalse;
5335}
5336
5337static VALUE
5338thread_shield_get_mutex(VALUE self)
5339{
5340 VALUE mutex = GetThreadShieldPtr(self);
5341 if (!mutex)
5342 rb_raise(rb_eThreadError, "destroyed thread shield - %p", (void *)self);
5343 return mutex;
5344}
5345
5346/*
5347 * Release a thread shield, and return true if it has waiting threads.
5348 */
5349VALUE
5350rb_thread_shield_release(VALUE self)
5351{
5352 VALUE mutex = thread_shield_get_mutex(self);
5353 rb_mutex_unlock(mutex);
5354 return RBOOL(rb_thread_shield_waiting(self) > 0);
5355}
5356
5357/*
5358 * Release and destroy a thread shield, and return true if it has waiting threads.
5359 */
5360VALUE
5361rb_thread_shield_destroy(VALUE self)
5362{
5363 VALUE mutex = thread_shield_get_mutex(self);
5364 DATA_PTR(self) = 0;
5365 rb_mutex_unlock(mutex);
5366 return RBOOL(rb_thread_shield_waiting(self) > 0);
5367}
5368
5369static VALUE
5370threadptr_recursive_hash(rb_thread_t *th)
5371{
5372 return th->ec->local_storage_recursive_hash;
5373}
5374
5375static void
5376threadptr_recursive_hash_set(rb_thread_t *th, VALUE hash)
5377{
5378 th->ec->local_storage_recursive_hash = hash;
5379}
5380
5382
5383/*
5384 * Returns the current "recursive list" used to detect recursion.
5385 * This list is a hash table, unique for the current thread and for
5386 * the current __callee__.
5387 */
5388
5389static VALUE
5390recursive_list_access(VALUE sym)
5391{
5392 rb_thread_t *th = GET_THREAD();
5393 VALUE hash = threadptr_recursive_hash(th);
5394 VALUE list;
5395 if (NIL_P(hash) || !RB_TYPE_P(hash, T_HASH)) {
5396 hash = rb_ident_hash_new();
5397 threadptr_recursive_hash_set(th, hash);
5398 list = Qnil;
5399 }
5400 else {
5401 list = rb_hash_aref(hash, sym);
5402 }
5403 if (NIL_P(list) || !RB_TYPE_P(list, T_HASH)) {
5404 list = rb_ident_hash_new();
5405 rb_hash_aset(hash, sym, list);
5406 }
5407 return list;
5408}
5409
5410/*
5411 * Returns true if and only if obj (or the pair <obj, paired_obj>) is already
5412 * in the recursion list.
5413 * Assumes the recursion list is valid.
5414 */
5415
5416static bool
5417recursive_check(VALUE list, VALUE obj, VALUE paired_obj_id)
5418{
5419#if SIZEOF_LONG == SIZEOF_VOIDP
5420 #define OBJ_ID_EQL(obj_id, other) ((obj_id) == (other))
5421#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
5422 #define OBJ_ID_EQL(obj_id, other) (RB_BIGNUM_TYPE_P((obj_id)) ? \
5423 rb_big_eql((obj_id), (other)) : ((obj_id) == (other)))
5424#endif
5425
5426 VALUE pair_list = rb_hash_lookup2(list, obj, Qundef);
5427 if (UNDEF_P(pair_list))
5428 return false;
5429 if (paired_obj_id) {
5430 if (!RB_TYPE_P(pair_list, T_HASH)) {
5431 if (!OBJ_ID_EQL(paired_obj_id, pair_list))
5432 return false;
5433 }
5434 else {
5435 if (NIL_P(rb_hash_lookup(pair_list, paired_obj_id)))
5436 return false;
5437 }
5438 }
5439 return true;
5440}
5441
5442/*
5443 * Pushes obj (or the pair <obj, paired_obj>) in the recursion list.
5444 * For a single obj, it sets list[obj] to Qtrue.
5445 * For a pair, it sets list[obj] to paired_obj_id if possible,
5446 * otherwise list[obj] becomes a hash like:
5447 * {paired_obj_id_1 => true, paired_obj_id_2 => true, ... }
5448 * Assumes the recursion list is valid.
5449 */
5450
5451static void
5452recursive_push(VALUE list, VALUE obj, VALUE paired_obj)
5453{
5454 VALUE pair_list;
5455
5456 if (!paired_obj) {
5457 rb_hash_aset(list, obj, Qtrue);
5458 }
5459 else if (UNDEF_P(pair_list = rb_hash_lookup2(list, obj, Qundef))) {
5460 rb_hash_aset(list, obj, paired_obj);
5461 }
5462 else {
5463 if (!RB_TYPE_P(pair_list, T_HASH)){
5464 VALUE other_paired_obj = pair_list;
5465 pair_list = rb_hash_new();
5466 rb_hash_aset(pair_list, other_paired_obj, Qtrue);
5467 rb_hash_aset(list, obj, pair_list);
5468 }
5469 rb_hash_aset(pair_list, paired_obj, Qtrue);
5470 }
5471}
5472
5473/*
5474 * Pops obj (or the pair <obj, paired_obj>) from the recursion list.
5475 * For a pair, if list[obj] is a hash, then paired_obj_id is
5476 * removed from the hash and no attempt is made to simplify
5477 * list[obj] from {only_one_paired_id => true} to only_one_paired_id
5478 * Assumes the recursion list is valid.
5479 */
5480
5481static int
5482recursive_pop(VALUE list, VALUE obj, VALUE paired_obj)
5483{
5484 if (paired_obj) {
5485 VALUE pair_list = rb_hash_lookup2(list, obj, Qundef);
5486 if (UNDEF_P(pair_list)) {
5487 return 0;
5488 }
5489 if (RB_TYPE_P(pair_list, T_HASH)) {
5490 rb_hash_delete_entry(pair_list, paired_obj);
5491 if (!RHASH_EMPTY_P(pair_list)) {
5492 return 1; /* keep hash until is empty */
5493 }
5494 }
5495 }
5496 rb_hash_delete_entry(list, obj);
5497 return 1;
5498}
5500struct exec_recursive_params {
5501 VALUE (*func) (VALUE, VALUE, int);
5502 VALUE list;
5503 VALUE obj;
5504 VALUE pairid;
5505 VALUE arg;
5506};
5507
5508static VALUE
5509exec_recursive_i(RB_BLOCK_CALL_FUNC_ARGLIST(tag, data))
5510{
5511 struct exec_recursive_params *p = (void *)data;
5512 return (*p->func)(p->obj, p->arg, FALSE);
5513}
5514
5515/*
5516 * Calls func(obj, arg, recursive), where recursive is non-zero if the
5517 * current method is called recursively on obj, or on the pair <obj, pairid>
5518 * If outer is 0, then the innermost func will be called with recursive set
5519 * to true, otherwise the outermost func will be called. In the latter case,
5520 * all inner func are short-circuited by throw.
5521 * Implementation details: the value thrown is the recursive list which is
5522 * proper to the current method and unlikely to be caught anywhere else.
5523 * list[recursive_key] is used as a flag for the outermost call.
5524 */
5525
5526static VALUE
5527exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE pairid, VALUE arg, int outer, ID mid)
5528{
5529 VALUE result = Qundef;
5530 const VALUE sym = mid ? ID2SYM(mid) : ID2SYM(idNULL);
5531 struct exec_recursive_params p;
5532 int outermost;
5533 p.list = recursive_list_access(sym);
5534 p.obj = obj;
5535 p.pairid = pairid;
5536 p.arg = arg;
5537 outermost = outer && !recursive_check(p.list, ID2SYM(recursive_key), 0);
5538
5539 if (recursive_check(p.list, p.obj, pairid)) {
5540 if (outer && !outermost) {
5541 rb_throw_obj(p.list, p.list);
5542 }
5543 return (*func)(obj, arg, TRUE);
5544 }
5545 else {
5546 enum ruby_tag_type state;
5547
5548 p.func = func;
5549
5550 if (outermost) {
5551 recursive_push(p.list, ID2SYM(recursive_key), 0);
5552 recursive_push(p.list, p.obj, p.pairid);
5553 result = rb_catch_protect(p.list, exec_recursive_i, (VALUE)&p, &state);
5554 if (!recursive_pop(p.list, p.obj, p.pairid)) goto invalid;
5555 if (!recursive_pop(p.list, ID2SYM(recursive_key), 0)) goto invalid;
5556 if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
5557 if (result == p.list) {
5558 result = (*func)(obj, arg, TRUE);
5559 }
5560 }
5561 else {
5562 volatile VALUE ret = Qundef;
5563 recursive_push(p.list, p.obj, p.pairid);
5564 EC_PUSH_TAG(GET_EC());
5565 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
5566 ret = (*func)(obj, arg, FALSE);
5567 }
5568 EC_POP_TAG();
5569 if (!recursive_pop(p.list, p.obj, p.pairid)) {
5570 goto invalid;
5571 }
5572 if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
5573 result = ret;
5574 }
5575 }
5576 *(volatile struct exec_recursive_params *)&p;
5577 return result;
5578
5579 invalid:
5580 rb_raise(rb_eTypeError, "invalid inspect_tbl pair_list "
5581 "for %+"PRIsVALUE" in %+"PRIsVALUE,
5582 sym, rb_thread_current());
5584}
5585
5586/*
5587 * Calls func(obj, arg, recursive), where recursive is non-zero if the
5588 * current method is called recursively on obj
5589 */
5590
5591VALUE
5592rb_exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
5593{
5594 return exec_recursive(func, obj, 0, arg, 0, rb_frame_last_func());
5595}
5596
5597/*
5598 * Calls func(obj, arg, recursive), where recursive is non-zero if the
5599 * current method is called recursively on the ordered pair <obj, paired_obj>
5600 */
5601
5602VALUE
5603rb_exec_recursive_paired(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
5604{
5605 return exec_recursive(func, obj, rb_memory_id(paired_obj), arg, 0, rb_frame_last_func());
5606}
5607
5608/*
5609 * If recursion is detected on the current method and obj, the outermost
5610 * func will be called with (obj, arg, true). All inner func will be
5611 * short-circuited using throw.
5612 */
5613
5614VALUE
5615rb_exec_recursive_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
5616{
5617 return exec_recursive(func, obj, 0, arg, 1, rb_frame_last_func());
5618}
5619
5620VALUE
5621rb_exec_recursive_outer_mid(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg, ID mid)
5622{
5623 return exec_recursive(func, obj, 0, arg, 1, mid);
5624}
5625
5626/*
5627 * If recursion is detected on the current method, obj and paired_obj,
5628 * the outermost func will be called with (obj, arg, true). All inner
5629 * func will be short-circuited using throw.
5630 */
5631
5632VALUE
5633rb_exec_recursive_paired_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
5634{
5635 return exec_recursive(func, obj, rb_memory_id(paired_obj), arg, 1, rb_frame_last_func());
5636}
5637
5638/*
5639 * call-seq:
5640 * thread.backtrace -> array or nil
5641 *
5642 * Returns the current backtrace of the target thread.
5643 *
5644 */
5645
5646static VALUE
5647rb_thread_backtrace_m(int argc, VALUE *argv, VALUE thval)
5648{
5649 return rb_vm_thread_backtrace(argc, argv, thval);
5650}
5651
5652/* call-seq:
5653 * thread.backtrace_locations(*args) -> array or nil
5654 *
5655 * Returns the execution stack for the target thread---an array containing
5656 * backtrace location objects.
5657 *
5658 * See Thread::Backtrace::Location for more information.
5659 *
5660 * This method behaves similarly to Kernel#caller_locations except it applies
5661 * to a specific thread.
5662 */
5663static VALUE
5664rb_thread_backtrace_locations_m(int argc, VALUE *argv, VALUE thval)
5665{
5666 return rb_vm_thread_backtrace_locations(argc, argv, thval);
5667}
5668
5669void
5670Init_Thread_Mutex(void)
5671{
5672 rb_thread_t *th = GET_THREAD();
5673
5674 rb_native_mutex_initialize(&th->vm->workqueue_lock);
5675 rb_native_mutex_initialize(&th->interrupt_lock);
5676}
5677
5678/*
5679 * Document-class: ThreadError
5680 *
5681 * Raised when an invalid operation is attempted on a thread.
5682 *
5683 * For example, when no other thread has been started:
5684 *
5685 * Thread.stop
5686 *
5687 * This will raises the following exception:
5688 *
5689 * ThreadError: stopping only thread
5690 * note: use sleep to stop forever
5691 */
5692
5693void
5694Init_Thread(void)
5695{
5696 rb_thread_t *th = GET_THREAD();
5697
5698 sym_never = ID2SYM(rb_intern_const("never"));
5699 sym_immediate = ID2SYM(rb_intern_const("immediate"));
5700 sym_on_blocking = ID2SYM(rb_intern_const("on_blocking"));
5701
5702 rb_define_singleton_method(rb_cThread, "new", thread_s_new, -1);
5703 rb_define_singleton_method(rb_cThread, "start", thread_start, -2);
5704 rb_define_singleton_method(rb_cThread, "fork", thread_start, -2);
5705 rb_define_singleton_method(rb_cThread, "main", rb_thread_s_main, 0);
5706 rb_define_singleton_method(rb_cThread, "current", thread_s_current, 0);
5707 rb_define_singleton_method(rb_cThread, "stop", thread_stop, 0);
5708 rb_define_singleton_method(rb_cThread, "kill", rb_thread_s_kill, 1);
5709 rb_define_singleton_method(rb_cThread, "exit", rb_thread_exit, 0);
5710 rb_define_singleton_method(rb_cThread, "pass", thread_s_pass, 0);
5711 rb_define_singleton_method(rb_cThread, "list", thread_list, 0);
5712 rb_define_singleton_method(rb_cThread, "abort_on_exception", rb_thread_s_abort_exc, 0);
5713 rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1);
5714 rb_define_singleton_method(rb_cThread, "report_on_exception", rb_thread_s_report_exc, 0);
5715 rb_define_singleton_method(rb_cThread, "report_on_exception=", rb_thread_s_report_exc_set, 1);
5716 rb_define_singleton_method(rb_cThread, "ignore_deadlock", rb_thread_s_ignore_deadlock, 0);
5717 rb_define_singleton_method(rb_cThread, "ignore_deadlock=", rb_thread_s_ignore_deadlock_set, 1);
5718 rb_define_singleton_method(rb_cThread, "handle_interrupt", rb_thread_s_handle_interrupt, 1);
5719 rb_define_singleton_method(rb_cThread, "pending_interrupt?", rb_thread_s_pending_interrupt_p, -1);
5720 rb_define_method(rb_cThread, "pending_interrupt?", rb_thread_pending_interrupt_p, -1);
5721
5722 rb_define_method(rb_cThread, "initialize", thread_initialize, -2);
5723 rb_define_method(rb_cThread, "raise", thread_raise_m, -1);
5724 rb_define_method(rb_cThread, "join", thread_join_m, -1);
5725 rb_define_method(rb_cThread, "value", thread_value, 0);
5726 rb_define_method(rb_cThread, "kill", rb_thread_kill, 0);
5727 rb_define_method(rb_cThread, "terminate", rb_thread_kill, 0);
5728 rb_define_method(rb_cThread, "exit", rb_thread_kill, 0);
5729 rb_define_method(rb_cThread, "run", rb_thread_run, 0);
5730 rb_define_method(rb_cThread, "wakeup", rb_thread_wakeup, 0);
5731 rb_define_method(rb_cThread, "[]", rb_thread_aref, 1);
5732 rb_define_method(rb_cThread, "[]=", rb_thread_aset, 2);
5733 rb_define_method(rb_cThread, "fetch", rb_thread_fetch, -1);
5734 rb_define_method(rb_cThread, "key?", rb_thread_key_p, 1);
5735 rb_define_method(rb_cThread, "keys", rb_thread_keys, 0);
5736 rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
5737 rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
5738 rb_define_method(rb_cThread, "status", rb_thread_status, 0);
5739 rb_define_method(rb_cThread, "thread_variable_get", rb_thread_variable_get, 1);
5740 rb_define_method(rb_cThread, "thread_variable_set", rb_thread_variable_set, 2);
5741 rb_define_method(rb_cThread, "thread_variables", rb_thread_variables, 0);
5742 rb_define_method(rb_cThread, "thread_variable?", rb_thread_variable_p, 1);
5743 rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
5744 rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
5745 rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
5746 rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1);
5747 rb_define_method(rb_cThread, "report_on_exception", rb_thread_report_exc, 0);
5748 rb_define_method(rb_cThread, "report_on_exception=", rb_thread_report_exc_set, 1);
5749 rb_define_method(rb_cThread, "group", rb_thread_group, 0);
5750 rb_define_method(rb_cThread, "backtrace", rb_thread_backtrace_m, -1);
5751 rb_define_method(rb_cThread, "backtrace_locations", rb_thread_backtrace_locations_m, -1);
5752
5753 rb_define_method(rb_cThread, "name", rb_thread_getname, 0);
5754 rb_define_method(rb_cThread, "name=", rb_thread_setname, 1);
5755 rb_define_method(rb_cThread, "native_thread_id", rb_thread_native_thread_id, 0);
5756 rb_define_method(rb_cThread, "to_s", rb_thread_to_s, 0);
5757 rb_define_alias(rb_cThread, "inspect", "to_s");
5758
5759 rb_vm_register_special_exception(ruby_error_stream_closed, rb_eIOError,
5760 "stream closed in another thread");
5761
5762 cThGroup = rb_define_class("ThreadGroup", rb_cObject);
5763 rb_define_alloc_func(cThGroup, thgroup_s_alloc);
5764 rb_define_method(cThGroup, "list", thgroup_list, 0);
5765 rb_define_method(cThGroup, "enclose", thgroup_enclose, 0);
5766 rb_define_method(cThGroup, "enclosed?", thgroup_enclosed_p, 0);
5767 rb_define_method(cThGroup, "add", thgroup_add, 1);
5768
5769 const char * ptr = getenv("RUBY_THREAD_TIMESLICE");
5770
5771 if (ptr) {
5772 long quantum = strtol(ptr, NULL, 0);
5773 if (quantum > 0 && !(SIZEOF_LONG > 4 && quantum > UINT32_MAX)) {
5774 thread_default_quantum_ms = (uint32_t)quantum;
5775 }
5776 else if (0) {
5777 fprintf(stderr, "Ignored RUBY_THREAD_TIMESLICE=%s\n", ptr);
5778 }
5779 }
5780
5781 {
5782 th->thgroup = th->ractor->thgroup_default = rb_obj_alloc(cThGroup);
5783 rb_define_const(cThGroup, "Default", th->thgroup);
5784 }
5785
5787
5788 /* init thread core */
5789 {
5790 /* main thread setting */
5791 {
5792 /* acquire global vm lock */
5793#ifdef HAVE_PTHREAD_NP_H
5794 VM_ASSERT(TH_SCHED(th)->running == th);
5795#endif
5796 // thread_sched_to_running() should not be called because
5797 // it assumes blocked by thread_sched_to_waiting().
5798 // thread_sched_to_running(sched, th);
5799
5800 th->pending_interrupt_queue = rb_ary_hidden_new(0);
5801 th->pending_interrupt_queue_checked = 0;
5802 th->pending_interrupt_mask_stack = rb_ary_hidden_new(0);
5803 }
5804 }
5805
5806 rb_thread_create_timer_thread();
5807
5808 Init_thread_sync();
5809
5810 // TODO: Suppress unused function warning for now
5811 // if (0) rb_thread_sched_destroy(NULL);
5812}
5813
5816{
5817 rb_thread_t *th = ruby_thread_from_native();
5818
5819 return th != 0;
5820}
5821
5822#ifdef NON_SCALAR_THREAD_ID
5823 #define thread_id_str(th) (NULL)
5824#else
5825 #define thread_id_str(th) ((void *)(uintptr_t)(th)->nt->thread_id)
5826#endif
5827
5828static void
5829debug_deadlock_check(rb_ractor_t *r, VALUE msg)
5830{
5831 rb_thread_t *th = 0;
5832 VALUE sep = rb_str_new_cstr("\n ");
5833
5834 rb_str_catf(msg, "\n%d threads, %d sleeps current:%p main thread:%p\n",
5835 rb_ractor_living_thread_num(r), rb_ractor_sleeper_thread_num(r),
5836 (void *)GET_THREAD(), (void *)r->threads.main);
5837
5838 ccan_list_for_each(&r->threads.set, th, lt_node) {
5839 rb_str_catf(msg, "* %+"PRIsVALUE"\n rb_thread_t:%p "
5840 "native:%p int:%u",
5841 th->self, (void *)th, th->nt ? thread_id_str(th) : "N/A", th->ec->interrupt_flag);
5842
5843 if (th->locking_mutex) {
5844 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
5845 rb_str_catf(msg, " mutex:%llu cond:%"PRIuSIZE,
5846 (unsigned long long)mutex->ec_serial, rb_mutex_num_waiting(mutex));
5847 }
5848
5849 {
5850 struct rb_waiting_list *list = th->join_list;
5851 while (list) {
5852 rb_str_catf(msg, "\n depended by: tb_thread_id:%p", (void *)list->thread);
5853 list = list->next;
5854 }
5855 }
5856 rb_str_catf(msg, "\n ");
5857 rb_str_concat(msg, rb_ary_join(rb_ec_backtrace_str_ary(th->ec, RUBY_BACKTRACE_START, RUBY_ALL_BACKTRACE_LINES), sep));
5858 rb_str_catf(msg, "\n");
5859 }
5860}
5861
5862static void
5863rb_check_deadlock(rb_ractor_t *r)
5864{
5865 if (GET_THREAD()->vm->thread_ignore_deadlock) return;
5866
5867#ifdef RUBY_THREAD_PTHREAD_H
5868 if (r->threads.sched.readyq_cnt > 0) return;
5869#endif
5870
5871 int sleeper_num = rb_ractor_sleeper_thread_num(r);
5872 int ltnum = rb_ractor_living_thread_num(r);
5873
5874 if (ltnum > sleeper_num) return;
5875 if (ltnum < sleeper_num) rb_bug("sleeper must not be more than vm_living_thread_num(vm)");
5876
5877 int found = 0;
5878 rb_thread_t *th = NULL;
5879
5880 ccan_list_for_each(&r->threads.set, th, lt_node) {
5881 if (th->status != THREAD_STOPPED_FOREVER || RUBY_VM_INTERRUPTED(th->ec)) {
5882 found = 1;
5883 }
5884 else if (th->locking_mutex) {
5885 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
5886 if (mutex->ec_serial == rb_ec_serial(th->ec) || (!mutex->ec_serial && !ccan_list_empty(&mutex->waitq))) {
5887 found = 1;
5888 }
5889 }
5890 if (found)
5891 break;
5892 }
5893
5894 if (!found) {
5895 VALUE argv[2];
5896 argv[0] = rb_eFatal;
5897 argv[1] = rb_str_new2("No live threads left. Deadlock?");
5898 debug_deadlock_check(r, argv[1]);
5899 rb_ractor_sleeper_threads_dec(GET_RACTOR());
5900 rb_threadptr_raise(r->threads.main, 2, argv);
5901 }
5902}
5903
5904static void
5905update_line_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
5906{
5907 const rb_control_frame_t *cfp = GET_EC()->cfp;
5908 VALUE coverage = rb_iseq_coverage(cfp->iseq);
5909 if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
5910 VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
5911 if (lines) {
5912 long line = rb_sourceline() - 1;
5913 VM_ASSERT(line >= 0);
5914 long count;
5915 VALUE num;
5916 void rb_iseq_clear_event_flags(const rb_iseq_t *iseq, size_t pos, rb_event_flag_t reset);
5917 if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
5918 rb_iseq_clear_event_flags(cfp->iseq, cfp->pc - ISEQ_BODY(cfp->iseq)->iseq_encoded - 1, RUBY_EVENT_COVERAGE_LINE);
5919 rb_ary_push(lines, LONG2FIX(line + 1));
5920 return;
5921 }
5922 if (line >= RARRAY_LEN(lines)) { /* no longer tracked */
5923 return;
5924 }
5925 num = RARRAY_AREF(lines, line);
5926 if (!FIXNUM_P(num)) return;
5927 count = FIX2LONG(num) + 1;
5928 if (POSFIXABLE(count)) {
5929 RARRAY_ASET(lines, line, LONG2FIX(count));
5930 }
5931 }
5932 }
5933}
5934
5935static void
5936update_branch_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
5937{
5938 const rb_control_frame_t *cfp = GET_EC()->cfp;
5939 VALUE coverage = rb_iseq_coverage(cfp->iseq);
5940 if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
5941 VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
5942 if (branches) {
5943 long pc = cfp->pc - ISEQ_BODY(cfp->iseq)->iseq_encoded - 1;
5944 long idx = FIX2INT(RARRAY_AREF(ISEQ_PC2BRANCHINDEX(cfp->iseq), pc)), count;
5945 VALUE counters = RARRAY_AREF(branches, 1);
5946 VALUE num = RARRAY_AREF(counters, idx);
5947 count = FIX2LONG(num) + 1;
5948 if (POSFIXABLE(count)) {
5949 RARRAY_ASET(counters, idx, LONG2FIX(count));
5950 }
5951 }
5952 }
5953}
5954
5955const rb_method_entry_t *
5956rb_resolve_me_location(const rb_method_entry_t *me, VALUE resolved_location[5])
5957{
5958 VALUE path, beg_pos_lineno, beg_pos_column, end_pos_lineno, end_pos_column;
5959
5960 if (!me->def) return NULL; // negative cme
5961
5962 retry:
5963 switch (me->def->type) {
5964 case VM_METHOD_TYPE_ISEQ: {
5965 const rb_iseq_t *iseq = me->def->body.iseq.iseqptr;
5966 rb_iseq_location_t *loc = &ISEQ_BODY(iseq)->location;
5967 path = rb_iseq_path(iseq);
5968 beg_pos_lineno = INT2FIX(loc->code_location.beg_pos.lineno);
5969 beg_pos_column = INT2FIX(loc->code_location.beg_pos.column);
5970 end_pos_lineno = INT2FIX(loc->code_location.end_pos.lineno);
5971 end_pos_column = INT2FIX(loc->code_location.end_pos.column);
5972 break;
5973 }
5974 case VM_METHOD_TYPE_BMETHOD: {
5975 const rb_iseq_t *iseq = rb_proc_get_iseq(me->def->body.bmethod.proc, 0);
5976 if (iseq) {
5977 rb_iseq_location_t *loc;
5978 rb_iseq_check(iseq);
5979 path = rb_iseq_path(iseq);
5980 loc = &ISEQ_BODY(iseq)->location;
5981 beg_pos_lineno = INT2FIX(loc->code_location.beg_pos.lineno);
5982 beg_pos_column = INT2FIX(loc->code_location.beg_pos.column);
5983 end_pos_lineno = INT2FIX(loc->code_location.end_pos.lineno);
5984 end_pos_column = INT2FIX(loc->code_location.end_pos.column);
5985 break;
5986 }
5987 return NULL;
5988 }
5989 case VM_METHOD_TYPE_ALIAS:
5990 me = me->def->body.alias.original_me;
5991 goto retry;
5992 case VM_METHOD_TYPE_REFINED:
5993 me = me->def->body.refined.orig_me;
5994 if (!me) return NULL;
5995 goto retry;
5996 default:
5997 return NULL;
5998 }
5999
6000 /* found */
6001 if (RB_TYPE_P(path, T_ARRAY)) {
6002 path = rb_ary_entry(path, 1);
6003 if (!RB_TYPE_P(path, T_STRING)) return NULL; /* just for the case... */
6004 }
6005 if (resolved_location) {
6006 resolved_location[0] = path;
6007 resolved_location[1] = beg_pos_lineno;
6008 resolved_location[2] = beg_pos_column;
6009 resolved_location[3] = end_pos_lineno;
6010 resolved_location[4] = end_pos_column;
6011 }
6012 return me;
6013}
6014
6015static void
6016update_method_coverage(VALUE me2counter, rb_trace_arg_t *trace_arg)
6017{
6018 const rb_control_frame_t *cfp = GET_EC()->cfp;
6019 const rb_callable_method_entry_t *cme = rb_vm_frame_method_entry(cfp);
6020 const rb_method_entry_t *me = (const rb_method_entry_t *)cme;
6021 VALUE rcount;
6022 long count;
6023
6024 me = rb_resolve_me_location(me, 0);
6025 if (!me) return;
6026
6027 rcount = rb_hash_aref(me2counter, (VALUE) me);
6028 count = FIXNUM_P(rcount) ? FIX2LONG(rcount) + 1 : 1;
6029 if (POSFIXABLE(count)) {
6030 rb_hash_aset(me2counter, (VALUE) me, LONG2FIX(count));
6031 }
6032}
6033
6034VALUE
6035rb_get_coverages(void)
6036{
6037 return GET_VM()->coverages;
6038}
6039
6040int
6041rb_get_coverage_mode(void)
6042{
6043 return GET_VM()->coverage_mode;
6044}
6045
6046void
6047rb_set_coverages(VALUE coverages, int mode, VALUE me2counter)
6048{
6049 GET_VM()->coverages = coverages;
6050 GET_VM()->me2counter = me2counter;
6051 GET_VM()->coverage_mode = mode;
6052}
6053
6054void
6055rb_resume_coverages(void)
6056{
6057 int mode = GET_VM()->coverage_mode;
6058 VALUE me2counter = GET_VM()->me2counter;
6059 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);
6060 if (mode & COVERAGE_TARGET_BRANCHES) {
6061 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);
6062 }
6063 if (mode & COVERAGE_TARGET_METHODS) {
6064 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);
6065 }
6066}
6067
6068void
6069rb_suspend_coverages(void)
6070{
6071 rb_remove_event_hook((rb_event_hook_func_t) update_line_coverage);
6072 if (GET_VM()->coverage_mode & COVERAGE_TARGET_BRANCHES) {
6073 rb_remove_event_hook((rb_event_hook_func_t) update_branch_coverage);
6074 }
6075 if (GET_VM()->coverage_mode & COVERAGE_TARGET_METHODS) {
6076 rb_remove_event_hook((rb_event_hook_func_t) update_method_coverage);
6077 }
6078}
6079
6080/* Make coverage arrays empty so old covered files are no longer tracked. */
6081void
6082rb_reset_coverages(void)
6083{
6084 rb_clear_coverages();
6085 rb_iseq_remove_coverage_all();
6086 GET_VM()->coverages = Qfalse;
6087}
6088
6089VALUE
6090rb_default_coverage(int n)
6091{
6092 VALUE coverage = rb_ary_hidden_new_fill(3);
6093 VALUE lines = Qfalse, branches = Qfalse;
6094 int mode = GET_VM()->coverage_mode;
6095
6096 if (mode & COVERAGE_TARGET_LINES) {
6097 lines = n > 0 ? rb_ary_hidden_new_fill(n) : rb_ary_hidden_new(0);
6098 }
6099 RARRAY_ASET(coverage, COVERAGE_INDEX_LINES, lines);
6100
6101 if (mode & COVERAGE_TARGET_BRANCHES) {
6102 branches = rb_ary_hidden_new_fill(2);
6103 /* internal data structures for branch coverage:
6104 *
6105 * { branch base node =>
6106 * [base_type, base_first_lineno, base_first_column, base_last_lineno, base_last_column, {
6107 * branch target id =>
6108 * [target_type, target_first_lineno, target_first_column, target_last_lineno, target_last_column, target_counter_index],
6109 * ...
6110 * }],
6111 * ...
6112 * }
6113 *
6114 * Example:
6115 * { NODE_CASE =>
6116 * [1, 0, 4, 3, {
6117 * NODE_WHEN => [2, 8, 2, 9, 0],
6118 * NODE_WHEN => [3, 8, 3, 9, 1],
6119 * ...
6120 * }],
6121 * ...
6122 * }
6123 */
6124 VALUE structure = rb_hash_new();
6125 rb_obj_hide(structure);
6126 RARRAY_ASET(branches, 0, structure);
6127 /* branch execution counters */
6128 RARRAY_ASET(branches, 1, rb_ary_hidden_new(0));
6129 }
6130 RARRAY_ASET(coverage, COVERAGE_INDEX_BRANCHES, branches);
6131
6132 return coverage;
6133}
6134
6135static VALUE
6136uninterruptible_exit(VALUE v)
6137{
6138 rb_thread_t *cur_th = GET_THREAD();
6139 rb_ary_pop(cur_th->pending_interrupt_mask_stack);
6140
6141 cur_th->pending_interrupt_queue_checked = 0;
6142 if (!rb_threadptr_pending_interrupt_empty_p(cur_th)) {
6143 RUBY_VM_SET_INTERRUPT(cur_th->ec);
6144 }
6145 return Qnil;
6146}
6147
6148VALUE
6149rb_uninterruptible(VALUE (*b_proc)(VALUE), VALUE data)
6150{
6151 VALUE interrupt_mask = rb_ident_hash_new();
6152 rb_thread_t *cur_th = GET_THREAD();
6153
6154 rb_hash_aset(interrupt_mask, rb_cObject, sym_never);
6155 OBJ_FREEZE(interrupt_mask);
6156 rb_ary_push(cur_th->pending_interrupt_mask_stack, interrupt_mask);
6157
6158 VALUE ret = rb_ensure(b_proc, data, uninterruptible_exit, Qnil);
6159
6160 RUBY_VM_CHECK_INTS(cur_th->ec);
6161 return ret;
6162}
6163
6164static void
6165thread_specific_storage_alloc(rb_thread_t *th)
6166{
6167 VM_ASSERT(th->specific_storage == NULL);
6168
6169 if (UNLIKELY(specific_key_count > 0)) {
6170 th->specific_storage = ZALLOC_N(void *, RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6171 }
6172}
6173
6174rb_internal_thread_specific_key_t
6176{
6177 rb_vm_t *vm = GET_VM();
6178
6179 if (specific_key_count == 0 && vm->ractor.cnt > 1) {
6180 rb_raise(rb_eThreadError, "The first rb_internal_thread_specific_key_create() is called with multiple ractors");
6181 }
6182 else if (specific_key_count > RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX) {
6183 rb_raise(rb_eThreadError, "rb_internal_thread_specific_key_create() is called more than %d times", RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6184 }
6185 else {
6186 rb_internal_thread_specific_key_t key = specific_key_count++;
6187
6188 if (key == 0) {
6189 // allocate
6190 rb_ractor_t *cr = GET_RACTOR();
6191 rb_thread_t *th;
6192
6193 ccan_list_for_each(&cr->threads.set, th, lt_node) {
6194 thread_specific_storage_alloc(th);
6195 }
6196 }
6197 return key;
6198 }
6199}
6200
6201// async and native thread safe.
6202void *
6203rb_internal_thread_specific_get(VALUE thread_val, rb_internal_thread_specific_key_t key)
6204{
6205 rb_thread_t *th = DATA_PTR(thread_val);
6206
6207 VM_ASSERT(rb_thread_ptr(thread_val) == th);
6208 VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6209 VM_ASSERT(th->specific_storage);
6210
6211 return th->specific_storage[key];
6212}
6213
6214// async and native thread safe.
6215void
6216rb_internal_thread_specific_set(VALUE thread_val, rb_internal_thread_specific_key_t key, void *data)
6217{
6218 rb_thread_t *th = DATA_PTR(thread_val);
6219
6220 VM_ASSERT(rb_thread_ptr(thread_val) == th);
6221 VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6222 VM_ASSERT(th->specific_storage);
6223
6224 th->specific_storage[key] = data;
6225}
6226
6227// interrupt_exec
6230 struct ccan_list_node node;
6231
6232 rb_interrupt_exec_func_t *func;
6233 void *data;
6234 enum rb_interrupt_exec_flag flags;
6235};
6236
6237void
6238rb_threadptr_interrupt_exec_task_mark(rb_thread_t *th)
6239{
6240 struct rb_interrupt_exec_task *task;
6241
6242 ccan_list_for_each(&th->interrupt_exec_tasks, task, node) {
6243 if (task->flags & rb_interrupt_exec_flag_value_data) {
6244 rb_gc_mark((VALUE)task->data);
6245 }
6246 }
6247}
6248
6249// native thread safe
6250// th should be available
6251void
6252rb_threadptr_interrupt_exec(rb_thread_t *th, rb_interrupt_exec_func_t *func, void *data, enum rb_interrupt_exec_flag flags)
6253{
6254 // should not use ALLOC
6256 *task = (struct rb_interrupt_exec_task) {
6257 .flags = flags,
6258 .func = func,
6259 .data = data,
6260 };
6261
6262 rb_native_mutex_lock(&th->interrupt_lock);
6263 {
6264 ccan_list_add_tail(&th->interrupt_exec_tasks, &task->node);
6265 threadptr_set_interrupt_locked(th, true);
6266 }
6267 rb_native_mutex_unlock(&th->interrupt_lock);
6268}
6269
6270static void
6271threadptr_interrupt_exec_exec(rb_thread_t *th)
6272{
6273 while (1) {
6274 struct rb_interrupt_exec_task *task;
6275
6276 rb_native_mutex_lock(&th->interrupt_lock);
6277 {
6278 task = ccan_list_pop(&th->interrupt_exec_tasks, struct rb_interrupt_exec_task, node);
6279 }
6280 rb_native_mutex_unlock(&th->interrupt_lock);
6281
6282 RUBY_DEBUG_LOG("task:%p", task);
6283
6284 if (task) {
6285 if (task->flags & rb_interrupt_exec_flag_new_thread) {
6286 rb_thread_create(task->func, task->data);
6287 }
6288 else {
6289 (*task->func)(task->data);
6290 }
6291 SIZED_FREE(task);
6292 }
6293 else {
6294 break;
6295 }
6296 }
6297}
6298
6299static void
6300threadptr_interrupt_exec_cleanup(rb_thread_t *th)
6301{
6302 rb_native_mutex_lock(&th->interrupt_lock);
6303 {
6304 struct rb_interrupt_exec_task *task;
6305
6306 while ((task = ccan_list_pop(&th->interrupt_exec_tasks, struct rb_interrupt_exec_task, node)) != NULL) {
6307 SIZED_FREE(task);
6308 }
6309 }
6310 rb_native_mutex_unlock(&th->interrupt_lock);
6311}
6312
6313// native thread safe
6314// func/data should be native thread safe
6315void
6316rb_ractor_interrupt_exec(struct rb_ractor_struct *target_r,
6317 rb_interrupt_exec_func_t *func, void *data, enum rb_interrupt_exec_flag flags)
6318{
6319 RUBY_DEBUG_LOG("flags:%d", (int)flags);
6320
6321 rb_thread_t *main_th = target_r->threads.main;
6322 rb_threadptr_interrupt_exec(main_th, func, data, flags | rb_interrupt_exec_flag_new_thread);
6323}
6324
#define RUBY_ASSERT_ALWAYS(expr,...)
A variant of RUBY_ASSERT that does not interface with RUBY_DEBUG.
Definition assert.h:199
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
Definition atomic.h:69
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
#define RUBY_INTERNAL_EVENT_SWITCH
Thread switched.
Definition event.h:90
int rb_remove_event_hook(rb_event_hook_func_t func)
Removes the passed function from the list of event hooks.
Definition vm_trace.c:427
#define RUBY_EVENT_THREAD_BEGIN
Encountered a new thread.
Definition event.h:57
void(* rb_event_hook_func_t)(rb_event_flag_t evflag, VALUE data, VALUE self, ID mid, VALUE klass)
Type of event hooks.
Definition event.h:120
uint32_t rb_event_flag_t
Represents event(s).
Definition event.h:108
#define RUBY_EVENT_CALL
A method, written in Ruby, is called.
Definition event.h:41
#define RUBY_EVENT_THREAD_END
Encountered an end of a thread.
Definition event.h:58
static void RB_FL_SET_RAW(VALUE obj, VALUE flags)
This is an implementation detail of RB_FL_SET().
Definition fl_type.h:541
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition class.c:1596
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition class.c:2965
ID rb_frame_last_func(void)
Returns the ID of the last method in the call stack.
Definition eval.c:1245
int rb_keyword_given_p(void)
Determines if the current method is given a keyword argument.
Definition eval.c:1030
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:1017
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1676
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
Definition fl_type.h:133
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:131
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
Definition assume.h:29
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:205
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define FIX2INT
Old name of RB_FIX2INT.
Definition int.h:41
#define ZALLOC_N
Old name of RB_ZALLOC_N.
Definition memory.h:401
#define T_ICLASS
Old name of RUBY_T_ICLASS.
Definition value_type.h:66
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define FIX2LONG
Old name of RB_FIX2LONG.
Definition long.h:46
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define T_OBJECT
Old name of RUBY_T_OBJECT.
Definition value_type.h:75
#define NIL_P
Old name of RB_NIL_P.
#define POSFIXABLE
Old name of RB_POSFIXABLE.
Definition fixnum.h:29
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition value_type.h:85
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define SYMBOL_P
Old name of RB_SYMBOL_P.
Definition value_type.h:88
void ruby_stop(int ex)
Calls ruby_cleanup() and exits the process.
Definition eval.c:290
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
Definition error.h:487
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:660
VALUE rb_eSystemExit
SystemExit exception.
Definition error.c:1411
VALUE rb_eIOError
IOError exception.
Definition io.c:189
VALUE rb_eStandardError
StandardError exception.
Definition error.c:1415
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1418
void rb_frozen_error_raise(VALUE frozen_obj, const char *fmt,...)
Raises an instance of rb_eFrozenError.
Definition error.c:4170
VALUE rb_eFatal
fatal exception.
Definition error.c:1414
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1416
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:1456
VALUE rb_eException
Mother of all exceptions.
Definition error.c:1410
VALUE rb_eThreadError
ThreadError exception.
Definition eval.c:1035
void rb_exit(int status)
Terminates the current execution context.
Definition process.c:4360
VALUE rb_eSignal
SignalException exception.
Definition error.c:1413
VALUE rb_cObject
Object class.
Definition object.c:61
VALUE rb_obj_alloc(VALUE klass)
Allocates an instance of the given class.
Definition object.c:2285
VALUE rb_cInteger
Module class.
Definition numeric.c:199
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:3875
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...
static int rb_check_arity(int argc, int min, int max)
Ensures that the passed integer is in the passed range.
Definition error.h:284
VALUE rb_block_proc(void)
Constructs a Proc object from implicitly passed components.
Definition proc.c:988
void rb_reset_random_seed(void)
Resets the RNG behind rb_genrand_int32()/rb_genrand_real().
Definition random.c:1818
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition string.c:1501
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:4053
#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:1482
VALUE rb_thread_local_aref(VALUE thread, ID key)
This badly named function reads from a Fiber local storage.
Definition thread.c:3782
VALUE rb_mutex_new(void)
Creates a mutex.
VALUE rb_thread_kill(VALUE thread)
Terminates the given thread.
Definition thread.c:2975
#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:3214
VALUE rb_exec_recursive(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h)
"Recursion" API entry point.
void rb_thread_sleep_forever(void)
Blocks indefinitely.
Definition thread.c:1411
void rb_thread_fd_close(int fd)
This funciton is now a no-op.
Definition thread.c:2911
void rb_thread_wait_for(struct timeval time)
Identical to rb_thread_sleep(), except it takes struct timeval instead.
Definition thread.c:1444
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:3126
VALUE rb_mutex_sleep(VALUE self, VALUE timeout)
Releases the lock held in the mutex and waits for the period of time; reacquires the lock on wakeup.
VALUE rb_exec_recursive_paired(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h)
Identical to rb_exec_recursive(), except it checks for the recursion on the ordered pair of { g,...
void rb_unblock_function_t(void *)
This is the type of UBFs.
Definition thread.h:336
void rb_thread_atfork_before_exec(void)
:FIXME: situation of this function is unclear.
Definition thread.c:5066
void rb_thread_check_ints(void)
Checks for interrupts.
Definition thread.c:1465
VALUE rb_thread_run(VALUE thread)
This is a rb_thread_wakeup() + rb_thread_schedule() combo.
Definition thread.c:3117
VALUE rb_thread_wakeup(VALUE thread)
Marks a given thread as eligible for scheduling.
Definition thread.c:3070
VALUE rb_mutex_unlock(VALUE mutex)
Releases the mutex.
VALUE rb_exec_recursive_paired_outer(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h)
Identical to rb_exec_recursive_outer(), except it checks for the recursion on the ordered pair of { g...
void rb_thread_sleep_deadly(void)
Identical to rb_thread_sleep_forever(), except the thread calling this function is considered "dead" ...
Definition thread.c:1418
void rb_thread_atfork(void)
A pthread_atfork(3posix)-like API.
Definition thread.c:5061
VALUE rb_thread_current(void)
Obtains the "current" thread.
Definition thread.c:3193
int rb_thread_alone(void)
Checks if the thread this function is running is the only thread that is currently alive.
Definition thread.c:4055
VALUE rb_thread_local_aset(VALUE thread, ID key, VALUE val)
This badly named function writes to a Fiber local storage.
Definition thread.c:3930
void rb_thread_schedule(void)
Tries to switch to another thread.
Definition thread.c:1513
#define RUBY_UBF_PROCESS
A special UBF for blocking process operations.
Definition thread.h:389
VALUE rb_exec_recursive_outer(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h)
Identical to rb_exec_recursive(), except it calls f for outermost recursion only.
VALUE rb_thread_wakeup_alive(VALUE thread)
Identical to rb_thread_wakeup(), except it doesn't raise on an already killed thread.
Definition thread.c:3079
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:1488
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:2954
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:2024
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:1492
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:2081
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
Definition symbol.h:285
ID rb_check_id(volatile VALUE *namep)
Detects if the given name is already interned or not.
Definition symbol.c:1171
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
Definition string.c:12701
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:12691
int capa
Designed capacity of the buffer.
Definition io.h:11
#define RB_IO_POINTER(obj, fp)
Queries the underlying IO pointer.
Definition io.h:436
VALUE rb_eIOTimeoutError
Indicates that a timeout has occurred while performing an IO operation.
Definition io.c:190
#define RB_NOGVL_UBF_ASYNC_SAFE
Passing this flag to rb_nogvl() indicates that the passed UBF is async-signal-safe.
Definition thread.h: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:6202
#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:6215
rb_internal_thread_specific_key_t rb_internal_thread_specific_key_create(void)
Create a key to store thread specific data.
Definition thread.c:6174
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:1593
void * rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
(Re-)acquires the GVL.
Definition thread.c:2062
#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:1730
void * rb_thread_call_without_gvl(void *(*func)(void *), void *data1, rb_unblock_function_t *ubf, void *data2)
Allows the passed function to run in parallel with other Ruby threads.
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
Shim for block function parameters.
Definition iterator.h:58
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c: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:2550
static int rb_fd_max(const rb_fdset_t *f)
It seems this function has no use.
Definition largesize.h:209
void rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
Destructively overwrites an fdset with another.
void rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
Identical to rb_fd_copy(), except it copies unlimited number of file descriptors.
void rb_fd_term(rb_fdset_t *f)
Destroys the rb_fdset_t, releasing any memory and resources it used.
static fd_set * rb_fd_ptr(const rb_fdset_t *f)
Raw pointer to fd_set.
Definition largesize.h:195
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
Definition memory.h:372
#define ALLOCA_N(type, n)
Definition memory.h:292
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
Definition memory.h:360
VALUE rb_thread_create(type *q, void *w)
Creates a rb_cThread instance.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
#define rb_fd_isset
Queries if the given fd is in the rb_fdset_t.
Definition posix.h:60
#define rb_fd_select
Waits for multiple file descriptors at once.
Definition posix.h:66
#define rb_fd_init
Initialises the :given :rb_fdset_t.
Definition posix.h:63
#define rb_fd_set
Sets the given fd to the rb_fdset_t.
Definition posix.h:54
#define rb_fd_zero
Clears the given rb_fdset_t.
Definition posix.h:51
#define rb_fd_clr
Unsets the given fd from the rb_fdset_t.
Definition posix.h:57
#define RARRAY_LEN
Just another name of rb_array_len.
Definition rarray.h:51
static int RARRAY_LENINT(VALUE ary)
Identical to rb_array_len(), except it differs for the return type.
Definition rarray.h:281
static void RARRAY_ASET(VALUE ary, long i, VALUE v)
Assigns an object in an array.
Definition rarray.h:386
#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
static VALUE RBASIC_CLASS(VALUE obj)
Queries the class of an object.
Definition rbasic.h:166
#define RBASIC(obj)
Convenient casting macro.
Definition rbasic.h:40
#define RCLASS_SUPER
Just another name of rb_class_get_superclass.
Definition rclass.h:44
#define 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 RUBY_TYPED_FREE_IMMEDIATELY
Macros to see if each corresponding flag is defined.
Definition rtypeddata.h:119
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:736
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:514
#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:561
#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:5814
int ruby_snprintf(char *str, size_t n, char const *fmt,...)
Our own locale-insensitive version of snprintf(3).
Definition sprintf.c:1043
#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:1091
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:458
VALUE rb_fiber_scheduler_fiber_interrupt(VALUE scheduler, VALUE fiber, VALUE exception)
Interrupt a fiber by raising an exception.
Definition scheduler.c:1131
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:647
VALUE rb_fiber_scheduler_yield(VALUE scheduler)
Yield to the scheduler, to be resumed on the next scheduling cycle.
Definition scheduler.c:548
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:420
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:471
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:666
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:4554
#define rb_fd_resize(n, f)
Does nothing (defined for compatibility).
Definition select.h:43
static bool RB_TEST(VALUE obj)
Emulates Ruby's "if" statement.
@ RUBY_Qundef
Represents so-called undef.
#define RTEST
This is an old name of RB_TEST.
#define _(args)
This was a transition path from K&R to ANSI.
Definition stdarg.h:35
Definition method.h:63
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:211
The data structure which wraps the fd_set bitmap used by select(2).
Definition largesize.h:71
int maxfd
Maximum allowed number of FDs.
Definition largesize.h:72
fd_set * fdset
File descriptors buffer.
Definition largesize.h:73
int capa
Maximum allowed number of FDs.
Definition win32.h:50
Ruby's IO, metadata and buffers.
Definition io.h:295
VALUE self
The IO's Ruby level counterpart.
Definition io.h:298
int fd
file descriptor.
Definition io.h:306
struct ccan_list_head blocking_operations
Threads that are performing a blocking operation without the GVL using this IO.
Definition io.h:131
Definition method.h:55
const rb_iseq_t * iseqptr
iseq pointer, should be separated from iseqval
Definition method.h:143
void rb_nativethread_lock_lock(rb_nativethread_lock_t *lock)
Blocks until the current thread obtains a lock.
Definition thread.c:307
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:313
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:295
void rb_nativethread_lock_destroy(rb_nativethread_lock_t *lock)
Destroys the passed mutex.
Definition thread.c:301
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
Definition value.h:52
uintptr_t VALUE
Type that represents a Ruby object.
Definition value.h:40
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.
Definition value_type.h:376