Ruby 4.0.6p0 (2026-07-14 revision 03b6d3f8898a28604fe6cb00eae3226b821168f4)
thread.c
1/**********************************************************************
2
3 thread.c -
4
5 $Author$
6
7 Copyright (C) 2004-2007 Koichi Sasada
8
9**********************************************************************/
10
11/*
12 YARV Thread Design
13
14 model 1: Userlevel Thread
15 Same as traditional ruby thread.
16
17 model 2: Native Thread with Global VM lock
18 Using pthread (or Windows thread) and Ruby threads run concurrent.
19
20 model 3: Native Thread with fine grain lock
21 Using pthread and Ruby threads run concurrent or parallel.
22
23 model 4: M:N User:Native threads with Global VM lock
24 Combination of model 1 and 2
25
26 model 5: M:N User:Native thread with fine grain lock
27 Combination of model 1 and 3
28
29------------------------------------------------------------------------
30
31 model 2:
32 A thread has mutex (GVL: Global VM Lock or Giant VM Lock) can run.
33 When thread scheduling, running thread release GVL. If running thread
34 try blocking operation, this thread must release GVL and another
35 thread can continue this flow. After blocking operation, thread
36 must check interrupt (RUBY_VM_CHECK_INTS).
37
38 Every VM can run parallel.
39
40 Ruby threads are scheduled by OS thread scheduler.
41
42------------------------------------------------------------------------
43
44 model 3:
45 Every threads run concurrent or parallel and to access shared object
46 exclusive access control is needed. For example, to access String
47 object or Array object, fine grain lock must be locked every time.
48 */
49
50
51/*
52 * FD_SET, FD_CLR and FD_ISSET have a small sanity check when using glibc
53 * 2.15 or later and set _FORTIFY_SOURCE > 0.
54 * However, the implementation is wrong. Even though Linux's select(2)
55 * supports large fd size (>FD_SETSIZE), it wrongly assumes fd is always
56 * less than FD_SETSIZE (i.e. 1024). And then when enabling HAVE_RB_FD_INIT,
57 * it doesn't work correctly and makes program abort. Therefore we need to
58 * disable FORTIFY_SOURCE until glibc fixes it.
59 */
60#undef _FORTIFY_SOURCE
61#undef __USE_FORTIFY_LEVEL
62#define __USE_FORTIFY_LEVEL 0
63
64/* for model 2 */
65
66#include "ruby/internal/config.h"
67
68#ifdef __linux__
69// Normally, gcc(1) translates calls to alloca() with inlined code. This is not done when either the -ansi, -std=c89, -std=c99, or the -std=c11 option is given and the header <alloca.h> is not included.
70# include <alloca.h>
71#endif
72
73#define TH_SCHED(th) (&(th)->ractor->threads.sched)
74
75#include "eval_intern.h"
76#include "hrtime.h"
77#include "internal.h"
78#include "internal/class.h"
79#include "internal/cont.h"
80#include "internal/error.h"
81#include "internal/eval.h"
82#include "internal/gc.h"
83#include "internal/hash.h"
84#include "internal/io.h"
85#include "internal/object.h"
86#include "internal/proc.h"
88#include "internal/signal.h"
89#include "internal/thread.h"
90#include "internal/time.h"
91#include "internal/warnings.h"
92#include "iseq.h"
93#include "ruby/debug.h"
94#include "ruby/io.h"
95#include "ruby/thread.h"
96#include "ruby/thread_native.h"
97#include "timev.h"
98#include "vm_core.h"
99#include "ractor_core.h"
100#include "vm_debug.h"
101#include "vm_sync.h"
102
103#include "ccan/list/list.h"
104
105#ifndef USE_NATIVE_THREAD_PRIORITY
106#define USE_NATIVE_THREAD_PRIORITY 0
107#define RUBY_THREAD_PRIORITY_MAX 3
108#define RUBY_THREAD_PRIORITY_MIN -3
109#endif
110
111static VALUE rb_cThreadShield;
112static VALUE cThGroup;
113
114static VALUE sym_immediate;
115static VALUE sym_on_blocking;
116static VALUE sym_never;
117
118static uint32_t thread_default_quantum_ms = 100;
119
120#define THREAD_LOCAL_STORAGE_INITIALISED FL_USER13
121#define THREAD_LOCAL_STORAGE_INITIALISED_P(th) RB_FL_TEST_RAW((th), THREAD_LOCAL_STORAGE_INITIALISED)
122
123static inline VALUE
124rb_thread_local_storage(VALUE thread)
125{
126 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
127 rb_ivar_set(thread, idLocals, rb_hash_new());
128 RB_FL_SET_RAW(thread, THREAD_LOCAL_STORAGE_INITIALISED);
129 }
130 return rb_ivar_get(thread, idLocals);
131}
132
133enum SLEEP_FLAGS {
134 SLEEP_DEADLOCKABLE = 0x01,
135 SLEEP_SPURIOUS_CHECK = 0x02,
136 SLEEP_ALLOW_SPURIOUS = 0x04,
137 SLEEP_NO_CHECKINTS = 0x08,
138};
139
140static void sleep_forever(rb_thread_t *th, unsigned int fl);
141static int sleep_hrtime(rb_thread_t *, rb_hrtime_t, unsigned int fl);
142
143static void rb_thread_sleep_deadly_allow_spurious_wakeup(VALUE blocker, VALUE timeout, rb_hrtime_t end);
144static int rb_threadptr_dead(rb_thread_t *th);
145static void rb_check_deadlock(rb_ractor_t *r);
146static int rb_threadptr_pending_interrupt_empty_p(const rb_thread_t *th);
147static const char *thread_status_name(rb_thread_t *th, int detail);
148static int hrtime_update_expire(rb_hrtime_t *, const rb_hrtime_t);
149NORETURN(static void async_bug_fd(const char *mesg, int errno_arg, int fd));
150MAYBE_UNUSED(static int consume_communication_pipe(int fd));
151
152static rb_atomic_t system_working = 1;
153static rb_internal_thread_specific_key_t specific_key_count;
154
155/********************************************************************************/
156
157#define THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
158
160 enum rb_thread_status prev_status;
161};
162
163static int unblock_function_set(rb_thread_t *th, rb_unblock_function_t *func, void *arg, int fail_if_interrupted);
164static void unblock_function_clear(rb_thread_t *th);
165
166static inline int blocking_region_begin(rb_thread_t *th, struct rb_blocking_region_buffer *region,
167 rb_unblock_function_t *ubf, void *arg, int fail_if_interrupted);
168static inline void blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region);
169
170#define THREAD_BLOCKING_BEGIN(th) do { \
171 struct rb_thread_sched * const sched = TH_SCHED(th); \
172 RB_VM_SAVE_MACHINE_CONTEXT(th); \
173 thread_sched_to_waiting((sched), (th), true);
174
175#define THREAD_BLOCKING_END(th) \
176 thread_sched_to_running((sched), (th)); \
177 rb_ractor_thread_switch(th->ractor, th, false); \
178} while(0)
179
180#ifdef __GNUC__
181#ifdef HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P
182#define only_if_constant(expr, notconst) __builtin_choose_expr(__builtin_constant_p(expr), (expr), (notconst))
183#else
184#define only_if_constant(expr, notconst) (__builtin_constant_p(expr) ? (expr) : (notconst))
185#endif
186#else
187#define only_if_constant(expr, notconst) notconst
188#endif
189#define BLOCKING_REGION(th, exec, ubf, ubfarg, fail_if_interrupted) do { \
190 struct rb_blocking_region_buffer __region; \
191 if (blocking_region_begin(th, &__region, (ubf), (ubfarg), fail_if_interrupted) || \
192 /* always return true unless fail_if_interrupted */ \
193 !only_if_constant(fail_if_interrupted, TRUE)) { \
194 /* Important that this is inlined into the macro, and not part of \
195 * blocking_region_begin - see bug #20493 */ \
196 RB_VM_SAVE_MACHINE_CONTEXT(th); \
197 thread_sched_to_waiting(TH_SCHED(th), th, false); \
198 exec; \
199 blocking_region_end(th, &__region); \
200 }; \
201} while(0)
202
203/*
204 * returns true if this thread was spuriously interrupted, false otherwise
205 * (e.g. hit by Thread#run or ran a Ruby-level Signal.trap handler)
206 */
207#define RUBY_VM_CHECK_INTS_BLOCKING(ec) vm_check_ints_blocking(ec)
208static inline int
209vm_check_ints_blocking(rb_execution_context_t *ec)
210{
211#ifdef RUBY_ASSERT_CRITICAL_SECTION
212 VM_ASSERT(ruby_assert_critical_section_entered == 0);
213#endif
214
215 rb_thread_t *th = rb_ec_thread_ptr(ec);
216
217 if (LIKELY(rb_threadptr_pending_interrupt_empty_p(th))) {
218 if (LIKELY(!RUBY_VM_INTERRUPTED_ANY(ec))) return FALSE;
219 }
220 else {
221 th->pending_interrupt_queue_checked = 0;
222 RUBY_VM_SET_INTERRUPT(ec);
223 }
224
225 int result = rb_threadptr_execute_interrupts(th, 1);
226
227 // When a signal is received, we yield to the scheduler as soon as possible:
228 if (result || RUBY_VM_INTERRUPTED(ec)) {
230 if (scheduler != Qnil) {
231 rb_fiber_scheduler_yield(scheduler);
232 }
233 }
234
235 return result;
236}
237
238int
239rb_vm_check_ints_blocking(rb_execution_context_t *ec)
240{
241 return vm_check_ints_blocking(ec);
242}
243
244/*
245 * poll() is supported by many OSes, but so far Linux is the only
246 * one we know of that supports using poll() in all places select()
247 * would work.
248 */
249#if defined(HAVE_POLL)
250# if defined(__linux__)
251# define USE_POLL
252# endif
253# if defined(__FreeBSD_version) && __FreeBSD_version >= 1100000
254# define USE_POLL
255 /* FreeBSD does not set POLLOUT when POLLHUP happens */
256# define POLLERR_SET (POLLHUP | POLLERR)
257# endif
258#endif
259
260static void
261timeout_prepare(rb_hrtime_t **to, rb_hrtime_t *rel, rb_hrtime_t *end,
262 const struct timeval *timeout)
263{
264 if (timeout) {
265 *rel = rb_timeval2hrtime(timeout);
266 *end = rb_hrtime_add(rb_hrtime_now(), *rel);
267 *to = rel;
268 }
269 else {
270 *to = 0;
271 }
272}
273
274MAYBE_UNUSED(NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_start)));
275MAYBE_UNUSED(static bool th_has_dedicated_nt(const rb_thread_t *th));
276MAYBE_UNUSED(static int waitfd_to_waiting_flag(int wfd_event));
277
278#include THREAD_IMPL_SRC
279
280/*
281 * TODO: somebody with win32 knowledge should be able to get rid of
282 * timer-thread by busy-waiting on signals. And it should be possible
283 * to make the GVL in thread_pthread.c be platform-independent.
284 */
285#ifndef BUSY_WAIT_SIGNALS
286# define BUSY_WAIT_SIGNALS (0)
287#endif
288
289#ifndef USE_EVENTFD
290# define USE_EVENTFD (0)
291#endif
292
293#include "thread_sync.c"
294
295void
296rb_nativethread_lock_initialize(rb_nativethread_lock_t *lock)
297{
299}
300
301void
302rb_nativethread_lock_destroy(rb_nativethread_lock_t *lock)
303{
305}
306
307void
308rb_nativethread_lock_lock(rb_nativethread_lock_t *lock)
309{
311}
312
313void
314rb_nativethread_lock_unlock(rb_nativethread_lock_t *lock)
315{
317}
318
319static int
320unblock_function_set(rb_thread_t *th, rb_unblock_function_t *func, void *arg, int fail_if_interrupted)
321{
322 do {
323 if (fail_if_interrupted) {
324 if (RUBY_VM_INTERRUPTED_ANY(th->ec)) {
325 return FALSE;
326 }
327 }
328 else {
329 RUBY_VM_CHECK_INTS(th->ec);
330 }
331
332 rb_native_mutex_lock(&th->interrupt_lock);
333 } while (!th->ec->raised_flag && RUBY_VM_INTERRUPTED_ANY(th->ec) &&
334 (rb_native_mutex_unlock(&th->interrupt_lock), TRUE));
335
336 VM_ASSERT(th->unblock.func == NULL);
337
338 th->unblock.func = func;
339 th->unblock.arg = arg;
340 rb_native_mutex_unlock(&th->interrupt_lock);
341
342 return TRUE;
343}
344
345static void
346unblock_function_clear(rb_thread_t *th)
347{
348 rb_native_mutex_lock(&th->interrupt_lock);
349 th->unblock.func = 0;
350 rb_native_mutex_unlock(&th->interrupt_lock);
351}
352
353static void
354threadptr_set_interrupt_locked(rb_thread_t *th, bool trap)
355{
356 // th->interrupt_lock should be acquired here
357
358 RUBY_DEBUG_LOG("th:%u trap:%d", rb_th_serial(th), trap);
359
360 if (trap) {
361 RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
362 }
363 else {
364 RUBY_VM_SET_INTERRUPT(th->ec);
365 }
366
367 if (th->unblock.func != NULL) {
368 (th->unblock.func)(th->unblock.arg);
369 }
370 else {
371 /* none */
372 }
373}
374
375static void
376threadptr_set_interrupt(rb_thread_t *th, int trap)
377{
378 rb_native_mutex_lock(&th->interrupt_lock);
379 {
380 threadptr_set_interrupt_locked(th, trap);
381 }
382 rb_native_mutex_unlock(&th->interrupt_lock);
383}
384
385/* Set interrupt flag on another thread or current thread, and call its UBF if it has one set */
386void
387rb_threadptr_interrupt(rb_thread_t *th)
388{
389 RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
390 threadptr_set_interrupt(th, false);
391}
392
393static void
394threadptr_trap_interrupt(rb_thread_t *th)
395{
396 threadptr_set_interrupt(th, true);
397}
398
399static void
400terminate_all(rb_ractor_t *r, const rb_thread_t *main_thread)
401{
402 rb_thread_t *th = 0;
403
404 ccan_list_for_each(&r->threads.set, th, lt_node) {
405 if (th != main_thread) {
406 RUBY_DEBUG_LOG("terminate start th:%u status:%s", rb_th_serial(th), thread_status_name(th, TRUE));
407
408 rb_threadptr_pending_interrupt_enque(th, RUBY_FATAL_THREAD_TERMINATED);
409 rb_threadptr_interrupt(th);
410
411 RUBY_DEBUG_LOG("terminate done th:%u status:%s", rb_th_serial(th), thread_status_name(th, TRUE));
412 }
413 else {
414 RUBY_DEBUG_LOG("main thread th:%u", rb_th_serial(th));
415 }
416 }
417}
418
419static void
420rb_threadptr_join_list_wakeup(rb_thread_t *thread)
421{
422 while (thread->join_list) {
423 struct rb_waiting_list *join_list = thread->join_list;
424
425 // Consume the entry from the join list:
426 thread->join_list = join_list->next;
427
428 rb_thread_t *target_thread = join_list->thread;
429
430 if (target_thread->scheduler != Qnil && join_list->fiber) {
431 rb_fiber_scheduler_unblock(target_thread->scheduler, target_thread->self, rb_fiberptr_self(join_list->fiber));
432 }
433 else {
434 rb_threadptr_interrupt(target_thread);
435
436 switch (target_thread->status) {
437 case THREAD_STOPPED:
438 case THREAD_STOPPED_FOREVER:
439 target_thread->status = THREAD_RUNNABLE;
440 break;
441 default:
442 break;
443 }
444 }
445 }
446}
447
448void
449rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th)
450{
451 while (th->keeping_mutexes) {
452 rb_mutex_t *mutex = th->keeping_mutexes;
453 th->keeping_mutexes = mutex->next_mutex;
454
455 // rb_warn("mutex #<%p> was not unlocked by thread #<%p>", (void *)mutex, (void*)th);
456 VM_ASSERT(mutex->ec_serial);
457 const char *error_message = rb_mutex_unlock_th(mutex, th, 0);
458 if (error_message) rb_bug("invalid keeping_mutexes: %s", error_message);
459 }
460}
461
462void
463rb_thread_terminate_all(rb_thread_t *th)
464{
465 rb_ractor_t *cr = th->ractor;
466 rb_execution_context_t * volatile ec = th->ec;
467 volatile int sleeping = 0;
468
469 if (cr->threads.main != th) {
470 rb_bug("rb_thread_terminate_all: called by child thread (%p, %p)",
471 (void *)cr->threads.main, (void *)th);
472 }
473
474 /* unlock all locking mutexes */
475 rb_threadptr_unlock_all_locking_mutexes(th);
476
477 EC_PUSH_TAG(ec);
478 if (EC_EXEC_TAG() == TAG_NONE) {
479 retry:
480 RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
481
482 terminate_all(cr, th);
483
484 while (rb_ractor_living_thread_num(cr) > 1) {
485 rb_hrtime_t rel = RB_HRTIME_PER_SEC;
486 /*q
487 * Thread exiting routine in thread_start_func_2 notify
488 * me when the last sub-thread exit.
489 */
490 sleeping = 1;
491 native_sleep(th, &rel);
492 RUBY_VM_CHECK_INTS_BLOCKING(ec);
493 sleeping = 0;
494 }
495 }
496 else {
497 /*
498 * When caught an exception (e.g. Ctrl+C), let's broadcast
499 * kill request again to ensure killing all threads even
500 * if they are blocked on sleep, mutex, etc.
501 */
502 if (sleeping) {
503 sleeping = 0;
504 goto retry;
505 }
506 }
507 EC_POP_TAG();
508}
509
510void rb_threadptr_root_fiber_terminate(rb_thread_t *th);
511static void threadptr_interrupt_exec_cleanup(rb_thread_t *th);
512
513static void
514thread_cleanup_func_before_exec(void *th_ptr)
515{
516 rb_thread_t *th = th_ptr;
517 th->status = THREAD_KILLED;
518
519 // The thread stack doesn't exist in the forked process:
520 th->ec->machine.stack_start = th->ec->machine.stack_end = NULL;
521
522 threadptr_interrupt_exec_cleanup(th);
523 rb_threadptr_root_fiber_terminate(th);
524}
525
526static void
527thread_cleanup_func(void *th_ptr, int atfork)
528{
529 rb_thread_t *th = th_ptr;
530
531 th->locking_mutex = Qfalse;
532 thread_cleanup_func_before_exec(th_ptr);
533
534 if (atfork) {
535 native_thread_destroy_atfork(th->nt);
536 th->nt = NULL;
537 return;
538 }
539
540 rb_native_mutex_destroy(&th->interrupt_lock);
541}
542
543void
544rb_thread_free_native_thread(void *th_ptr)
545{
546 rb_thread_t *th = th_ptr;
547
548 native_thread_destroy_atfork(th->nt);
549 th->nt = NULL;
550}
551
552static VALUE rb_threadptr_raise(rb_thread_t *, int, VALUE *);
553static VALUE rb_thread_to_s(VALUE thread);
554
555void
556ruby_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame)
557{
558 native_thread_init_stack(th, local_in_parent_frame);
559}
560
561const VALUE *
562rb_vm_proc_local_ep(VALUE proc)
563{
564 const VALUE *ep = vm_proc_ep(proc);
565
566 if (ep) {
567 return rb_vm_ep_local_ep(ep);
568 }
569 else {
570 return NULL;
571 }
572}
573
574// for ractor, defined in vm.c
575VALUE rb_vm_invoke_proc_with_self(rb_execution_context_t *ec, rb_proc_t *proc, VALUE self,
576 int argc, const VALUE *argv, int kw_splat, VALUE passed_block_handler);
577
578static VALUE
579thread_do_start_proc(rb_thread_t *th)
580{
581 VALUE args = th->invoke_arg.proc.args;
582 const VALUE *args_ptr;
583 int args_len;
584 VALUE procval = th->invoke_arg.proc.proc;
585 rb_proc_t *proc;
586 GetProcPtr(procval, proc);
587
588 th->ec->errinfo = Qnil;
589 th->ec->root_lep = rb_vm_proc_local_ep(procval);
590 th->ec->root_svar = Qfalse;
591
592 vm_check_ints_blocking(th->ec);
593
594 if (th->invoke_type == thread_invoke_type_ractor_proc) {
595 VALUE self = rb_ractor_self(th->ractor);
596 th->thgroup = th->ractor->thgroup_default = rb_obj_alloc(cThGroup);
597
598 VM_ASSERT(FIXNUM_P(args));
599 args_len = FIX2INT(args);
600 args_ptr = ALLOCA_N(VALUE, args_len);
601 rb_ractor_receive_parameters(th->ec, th->ractor, args_len, (VALUE *)args_ptr);
602 vm_check_ints_blocking(th->ec);
603
604 return rb_vm_invoke_proc_with_self(
605 th->ec, proc, self,
606 args_len, args_ptr,
607 th->invoke_arg.proc.kw_splat,
608 VM_BLOCK_HANDLER_NONE
609 );
610 }
611 else {
612 args_len = RARRAY_LENINT(args);
613 if (args_len < 8) {
614 /* free proc.args if the length is enough small */
615 args_ptr = ALLOCA_N(VALUE, args_len);
616 MEMCPY((VALUE *)args_ptr, RARRAY_CONST_PTR(args), VALUE, args_len);
617 th->invoke_arg.proc.args = Qnil;
618 }
619 else {
620 args_ptr = RARRAY_CONST_PTR(args);
621 }
622
623 vm_check_ints_blocking(th->ec);
624
625 return rb_vm_invoke_proc(
626 th->ec, proc,
627 args_len, args_ptr,
628 th->invoke_arg.proc.kw_splat,
629 VM_BLOCK_HANDLER_NONE
630 );
631 }
632}
633
634static VALUE
635thread_do_start(rb_thread_t *th)
636{
637 native_set_thread_name(th);
638 VALUE result = Qundef;
639
640 switch (th->invoke_type) {
641 case thread_invoke_type_proc:
642 result = thread_do_start_proc(th);
643 break;
644
645 case thread_invoke_type_ractor_proc:
646 result = thread_do_start_proc(th);
647 rb_ractor_atexit(th->ec, result);
648 break;
649
650 case thread_invoke_type_func:
651 result = (*th->invoke_arg.func.func)(th->invoke_arg.func.arg);
652 break;
653
654 case thread_invoke_type_none:
655 rb_bug("unreachable");
656 }
657
658 return result;
659}
660
661void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
662
663static int
664thread_start_func_2(rb_thread_t *th, VALUE *stack_start)
665{
666 RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
667 VM_ASSERT(th != th->vm->ractor.main_thread);
668
669 enum ruby_tag_type state;
670 VALUE errinfo = Qnil;
671 rb_thread_t *ractor_main_th = th->ractor->threads.main;
672
673 // setup ractor
674 if (rb_ractor_status_p(th->ractor, ractor_blocking)) {
675 RB_VM_LOCK();
676 {
677 rb_vm_ractor_blocking_cnt_dec(th->vm, th->ractor, __FILE__, __LINE__);
678 rb_ractor_t *r = th->ractor;
679 r->r_stdin = rb_io_prep_stdin();
680 r->r_stdout = rb_io_prep_stdout();
681 r->r_stderr = rb_io_prep_stderr();
682 }
683 RB_VM_UNLOCK();
684 }
685
686 // Ensure that we are not joinable.
687 VM_ASSERT(UNDEF_P(th->value));
688
689 int fiber_scheduler_closed = 0, event_thread_end_hooked = 0;
690 VALUE result = Qundef;
691
692 EC_PUSH_TAG(th->ec);
693
694 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
695 EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_BEGIN, th->self, 0, 0, 0, Qundef);
696
697 result = thread_do_start(th);
698 }
699
700 if (!fiber_scheduler_closed) {
701 fiber_scheduler_closed = 1;
703 }
704
705 if (!event_thread_end_hooked) {
706 event_thread_end_hooked = 1;
707 EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
708 }
709
710 if (state == TAG_NONE) {
711 // This must be set AFTER doing all user-level code. At this point, the thread is effectively finished and calls to `Thread#join` will succeed.
712 th->value = result;
713 }
714 else {
715 errinfo = th->ec->errinfo;
716
717 VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
718 if (!NIL_P(exc)) errinfo = exc;
719
720 if (state == TAG_FATAL) {
721 if (th->invoke_type == thread_invoke_type_ractor_proc) {
722 rb_ractor_atexit(th->ec, Qnil);
723 }
724 /* fatal error within this thread, need to stop whole script */
725 }
726 else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
727 if (th->invoke_type == thread_invoke_type_ractor_proc) {
728 rb_ractor_atexit_exception(th->ec);
729 }
730
731 /* exit on main_thread. */
732 }
733 else {
734 if (th->report_on_exception) {
735 VALUE mesg = rb_thread_to_s(th->self);
736 rb_str_cat_cstr(mesg, " terminated with exception (report_on_exception is true):\n");
737 rb_write_error_str(mesg);
738 rb_ec_error_print(th->ec, errinfo);
739 }
740
741 if (th->invoke_type == thread_invoke_type_ractor_proc) {
742 rb_ractor_atexit_exception(th->ec);
743 }
744
745 if (th->vm->thread_abort_on_exception ||
746 th->abort_on_exception || RTEST(ruby_debug)) {
747 /* exit on main_thread */
748 }
749 else {
750 errinfo = Qnil;
751 }
752 }
753 th->value = Qnil;
754 }
755
756 // The thread is effectively finished and can be joined.
757 VM_ASSERT(!UNDEF_P(th->value));
758
759 rb_threadptr_join_list_wakeup(th);
760 rb_threadptr_unlock_all_locking_mutexes(th);
761
762 if (th->invoke_type == thread_invoke_type_ractor_proc) {
763 rb_thread_terminate_all(th);
764 rb_ractor_teardown(th->ec);
765 }
766
767 th->status = THREAD_KILLED;
768 RUBY_DEBUG_LOG("killed th:%u", rb_th_serial(th));
769
770 if (th->vm->ractor.main_thread == th) {
771 ruby_stop(0);
772 }
773
774 if (RB_TYPE_P(errinfo, T_OBJECT)) {
775 /* treat with normal error object */
776 rb_threadptr_raise(ractor_main_th, 1, &errinfo);
777 }
778
779 EC_POP_TAG();
780
781 rb_ec_clear_current_thread_trace_func(th->ec);
782
783 /* locking_mutex must be Qfalse */
784 if (th->locking_mutex != Qfalse) {
785 rb_bug("thread_start_func_2: locking_mutex must not be set (%p:%"PRIxVALUE")",
786 (void *)th, th->locking_mutex);
787 }
788
789 if (ractor_main_th->status == THREAD_KILLED &&
790 th->ractor->threads.cnt <= 2 /* main thread and this thread */) {
791 /* I'm last thread. wake up main thread from rb_thread_terminate_all */
792 rb_threadptr_interrupt(ractor_main_th);
793 }
794
795 rb_check_deadlock(th->ractor);
796
797 rb_fiber_close(th->ec->fiber_ptr);
798
799 thread_cleanup_func(th, FALSE);
800 VM_ASSERT(th->ec->vm_stack == NULL);
801
802 if (th->invoke_type == thread_invoke_type_ractor_proc) {
803 // after rb_ractor_living_threads_remove()
804 // GC will happen anytime and this ractor can be collected (and destroy GVL).
805 // So gvl_release() should be before it.
806 thread_sched_to_dead(TH_SCHED(th), th);
807 rb_ractor_living_threads_remove(th->ractor, th);
808 }
809 else {
810 rb_ractor_living_threads_remove(th->ractor, th);
811 thread_sched_to_dead(TH_SCHED(th), th);
812 }
813
814 return 0;
815}
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
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, true);
2096 return r;
2097}
2098
2099/*
2100 * ruby_thread_has_gvl_p - check if current native thread has GVL.
2101 */
2102
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_interrupt_raise(rb_thread_t *th)
2778{
2779 rb_thread_t *target_th = th->vm->ractor.main_thread;
2780
2781 if (rb_threadptr_dead(target_th)) {
2782 return;
2783 }
2784
2785 /* Preserve the traditional no-message Interrupt from default SIGINT. */
2786 VALUE exc = rb_exc_new(rb_eInterrupt, 0, 0);
2787
2788 /* making an exception object can switch thread,
2789 so we need to check thread deadness again */
2790 if (rb_threadptr_dead(target_th)) {
2791 return;
2792 }
2793
2794 rb_ec_setup_exception(GET_EC(), exc, Qundef);
2795 rb_threadptr_pending_interrupt_enque(target_th, exc);
2796 rb_threadptr_interrupt(target_th);
2797}
2798
2799void
2800rb_threadptr_signal_exit(rb_thread_t *th)
2801{
2802 VALUE argv[2];
2803
2804 argv[0] = rb_eSystemExit;
2805 argv[1] = rb_str_new2("exit");
2806
2807 // TODO: check signal raise deliverly
2808 rb_threadptr_raise(th->vm->ractor.main_thread, 2, argv);
2809}
2810
2811int
2812rb_ec_set_raised(rb_execution_context_t *ec)
2813{
2814 if (ec->raised_flag & RAISED_EXCEPTION) {
2815 return 1;
2816 }
2817 ec->raised_flag |= RAISED_EXCEPTION;
2818 return 0;
2819}
2820
2821int
2822rb_ec_reset_raised(rb_execution_context_t *ec)
2823{
2824 if (!(ec->raised_flag & RAISED_EXCEPTION)) {
2825 return 0;
2826 }
2827 ec->raised_flag &= ~RAISED_EXCEPTION;
2828 return 1;
2829}
2830
2831/*
2832 * Thread-safe IO closing mechanism.
2833 *
2834 * When an IO is closed while other threads or fibers are blocked on it, we need to:
2835 * 1. Track and notify all blocking operations through io->blocking_operations
2836 * 2. Ensure only one thread can close at a time using io->closing_ec
2837 * 3. Synchronize cleanup using wakeup_mutex
2838 *
2839 * The close process works as follows:
2840 * - First check if any thread is already closing (io->closing_ec)
2841 * - Set up wakeup_mutex for synchronization
2842 * - Iterate through all blocking operations in io->blocking_operations
2843 * - For each blocked fiber with a scheduler:
2844 * - Notify via rb_fiber_scheduler_fiber_interrupt
2845 * - For each blocked thread without a scheduler:
2846 * - Enqueue IOError via rb_threadptr_pending_interrupt_enque
2847 * - Wake via rb_threadptr_interrupt
2848 * - Wait on wakeup_mutex until all operations are cleaned up
2849 * - Only then clear closing state and allow actual close to proceed
2850 */
2851static VALUE
2852thread_io_close_notify_all(VALUE _io)
2853{
2854 struct rb_io *io = (struct rb_io *)_io;
2855
2856 size_t count = 0;
2857 rb_vm_t *vm = io->closing_ec->thread_ptr->vm;
2858 VALUE error = vm->special_exceptions[ruby_error_stream_closed];
2859
2860 struct rb_io_blocking_operation *blocking_operation;
2861 ccan_list_for_each(rb_io_blocking_operations(io), blocking_operation, list) {
2862 rb_execution_context_t *ec = blocking_operation->ec;
2863
2864 // If the operation is in progress, we need to interrupt it:
2865 if (ec) {
2866 rb_thread_t *thread = ec->thread_ptr;
2867
2868 VALUE result = RUBY_Qundef;
2869 if (thread->scheduler != Qnil) {
2870 result = rb_fiber_scheduler_fiber_interrupt(thread->scheduler, rb_fiberptr_self(ec->fiber_ptr), error);
2871 }
2872
2873 if (result == RUBY_Qundef) {
2874 // If the thread is not the current thread, we need to enqueue an error:
2875 rb_threadptr_pending_interrupt_enque(thread, error);
2876 rb_threadptr_interrupt(thread);
2877 }
2878 }
2879
2880 count += 1;
2881 }
2882
2883 return (VALUE)count;
2884}
2885
2886size_t
2887rb_thread_io_close_interrupt(struct rb_io *io)
2888{
2889 // We guard this operation based on `io->closing_ec` -> only one thread will ever enter this function.
2890 if (io->closing_ec) {
2891 return 0;
2892 }
2893
2894 // If there are no blocking operations, we are done:
2895 if (ccan_list_empty(rb_io_blocking_operations(io))) {
2896 return 0;
2897 }
2898
2899 // Otherwise, we are now closing the IO:
2900 rb_execution_context_t *ec = GET_EC();
2901 io->closing_ec = ec;
2902
2903 // This is used to ensure the correct execution context is woken up after the blocking operation is interrupted:
2904 io->wakeup_mutex = rb_mutex_new();
2905 rb_mutex_allow_trap(io->wakeup_mutex, 1);
2906
2907 // We need to use a mutex here as entering the fiber scheduler may cause a context switch:
2908 VALUE result = rb_mutex_synchronize(io->wakeup_mutex, thread_io_close_notify_all, (VALUE)io);
2909
2910 return (size_t)result;
2911}
2912
2913void
2914rb_thread_io_close_wait(struct rb_io* io)
2915{
2916 VALUE wakeup_mutex = io->wakeup_mutex;
2917
2918 if (!RB_TEST(wakeup_mutex)) {
2919 // There was nobody else using this file when we closed it, so we never bothered to allocate a mutex:
2920 return;
2921 }
2922
2923 rb_mutex_lock(wakeup_mutex);
2924 while (!ccan_list_empty(rb_io_blocking_operations(io))) {
2925 rb_mutex_sleep(wakeup_mutex, Qnil);
2926 }
2927 rb_mutex_unlock(wakeup_mutex);
2928
2929 // We are done closing:
2930 io->wakeup_mutex = Qnil;
2931 io->closing_ec = NULL;
2932}
2933
2934void
2935rb_thread_fd_close(int fd)
2936{
2937 rb_warn("rb_thread_fd_close is deprecated (and is now a no-op).");
2938}
2939
2940/*
2941 * call-seq:
2942 * raise(exception, message = exception.to_s, backtrace = nil, cause: $!)
2943 * raise(message = nil, cause: $!)
2944 *
2945 * Raises an exception from the given thread. The caller does not have to be
2946 * +thr+. See Kernel#raise for more information on arguments.
2947 *
2948 * Thread.abort_on_exception = true
2949 * a = Thread.new { sleep(200) }
2950 * a.raise("Gotcha")
2951 *
2952 * This will produce:
2953 *
2954 * prog.rb:3: Gotcha (RuntimeError)
2955 * from prog.rb:2:in `initialize'
2956 * from prog.rb:2:in `new'
2957 * from prog.rb:2
2958 */
2959
2960static VALUE
2961thread_raise_m(int argc, VALUE *argv, VALUE self)
2962{
2963 rb_thread_t *target_th = rb_thread_ptr(self);
2964 const rb_thread_t *current_th = GET_THREAD();
2965
2966 threadptr_check_pending_interrupt_queue(target_th);
2967
2968 if (rb_threadptr_dead(target_th)) {
2969 return Qnil;
2970 }
2971
2972 VALUE exception = rb_exception_setup(argc, argv);
2973 rb_threadptr_pending_interrupt_enque(target_th, exception);
2974 rb_threadptr_interrupt(target_th);
2975
2976 /* To perform Thread.current.raise as Kernel.raise */
2977 if (current_th == target_th) {
2978 RUBY_VM_CHECK_INTS(target_th->ec);
2979 }
2980 return Qnil;
2981}
2982
2983
2984/*
2985 * call-seq:
2986 * thr.exit -> thr
2987 * thr.kill -> thr
2988 * thr.terminate -> thr
2989 *
2990 * Terminates +thr+ and schedules another thread to be run, returning
2991 * the terminated Thread. If this is the main thread, or the last
2992 * thread, exits the process. Note that the caller does not wait for
2993 * the thread to terminate if the receiver is different from the currently
2994 * running thread. The termination is asynchronous, and the thread can still
2995 * run a small amount of ruby code before exiting.
2996 */
2997
2999rb_thread_kill(VALUE thread)
3000{
3001 rb_thread_t *target_th = rb_thread_ptr(thread);
3002
3003 if (target_th->to_kill || target_th->status == THREAD_KILLED) {
3004 return thread;
3005 }
3006 if (target_th == target_th->vm->ractor.main_thread) {
3007 rb_exit(EXIT_SUCCESS);
3008 }
3009
3010 RUBY_DEBUG_LOG("target_th:%u", rb_th_serial(target_th));
3011
3012 if (target_th == GET_THREAD()) {
3013 /* kill myself immediately */
3014 rb_threadptr_to_kill(target_th);
3015 }
3016 else {
3017 threadptr_check_pending_interrupt_queue(target_th);
3018 rb_threadptr_pending_interrupt_enque(target_th, RUBY_FATAL_THREAD_KILLED);
3019 rb_threadptr_interrupt(target_th);
3020 }
3021
3022 return thread;
3023}
3024
3025int
3026rb_thread_to_be_killed(VALUE thread)
3027{
3028 rb_thread_t *target_th = rb_thread_ptr(thread);
3029
3030 if (target_th->to_kill || target_th->status == THREAD_KILLED) {
3031 return TRUE;
3032 }
3033 return FALSE;
3034}
3035
3036/*
3037 * call-seq:
3038 * Thread.kill(thread) -> thread
3039 *
3040 * Causes the given +thread+ to exit, see also Thread::exit.
3041 *
3042 * count = 0
3043 * a = Thread.new { loop { count += 1 } }
3044 * sleep(0.1) #=> 0
3045 * Thread.kill(a) #=> #<Thread:0x401b3d30 dead>
3046 * count #=> 93947
3047 * a.alive? #=> false
3048 */
3049
3050static VALUE
3051rb_thread_s_kill(VALUE obj, VALUE th)
3052{
3053 return rb_thread_kill(th);
3054}
3055
3056
3057/*
3058 * call-seq:
3059 * Thread.exit -> thread
3060 *
3061 * Terminates the currently running thread and schedules another thread to be
3062 * run.
3063 *
3064 * If this thread is already marked to be killed, ::exit returns the Thread.
3065 *
3066 * If this is the main thread, or the last thread, exit the process.
3067 */
3068
3069static VALUE
3070rb_thread_exit(VALUE _)
3071{
3072 rb_thread_t *th = GET_THREAD();
3073 return rb_thread_kill(th->self);
3074}
3075
3076
3077/*
3078 * call-seq:
3079 * thr.wakeup -> thr
3080 *
3081 * Marks a given thread as eligible for scheduling, however it may still
3082 * remain blocked on I/O.
3083 *
3084 * *Note:* This does not invoke the scheduler, see #run for more information.
3085 *
3086 * c = Thread.new { Thread.stop; puts "hey!" }
3087 * sleep 0.1 while c.status!='sleep'
3088 * c.wakeup
3089 * c.join
3090 * #=> "hey!"
3091 */
3092
3094rb_thread_wakeup(VALUE thread)
3095{
3096 if (!RTEST(rb_thread_wakeup_alive(thread))) {
3097 rb_raise(rb_eThreadError, "killed thread");
3098 }
3099 return thread;
3100}
3101
3104{
3105 rb_thread_t *target_th = rb_thread_ptr(thread);
3106 if (target_th->status == THREAD_KILLED) return Qnil;
3107
3108 rb_threadptr_ready(target_th);
3109
3110 if (target_th->status == THREAD_STOPPED ||
3111 target_th->status == THREAD_STOPPED_FOREVER) {
3112 target_th->status = THREAD_RUNNABLE;
3113 }
3114
3115 return thread;
3116}
3117
3118
3119/*
3120 * call-seq:
3121 * thr.run -> thr
3122 *
3123 * Wakes up +thr+, making it eligible for scheduling.
3124 *
3125 * a = Thread.new { puts "a"; Thread.stop; puts "c" }
3126 * sleep 0.1 while a.status!='sleep'
3127 * puts "Got here"
3128 * a.run
3129 * a.join
3130 *
3131 * This will produce:
3132 *
3133 * a
3134 * Got here
3135 * c
3136 *
3137 * See also the instance method #wakeup.
3138 */
3139
3141rb_thread_run(VALUE thread)
3142{
3143 rb_thread_wakeup(thread);
3145 return thread;
3146}
3147
3148
3150rb_thread_stop(void)
3151{
3152 if (rb_thread_alone()) {
3153 rb_raise(rb_eThreadError,
3154 "stopping only thread\n\tnote: use sleep to stop forever");
3155 }
3157 return Qnil;
3158}
3159
3160/*
3161 * call-seq:
3162 * Thread.stop -> nil
3163 *
3164 * Stops execution of the current thread, putting it into a ``sleep'' state,
3165 * and schedules execution of another thread.
3166 *
3167 * a = Thread.new { print "a"; Thread.stop; print "c" }
3168 * sleep 0.1 while a.status!='sleep'
3169 * print "b"
3170 * a.run
3171 * a.join
3172 * #=> "abc"
3173 */
3174
3175static VALUE
3176thread_stop(VALUE _)
3177{
3178 return rb_thread_stop();
3179}
3180
3181/********************************************************************/
3182
3183VALUE
3184rb_thread_list(void)
3185{
3186 // TODO
3187 return rb_ractor_thread_list();
3188}
3189
3190/*
3191 * call-seq:
3192 * Thread.list -> array
3193 *
3194 * Returns an array of Thread objects for all threads that are either runnable
3195 * or stopped.
3196 *
3197 * Thread.new { sleep(200) }
3198 * Thread.new { 1000000.times {|i| i*i } }
3199 * Thread.new { Thread.stop }
3200 * Thread.list.each {|t| p t}
3201 *
3202 * This will produce:
3203 *
3204 * #<Thread:0x401b3e84 sleep>
3205 * #<Thread:0x401b3f38 run>
3206 * #<Thread:0x401b3fb0 sleep>
3207 * #<Thread:0x401bdf4c run>
3208 */
3209
3210static VALUE
3211thread_list(VALUE _)
3212{
3213 return rb_thread_list();
3214}
3215
3218{
3219 return GET_THREAD()->self;
3220}
3221
3222/*
3223 * call-seq:
3224 * Thread.current -> thread
3225 *
3226 * Returns the currently executing thread.
3227 *
3228 * Thread.current #=> #<Thread:0x401bdf4c run>
3229 */
3230
3231static VALUE
3232thread_s_current(VALUE klass)
3233{
3234 return rb_thread_current();
3235}
3236
3238rb_thread_main(void)
3239{
3240 return GET_RACTOR()->threads.main->self;
3241}
3242
3243/*
3244 * call-seq:
3245 * Thread.main -> thread
3246 *
3247 * Returns the main thread.
3248 */
3249
3250static VALUE
3251rb_thread_s_main(VALUE klass)
3252{
3253 return rb_thread_main();
3254}
3255
3256
3257/*
3258 * call-seq:
3259 * Thread.abort_on_exception -> true or false
3260 *
3261 * Returns the status of the global ``abort on exception'' condition.
3262 *
3263 * The default is +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 *
3268 * Can also be specified by the global $DEBUG flag or command line option
3269 * +-d+.
3270 *
3271 * See also ::abort_on_exception=.
3272 *
3273 * There is also an instance level method to set this for a specific thread,
3274 * see #abort_on_exception.
3275 */
3276
3277static VALUE
3278rb_thread_s_abort_exc(VALUE _)
3279{
3280 return RBOOL(GET_THREAD()->vm->thread_abort_on_exception);
3281}
3282
3283
3284/*
3285 * call-seq:
3286 * Thread.abort_on_exception= boolean -> true or false
3287 *
3288 * When set to +true+, if any thread is aborted by an exception, the
3289 * raised exception will be re-raised in the main thread.
3290 * Returns the new state.
3291 *
3292 * Thread.abort_on_exception = true
3293 * t1 = Thread.new do
3294 * puts "In new thread"
3295 * raise "Exception from thread"
3296 * end
3297 * sleep(1)
3298 * puts "not reached"
3299 *
3300 * This will produce:
3301 *
3302 * In new thread
3303 * prog.rb:4: Exception from thread (RuntimeError)
3304 * from prog.rb:2:in `initialize'
3305 * from prog.rb:2:in `new'
3306 * from prog.rb:2
3307 *
3308 * See also ::abort_on_exception.
3309 *
3310 * There is also an instance level method to set this for a specific thread,
3311 * see #abort_on_exception=.
3312 */
3313
3314static VALUE
3315rb_thread_s_abort_exc_set(VALUE self, VALUE val)
3316{
3317 GET_THREAD()->vm->thread_abort_on_exception = RTEST(val);
3318 return val;
3319}
3320
3321
3322/*
3323 * call-seq:
3324 * thr.abort_on_exception -> true or false
3325 *
3326 * Returns the status of the thread-local ``abort on exception'' condition for
3327 * this +thr+.
3328 *
3329 * The default is +false+.
3330 *
3331 * See also #abort_on_exception=.
3332 *
3333 * There is also a class level method to set this for all threads, see
3334 * ::abort_on_exception.
3335 */
3336
3337static VALUE
3338rb_thread_abort_exc(VALUE thread)
3339{
3340 return RBOOL(rb_thread_ptr(thread)->abort_on_exception);
3341}
3342
3343
3344/*
3345 * call-seq:
3346 * thr.abort_on_exception= boolean -> true or false
3347 *
3348 * When set to +true+, if this +thr+ is aborted by an exception, the
3349 * raised exception will be re-raised in the main thread.
3350 *
3351 * See also #abort_on_exception.
3352 *
3353 * There is also a class level method to set this for all threads, see
3354 * ::abort_on_exception=.
3355 */
3356
3357static VALUE
3358rb_thread_abort_exc_set(VALUE thread, VALUE val)
3359{
3360 rb_thread_ptr(thread)->abort_on_exception = RTEST(val);
3361 return val;
3362}
3363
3364
3365/*
3366 * call-seq:
3367 * Thread.report_on_exception -> true or false
3368 *
3369 * Returns the status of the global ``report on exception'' condition.
3370 *
3371 * The default is +true+ since Ruby 2.5.
3372 *
3373 * All threads created when this flag is true will report
3374 * a message on $stderr if an exception kills the thread.
3375 *
3376 * Thread.new { 1.times { raise } }
3377 *
3378 * will produce this output on $stderr:
3379 *
3380 * #<Thread:...> terminated with exception (report_on_exception is true):
3381 * Traceback (most recent call last):
3382 * 2: from -e:1:in `block in <main>'
3383 * 1: from -e:1:in `times'
3384 *
3385 * This is done to catch errors in threads early.
3386 * In some cases, you might not want this output.
3387 * There are multiple ways to avoid the extra output:
3388 *
3389 * * If the exception is not intended, the best is to fix the cause of
3390 * the exception so it does not happen anymore.
3391 * * If the exception is intended, it might be better to rescue it closer to
3392 * where it is raised rather then let it kill the Thread.
3393 * * If it is guaranteed the Thread will be joined with Thread#join or
3394 * Thread#value, then it is safe to disable this report with
3395 * <code>Thread.current.report_on_exception = false</code>
3396 * when starting the Thread.
3397 * However, this might handle the exception much later, or not at all
3398 * if the Thread is never joined due to the parent thread being blocked, etc.
3399 *
3400 * See also ::report_on_exception=.
3401 *
3402 * There is also an instance level method to set this for a specific thread,
3403 * see #report_on_exception=.
3404 *
3405 */
3406
3407static VALUE
3408rb_thread_s_report_exc(VALUE _)
3409{
3410 return RBOOL(GET_THREAD()->vm->thread_report_on_exception);
3411}
3412
3413
3414/*
3415 * call-seq:
3416 * Thread.report_on_exception= boolean -> true or false
3417 *
3418 * Returns the new state.
3419 * When set to +true+, all threads created afterwards will inherit the
3420 * condition and report a message on $stderr if an exception kills a thread:
3421 *
3422 * Thread.report_on_exception = true
3423 * t1 = Thread.new do
3424 * puts "In new thread"
3425 * raise "Exception from thread"
3426 * end
3427 * sleep(1)
3428 * puts "In the main thread"
3429 *
3430 * This will produce:
3431 *
3432 * In new thread
3433 * #<Thread:...prog.rb:2> terminated with exception (report_on_exception is true):
3434 * Traceback (most recent call last):
3435 * prog.rb:4:in `block in <main>': Exception from thread (RuntimeError)
3436 * In the main thread
3437 *
3438 * See also ::report_on_exception.
3439 *
3440 * There is also an instance level method to set this for a specific thread,
3441 * see #report_on_exception=.
3442 */
3443
3444static VALUE
3445rb_thread_s_report_exc_set(VALUE self, VALUE val)
3446{
3447 GET_THREAD()->vm->thread_report_on_exception = RTEST(val);
3448 return val;
3449}
3450
3451
3452/*
3453 * call-seq:
3454 * Thread.ignore_deadlock -> true or false
3455 *
3456 * Returns the status of the global ``ignore deadlock'' condition.
3457 * The default is +false+, so that deadlock conditions are not ignored.
3458 *
3459 * See also ::ignore_deadlock=.
3460 *
3461 */
3462
3463static VALUE
3464rb_thread_s_ignore_deadlock(VALUE _)
3465{
3466 return RBOOL(GET_THREAD()->vm->thread_ignore_deadlock);
3467}
3468
3469
3470/*
3471 * call-seq:
3472 * Thread.ignore_deadlock = boolean -> true or false
3473 *
3474 * Returns the new state.
3475 * When set to +true+, the VM will not check for deadlock conditions.
3476 * It is only useful to set this if your application can break a
3477 * deadlock condition via some other means, such as a signal.
3478 *
3479 * Thread.ignore_deadlock = true
3480 * queue = Thread::Queue.new
3481 *
3482 * trap(:SIGUSR1){queue.push "Received signal"}
3483 *
3484 * # raises fatal error unless ignoring deadlock
3485 * puts queue.pop
3486 *
3487 * See also ::ignore_deadlock.
3488 */
3489
3490static VALUE
3491rb_thread_s_ignore_deadlock_set(VALUE self, VALUE val)
3492{
3493 GET_THREAD()->vm->thread_ignore_deadlock = RTEST(val);
3494 return val;
3495}
3496
3497
3498/*
3499 * call-seq:
3500 * thr.report_on_exception -> true or false
3501 *
3502 * Returns the status of the thread-local ``report on exception'' condition for
3503 * this +thr+.
3504 *
3505 * The default value when creating a Thread is the value of
3506 * the global flag Thread.report_on_exception.
3507 *
3508 * See also #report_on_exception=.
3509 *
3510 * There is also a class level method to set this for all new threads, see
3511 * ::report_on_exception=.
3512 */
3513
3514static VALUE
3515rb_thread_report_exc(VALUE thread)
3516{
3517 return RBOOL(rb_thread_ptr(thread)->report_on_exception);
3518}
3519
3520
3521/*
3522 * call-seq:
3523 * thr.report_on_exception= boolean -> true or false
3524 *
3525 * When set to +true+, a message is printed on $stderr if an exception
3526 * kills this +thr+. See ::report_on_exception for details.
3527 *
3528 * See also #report_on_exception.
3529 *
3530 * There is also a class level method to set this for all new threads, see
3531 * ::report_on_exception=.
3532 */
3533
3534static VALUE
3535rb_thread_report_exc_set(VALUE thread, VALUE val)
3536{
3537 rb_thread_ptr(thread)->report_on_exception = RTEST(val);
3538 return val;
3539}
3540
3541
3542/*
3543 * call-seq:
3544 * thr.group -> thgrp or nil
3545 *
3546 * Returns the ThreadGroup which contains the given thread.
3547 *
3548 * Thread.main.group #=> #<ThreadGroup:0x4029d914>
3549 */
3550
3551VALUE
3552rb_thread_group(VALUE thread)
3553{
3554 return rb_thread_ptr(thread)->thgroup;
3555}
3556
3557static const char *
3558thread_status_name(rb_thread_t *th, int detail)
3559{
3560 switch (th->status) {
3561 case THREAD_RUNNABLE:
3562 return th->to_kill ? "aborting" : "run";
3563 case THREAD_STOPPED_FOREVER:
3564 if (detail) return "sleep_forever";
3565 case THREAD_STOPPED:
3566 return "sleep";
3567 case THREAD_KILLED:
3568 return "dead";
3569 default:
3570 return "unknown";
3571 }
3572}
3573
3574static int
3575rb_threadptr_dead(rb_thread_t *th)
3576{
3577 return th->status == THREAD_KILLED;
3578}
3579
3580
3581/*
3582 * call-seq:
3583 * thr.status -> string, false or nil
3584 *
3585 * Returns the status of +thr+.
3586 *
3587 * [<tt>"sleep"</tt>]
3588 * Returned if this thread is sleeping or waiting on I/O
3589 * [<tt>"run"</tt>]
3590 * When this thread is executing
3591 * [<tt>"aborting"</tt>]
3592 * If this thread is aborting
3593 * [+false+]
3594 * When this thread is terminated normally
3595 * [+nil+]
3596 * If terminated with an exception.
3597 *
3598 * a = Thread.new { raise("die now") }
3599 * b = Thread.new { Thread.stop }
3600 * c = Thread.new { Thread.exit }
3601 * d = Thread.new { sleep }
3602 * d.kill #=> #<Thread:0x401b3678 aborting>
3603 * a.status #=> nil
3604 * b.status #=> "sleep"
3605 * c.status #=> false
3606 * d.status #=> "aborting"
3607 * Thread.current.status #=> "run"
3608 *
3609 * See also the instance methods #alive? and #stop?
3610 */
3611
3612static VALUE
3613rb_thread_status(VALUE thread)
3614{
3615 rb_thread_t *target_th = rb_thread_ptr(thread);
3616
3617 if (rb_threadptr_dead(target_th)) {
3618 if (!NIL_P(target_th->ec->errinfo) &&
3619 !FIXNUM_P(target_th->ec->errinfo)) {
3620 return Qnil;
3621 }
3622 else {
3623 return Qfalse;
3624 }
3625 }
3626 else {
3627 return rb_str_new2(thread_status_name(target_th, FALSE));
3628 }
3629}
3630
3631
3632/*
3633 * call-seq:
3634 * thr.alive? -> true or false
3635 *
3636 * Returns +true+ if +thr+ is running or sleeping.
3637 *
3638 * thr = Thread.new { }
3639 * thr.join #=> #<Thread:0x401b3fb0 dead>
3640 * Thread.current.alive? #=> true
3641 * thr.alive? #=> false
3642 *
3643 * See also #stop? and #status.
3644 */
3645
3646static VALUE
3647rb_thread_alive_p(VALUE thread)
3648{
3649 return RBOOL(!thread_finished(rb_thread_ptr(thread)));
3650}
3651
3652/*
3653 * call-seq:
3654 * thr.stop? -> true or false
3655 *
3656 * Returns +true+ if +thr+ is dead or sleeping.
3657 *
3658 * a = Thread.new { Thread.stop }
3659 * b = Thread.current
3660 * a.stop? #=> true
3661 * b.stop? #=> false
3662 *
3663 * See also #alive? and #status.
3664 */
3665
3666static VALUE
3667rb_thread_stop_p(VALUE thread)
3668{
3669 rb_thread_t *th = rb_thread_ptr(thread);
3670
3671 if (rb_threadptr_dead(th)) {
3672 return Qtrue;
3673 }
3674 return RBOOL(th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER);
3675}
3676
3677/*
3678 * call-seq:
3679 * thr.name -> string
3680 *
3681 * show the name of the thread.
3682 */
3683
3684static VALUE
3685rb_thread_getname(VALUE thread)
3686{
3687 return rb_thread_ptr(thread)->name;
3688}
3689
3690/*
3691 * call-seq:
3692 * thr.name=(name) -> string
3693 *
3694 * set given name to the ruby thread.
3695 * On some platform, it may set the name to pthread and/or kernel.
3696 */
3697
3698static VALUE
3699rb_thread_setname(VALUE thread, VALUE name)
3700{
3701 rb_thread_t *target_th = rb_thread_ptr(thread);
3702
3703 if (!NIL_P(name)) {
3704 rb_encoding *enc;
3705 StringValueCStr(name);
3706 enc = rb_enc_get(name);
3707 if (!rb_enc_asciicompat(enc)) {
3708 rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
3709 rb_enc_name(enc));
3710 }
3711 name = rb_str_new_frozen(name);
3712 }
3713 target_th->name = name;
3714 if (threadptr_initialized(target_th) && target_th->has_dedicated_nt) {
3715 native_set_another_thread_name(target_th->nt->thread_id, name);
3716 }
3717 return name;
3718}
3719
3720#if USE_NATIVE_THREAD_NATIVE_THREAD_ID
3721/*
3722 * call-seq:
3723 * thr.native_thread_id -> integer
3724 *
3725 * Return the native thread ID which is used by the Ruby thread.
3726 *
3727 * The ID depends on the OS. (not POSIX thread ID returned by pthread_self(3))
3728 * * On Linux it is TID returned by gettid(2).
3729 * * On macOS it is the system-wide unique integral ID of thread returned
3730 * by pthread_threadid_np(3).
3731 * * On FreeBSD it is the unique integral ID of the thread returned by
3732 * pthread_getthreadid_np(3).
3733 * * On Windows it is the thread identifier returned by GetThreadId().
3734 * * On other platforms, it raises NotImplementedError.
3735 *
3736 * NOTE:
3737 * If the thread is not associated yet or already deassociated with a native
3738 * thread, it returns _nil_.
3739 * If the Ruby implementation uses M:N thread model, the ID may change
3740 * depending on the timing.
3741 */
3742
3743static VALUE
3744rb_thread_native_thread_id(VALUE thread)
3745{
3746 rb_thread_t *target_th = rb_thread_ptr(thread);
3747 if (rb_threadptr_dead(target_th)) return Qnil;
3748 return native_thread_native_thread_id(target_th);
3749}
3750#else
3751# define rb_thread_native_thread_id rb_f_notimplement
3752#endif
3753
3754/*
3755 * call-seq:
3756 * thr.to_s -> string
3757 *
3758 * Dump the name, id, and status of _thr_ to a string.
3759 */
3760
3761static VALUE
3762rb_thread_to_s(VALUE thread)
3763{
3764 VALUE cname = rb_class_path(rb_obj_class(thread));
3765 rb_thread_t *target_th = rb_thread_ptr(thread);
3766 const char *status;
3767 VALUE str, loc;
3768
3769 status = thread_status_name(target_th, TRUE);
3770 str = rb_sprintf("#<%"PRIsVALUE":%p", cname, (void *)thread);
3771 if (!NIL_P(target_th->name)) {
3772 rb_str_catf(str, "@%"PRIsVALUE, target_th->name);
3773 }
3774 if ((loc = threadptr_invoke_proc_location(target_th)) != Qnil) {
3775 rb_str_catf(str, " %"PRIsVALUE":%"PRIsVALUE,
3776 RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
3777 }
3778 rb_str_catf(str, " %s>", status);
3779
3780 return str;
3781}
3782
3783/* variables for recursive traversals */
3784#define recursive_key id__recursive_key__
3785
3786static VALUE
3787threadptr_local_aref(rb_thread_t *th, ID id)
3788{
3789 if (id == recursive_key) {
3790 return th->ec->local_storage_recursive_hash;
3791 }
3792 else {
3793 VALUE val;
3794 struct rb_id_table *local_storage = th->ec->local_storage;
3795
3796 if (local_storage != NULL && rb_id_table_lookup(local_storage, id, &val)) {
3797 return val;
3798 }
3799 else {
3800 return Qnil;
3801 }
3802 }
3803}
3804
3806rb_thread_local_aref(VALUE thread, ID id)
3807{
3808 return threadptr_local_aref(rb_thread_ptr(thread), id);
3809}
3810
3811/*
3812 * call-seq:
3813 * thr[sym] -> obj or nil
3814 *
3815 * Attribute Reference---Returns the value of a fiber-local variable (current thread's root fiber
3816 * if not explicitly inside a Fiber), using either a symbol or a string name.
3817 * If the specified variable does not exist, returns +nil+.
3818 *
3819 * [
3820 * Thread.new { Thread.current["name"] = "A" },
3821 * Thread.new { Thread.current[:name] = "B" },
3822 * Thread.new { Thread.current["name"] = "C" }
3823 * ].each do |th|
3824 * th.join
3825 * puts "#{th.inspect}: #{th[:name]}"
3826 * end
3827 *
3828 * This will produce:
3829 *
3830 * #<Thread:0x00000002a54220 dead>: A
3831 * #<Thread:0x00000002a541a8 dead>: B
3832 * #<Thread:0x00000002a54130 dead>: C
3833 *
3834 * Thread#[] and Thread#[]= are not thread-local but fiber-local.
3835 * This confusion did not exist in Ruby 1.8 because
3836 * fibers are only available since Ruby 1.9.
3837 * Ruby 1.9 chooses that the methods behaves fiber-local to save
3838 * following idiom for dynamic scope.
3839 *
3840 * def meth(newvalue)
3841 * begin
3842 * oldvalue = Thread.current[:name]
3843 * Thread.current[:name] = newvalue
3844 * yield
3845 * ensure
3846 * Thread.current[:name] = oldvalue
3847 * end
3848 * end
3849 *
3850 * The idiom may not work as dynamic scope if the methods are thread-local
3851 * and a given block switches fiber.
3852 *
3853 * f = Fiber.new {
3854 * meth(1) {
3855 * Fiber.yield
3856 * }
3857 * }
3858 * meth(2) {
3859 * f.resume
3860 * }
3861 * f.resume
3862 * p Thread.current[:name]
3863 * #=> nil if fiber-local
3864 * #=> 2 if thread-local (The value 2 is leaked to outside of meth method.)
3865 *
3866 * For thread-local variables, please see #thread_variable_get and
3867 * #thread_variable_set.
3868 *
3869 */
3870
3871static VALUE
3872rb_thread_aref(VALUE thread, VALUE key)
3873{
3874 ID id = rb_check_id(&key);
3875 if (!id) return Qnil;
3876 return rb_thread_local_aref(thread, id);
3877}
3878
3879/*
3880 * call-seq:
3881 * thr.fetch(sym) -> obj
3882 * thr.fetch(sym) { } -> obj
3883 * thr.fetch(sym, default) -> obj
3884 *
3885 * Returns a fiber-local for the given key. If the key can't be
3886 * found, there are several options: With no other arguments, it will
3887 * raise a KeyError exception; if <i>default</i> is given, then that
3888 * will be returned; if the optional code block is specified, then
3889 * that will be run and its result returned. See Thread#[] and
3890 * Hash#fetch.
3891 */
3892static VALUE
3893rb_thread_fetch(int argc, VALUE *argv, VALUE self)
3894{
3895 VALUE key, val;
3896 ID id;
3897 rb_thread_t *target_th = rb_thread_ptr(self);
3898 int block_given;
3899
3900 rb_check_arity(argc, 1, 2);
3901 key = argv[0];
3902
3903 block_given = rb_block_given_p();
3904 if (block_given && argc == 2) {
3905 rb_warn("block supersedes default value argument");
3906 }
3907
3908 id = rb_check_id(&key);
3909
3910 if (id == recursive_key) {
3911 return target_th->ec->local_storage_recursive_hash;
3912 }
3913 else if (id && target_th->ec->local_storage &&
3914 rb_id_table_lookup(target_th->ec->local_storage, id, &val)) {
3915 return val;
3916 }
3917 else if (block_given) {
3918 return rb_yield(key);
3919 }
3920 else if (argc == 1) {
3921 rb_key_err_raise(rb_sprintf("key not found: %+"PRIsVALUE, key), self, key);
3922 }
3923 else {
3924 return argv[1];
3925 }
3926}
3927
3928static VALUE
3929threadptr_local_aset(rb_thread_t *th, ID id, VALUE val)
3930{
3931 if (id == recursive_key) {
3932 th->ec->local_storage_recursive_hash = val;
3933 return val;
3934 }
3935 else {
3936 struct rb_id_table *local_storage = th->ec->local_storage;
3937
3938 if (NIL_P(val)) {
3939 if (!local_storage) return Qnil;
3940 rb_id_table_delete(local_storage, id);
3941 return Qnil;
3942 }
3943 else {
3944 if (local_storage == NULL) {
3945 th->ec->local_storage = local_storage = rb_id_table_create(0);
3946 }
3947 rb_id_table_insert(local_storage, id, val);
3948 return val;
3949 }
3950 }
3951}
3952
3954rb_thread_local_aset(VALUE thread, ID id, VALUE val)
3955{
3956 if (OBJ_FROZEN(thread)) {
3957 rb_frozen_error_raise(thread, "can't modify frozen thread locals");
3958 }
3959
3960 return threadptr_local_aset(rb_thread_ptr(thread), id, val);
3961}
3962
3963/*
3964 * call-seq:
3965 * thr[sym] = obj -> obj
3966 *
3967 * Attribute Assignment---Sets or creates the value of a fiber-local variable,
3968 * using either a symbol or a string.
3969 *
3970 * See also Thread#[].
3971 *
3972 * For thread-local variables, please see #thread_variable_set and
3973 * #thread_variable_get.
3974 */
3975
3976static VALUE
3977rb_thread_aset(VALUE self, VALUE id, VALUE val)
3978{
3979 return rb_thread_local_aset(self, rb_to_id(id), val);
3980}
3981
3982/*
3983 * call-seq:
3984 * thr.thread_variable_get(key) -> obj or nil
3985 *
3986 * Returns the value of a thread local variable that has been set. Note that
3987 * these are different than fiber local values. For fiber local values,
3988 * please see Thread#[] and Thread#[]=.
3989 *
3990 * Thread local values are carried along with threads, and do not respect
3991 * fibers. For example:
3992 *
3993 * Thread.new {
3994 * Thread.current.thread_variable_set("foo", "bar") # set a thread local
3995 * Thread.current["foo"] = "bar" # set a fiber local
3996 *
3997 * Fiber.new {
3998 * Fiber.yield [
3999 * Thread.current.thread_variable_get("foo"), # get the thread local
4000 * Thread.current["foo"], # get the fiber local
4001 * ]
4002 * }.resume
4003 * }.join.value # => ['bar', nil]
4004 *
4005 * The value "bar" is returned for the thread local, where nil is returned
4006 * for the fiber local. The fiber is executed in the same thread, so the
4007 * thread local values are available.
4008 */
4009
4010static VALUE
4011rb_thread_variable_get(VALUE thread, VALUE key)
4012{
4013 VALUE locals;
4014 VALUE symbol = rb_to_symbol(key);
4015
4016 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4017 return Qnil;
4018 }
4019 locals = rb_thread_local_storage(thread);
4020 return rb_hash_aref(locals, symbol);
4021}
4022
4023/*
4024 * call-seq:
4025 * thr.thread_variable_set(key, value)
4026 *
4027 * Sets a thread local with +key+ to +value+. Note that these are local to
4028 * threads, and not to fibers. Please see Thread#thread_variable_get and
4029 * Thread#[] for more information.
4030 */
4031
4032static VALUE
4033rb_thread_variable_set(VALUE thread, VALUE key, VALUE val)
4034{
4035 VALUE locals;
4036
4037 if (OBJ_FROZEN(thread)) {
4038 rb_frozen_error_raise(thread, "can't modify frozen thread locals");
4039 }
4040
4041 locals = rb_thread_local_storage(thread);
4042 return rb_hash_aset(locals, rb_to_symbol(key), val);
4043}
4044
4045/*
4046 * call-seq:
4047 * thr.key?(sym) -> true or false
4048 *
4049 * Returns +true+ if the given string (or symbol) exists as a fiber-local
4050 * variable.
4051 *
4052 * me = Thread.current
4053 * me[:oliver] = "a"
4054 * me.key?(:oliver) #=> true
4055 * me.key?(:stanley) #=> false
4056 */
4057
4058static VALUE
4059rb_thread_key_p(VALUE self, VALUE key)
4060{
4061 VALUE val;
4062 ID id = rb_check_id(&key);
4063 struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
4064
4065 if (!id || local_storage == NULL) {
4066 return Qfalse;
4067 }
4068 return RBOOL(rb_id_table_lookup(local_storage, id, &val));
4069}
4070
4071static enum rb_id_table_iterator_result
4072thread_keys_i(ID key, VALUE value, void *ary)
4073{
4074 rb_ary_push((VALUE)ary, ID2SYM(key));
4075 return ID_TABLE_CONTINUE;
4076}
4077
4079rb_thread_alone(void)
4080{
4081 // TODO
4082 return rb_ractor_living_thread_num(GET_RACTOR()) == 1;
4083}
4084
4085/*
4086 * call-seq:
4087 * thr.keys -> array
4088 *
4089 * Returns an array of the names of the fiber-local variables (as Symbols).
4090 *
4091 * thr = Thread.new do
4092 * Thread.current[:cat] = 'meow'
4093 * Thread.current["dog"] = 'woof'
4094 * end
4095 * thr.join #=> #<Thread:0x401b3f10 dead>
4096 * thr.keys #=> [:dog, :cat]
4097 */
4098
4099static VALUE
4100rb_thread_keys(VALUE self)
4101{
4102 struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
4103 VALUE ary = rb_ary_new();
4104
4105 if (local_storage) {
4106 rb_id_table_foreach(local_storage, thread_keys_i, (void *)ary);
4107 }
4108 return ary;
4109}
4110
4111static int
4112keys_i(VALUE key, VALUE value, VALUE ary)
4113{
4114 rb_ary_push(ary, key);
4115 return ST_CONTINUE;
4116}
4117
4118/*
4119 * call-seq:
4120 * thr.thread_variables -> array
4121 *
4122 * Returns an array of the names of the thread-local variables (as Symbols).
4123 *
4124 * thr = Thread.new do
4125 * Thread.current.thread_variable_set(:cat, 'meow')
4126 * Thread.current.thread_variable_set("dog", 'woof')
4127 * end
4128 * thr.join #=> #<Thread:0x401b3f10 dead>
4129 * thr.thread_variables #=> [:dog, :cat]
4130 *
4131 * Note that these are not fiber local variables. Please see Thread#[] and
4132 * Thread#thread_variable_get for more details.
4133 */
4134
4135static VALUE
4136rb_thread_variables(VALUE thread)
4137{
4138 VALUE locals;
4139 VALUE ary;
4140
4141 ary = rb_ary_new();
4142 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4143 return ary;
4144 }
4145 locals = rb_thread_local_storage(thread);
4146 rb_hash_foreach(locals, keys_i, ary);
4147
4148 return ary;
4149}
4150
4151/*
4152 * call-seq:
4153 * thr.thread_variable?(key) -> true or false
4154 *
4155 * Returns +true+ if the given string (or symbol) exists as a thread-local
4156 * variable.
4157 *
4158 * me = Thread.current
4159 * me.thread_variable_set(:oliver, "a")
4160 * me.thread_variable?(:oliver) #=> true
4161 * me.thread_variable?(:stanley) #=> false
4162 *
4163 * Note that these are not fiber local variables. Please see Thread#[] and
4164 * Thread#thread_variable_get for more details.
4165 */
4166
4167static VALUE
4168rb_thread_variable_p(VALUE thread, VALUE key)
4169{
4170 VALUE locals;
4171 VALUE symbol = rb_to_symbol(key);
4172
4173 if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4174 return Qfalse;
4175 }
4176 locals = rb_thread_local_storage(thread);
4177
4178 return RBOOL(rb_hash_lookup(locals, symbol) != Qnil);
4179}
4180
4181/*
4182 * call-seq:
4183 * thr.priority -> integer
4184 *
4185 * Returns the priority of <i>thr</i>. Default is inherited from the
4186 * current thread which creating the new thread, or zero for the
4187 * initial main thread; higher-priority thread will run more frequently
4188 * than lower-priority threads (but lower-priority threads can also run).
4189 *
4190 * This is just hint for Ruby thread scheduler. It may be ignored on some
4191 * platform.
4192 *
4193 * Thread.current.priority #=> 0
4194 */
4195
4196static VALUE
4197rb_thread_priority(VALUE thread)
4198{
4199 return INT2NUM(rb_thread_ptr(thread)->priority);
4200}
4201
4202
4203/*
4204 * call-seq:
4205 * thr.priority= integer -> thr
4206 *
4207 * Sets the priority of <i>thr</i> to <i>integer</i>. Higher-priority threads
4208 * will run more frequently than lower-priority threads (but lower-priority
4209 * threads can also run).
4210 *
4211 * This is just hint for Ruby thread scheduler. It may be ignored on some
4212 * platform.
4213 *
4214 * count1 = count2 = 0
4215 * a = Thread.new do
4216 * loop { count1 += 1 }
4217 * end
4218 * a.priority = -1
4219 *
4220 * b = Thread.new do
4221 * loop { count2 += 1 }
4222 * end
4223 * b.priority = -2
4224 * sleep 1 #=> 1
4225 * count1 #=> 622504
4226 * count2 #=> 5832
4227 */
4228
4229static VALUE
4230rb_thread_priority_set(VALUE thread, VALUE prio)
4231{
4232 rb_thread_t *target_th = rb_thread_ptr(thread);
4233 int priority;
4234
4235#if USE_NATIVE_THREAD_PRIORITY
4236 target_th->priority = NUM2INT(prio);
4237 native_thread_apply_priority(th);
4238#else
4239 priority = NUM2INT(prio);
4240 if (priority > RUBY_THREAD_PRIORITY_MAX) {
4241 priority = RUBY_THREAD_PRIORITY_MAX;
4242 }
4243 else if (priority < RUBY_THREAD_PRIORITY_MIN) {
4244 priority = RUBY_THREAD_PRIORITY_MIN;
4245 }
4246 target_th->priority = (int8_t)priority;
4247#endif
4248 return INT2NUM(target_th->priority);
4249}
4250
4251/* for IO */
4252
4253#if defined(NFDBITS) && defined(HAVE_RB_FD_INIT)
4254
4255/*
4256 * several Unix platforms support file descriptors bigger than FD_SETSIZE
4257 * in select(2) system call.
4258 *
4259 * - Linux 2.2.12 (?)
4260 * - NetBSD 1.2 (src/sys/kern/sys_generic.c:1.25)
4261 * select(2) documents how to allocate fd_set dynamically.
4262 * http://netbsd.gw.com/cgi-bin/man-cgi?select++NetBSD-4.0
4263 * - FreeBSD 2.2 (src/sys/kern/sys_generic.c:1.19)
4264 * - OpenBSD 2.0 (src/sys/kern/sys_generic.c:1.4)
4265 * select(2) documents how to allocate fd_set dynamically.
4266 * http://www.openbsd.org/cgi-bin/man.cgi?query=select&manpath=OpenBSD+4.4
4267 * - Solaris 8 has select_large_fdset
4268 * - Mac OS X 10.7 (Lion)
4269 * select(2) returns EINVAL if nfds is greater than FD_SET_SIZE and
4270 * _DARWIN_UNLIMITED_SELECT (or _DARWIN_C_SOURCE) isn't defined.
4271 * https://developer.apple.com/library/archive/releasenotes/Darwin/SymbolVariantsRelNotes/index.html
4272 *
4273 * When fd_set is not big enough to hold big file descriptors,
4274 * it should be allocated dynamically.
4275 * Note that this assumes fd_set is structured as bitmap.
4276 *
4277 * rb_fd_init allocates the memory.
4278 * rb_fd_term free the memory.
4279 * rb_fd_set may re-allocates bitmap.
4280 *
4281 * So rb_fd_set doesn't reject file descriptors bigger than FD_SETSIZE.
4282 */
4283
4284void
4286{
4287 fds->maxfd = 0;
4288 fds->fdset = ALLOC(fd_set);
4289 FD_ZERO(fds->fdset);
4290}
4291
4292void
4293rb_fd_init_copy(rb_fdset_t *dst, rb_fdset_t *src)
4294{
4295 size_t size = howmany(rb_fd_max(src), NFDBITS) * sizeof(fd_mask);
4296
4297 if (size < sizeof(fd_set))
4298 size = sizeof(fd_set);
4299 dst->maxfd = src->maxfd;
4300 dst->fdset = xmalloc(size);
4301 memcpy(dst->fdset, src->fdset, size);
4302}
4303
4304void
4306{
4307 xfree(fds->fdset);
4308 fds->maxfd = 0;
4309 fds->fdset = 0;
4310}
4311
4312void
4314{
4315 if (fds->fdset)
4316 MEMZERO(fds->fdset, fd_mask, howmany(fds->maxfd, NFDBITS));
4317}
4318
4319static void
4320rb_fd_resize(int n, rb_fdset_t *fds)
4321{
4322 size_t m = howmany(n + 1, NFDBITS) * sizeof(fd_mask);
4323 size_t o = howmany(fds->maxfd, NFDBITS) * sizeof(fd_mask);
4324
4325 if (m < sizeof(fd_set)) m = sizeof(fd_set);
4326 if (o < sizeof(fd_set)) o = sizeof(fd_set);
4327
4328 if (m > o) {
4329 fds->fdset = xrealloc(fds->fdset, m);
4330 memset((char *)fds->fdset + o, 0, m - o);
4331 }
4332 if (n >= fds->maxfd) fds->maxfd = n + 1;
4333}
4334
4335void
4336rb_fd_set(int n, rb_fdset_t *fds)
4337{
4338 rb_fd_resize(n, fds);
4339 FD_SET(n, fds->fdset);
4340}
4341
4342void
4343rb_fd_clr(int n, rb_fdset_t *fds)
4344{
4345 if (n >= fds->maxfd) return;
4346 FD_CLR(n, fds->fdset);
4347}
4348
4349int
4350rb_fd_isset(int n, const rb_fdset_t *fds)
4351{
4352 if (n >= fds->maxfd) return 0;
4353 return FD_ISSET(n, fds->fdset) != 0; /* "!= 0" avoids FreeBSD PR 91421 */
4354}
4355
4356void
4357rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
4358{
4359 size_t size = howmany(max, NFDBITS) * sizeof(fd_mask);
4360
4361 if (size < sizeof(fd_set)) size = sizeof(fd_set);
4362 dst->maxfd = max;
4363 dst->fdset = xrealloc(dst->fdset, size);
4364 memcpy(dst->fdset, src, size);
4365}
4366
4367void
4368rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
4369{
4370 size_t size = howmany(rb_fd_max(src), NFDBITS) * sizeof(fd_mask);
4371
4372 if (size < sizeof(fd_set))
4373 size = sizeof(fd_set);
4374 dst->maxfd = src->maxfd;
4375 dst->fdset = xrealloc(dst->fdset, size);
4376 memcpy(dst->fdset, src->fdset, size);
4377}
4378
4379int
4380rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout)
4381{
4382 fd_set *r = NULL, *w = NULL, *e = NULL;
4383 if (readfds) {
4384 rb_fd_resize(n - 1, readfds);
4385 r = rb_fd_ptr(readfds);
4386 }
4387 if (writefds) {
4388 rb_fd_resize(n - 1, writefds);
4389 w = rb_fd_ptr(writefds);
4390 }
4391 if (exceptfds) {
4392 rb_fd_resize(n - 1, exceptfds);
4393 e = rb_fd_ptr(exceptfds);
4394 }
4395 return select(n, r, w, e, timeout);
4396}
4397
4398#define rb_fd_no_init(fds) ((void)((fds)->fdset = 0), (void)((fds)->maxfd = 0))
4399
4400#undef FD_ZERO
4401#undef FD_SET
4402#undef FD_CLR
4403#undef FD_ISSET
4404
4405#define FD_ZERO(f) rb_fd_zero(f)
4406#define FD_SET(i, f) rb_fd_set((i), (f))
4407#define FD_CLR(i, f) rb_fd_clr((i), (f))
4408#define FD_ISSET(i, f) rb_fd_isset((i), (f))
4409
4410#elif defined(_WIN32)
4411
4412void
4414{
4415 set->capa = FD_SETSIZE;
4416 set->fdset = ALLOC(fd_set);
4417 FD_ZERO(set->fdset);
4418}
4419
4420void
4421rb_fd_init_copy(rb_fdset_t *dst, rb_fdset_t *src)
4422{
4423 rb_fd_init(dst);
4424 rb_fd_dup(dst, src);
4425}
4426
4427void
4429{
4430 xfree(set->fdset);
4431 set->fdset = NULL;
4432 set->capa = 0;
4433}
4434
4435void
4436rb_fd_set(int fd, rb_fdset_t *set)
4437{
4438 unsigned int i;
4439 SOCKET s = rb_w32_get_osfhandle(fd);
4440
4441 for (i = 0; i < set->fdset->fd_count; i++) {
4442 if (set->fdset->fd_array[i] == s) {
4443 return;
4444 }
4445 }
4446 if (set->fdset->fd_count >= (unsigned)set->capa) {
4447 set->capa = (set->fdset->fd_count / FD_SETSIZE + 1) * FD_SETSIZE;
4448 set->fdset =
4449 rb_xrealloc_mul_add(
4450 set->fdset, set->capa, sizeof(SOCKET), sizeof(unsigned int));
4451 }
4452 set->fdset->fd_array[set->fdset->fd_count++] = s;
4453}
4454
4455#undef FD_ZERO
4456#undef FD_SET
4457#undef FD_CLR
4458#undef FD_ISSET
4459
4460#define FD_ZERO(f) rb_fd_zero(f)
4461#define FD_SET(i, f) rb_fd_set((i), (f))
4462#define FD_CLR(i, f) rb_fd_clr((i), (f))
4463#define FD_ISSET(i, f) rb_fd_isset((i), (f))
4464
4465#define rb_fd_no_init(fds) (void)((fds)->fdset = 0)
4466
4467#endif
4468
4469#ifndef rb_fd_no_init
4470#define rb_fd_no_init(fds) (void)(fds)
4471#endif
4472
4473static int
4474wait_retryable(volatile int *result, int errnum, rb_hrtime_t *rel, rb_hrtime_t end)
4475{
4476 int r = *result;
4477 if (r < 0) {
4478 switch (errnum) {
4479 case EINTR:
4480#ifdef ERESTART
4481 case ERESTART:
4482#endif
4483 *result = 0;
4484 if (rel && hrtime_update_expire(rel, end)) {
4485 *rel = 0;
4486 }
4487 return TRUE;
4488 }
4489 return FALSE;
4490 }
4491 else if (r == 0) {
4492 /* check for spurious wakeup */
4493 if (rel) {
4494 return !hrtime_update_expire(rel, end);
4495 }
4496 return TRUE;
4497 }
4498 return FALSE;
4499}
4501struct select_set {
4502 int max;
4503 rb_thread_t *th;
4504 rb_fdset_t *rset;
4505 rb_fdset_t *wset;
4506 rb_fdset_t *eset;
4507 rb_fdset_t orig_rset;
4508 rb_fdset_t orig_wset;
4509 rb_fdset_t orig_eset;
4510 struct timeval *timeout;
4511};
4512
4513static VALUE
4514select_set_free(VALUE p)
4515{
4516 struct select_set *set = (struct select_set *)p;
4517
4518 rb_fd_term(&set->orig_rset);
4519 rb_fd_term(&set->orig_wset);
4520 rb_fd_term(&set->orig_eset);
4521
4522 return Qfalse;
4523}
4524
4525static VALUE
4526do_select(VALUE p)
4527{
4528 struct select_set *set = (struct select_set *)p;
4529 volatile int result = 0;
4530 int lerrno;
4531 rb_hrtime_t *to, rel, end = 0;
4532
4533 timeout_prepare(&to, &rel, &end, set->timeout);
4534 volatile rb_hrtime_t endtime = end;
4535#define restore_fdset(dst, src) \
4536 ((dst) ? rb_fd_dup(dst, src) : (void)0)
4537#define do_select_update() \
4538 (restore_fdset(set->rset, &set->orig_rset), \
4539 restore_fdset(set->wset, &set->orig_wset), \
4540 restore_fdset(set->eset, &set->orig_eset), \
4541 TRUE)
4542
4543 do {
4544 lerrno = 0;
4545
4546 BLOCKING_REGION(set->th, {
4547 struct timeval tv;
4548
4549 if (!RUBY_VM_INTERRUPTED(set->th->ec)) {
4550 result = native_fd_select(set->max,
4551 set->rset, set->wset, set->eset,
4552 rb_hrtime2timeval(&tv, to), set->th);
4553 if (result < 0) lerrno = errno;
4554 }
4555 }, ubf_select, set->th, TRUE);
4556
4557 RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec); /* may raise */
4558 } while (wait_retryable(&result, lerrno, to, endtime) && do_select_update());
4559
4560 RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec);
4561
4562 if (result < 0) {
4563 errno = lerrno;
4564 }
4565
4566 return (VALUE)result;
4567}
4568
4570rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t * except,
4571 struct timeval *timeout)
4572{
4573 struct select_set set;
4574
4575 set.th = GET_THREAD();
4576 RUBY_VM_CHECK_INTS_BLOCKING(set.th->ec);
4577 set.max = max;
4578 set.rset = read;
4579 set.wset = write;
4580 set.eset = except;
4581 set.timeout = timeout;
4582
4583 if (!set.rset && !set.wset && !set.eset) {
4584 if (!timeout) {
4586 return 0;
4587 }
4588 rb_thread_wait_for(*timeout);
4589 return 0;
4590 }
4591
4592#define fd_init_copy(f) do { \
4593 if (set.f) { \
4594 rb_fd_resize(set.max - 1, set.f); \
4595 if (&set.orig_##f != set.f) { /* sigwait_fd */ \
4596 rb_fd_init_copy(&set.orig_##f, set.f); \
4597 } \
4598 } \
4599 else { \
4600 rb_fd_no_init(&set.orig_##f); \
4601 } \
4602 } while (0)
4603 fd_init_copy(rset);
4604 fd_init_copy(wset);
4605 fd_init_copy(eset);
4606#undef fd_init_copy
4607
4608 return (int)rb_ensure(do_select, (VALUE)&set, select_set_free, (VALUE)&set);
4609}
4610
4611#ifdef USE_POLL
4612
4613/* The same with linux kernel. TODO: make platform independent definition. */
4614#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
4615#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
4616#define POLLEX_SET (POLLPRI)
4617
4618#ifndef POLLERR_SET /* defined for FreeBSD for now */
4619# define POLLERR_SET (0)
4620#endif
4621
4622static int
4623wait_for_single_fd_blocking_region(rb_thread_t *th, struct pollfd *fds, nfds_t nfds,
4624 rb_hrtime_t *const to, volatile int *lerrno)
4625{
4626 struct timespec ts;
4627 volatile int result = 0;
4628
4629 *lerrno = 0;
4630 BLOCKING_REGION(th, {
4631 if (!RUBY_VM_INTERRUPTED(th->ec)) {
4632 result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, to), 0);
4633 if (result < 0) *lerrno = errno;
4634 }
4635 }, ubf_select, th, TRUE);
4636 return result;
4637}
4638
4639/*
4640 * returns a mask of events
4641 */
4642static int
4643thread_io_wait(rb_thread_t *th, struct rb_io *io, int fd, int events, struct timeval *timeout)
4644{
4645 struct pollfd fds[1] = {{
4646 .fd = fd,
4647 .events = (short)events,
4648 .revents = 0,
4649 }};
4650 volatile int result = 0;
4651 nfds_t nfds;
4652 struct rb_io_blocking_operation blocking_operation;
4653 enum ruby_tag_type state;
4654 volatile int lerrno;
4655
4656 RUBY_ASSERT(th);
4657 rb_execution_context_t *ec = th->ec;
4658
4659 if (io) {
4660 blocking_operation.ec = ec;
4661 rb_io_blocking_operation_enter(io, &blocking_operation);
4662 }
4663
4664 if (timeout == NULL && thread_io_wait_events(th, fd, events, NULL)) {
4665 // fd is readable
4666 state = 0;
4667 fds[0].revents = events;
4668 errno = 0;
4669 }
4670 else {
4671 EC_PUSH_TAG(ec);
4672 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
4673 rb_hrtime_t *to, rel, end = 0;
4674 RUBY_VM_CHECK_INTS_BLOCKING(ec);
4675 timeout_prepare(&to, &rel, &end, timeout);
4676 do {
4677 nfds = numberof(fds);
4678 result = wait_for_single_fd_blocking_region(th, fds, nfds, to, &lerrno);
4679
4680 RUBY_VM_CHECK_INTS_BLOCKING(ec);
4681 } while (wait_retryable(&result, lerrno, to, end));
4682
4683 RUBY_VM_CHECK_INTS_BLOCKING(ec);
4684 }
4685
4686 EC_POP_TAG();
4687 }
4688
4689 if (io) {
4690 rb_io_blocking_operation_exit(io, &blocking_operation);
4691 }
4692
4693 if (state) {
4694 EC_JUMP_TAG(ec, state);
4695 }
4696
4697 if (result < 0) {
4698 errno = lerrno;
4699 return -1;
4700 }
4701
4702 if (fds[0].revents & POLLNVAL) {
4703 errno = EBADF;
4704 return -1;
4705 }
4706
4707 /*
4708 * POLLIN, POLLOUT have a different meanings from select(2)'s read/write bit.
4709 * Therefore we need to fix it up.
4710 */
4711 result = 0;
4712 if (fds[0].revents & POLLIN_SET)
4713 result |= RB_WAITFD_IN;
4714 if (fds[0].revents & POLLOUT_SET)
4715 result |= RB_WAITFD_OUT;
4716 if (fds[0].revents & POLLEX_SET)
4717 result |= RB_WAITFD_PRI;
4718
4719 /* all requested events are ready if there is an error */
4720 if (fds[0].revents & POLLERR_SET)
4721 result |= events;
4722
4723 return result;
4724}
4725#else /* ! USE_POLL - implement rb_io_poll_fd() using select() */
4726struct select_args {
4727 struct rb_io *io;
4728 struct rb_io_blocking_operation *blocking_operation;
4729
4730 union {
4731 int fd;
4732 int error;
4733 } as;
4734 rb_fdset_t *read;
4735 rb_fdset_t *write;
4736 rb_fdset_t *except;
4737 struct timeval *tv;
4738};
4739
4740static VALUE
4741select_single(VALUE ptr)
4742{
4743 struct select_args *args = (struct select_args *)ptr;
4744 int r;
4745
4746 r = rb_thread_fd_select(args->as.fd + 1,
4747 args->read, args->write, args->except, args->tv);
4748 if (r == -1)
4749 args->as.error = errno;
4750 if (r > 0) {
4751 r = 0;
4752 if (args->read && rb_fd_isset(args->as.fd, args->read))
4753 r |= RB_WAITFD_IN;
4754 if (args->write && rb_fd_isset(args->as.fd, args->write))
4755 r |= RB_WAITFD_OUT;
4756 if (args->except && rb_fd_isset(args->as.fd, args->except))
4757 r |= RB_WAITFD_PRI;
4758 }
4759 return (VALUE)r;
4760}
4761
4762static VALUE
4763select_single_cleanup(VALUE ptr)
4764{
4765 struct select_args *args = (struct select_args *)ptr;
4766
4767 if (args->blocking_operation) {
4768 rb_io_blocking_operation_exit(args->io, args->blocking_operation);
4769 }
4770
4771 if (args->read) rb_fd_term(args->read);
4772 if (args->write) rb_fd_term(args->write);
4773 if (args->except) rb_fd_term(args->except);
4774
4775 return (VALUE)-1;
4776}
4777
4778static rb_fdset_t *
4779init_set_fd(int fd, rb_fdset_t *fds)
4780{
4781 if (fd < 0) {
4782 return 0;
4783 }
4784 rb_fd_init(fds);
4785 rb_fd_set(fd, fds);
4786
4787 return fds;
4788}
4789
4790static int
4791thread_io_wait(rb_thread_t *th, struct rb_io *io, int fd, int events, struct timeval *timeout)
4792{
4793 rb_fdset_t rfds, wfds, efds;
4794 struct select_args args;
4795 VALUE ptr = (VALUE)&args;
4796
4797 struct rb_io_blocking_operation blocking_operation;
4798 if (io) {
4799 args.io = io;
4800 blocking_operation.ec = th->ec;
4801 rb_io_blocking_operation_enter(io, &blocking_operation);
4802 args.blocking_operation = &blocking_operation;
4803 }
4804 else {
4805 args.io = NULL;
4806 blocking_operation.ec = NULL;
4807 args.blocking_operation = NULL;
4808 }
4809
4810 args.as.fd = fd;
4811 args.read = (events & RB_WAITFD_IN) ? init_set_fd(fd, &rfds) : NULL;
4812 args.write = (events & RB_WAITFD_OUT) ? init_set_fd(fd, &wfds) : NULL;
4813 args.except = (events & RB_WAITFD_PRI) ? init_set_fd(fd, &efds) : NULL;
4814 args.tv = timeout;
4815
4816 int result = (int)rb_ensure(select_single, ptr, select_single_cleanup, ptr);
4817 if (result == -1)
4818 errno = args.as.error;
4819
4820 return result;
4821}
4822#endif /* ! USE_POLL */
4823
4824int
4825rb_thread_wait_for_single_fd(rb_thread_t *th, int fd, int events, struct timeval *timeout)
4826{
4827 return thread_io_wait(th, NULL, fd, events, timeout);
4828}
4829
4830int
4831rb_thread_io_wait(rb_thread_t *th, struct rb_io *io, int events, struct timeval * timeout)
4832{
4833 return thread_io_wait(th, io, io->fd, events, timeout);
4834}
4835
4836/*
4837 * for GC
4838 */
4839
4840#ifdef USE_CONSERVATIVE_STACK_END
4841void
4842rb_gc_set_stack_end(VALUE **stack_end_p)
4843{
4844 VALUE stack_end;
4845COMPILER_WARNING_PUSH
4846#if RBIMPL_COMPILER_IS(GCC)
4847COMPILER_WARNING_IGNORED(-Wdangling-pointer);
4848#endif
4849 *stack_end_p = &stack_end;
4850COMPILER_WARNING_POP
4851}
4852#endif
4853
4854/*
4855 *
4856 */
4857
4858void
4859rb_threadptr_check_signal(rb_thread_t *mth)
4860{
4861 /* mth must be main_thread */
4862 if (rb_signal_buff_size() > 0) {
4863 /* wakeup main thread */
4864 threadptr_trap_interrupt(mth);
4865 }
4866}
4867
4868static void
4869async_bug_fd(const char *mesg, int errno_arg, int fd)
4870{
4871 char buff[64];
4872 size_t n = strlcpy(buff, mesg, sizeof(buff));
4873 if (n < sizeof(buff)-3) {
4874 ruby_snprintf(buff+n, sizeof(buff)-n, "(%d)", fd);
4875 }
4876 rb_async_bug_errno(buff, errno_arg);
4877}
4878
4879/* VM-dependent API is not available for this function */
4880static int
4881consume_communication_pipe(int fd)
4882{
4883#if USE_EVENTFD
4884 uint64_t buff[1];
4885#else
4886 /* buffer can be shared because no one refers to them. */
4887 static char buff[1024];
4888#endif
4889 ssize_t result;
4890 int ret = FALSE; /* for rb_sigwait_sleep */
4891
4892 while (1) {
4893 result = read(fd, buff, sizeof(buff));
4894#if USE_EVENTFD
4895 RUBY_DEBUG_LOG("resultf:%d buff:%lu", (int)result, (unsigned long)buff[0]);
4896#else
4897 RUBY_DEBUG_LOG("result:%d", (int)result);
4898#endif
4899 if (result > 0) {
4900 ret = TRUE;
4901 if (USE_EVENTFD || result < (ssize_t)sizeof(buff)) {
4902 return ret;
4903 }
4904 }
4905 else if (result == 0) {
4906 return ret;
4907 }
4908 else if (result < 0) {
4909 int e = errno;
4910 switch (e) {
4911 case EINTR:
4912 continue; /* retry */
4913 case EAGAIN:
4914#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
4915 case EWOULDBLOCK:
4916#endif
4917 return ret;
4918 default:
4919 async_bug_fd("consume_communication_pipe: read", e, fd);
4920 }
4921 }
4922 }
4923}
4924
4925void
4926rb_thread_stop_timer_thread(void)
4927{
4928 if (TIMER_THREAD_CREATED_P() && native_stop_timer_thread()) {
4929 native_reset_timer_thread();
4930 }
4931}
4932
4933void
4934rb_thread_reset_timer_thread(void)
4935{
4936 native_reset_timer_thread();
4937}
4938
4939void
4940rb_thread_start_timer_thread(void)
4941{
4942 system_working = 1;
4943 rb_thread_create_timer_thread();
4944}
4945
4946static int
4947clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
4948{
4949 int i;
4950 VALUE coverage = (VALUE)val;
4951 VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
4952 VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
4953
4954 if (lines) {
4955 if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
4956 rb_ary_clear(lines);
4957 }
4958 else {
4959 int i;
4960 for (i = 0; i < RARRAY_LEN(lines); i++) {
4961 if (RARRAY_AREF(lines, i) != Qnil)
4962 RARRAY_ASET(lines, i, INT2FIX(0));
4963 }
4964 }
4965 }
4966 if (branches) {
4967 VALUE counters = RARRAY_AREF(branches, 1);
4968 for (i = 0; i < RARRAY_LEN(counters); i++) {
4969 RARRAY_ASET(counters, i, INT2FIX(0));
4970 }
4971 }
4972
4973 return ST_CONTINUE;
4974}
4975
4976void
4977rb_clear_coverages(void)
4978{
4979 VALUE coverages = rb_get_coverages();
4980 if (RTEST(coverages)) {
4981 rb_hash_foreach(coverages, clear_coverage_i, 0);
4982 }
4983}
4984
4985#if defined(HAVE_WORKING_FORK)
4986
4987static void
4988rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const rb_thread_t *))
4989{
4990 rb_thread_t *i = 0;
4991 rb_vm_t *vm = th->vm;
4992 rb_ractor_t *r = th->ractor;
4993 vm->ractor.main_ractor = r;
4994 vm->ractor.main_thread = th;
4995 r->threads.main = th;
4996 r->status_ = ractor_created;
4997
4998 thread_sched_atfork(TH_SCHED(th));
4999 ubf_list_atfork();
5000 rb_signal_atfork();
5001
5002 // OK. Only this thread accesses:
5003 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
5004 if (r != vm->ractor.main_ractor) {
5005 rb_ractor_terminate_atfork(vm, r);
5006 }
5007 ccan_list_for_each(&r->threads.set, i, lt_node) {
5008 atfork(i, th);
5009 }
5010 }
5011 rb_vm_living_threads_init(vm);
5012
5013 rb_ractor_atfork(vm, th);
5014 rb_vm_postponed_job_atfork();
5015
5016 /* may be held by any thread in parent */
5017 rb_native_mutex_initialize(&th->interrupt_lock);
5018 ccan_list_head_init(&th->interrupt_exec_tasks);
5019
5020 vm->fork_gen++;
5021 rb_ractor_sleeper_threads_clear(th->ractor);
5022 rb_clear_coverages();
5023
5024 // restart timer thread (timer threads access to `vm->waitpid_lock` and so on.
5025 rb_thread_reset_timer_thread();
5026 rb_thread_start_timer_thread();
5027
5028 VM_ASSERT(vm->ractor.blocking_cnt == 0);
5029 VM_ASSERT(vm->ractor.cnt == 1);
5030}
5031
5032static void
5033terminate_atfork_i(rb_thread_t *th, const rb_thread_t *current_th)
5034{
5035 if (th != current_th) {
5036 // Clear the scheduler as it is no longer operational:
5037 th->scheduler = Qnil;
5038
5039 rb_native_mutex_initialize(&th->interrupt_lock);
5040 rb_mutex_abandon_keeping_mutexes(th);
5041 rb_mutex_abandon_locking_mutex(th);
5042 thread_cleanup_func(th, TRUE);
5043 }
5044}
5045
5046void rb_fiber_atfork(rb_thread_t *);
5047void
5048rb_thread_atfork(void)
5049{
5050 rb_thread_t *th = GET_THREAD();
5051 rb_threadptr_pending_interrupt_clear(th);
5052 rb_thread_atfork_internal(th, terminate_atfork_i);
5053 th->join_list = NULL;
5054 th->scheduler = Qnil;
5055 rb_fiber_atfork(th);
5056
5057 /* We don't want reproduce CVE-2003-0900. */
5059}
5060
5061static void
5062terminate_atfork_before_exec_i(rb_thread_t *th, const rb_thread_t *current_th)
5063{
5064 if (th != current_th) {
5065 thread_cleanup_func_before_exec(th);
5066 }
5067}
5068
5069void
5071{
5072 rb_thread_t *th = GET_THREAD();
5073 rb_thread_atfork_internal(th, terminate_atfork_before_exec_i);
5074}
5075#else
5076void
5077rb_thread_atfork(void)
5078{
5079}
5080
5081void
5083{
5084}
5085#endif
5087struct thgroup {
5088 int enclosed;
5089};
5090
5091static const rb_data_type_t thgroup_data_type = {
5092 "thgroup",
5093 {
5094 0,
5096 NULL, // No external memory to report
5097 },
5098 0, 0, RUBY_TYPED_FREE_IMMEDIATELY | RUBY_TYPED_WB_PROTECTED | RUBY_TYPED_EMBEDDABLE
5099};
5100
5101/*
5102 * Document-class: ThreadGroup
5103 *
5104 * ThreadGroup provides a means of keeping track of a number of threads as a
5105 * group.
5106 *
5107 * A given Thread object can only belong to one ThreadGroup at a time; adding
5108 * a thread to a new group will remove it from any previous group.
5109 *
5110 * Newly created threads belong to the same group as the thread from which they
5111 * were created.
5112 */
5113
5114/*
5115 * Document-const: Default
5116 *
5117 * The default ThreadGroup created when Ruby starts; all Threads belong to it
5118 * by default.
5119 */
5120static VALUE
5121thgroup_s_alloc(VALUE klass)
5122{
5123 VALUE group;
5124 struct thgroup *data;
5125
5126 group = TypedData_Make_Struct(klass, struct thgroup, &thgroup_data_type, data);
5127 data->enclosed = 0;
5128
5129 return group;
5130}
5131
5132/*
5133 * call-seq:
5134 * thgrp.list -> array
5135 *
5136 * Returns an array of all existing Thread objects that belong to this group.
5137 *
5138 * ThreadGroup::Default.list #=> [#<Thread:0x401bdf4c run>]
5139 */
5140
5141static VALUE
5142thgroup_list(VALUE group)
5143{
5144 VALUE ary = rb_ary_new();
5145 rb_thread_t *th = 0;
5146 rb_ractor_t *r = GET_RACTOR();
5147
5148 ccan_list_for_each(&r->threads.set, th, lt_node) {
5149 if (th->thgroup == group) {
5150 rb_ary_push(ary, th->self);
5151 }
5152 }
5153 return ary;
5154}
5155
5156
5157/*
5158 * call-seq:
5159 * thgrp.enclose -> thgrp
5160 *
5161 * Prevents threads from being added to or removed from the receiving
5162 * ThreadGroup.
5163 *
5164 * New threads can still be started in an enclosed ThreadGroup.
5165 *
5166 * ThreadGroup::Default.enclose #=> #<ThreadGroup:0x4029d914>
5167 * thr = Thread.new { Thread.stop } #=> #<Thread:0x402a7210 sleep>
5168 * tg = ThreadGroup.new #=> #<ThreadGroup:0x402752d4>
5169 * tg.add thr
5170 * #=> ThreadError: can't move from the enclosed thread group
5171 */
5172
5173static VALUE
5174thgroup_enclose(VALUE group)
5175{
5176 struct thgroup *data;
5177
5178 TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
5179 data->enclosed = 1;
5180
5181 return group;
5182}
5183
5184
5185/*
5186 * call-seq:
5187 * thgrp.enclosed? -> true or false
5188 *
5189 * Returns +true+ if the +thgrp+ is enclosed. See also ThreadGroup#enclose.
5190 */
5191
5192static VALUE
5193thgroup_enclosed_p(VALUE group)
5194{
5195 struct thgroup *data;
5196
5197 TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
5198 return RBOOL(data->enclosed);
5199}
5200
5201
5202/*
5203 * call-seq:
5204 * thgrp.add(thread) -> thgrp
5205 *
5206 * Adds the given +thread+ to this group, removing it from any other
5207 * group to which it may have previously been a member.
5208 *
5209 * puts "Initial group is #{ThreadGroup::Default.list}"
5210 * tg = ThreadGroup.new
5211 * t1 = Thread.new { sleep }
5212 * t2 = Thread.new { sleep }
5213 * puts "t1 is #{t1}"
5214 * puts "t2 is #{t2}"
5215 * tg.add(t1)
5216 * puts "Initial group now #{ThreadGroup::Default.list}"
5217 * puts "tg group now #{tg.list}"
5218 *
5219 * This will produce:
5220 *
5221 * Initial group is #<Thread:0x401bdf4c>
5222 * t1 is #<Thread:0x401b3c90>
5223 * t2 is #<Thread:0x401b3c18>
5224 * Initial group now #<Thread:0x401b3c18>#<Thread:0x401bdf4c>
5225 * tg group now #<Thread:0x401b3c90>
5226 */
5227
5228static VALUE
5229thgroup_add(VALUE group, VALUE thread)
5230{
5231 rb_thread_t *target_th = rb_thread_ptr(thread);
5232 struct thgroup *data;
5233
5234 if (OBJ_FROZEN(group)) {
5235 rb_raise(rb_eThreadError, "can't move to the frozen thread group");
5236 }
5237 TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
5238 if (data->enclosed) {
5239 rb_raise(rb_eThreadError, "can't move to the enclosed thread group");
5240 }
5241
5242 if (OBJ_FROZEN(target_th->thgroup)) {
5243 rb_raise(rb_eThreadError, "can't move from the frozen thread group");
5244 }
5245 TypedData_Get_Struct(target_th->thgroup, struct thgroup, &thgroup_data_type, data);
5246 if (data->enclosed) {
5247 rb_raise(rb_eThreadError,
5248 "can't move from the enclosed thread group");
5249 }
5250
5251 target_th->thgroup = group;
5252 return group;
5253}
5254
5255/*
5256 * Document-class: ThreadShield
5257 */
5258static void
5259thread_shield_mark(void *ptr)
5260{
5261 rb_gc_mark((VALUE)ptr);
5262}
5263
5264static const rb_data_type_t thread_shield_data_type = {
5265 "thread_shield",
5266 {thread_shield_mark, 0, 0,},
5267 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
5268};
5269
5270static VALUE
5271thread_shield_alloc(VALUE klass)
5272{
5273 return TypedData_Wrap_Struct(klass, &thread_shield_data_type, (void *)mutex_alloc(0));
5274}
5275
5276#define GetThreadShieldPtr(obj) ((VALUE)rb_check_typeddata((obj), &thread_shield_data_type))
5277#define THREAD_SHIELD_WAITING_MASK (((FL_USER19-1)&~(FL_USER0-1))|FL_USER19)
5278#define THREAD_SHIELD_WAITING_SHIFT (FL_USHIFT)
5279#define THREAD_SHIELD_WAITING_MAX (THREAD_SHIELD_WAITING_MASK>>THREAD_SHIELD_WAITING_SHIFT)
5280STATIC_ASSERT(THREAD_SHIELD_WAITING_MAX, THREAD_SHIELD_WAITING_MAX <= UINT_MAX);
5281static inline unsigned int
5282rb_thread_shield_waiting(VALUE b)
5283{
5284 return ((RBASIC(b)->flags&THREAD_SHIELD_WAITING_MASK)>>THREAD_SHIELD_WAITING_SHIFT);
5285}
5286
5287static inline void
5288rb_thread_shield_waiting_inc(VALUE b)
5289{
5290 unsigned int w = rb_thread_shield_waiting(b);
5291 w++;
5292 if (w > THREAD_SHIELD_WAITING_MAX)
5293 rb_raise(rb_eRuntimeError, "waiting count overflow");
5294 RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
5295 RBASIC(b)->flags |= ((VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
5296}
5297
5298static inline void
5299rb_thread_shield_waiting_dec(VALUE b)
5300{
5301 unsigned int w = rb_thread_shield_waiting(b);
5302 if (!w) rb_raise(rb_eRuntimeError, "waiting count underflow");
5303 w--;
5304 RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
5305 RBASIC(b)->flags |= ((VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
5306}
5307
5308VALUE
5309rb_thread_shield_new(void)
5310{
5311 VALUE thread_shield = thread_shield_alloc(rb_cThreadShield);
5312 rb_mutex_lock((VALUE)DATA_PTR(thread_shield));
5313 return thread_shield;
5314}
5315
5316bool
5317rb_thread_shield_owned(VALUE self)
5318{
5319 VALUE mutex = GetThreadShieldPtr(self);
5320 if (!mutex) return false;
5321
5322 rb_mutex_t *m = mutex_ptr(mutex);
5323
5324 return m->ec_serial == rb_ec_serial(GET_EC());
5325}
5326
5327/*
5328 * Wait a thread shield.
5329 *
5330 * Returns
5331 * true: acquired the thread shield
5332 * false: the thread shield was destroyed and no other threads waiting
5333 * nil: the thread shield was destroyed but still in use
5334 */
5335VALUE
5336rb_thread_shield_wait(VALUE self)
5337{
5338 VALUE mutex = GetThreadShieldPtr(self);
5339 rb_mutex_t *m;
5340
5341 if (!mutex) return Qfalse;
5342 m = mutex_ptr(mutex);
5343 if (m->ec_serial == rb_ec_serial(GET_EC())) return Qnil;
5344 rb_thread_shield_waiting_inc(self);
5345 rb_mutex_lock(mutex);
5346 rb_thread_shield_waiting_dec(self);
5347 if (DATA_PTR(self)) return Qtrue;
5348 rb_mutex_unlock(mutex);
5349 return rb_thread_shield_waiting(self) > 0 ? Qnil : Qfalse;
5350}
5351
5352static VALUE
5353thread_shield_get_mutex(VALUE self)
5354{
5355 VALUE mutex = GetThreadShieldPtr(self);
5356 if (!mutex)
5357 rb_raise(rb_eThreadError, "destroyed thread shield - %p", (void *)self);
5358 return mutex;
5359}
5360
5361/*
5362 * Release a thread shield, and return true if it has waiting threads.
5363 */
5364VALUE
5365rb_thread_shield_release(VALUE self)
5366{
5367 VALUE mutex = thread_shield_get_mutex(self);
5368 rb_mutex_unlock(mutex);
5369 return RBOOL(rb_thread_shield_waiting(self) > 0);
5370}
5371
5372/*
5373 * Release and destroy a thread shield, and return true if it has waiting threads.
5374 */
5375VALUE
5376rb_thread_shield_destroy(VALUE self)
5377{
5378 VALUE mutex = thread_shield_get_mutex(self);
5379 DATA_PTR(self) = 0;
5380 rb_mutex_unlock(mutex);
5381 return RBOOL(rb_thread_shield_waiting(self) > 0);
5382}
5383
5384static VALUE
5385threadptr_recursive_hash(rb_thread_t *th)
5386{
5387 return th->ec->local_storage_recursive_hash;
5388}
5389
5390static void
5391threadptr_recursive_hash_set(rb_thread_t *th, VALUE hash)
5392{
5393 th->ec->local_storage_recursive_hash = hash;
5394}
5395
5397
5398/*
5399 * Returns the current "recursive list" used to detect recursion.
5400 * This list is a hash table, unique for the current thread and for
5401 * the current __callee__.
5402 */
5403
5404static VALUE
5405recursive_list_access(VALUE sym)
5406{
5407 rb_thread_t *th = GET_THREAD();
5408 VALUE hash = threadptr_recursive_hash(th);
5409 VALUE list;
5410 if (NIL_P(hash) || !RB_TYPE_P(hash, T_HASH)) {
5411 hash = rb_ident_hash_new();
5412 threadptr_recursive_hash_set(th, hash);
5413 list = Qnil;
5414 }
5415 else {
5416 list = rb_hash_aref(hash, sym);
5417 }
5418 if (NIL_P(list) || !RB_TYPE_P(list, T_HASH)) {
5419 list = rb_ident_hash_new();
5420 rb_hash_aset(hash, sym, list);
5421 }
5422 return list;
5423}
5424
5425/*
5426 * Returns true if and only if obj (or the pair <obj, paired_obj>) is already
5427 * in the recursion list.
5428 * Assumes the recursion list is valid.
5429 */
5430
5431static bool
5432recursive_check(VALUE list, VALUE obj, VALUE paired_obj_id)
5433{
5434#if SIZEOF_LONG == SIZEOF_VOIDP
5435 #define OBJ_ID_EQL(obj_id, other) ((obj_id) == (other))
5436#elif SIZEOF_LONG_LONG == SIZEOF_VOIDP
5437 #define OBJ_ID_EQL(obj_id, other) (RB_BIGNUM_TYPE_P((obj_id)) ? \
5438 rb_big_eql((obj_id), (other)) : ((obj_id) == (other)))
5439#endif
5440
5441 VALUE pair_list = rb_hash_lookup2(list, obj, Qundef);
5442 if (UNDEF_P(pair_list))
5443 return false;
5444 if (paired_obj_id) {
5445 if (!RB_TYPE_P(pair_list, T_HASH)) {
5446 if (!OBJ_ID_EQL(paired_obj_id, pair_list))
5447 return false;
5448 }
5449 else {
5450 if (NIL_P(rb_hash_lookup(pair_list, paired_obj_id)))
5451 return false;
5452 }
5453 }
5454 return true;
5455}
5456
5457/*
5458 * Pushes obj (or the pair <obj, paired_obj>) in the recursion list.
5459 * For a single obj, it sets list[obj] to Qtrue.
5460 * For a pair, it sets list[obj] to paired_obj_id if possible,
5461 * otherwise list[obj] becomes a hash like:
5462 * {paired_obj_id_1 => true, paired_obj_id_2 => true, ... }
5463 * Assumes the recursion list is valid.
5464 */
5465
5466static void
5467recursive_push(VALUE list, VALUE obj, VALUE paired_obj)
5468{
5469 VALUE pair_list;
5470
5471 if (!paired_obj) {
5472 rb_hash_aset(list, obj, Qtrue);
5473 }
5474 else if (UNDEF_P(pair_list = rb_hash_lookup2(list, obj, Qundef))) {
5475 rb_hash_aset(list, obj, paired_obj);
5476 }
5477 else {
5478 if (!RB_TYPE_P(pair_list, T_HASH)){
5479 VALUE other_paired_obj = pair_list;
5480 pair_list = rb_hash_new();
5481 rb_hash_aset(pair_list, other_paired_obj, Qtrue);
5482 rb_hash_aset(list, obj, pair_list);
5483 }
5484 rb_hash_aset(pair_list, paired_obj, Qtrue);
5485 }
5486}
5487
5488/*
5489 * Pops obj (or the pair <obj, paired_obj>) from the recursion list.
5490 * For a pair, if list[obj] is a hash, then paired_obj_id is
5491 * removed from the hash and no attempt is made to simplify
5492 * list[obj] from {only_one_paired_id => true} to only_one_paired_id
5493 * Assumes the recursion list is valid.
5494 */
5495
5496static int
5497recursive_pop(VALUE list, VALUE obj, VALUE paired_obj)
5498{
5499 if (paired_obj) {
5500 VALUE pair_list = rb_hash_lookup2(list, obj, Qundef);
5501 if (UNDEF_P(pair_list)) {
5502 return 0;
5503 }
5504 if (RB_TYPE_P(pair_list, T_HASH)) {
5505 rb_hash_delete_entry(pair_list, paired_obj);
5506 if (!RHASH_EMPTY_P(pair_list)) {
5507 return 1; /* keep hash until is empty */
5508 }
5509 }
5510 }
5511 rb_hash_delete_entry(list, obj);
5512 return 1;
5513}
5515struct exec_recursive_params {
5516 VALUE (*func) (VALUE, VALUE, int);
5517 VALUE list;
5518 VALUE obj;
5519 VALUE pairid;
5520 VALUE arg;
5521};
5522
5523static VALUE
5524exec_recursive_i(RB_BLOCK_CALL_FUNC_ARGLIST(tag, data))
5525{
5526 struct exec_recursive_params *p = (void *)data;
5527 return (*p->func)(p->obj, p->arg, FALSE);
5528}
5529
5530/*
5531 * Calls func(obj, arg, recursive), where recursive is non-zero if the
5532 * current method is called recursively on obj, or on the pair <obj, pairid>
5533 * If outer is 0, then the innermost func will be called with recursive set
5534 * to true, otherwise the outermost func will be called. In the latter case,
5535 * all inner func are short-circuited by throw.
5536 * Implementation details: the value thrown is the recursive list which is
5537 * proper to the current method and unlikely to be caught anywhere else.
5538 * list[recursive_key] is used as a flag for the outermost call.
5539 */
5540
5541static VALUE
5542exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE pairid, VALUE arg, int outer, ID mid)
5543{
5544 VALUE result = Qundef;
5545 const VALUE sym = mid ? ID2SYM(mid) : ID2SYM(idNULL);
5546 struct exec_recursive_params p;
5547 int outermost;
5548 p.list = recursive_list_access(sym);
5549 p.obj = obj;
5550 p.pairid = pairid;
5551 p.arg = arg;
5552 outermost = outer && !recursive_check(p.list, ID2SYM(recursive_key), 0);
5553
5554 if (recursive_check(p.list, p.obj, pairid)) {
5555 if (outer && !outermost) {
5556 rb_throw_obj(p.list, p.list);
5557 }
5558 return (*func)(obj, arg, TRUE);
5559 }
5560 else {
5561 enum ruby_tag_type state;
5562
5563 p.func = func;
5564
5565 if (outermost) {
5566 recursive_push(p.list, ID2SYM(recursive_key), 0);
5567 recursive_push(p.list, p.obj, p.pairid);
5568 result = rb_catch_protect(p.list, exec_recursive_i, (VALUE)&p, &state);
5569 if (!recursive_pop(p.list, p.obj, p.pairid)) goto invalid;
5570 if (!recursive_pop(p.list, ID2SYM(recursive_key), 0)) goto invalid;
5571 if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
5572 if (result == p.list) {
5573 result = (*func)(obj, arg, TRUE);
5574 }
5575 }
5576 else {
5577 volatile VALUE ret = Qundef;
5578 recursive_push(p.list, p.obj, p.pairid);
5579 EC_PUSH_TAG(GET_EC());
5580 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
5581 ret = (*func)(obj, arg, FALSE);
5582 }
5583 EC_POP_TAG();
5584 if (!recursive_pop(p.list, p.obj, p.pairid)) {
5585 goto invalid;
5586 }
5587 if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
5588 result = ret;
5589 }
5590 }
5591 *(volatile struct exec_recursive_params *)&p;
5592 return result;
5593
5594 invalid:
5595 rb_raise(rb_eTypeError, "invalid inspect_tbl pair_list "
5596 "for %+"PRIsVALUE" in %+"PRIsVALUE,
5597 sym, rb_thread_current());
5599}
5600
5601/*
5602 * Calls func(obj, arg, recursive), where recursive is non-zero if the
5603 * current method is called recursively on obj
5604 */
5605
5607rb_exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
5608{
5609 return exec_recursive(func, obj, 0, arg, 0, rb_frame_last_func());
5610}
5611
5612/*
5613 * Calls func(obj, arg, recursive), where recursive is non-zero if the
5614 * current method is called recursively on the ordered pair <obj, paired_obj>
5615 */
5616
5618rb_exec_recursive_paired(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
5619{
5620 return exec_recursive(func, obj, rb_memory_id(paired_obj), arg, 0, rb_frame_last_func());
5621}
5622
5623/*
5624 * If recursion is detected on the current method and obj, the outermost
5625 * func will be called with (obj, arg, true). All inner func will be
5626 * short-circuited using throw.
5627 */
5628
5630rb_exec_recursive_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
5631{
5632 return exec_recursive(func, obj, 0, arg, 1, rb_frame_last_func());
5633}
5634
5635VALUE
5636rb_exec_recursive_outer_mid(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg, ID mid)
5637{
5638 return exec_recursive(func, obj, 0, arg, 1, mid);
5639}
5640
5641/*
5642 * If recursion is detected on the current method, obj and paired_obj,
5643 * the outermost func will be called with (obj, arg, true). All inner
5644 * func will be short-circuited using throw.
5645 */
5646
5648rb_exec_recursive_paired_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
5649{
5650 return exec_recursive(func, obj, rb_memory_id(paired_obj), arg, 1, rb_frame_last_func());
5651}
5652
5653/*
5654 * call-seq:
5655 * thread.backtrace -> array or nil
5656 *
5657 * Returns the current backtrace of the target thread.
5658 *
5659 */
5660
5661static VALUE
5662rb_thread_backtrace_m(int argc, VALUE *argv, VALUE thval)
5663{
5664 return rb_vm_thread_backtrace(argc, argv, thval);
5665}
5666
5667/* call-seq:
5668 * thread.backtrace_locations(*args) -> array or nil
5669 *
5670 * Returns the execution stack for the target thread---an array containing
5671 * backtrace location objects.
5672 *
5673 * See Thread::Backtrace::Location for more information.
5674 *
5675 * This method behaves similarly to Kernel#caller_locations except it applies
5676 * to a specific thread.
5677 */
5678static VALUE
5679rb_thread_backtrace_locations_m(int argc, VALUE *argv, VALUE thval)
5680{
5681 return rb_vm_thread_backtrace_locations(argc, argv, thval);
5682}
5683
5684void
5685Init_Thread_Mutex(void)
5686{
5687 rb_thread_t *th = GET_THREAD();
5688
5689 rb_native_mutex_initialize(&th->vm->workqueue_lock);
5690 rb_native_mutex_initialize(&th->interrupt_lock);
5691}
5692
5693/*
5694 * Document-class: ThreadError
5695 *
5696 * Raised when an invalid operation is attempted on a thread.
5697 *
5698 * For example, when no other thread has been started:
5699 *
5700 * Thread.stop
5701 *
5702 * This will raises the following exception:
5703 *
5704 * ThreadError: stopping only thread
5705 * note: use sleep to stop forever
5706 */
5707
5708void
5709Init_Thread(void)
5710{
5711 rb_thread_t *th = GET_THREAD();
5712
5713 sym_never = ID2SYM(rb_intern_const("never"));
5714 sym_immediate = ID2SYM(rb_intern_const("immediate"));
5715 sym_on_blocking = ID2SYM(rb_intern_const("on_blocking"));
5716
5717 rb_define_singleton_method(rb_cThread, "new", thread_s_new, -1);
5718 rb_define_singleton_method(rb_cThread, "start", thread_start, -2);
5719 rb_define_singleton_method(rb_cThread, "fork", thread_start, -2);
5720 rb_define_singleton_method(rb_cThread, "main", rb_thread_s_main, 0);
5721 rb_define_singleton_method(rb_cThread, "current", thread_s_current, 0);
5722 rb_define_singleton_method(rb_cThread, "stop", thread_stop, 0);
5723 rb_define_singleton_method(rb_cThread, "kill", rb_thread_s_kill, 1);
5724 rb_define_singleton_method(rb_cThread, "exit", rb_thread_exit, 0);
5725 rb_define_singleton_method(rb_cThread, "pass", thread_s_pass, 0);
5726 rb_define_singleton_method(rb_cThread, "list", thread_list, 0);
5727 rb_define_singleton_method(rb_cThread, "abort_on_exception", rb_thread_s_abort_exc, 0);
5728 rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1);
5729 rb_define_singleton_method(rb_cThread, "report_on_exception", rb_thread_s_report_exc, 0);
5730 rb_define_singleton_method(rb_cThread, "report_on_exception=", rb_thread_s_report_exc_set, 1);
5731 rb_define_singleton_method(rb_cThread, "ignore_deadlock", rb_thread_s_ignore_deadlock, 0);
5732 rb_define_singleton_method(rb_cThread, "ignore_deadlock=", rb_thread_s_ignore_deadlock_set, 1);
5733 rb_define_singleton_method(rb_cThread, "handle_interrupt", rb_thread_s_handle_interrupt, 1);
5734 rb_define_singleton_method(rb_cThread, "pending_interrupt?", rb_thread_s_pending_interrupt_p, -1);
5735 rb_define_method(rb_cThread, "pending_interrupt?", rb_thread_pending_interrupt_p, -1);
5736
5737 rb_define_method(rb_cThread, "initialize", thread_initialize, -2);
5738 rb_define_method(rb_cThread, "raise", thread_raise_m, -1);
5739 rb_define_method(rb_cThread, "join", thread_join_m, -1);
5740 rb_define_method(rb_cThread, "value", thread_value, 0);
5742 rb_define_method(rb_cThread, "terminate", rb_thread_kill, 0);
5746 rb_define_method(rb_cThread, "[]", rb_thread_aref, 1);
5747 rb_define_method(rb_cThread, "[]=", rb_thread_aset, 2);
5748 rb_define_method(rb_cThread, "fetch", rb_thread_fetch, -1);
5749 rb_define_method(rb_cThread, "key?", rb_thread_key_p, 1);
5750 rb_define_method(rb_cThread, "keys", rb_thread_keys, 0);
5751 rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
5752 rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
5753 rb_define_method(rb_cThread, "status", rb_thread_status, 0);
5754 rb_define_method(rb_cThread, "thread_variable_get", rb_thread_variable_get, 1);
5755 rb_define_method(rb_cThread, "thread_variable_set", rb_thread_variable_set, 2);
5756 rb_define_method(rb_cThread, "thread_variables", rb_thread_variables, 0);
5757 rb_define_method(rb_cThread, "thread_variable?", rb_thread_variable_p, 1);
5758 rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
5759 rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
5760 rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
5761 rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1);
5762 rb_define_method(rb_cThread, "report_on_exception", rb_thread_report_exc, 0);
5763 rb_define_method(rb_cThread, "report_on_exception=", rb_thread_report_exc_set, 1);
5764 rb_define_method(rb_cThread, "group", rb_thread_group, 0);
5765 rb_define_method(rb_cThread, "backtrace", rb_thread_backtrace_m, -1);
5766 rb_define_method(rb_cThread, "backtrace_locations", rb_thread_backtrace_locations_m, -1);
5767
5768 rb_define_method(rb_cThread, "name", rb_thread_getname, 0);
5769 rb_define_method(rb_cThread, "name=", rb_thread_setname, 1);
5770 rb_define_method(rb_cThread, "native_thread_id", rb_thread_native_thread_id, 0);
5771 rb_define_method(rb_cThread, "to_s", rb_thread_to_s, 0);
5772 rb_define_alias(rb_cThread, "inspect", "to_s");
5773
5774 rb_vm_register_special_exception(ruby_error_stream_closed, rb_eIOError,
5775 "stream closed in another thread");
5776
5777 cThGroup = rb_define_class("ThreadGroup", rb_cObject);
5778 rb_define_alloc_func(cThGroup, thgroup_s_alloc);
5779 rb_define_method(cThGroup, "list", thgroup_list, 0);
5780 rb_define_method(cThGroup, "enclose", thgroup_enclose, 0);
5781 rb_define_method(cThGroup, "enclosed?", thgroup_enclosed_p, 0);
5782 rb_define_method(cThGroup, "add", thgroup_add, 1);
5783
5784 const char * ptr = getenv("RUBY_THREAD_TIMESLICE");
5785
5786 if (ptr) {
5787 long quantum = strtol(ptr, NULL, 0);
5788 if (quantum > 0 && !(SIZEOF_LONG > 4 && quantum > UINT32_MAX)) {
5789 thread_default_quantum_ms = (uint32_t)quantum;
5790 }
5791 else if (0) {
5792 fprintf(stderr, "Ignored RUBY_THREAD_TIMESLICE=%s\n", ptr);
5793 }
5794 }
5795
5796 {
5797 th->thgroup = th->ractor->thgroup_default = rb_obj_alloc(cThGroup);
5798 rb_define_const(cThGroup, "Default", th->thgroup);
5799 }
5800
5802
5803 /* init thread core */
5804 {
5805 /* main thread setting */
5806 {
5807 /* acquire global vm lock */
5808#ifdef HAVE_PTHREAD_NP_H
5809 VM_ASSERT(TH_SCHED(th)->running == th);
5810#endif
5811 // thread_sched_to_running() should not be called because
5812 // it assumes blocked by thread_sched_to_waiting().
5813 // thread_sched_to_running(sched, th);
5814
5815 th->pending_interrupt_queue = rb_ary_hidden_new(0);
5816 th->pending_interrupt_queue_checked = 0;
5817 th->pending_interrupt_mask_stack = rb_ary_hidden_new(0);
5818 }
5819 }
5820
5821 rb_thread_create_timer_thread();
5822
5823 Init_thread_sync();
5824
5825 // TODO: Suppress unused function warning for now
5826 // if (0) rb_thread_sched_destroy(NULL);
5827}
5828
5831{
5832 rb_thread_t *th = ruby_thread_from_native();
5833
5834 return th != 0;
5835}
5836
5837#ifdef NON_SCALAR_THREAD_ID
5838 #define thread_id_str(th) (NULL)
5839#else
5840 #define thread_id_str(th) ((void *)(uintptr_t)(th)->nt->thread_id)
5841#endif
5842
5843static void
5844debug_deadlock_check(rb_ractor_t *r, VALUE msg)
5845{
5846 rb_thread_t *th = 0;
5847 VALUE sep = rb_str_new_cstr("\n ");
5848
5849 rb_str_catf(msg, "\n%d threads, %d sleeps current:%p main thread:%p\n",
5850 rb_ractor_living_thread_num(r), rb_ractor_sleeper_thread_num(r),
5851 (void *)GET_THREAD(), (void *)r->threads.main);
5852
5853 ccan_list_for_each(&r->threads.set, th, lt_node) {
5854 rb_str_catf(msg, "* %+"PRIsVALUE"\n rb_thread_t:%p "
5855 "native:%p int:%u",
5856 th->self, (void *)th, th->nt ? thread_id_str(th) : "N/A", th->ec->interrupt_flag);
5857
5858 if (th->locking_mutex) {
5859 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
5860 rb_str_catf(msg, " mutex:%llu cond:%"PRIuSIZE,
5861 (unsigned long long)mutex->ec_serial, rb_mutex_num_waiting(mutex));
5862 }
5863
5864 {
5865 struct rb_waiting_list *list = th->join_list;
5866 while (list) {
5867 rb_str_catf(msg, "\n depended by: tb_thread_id:%p", (void *)list->thread);
5868 list = list->next;
5869 }
5870 }
5871 rb_str_catf(msg, "\n ");
5872 rb_str_concat(msg, rb_ary_join(rb_ec_backtrace_str_ary(th->ec, RUBY_BACKTRACE_START, RUBY_ALL_BACKTRACE_LINES), sep));
5873 rb_str_catf(msg, "\n");
5874 }
5875}
5876
5877static void
5878rb_check_deadlock(rb_ractor_t *r)
5879{
5880 if (GET_THREAD()->vm->thread_ignore_deadlock) return;
5881
5882#ifdef RUBY_THREAD_PTHREAD_H
5883 if (r->threads.sched.readyq_cnt > 0) return;
5884#endif
5885
5886 int sleeper_num = rb_ractor_sleeper_thread_num(r);
5887 int ltnum = rb_ractor_living_thread_num(r);
5888
5889 if (ltnum > sleeper_num) return;
5890 if (ltnum < sleeper_num) rb_bug("sleeper must not be more than vm_living_thread_num(vm)");
5891
5892 int found = 0;
5893 rb_thread_t *th = NULL;
5894
5895 ccan_list_for_each(&r->threads.set, th, lt_node) {
5896 if (th->status != THREAD_STOPPED_FOREVER || RUBY_VM_INTERRUPTED(th->ec)) {
5897 found = 1;
5898 }
5899 else if (th->locking_mutex) {
5900 rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
5901 if (mutex->ec_serial == rb_ec_serial(th->ec) || (!mutex->ec_serial && !ccan_list_empty(&mutex->waitq))) {
5902 found = 1;
5903 }
5904 }
5905 if (found)
5906 break;
5907 }
5908
5909 if (!found) {
5910 VALUE argv[2];
5911 argv[0] = rb_eFatal;
5912 argv[1] = rb_str_new2("No live threads left. Deadlock?");
5913 debug_deadlock_check(r, argv[1]);
5914 rb_ractor_sleeper_threads_dec(GET_RACTOR());
5915 rb_threadptr_raise(r->threads.main, 2, argv);
5916 }
5917}
5918
5919static void
5920update_line_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
5921{
5922 const rb_control_frame_t *cfp = GET_EC()->cfp;
5923 VALUE coverage = rb_iseq_coverage(cfp->iseq);
5924 if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
5925 VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
5926 if (lines) {
5927 long line = rb_sourceline() - 1;
5928 VM_ASSERT(line >= 0);
5929 long count;
5930 VALUE num;
5931 void rb_iseq_clear_event_flags(const rb_iseq_t *iseq, size_t pos, rb_event_flag_t reset);
5932 if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
5933 rb_iseq_clear_event_flags(cfp->iseq, cfp->pc - ISEQ_BODY(cfp->iseq)->iseq_encoded - 1, RUBY_EVENT_COVERAGE_LINE);
5934 rb_ary_push(lines, LONG2FIX(line + 1));
5935 return;
5936 }
5937 if (line >= RARRAY_LEN(lines)) { /* no longer tracked */
5938 return;
5939 }
5940 num = RARRAY_AREF(lines, line);
5941 if (!FIXNUM_P(num)) return;
5942 count = FIX2LONG(num) + 1;
5943 if (POSFIXABLE(count)) {
5944 RARRAY_ASET(lines, line, LONG2FIX(count));
5945 }
5946 }
5947 }
5948}
5949
5950static void
5951update_branch_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
5952{
5953 const rb_control_frame_t *cfp = GET_EC()->cfp;
5954 VALUE coverage = rb_iseq_coverage(cfp->iseq);
5955 if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
5956 VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
5957 if (branches) {
5958 long pc = cfp->pc - ISEQ_BODY(cfp->iseq)->iseq_encoded - 1;
5959 long idx = FIX2INT(RARRAY_AREF(ISEQ_PC2BRANCHINDEX(cfp->iseq), pc)), count;
5960 VALUE counters = RARRAY_AREF(branches, 1);
5961 VALUE num = RARRAY_AREF(counters, idx);
5962 count = FIX2LONG(num) + 1;
5963 if (POSFIXABLE(count)) {
5964 RARRAY_ASET(counters, idx, LONG2FIX(count));
5965 }
5966 }
5967 }
5968}
5969
5970const rb_method_entry_t *
5971rb_resolve_me_location(const rb_method_entry_t *me, VALUE resolved_location[5])
5972{
5973 VALUE path, beg_pos_lineno, beg_pos_column, end_pos_lineno, end_pos_column;
5974
5975 if (!me->def) return NULL; // negative cme
5976
5977 retry:
5978 switch (me->def->type) {
5979 case VM_METHOD_TYPE_ISEQ: {
5980 const rb_iseq_t *iseq = me->def->body.iseq.iseqptr;
5981 rb_iseq_location_t *loc = &ISEQ_BODY(iseq)->location;
5982 path = rb_iseq_path(iseq);
5983 beg_pos_lineno = INT2FIX(loc->code_location.beg_pos.lineno);
5984 beg_pos_column = INT2FIX(loc->code_location.beg_pos.column);
5985 end_pos_lineno = INT2FIX(loc->code_location.end_pos.lineno);
5986 end_pos_column = INT2FIX(loc->code_location.end_pos.column);
5987 break;
5988 }
5989 case VM_METHOD_TYPE_BMETHOD: {
5990 const rb_iseq_t *iseq = rb_proc_get_iseq(me->def->body.bmethod.proc, 0);
5991 if (iseq) {
5992 rb_iseq_location_t *loc;
5993 rb_iseq_check(iseq);
5994 path = rb_iseq_path(iseq);
5995 loc = &ISEQ_BODY(iseq)->location;
5996 beg_pos_lineno = INT2FIX(loc->code_location.beg_pos.lineno);
5997 beg_pos_column = INT2FIX(loc->code_location.beg_pos.column);
5998 end_pos_lineno = INT2FIX(loc->code_location.end_pos.lineno);
5999 end_pos_column = INT2FIX(loc->code_location.end_pos.column);
6000 break;
6001 }
6002 return NULL;
6003 }
6004 case VM_METHOD_TYPE_ALIAS:
6005 me = me->def->body.alias.original_me;
6006 goto retry;
6007 case VM_METHOD_TYPE_REFINED:
6008 me = me->def->body.refined.orig_me;
6009 if (!me) return NULL;
6010 goto retry;
6011 default:
6012 return NULL;
6013 }
6014
6015 /* found */
6016 if (RB_TYPE_P(path, T_ARRAY)) {
6017 path = rb_ary_entry(path, 1);
6018 if (!RB_TYPE_P(path, T_STRING)) return NULL; /* just for the case... */
6019 }
6020 if (resolved_location) {
6021 resolved_location[0] = path;
6022 resolved_location[1] = beg_pos_lineno;
6023 resolved_location[2] = beg_pos_column;
6024 resolved_location[3] = end_pos_lineno;
6025 resolved_location[4] = end_pos_column;
6026 }
6027 return me;
6028}
6029
6030static void
6031update_method_coverage(VALUE me2counter, rb_trace_arg_t *trace_arg)
6032{
6033 const rb_control_frame_t *cfp = GET_EC()->cfp;
6034 const rb_callable_method_entry_t *cme = rb_vm_frame_method_entry(cfp);
6035 const rb_method_entry_t *me = (const rb_method_entry_t *)cme;
6036 VALUE rcount;
6037 long count;
6038
6039 me = rb_resolve_me_location(me, 0);
6040 if (!me) return;
6041
6042 rcount = rb_hash_aref(me2counter, (VALUE) me);
6043 count = FIXNUM_P(rcount) ? FIX2LONG(rcount) + 1 : 1;
6044 if (POSFIXABLE(count)) {
6045 rb_hash_aset(me2counter, (VALUE) me, LONG2FIX(count));
6046 }
6047}
6048
6049VALUE
6050rb_get_coverages(void)
6051{
6052 return GET_VM()->coverages;
6053}
6054
6055int
6056rb_get_coverage_mode(void)
6057{
6058 return GET_VM()->coverage_mode;
6059}
6060
6061void
6062rb_set_coverages(VALUE coverages, int mode, VALUE me2counter)
6063{
6064 GET_VM()->coverages = coverages;
6065 GET_VM()->me2counter = me2counter;
6066 GET_VM()->coverage_mode = mode;
6067}
6068
6069void
6070rb_resume_coverages(void)
6071{
6072 int mode = GET_VM()->coverage_mode;
6073 VALUE me2counter = GET_VM()->me2counter;
6074 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);
6075 if (mode & COVERAGE_TARGET_BRANCHES) {
6076 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);
6077 }
6078 if (mode & COVERAGE_TARGET_METHODS) {
6079 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);
6080 }
6081}
6082
6083void
6084rb_suspend_coverages(void)
6085{
6086 rb_remove_event_hook((rb_event_hook_func_t) update_line_coverage);
6087 if (GET_VM()->coverage_mode & COVERAGE_TARGET_BRANCHES) {
6088 rb_remove_event_hook((rb_event_hook_func_t) update_branch_coverage);
6089 }
6090 if (GET_VM()->coverage_mode & COVERAGE_TARGET_METHODS) {
6091 rb_remove_event_hook((rb_event_hook_func_t) update_method_coverage);
6092 }
6093}
6094
6095/* Make coverage arrays empty so old covered files are no longer tracked. */
6096void
6097rb_reset_coverages(void)
6098{
6099 rb_clear_coverages();
6100 rb_iseq_remove_coverage_all();
6101 GET_VM()->coverages = Qfalse;
6102}
6103
6104VALUE
6105rb_default_coverage(int n)
6106{
6107 VALUE coverage = rb_ary_hidden_new_fill(3);
6108 VALUE lines = Qfalse, branches = Qfalse;
6109 int mode = GET_VM()->coverage_mode;
6110
6111 if (mode & COVERAGE_TARGET_LINES) {
6112 lines = n > 0 ? rb_ary_hidden_new_fill(n) : rb_ary_hidden_new(0);
6113 }
6114 RARRAY_ASET(coverage, COVERAGE_INDEX_LINES, lines);
6115
6116 if (mode & COVERAGE_TARGET_BRANCHES) {
6117 branches = rb_ary_hidden_new_fill(2);
6118 /* internal data structures for branch coverage:
6119 *
6120 * { branch base node =>
6121 * [base_type, base_first_lineno, base_first_column, base_last_lineno, base_last_column, {
6122 * branch target id =>
6123 * [target_type, target_first_lineno, target_first_column, target_last_lineno, target_last_column, target_counter_index],
6124 * ...
6125 * }],
6126 * ...
6127 * }
6128 *
6129 * Example:
6130 * { NODE_CASE =>
6131 * [1, 0, 4, 3, {
6132 * NODE_WHEN => [2, 8, 2, 9, 0],
6133 * NODE_WHEN => [3, 8, 3, 9, 1],
6134 * ...
6135 * }],
6136 * ...
6137 * }
6138 */
6139 VALUE structure = rb_hash_new();
6140 rb_obj_hide(structure);
6141 RARRAY_ASET(branches, 0, structure);
6142 /* branch execution counters */
6143 RARRAY_ASET(branches, 1, rb_ary_hidden_new(0));
6144 }
6145 RARRAY_ASET(coverage, COVERAGE_INDEX_BRANCHES, branches);
6146
6147 return coverage;
6148}
6149
6150static VALUE
6151uninterruptible_exit(VALUE v)
6152{
6153 rb_thread_t *cur_th = GET_THREAD();
6154 rb_ary_pop(cur_th->pending_interrupt_mask_stack);
6155
6156 cur_th->pending_interrupt_queue_checked = 0;
6157 if (!rb_threadptr_pending_interrupt_empty_p(cur_th)) {
6158 RUBY_VM_SET_INTERRUPT(cur_th->ec);
6159 }
6160 return Qnil;
6161}
6162
6163VALUE
6164rb_uninterruptible(VALUE (*b_proc)(VALUE), VALUE data)
6165{
6166 VALUE interrupt_mask = rb_ident_hash_new();
6167 rb_thread_t *cur_th = GET_THREAD();
6168
6169 rb_hash_aset(interrupt_mask, rb_cObject, sym_never);
6170 OBJ_FREEZE(interrupt_mask);
6171 rb_ary_push(cur_th->pending_interrupt_mask_stack, interrupt_mask);
6172
6173 VALUE ret = rb_ensure(b_proc, data, uninterruptible_exit, Qnil);
6174
6175 RUBY_VM_CHECK_INTS(cur_th->ec);
6176 return ret;
6177}
6178
6179static void
6180thread_specific_storage_alloc(rb_thread_t *th)
6181{
6182 VM_ASSERT(th->specific_storage == NULL);
6183
6184 if (UNLIKELY(specific_key_count > 0)) {
6185 th->specific_storage = ZALLOC_N(void *, RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6186 }
6187}
6188
6189rb_internal_thread_specific_key_t
6191{
6192 rb_vm_t *vm = GET_VM();
6193
6194 if (specific_key_count == 0 && vm->ractor.cnt > 1) {
6195 rb_raise(rb_eThreadError, "The first rb_internal_thread_specific_key_create() is called with multiple ractors");
6196 }
6197 else if (specific_key_count > RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX) {
6198 rb_raise(rb_eThreadError, "rb_internal_thread_specific_key_create() is called more than %d times", RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6199 }
6200 else {
6201 rb_internal_thread_specific_key_t key = specific_key_count++;
6202
6203 if (key == 0) {
6204 // allocate
6205 rb_ractor_t *cr = GET_RACTOR();
6206 rb_thread_t *th;
6207
6208 ccan_list_for_each(&cr->threads.set, th, lt_node) {
6209 thread_specific_storage_alloc(th);
6210 }
6211 }
6212 return key;
6213 }
6214}
6215
6216// async and native thread safe.
6217void *
6218rb_internal_thread_specific_get(VALUE thread_val, rb_internal_thread_specific_key_t key)
6219{
6220 rb_thread_t *th = DATA_PTR(thread_val);
6221
6222 VM_ASSERT(rb_thread_ptr(thread_val) == th);
6223 VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6224 VM_ASSERT(th->specific_storage);
6225
6226 return th->specific_storage[key];
6227}
6228
6229// async and native thread safe.
6230void
6231rb_internal_thread_specific_set(VALUE thread_val, rb_internal_thread_specific_key_t key, void *data)
6232{
6233 rb_thread_t *th = DATA_PTR(thread_val);
6234
6235 VM_ASSERT(rb_thread_ptr(thread_val) == th);
6236 VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6237 VM_ASSERT(th->specific_storage);
6238
6239 th->specific_storage[key] = data;
6240}
6241
6242// interrupt_exec
6245 struct ccan_list_node node;
6246
6247 rb_interrupt_exec_func_t *func;
6248 void *data;
6249 enum rb_interrupt_exec_flag flags;
6250};
6251
6252void
6253rb_threadptr_interrupt_exec_task_mark(rb_thread_t *th)
6254{
6255 struct rb_interrupt_exec_task *task;
6256
6257 ccan_list_for_each(&th->interrupt_exec_tasks, task, node) {
6258 if (task->flags & rb_interrupt_exec_flag_value_data) {
6259 rb_gc_mark((VALUE)task->data);
6260 }
6261 }
6262}
6263
6264// native thread safe
6265// th should be available
6266void
6267rb_threadptr_interrupt_exec(rb_thread_t *th, rb_interrupt_exec_func_t *func, void *data, enum rb_interrupt_exec_flag flags)
6268{
6269 // should not use ALLOC
6271 *task = (struct rb_interrupt_exec_task) {
6272 .flags = flags,
6273 .func = func,
6274 .data = data,
6275 };
6276
6277 rb_native_mutex_lock(&th->interrupt_lock);
6278 {
6279 ccan_list_add_tail(&th->interrupt_exec_tasks, &task->node);
6280 threadptr_set_interrupt_locked(th, true);
6281 }
6282 rb_native_mutex_unlock(&th->interrupt_lock);
6283}
6284
6285static void
6286threadptr_interrupt_exec_exec(rb_thread_t *th)
6287{
6288 while (1) {
6289 struct rb_interrupt_exec_task *task;
6290
6291 rb_native_mutex_lock(&th->interrupt_lock);
6292 {
6293 task = ccan_list_pop(&th->interrupt_exec_tasks, struct rb_interrupt_exec_task, node);
6294 }
6295 rb_native_mutex_unlock(&th->interrupt_lock);
6296
6297 RUBY_DEBUG_LOG("task:%p", task);
6298
6299 if (task) {
6300 if (task->flags & rb_interrupt_exec_flag_new_thread) {
6301 rb_thread_create(task->func, task->data);
6302 }
6303 else {
6304 (*task->func)(task->data);
6305 }
6306 ruby_xfree(task);
6307 }
6308 else {
6309 break;
6310 }
6311 }
6312}
6313
6314static void
6315threadptr_interrupt_exec_cleanup(rb_thread_t *th)
6316{
6317 rb_native_mutex_lock(&th->interrupt_lock);
6318 {
6319 struct rb_interrupt_exec_task *task;
6320
6321 while ((task = ccan_list_pop(&th->interrupt_exec_tasks, struct rb_interrupt_exec_task, node)) != NULL) {
6322 ruby_xfree(task);
6323 }
6324 }
6325 rb_native_mutex_unlock(&th->interrupt_lock);
6326}
6327
6328// native thread safe
6329// func/data should be native thread safe
6330void
6331rb_ractor_interrupt_exec(struct rb_ractor_struct *target_r,
6332 rb_interrupt_exec_func_t *func, void *data, enum rb_interrupt_exec_flag flags)
6333{
6334 RUBY_DEBUG_LOG("flags:%d", (int)flags);
6335
6336 rb_thread_t *main_th = target_r->threads.main;
6337 rb_threadptr_interrupt_exec(main_th, func, data, flags | rb_interrupt_exec_flag_new_thread);
6338}
6339
#define RUBY_ASSERT_ALWAYS(expr,...)
A variant of RUBY_ASSERT that does not interface with RUBY_DEBUG.
Definition assert.h:199
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
Definition assert.h:219
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
Definition atomic.h:69
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
struct rb_trace_arg_struct rb_trace_arg_t
Type that represents a specific trace event.
Definition debug.h:465
#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:600
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
Definition class.c:1477
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
Definition class.c:2842
ID rb_frame_last_func(void)
Returns the ID of the last method in the call stack.
Definition eval.c:1249
int rb_keyword_given_p(void)
Determines if the current method is given a keyword argument.
Definition eval.c:1034
int rb_block_given_p(void)
Determines if the current method is given a block.
Definition eval.c:1021
#define rb_str_new2
Old name of rb_str_new_cstr.
Definition string.h:1676
#define ALLOC
Old name of RB_ALLOC.
Definition memory.h:400
#define T_STRING
Old name of RUBY_T_STRING.
Definition value_type.h:78
#define xfree
Old name of ruby_xfree.
Definition xmalloc.h:58
#define Qundef
Old name of RUBY_Qundef.
#define INT2FIX
Old name of RB_INT2FIX.
Definition long.h:48
#define OBJ_FROZEN
Old name of RB_OBJ_FROZEN.
Definition fl_type.h:136
#define xrealloc
Old name of ruby_xrealloc.
Definition xmalloc.h:56
#define ID2SYM
Old name of RB_ID2SYM.
Definition symbol.h:44
#define OBJ_FREEZE
Old name of RB_OBJ_FREEZE.
Definition fl_type.h:134
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
Definition assume.h:29
#define CLASS_OF
Old name of rb_class_of.
Definition globals.h:205
#define xmalloc
Old name of ruby_xmalloc.
Definition xmalloc.h:53
#define LONG2FIX
Old name of RB_INT2FIX.
Definition long.h:49
#define FIX2INT
Old name of RB_FIX2INT.
Definition int.h:41
#define ZALLOC_N
Old name of RB_ZALLOC_N.
Definition memory.h:401
#define T_ICLASS
Old name of RUBY_T_ICLASS.
Definition value_type.h:66
#define T_HASH
Old name of RUBY_T_HASH.
Definition value_type.h:65
#define Qtrue
Old name of RUBY_Qtrue.
#define NUM2INT
Old name of RB_NUM2INT.
Definition int.h:44
#define INT2NUM
Old name of RB_INT2NUM.
Definition int.h:43
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define FIX2LONG
Old name of RB_FIX2LONG.
Definition long.h:46
#define T_ARRAY
Old name of RUBY_T_ARRAY.
Definition value_type.h:56
#define T_OBJECT
Old name of RUBY_T_OBJECT.
Definition value_type.h:75
#define NIL_P
Old name of RB_NIL_P.
#define POSFIXABLE
Old name of RB_POSFIXABLE.
Definition fixnum.h:29
#define BUILTIN_TYPE
Old name of RB_BUILTIN_TYPE.
Definition value_type.h:85
#define FIXNUM_P
Old name of RB_FIXNUM_P.
#define SYMBOL_P
Old name of RB_SYMBOL_P.
Definition value_type.h:88
void ruby_stop(int ex)
Calls ruby_cleanup() and exits the process.
Definition eval.c:301
#define ruby_debug
This variable controls whether the interpreter is in debug mode.
Definition error.h:486
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
Definition eval.c:664
VALUE rb_eSystemExit
SystemExit exception.
Definition error.c:1424
VALUE rb_eIOError
IOError exception.
Definition io.c:189
VALUE rb_eStandardError
StandardError exception.
Definition error.c:1428
VALUE rb_eTypeError
TypeError exception.
Definition error.c:1431
void rb_frozen_error_raise(VALUE frozen_obj, const char *fmt,...)
Raises an instance of rb_eFrozenError.
Definition error.c:4121
VALUE rb_eFatal
fatal exception.
Definition error.c:1427
VALUE rb_eInterrupt
Interrupt exception.
Definition error.c:1425
VALUE rb_eRuntimeError
RuntimeError exception.
Definition error.c:1429
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
Definition error.c:466
VALUE rb_exc_new(VALUE etype, const char *ptr, long len)
Creates an instance of the passed exception class.
Definition error.c:1469
VALUE rb_eException
Mother of all exceptions.
Definition error.c:1423
VALUE rb_eThreadError
ThreadError exception.
Definition eval.c:1039
void rb_exit(int status)
Terminates the current execution context.
Definition process.c:4363
VALUE rb_eSignal
SignalException exception.
Definition error.c:1426
VALUE rb_obj_alloc(VALUE klass)
Allocates an instance of the given class.
Definition object.c:2208
VALUE rb_cInteger
Module class.
Definition numeric.c:200
VALUE rb_obj_hide(VALUE obj)
Make the object invisible from Ruby code.
Definition object.c:100
VALUE rb_obj_class(VALUE obj)
Queries the class of an object.
Definition object.c:264
VALUE rb_cThread
Thread class.
Definition vm.c:671
VALUE rb_cModule
Module class.
Definition object.c:62
double rb_num2dbl(VALUE num)
Converts an instance of rb_cNumeric into C's double.
Definition object.c:3829
VALUE rb_obj_is_kind_of(VALUE obj, VALUE klass)
Queries if the given object is an instance (of possibly descendants) of the given class.
Definition object.c:923
VALUE rb_ary_shift(VALUE ary)
Destructively deletes an element from the beginning of the passed array and returns what was deleted.
VALUE rb_ary_dup(VALUE ary)
Duplicates an array.
VALUE rb_ary_delete_at(VALUE ary, long pos)
Destructively removes an element which resides at the specific index of the passed array.
VALUE rb_ary_new(void)
Allocates a new, empty array.
VALUE rb_ary_pop(VALUE ary)
Destructively deletes an element from the end of the passed array and returns what was deleted.
VALUE rb_ary_hidden_new(long capa)
Allocates a hidden (no class) empty array.
VALUE rb_ary_clear(VALUE ary)
Destructively removes everything form an array.
VALUE rb_ary_push(VALUE ary, VALUE elem)
Special case of rb_ary_cat() that it adds only one element.
VALUE rb_ary_entry(VALUE ary, long off)
Queries an element of an array.
VALUE rb_ary_join(VALUE ary, VALUE sep)
Recursively stringises the elements of the passed array, flattens that result, then joins the sequenc...
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_hash_new(void)
Creates a new, empty hash object.
Definition hash.c:1484
VALUE rb_block_proc(void)
Constructs a Proc object from implicitly passed components.
Definition proc.c:983
void rb_reset_random_seed(void)
Resets the RNG behind rb_genrand_int32()/rb_genrand_real().
Definition random.c:1817
VALUE rb_str_new_frozen(VALUE str)
Creates a frozen copy of the string, if necessary.
Definition string.c:1518
VALUE rb_str_concat(VALUE dst, VALUE src)
Identical to rb_str_append(), except it also accepts an integer as a codepoint.
Definition string.c:4036
#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:3805
VALUE rb_mutex_new(void)
Creates a mutex.
VALUE rb_thread_kill(VALUE thread)
Terminates the given thread.
Definition thread.c:2998
#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:3237
VALUE rb_exec_recursive(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE h)
"Recursion" API entry point.
Definition thread.c:5606
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:2934
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:3149
VALUE rb_mutex_sleep(VALUE self, VALUE timeout)
Releases the lock held in the mutex and waits for the period of time; reacquires the lock on wakeup.
VALUE rb_exec_recursive_paired(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h)
Identical to rb_exec_recursive(), except it checks for the recursion on the ordered pair of { g,...
Definition thread.c:5617
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:5081
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:3140
VALUE rb_thread_wakeup(VALUE thread)
Marks a given thread as eligible for scheduling.
Definition thread.c:3093
VALUE rb_mutex_unlock(VALUE mutex)
Releases the mutex.
VALUE rb_exec_recursive_paired_outer(VALUE(*f)(VALUE g, VALUE h, int r), VALUE g, VALUE p, VALUE h)
Identical to rb_exec_recursive_outer(), except it checks for the recursion on the ordered pair of { g...
Definition thread.c:5647
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:5076
VALUE rb_thread_current(void)
Obtains the "current" thread.
Definition thread.c:3216
int rb_thread_alone(void)
Checks if the thread this function is running is the only thread that is currently alive.
Definition thread.c:4078
VALUE rb_thread_local_aset(VALUE thread, ID key, VALUE val)
This badly named function writes to a Fiber local storage.
Definition thread.c:3953
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.
Definition thread.c:5629
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:3102
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:2955
VALUE rb_ivar_set(VALUE obj, ID name, VALUE val)
Identical to rb_iv_set(), except it accepts the name as an ID instead of a C string.
Definition variable.c:2030
VALUE rb_ivar_get(VALUE obj, ID name)
Identical to rb_iv_get(), except it accepts the name as an ID instead of a C string.
Definition variable.c:1505
VALUE rb_class_path(VALUE mod)
Identical to rb_mod_name(), except it returns #<Class: ...> style inspection for anonymous modules.
Definition variable.c:380
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
int rb_sourceline(void)
Resembles __LINE__.
Definition vm.c: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:1133
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
Definition string.c:12674
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:12664
#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
int ruby_thread_has_gvl_p(void)
Whether the current thread is holding the GVL.
Definition thread.c:2103
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:6217
#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:6230
rb_internal_thread_specific_key_t rb_internal_thread_specific_key_create(void)
Create a key to store thread specific data.
Definition thread.c:6189
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.
Definition thread.c:1737
#define RB_BLOCK_CALL_FUNC_ARGLIST(yielded_arg, callback_arg)
Shim for block function parameters.
Definition iterator.h:58
VALUE rb_yield(VALUE val)
Yields the block.
Definition vm_eval.c:1372
void rb_throw_obj(VALUE tag, VALUE val)
Identical to rb_throw(), except it allows arbitrary Ruby object to become a tag.
Definition vm_eval.c:2540
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
fd_set rb_fdset_t
The data structure which wraps the fd_set bitmap used by select(2).
Definition posix.h:48
#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 TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
Definition rtypeddata.h:649
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
Definition rtypeddata.h:461
struct rb_data_type_struct rb_data_type_t
This is the struct that holds necessary info for a struct.
Definition rtypeddata.h:205
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
Definition rtypeddata.h:508
int rb_errno(void)
Identical to system errno.
Definition eval.c:2288
#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:5829
int ruby_snprintf(char *str, size_t n, char const *fmt,...)
Our own locale-insensitive version of snprintf(3).
Definition sprintf.c:1041
#define RB_PASS_CALLED_KEYWORDS
Pass keywords if current method is called with keywords, useful for argument delegation.
Definition scan_args.h:78
Scheduler APIs.
VALUE rb_fiber_scheduler_blocking_operation_wait(VALUE scheduler, void *(*function)(void *), void *data, rb_unblock_function_t *unblock_function, void *data2, int flags, struct rb_fiber_scheduler_blocking_operation_state *state)
Defer the execution of the passed function to the scheduler.
Definition scheduler.c:1104
VALUE rb_fiber_scheduler_current(void)
Identical to rb_fiber_scheduler_get(), except it also returns RUBY_Qnil in case of a blocking fiber.
Definition scheduler.c:471
VALUE rb_fiber_scheduler_fiber_interrupt(VALUE scheduler, VALUE fiber, VALUE exception)
Interrupt a fiber by raising an exception.
Definition scheduler.c:1144
VALUE rb_fiber_scheduler_block(VALUE scheduler, VALUE blocker, VALUE timeout)
Non-blocking wait for the passed "blocker", which is for instance Thread.join or Mutex....
Definition scheduler.c:660
VALUE rb_fiber_scheduler_yield(VALUE scheduler)
Yield to the scheduler, to be resumed on the next scheduling cycle.
Definition scheduler.c:561
VALUE rb_fiber_scheduler_set(VALUE scheduler)
Destructively assigns the passed scheduler to that of the current thread that is calling this functio...
Definition scheduler.c:433
VALUE rb_fiber_scheduler_current_for_threadptr(struct rb_thread_struct *thread)
Identical to rb_fiber_scheduler_current_for_thread(), except it expects a threadptr instead of a thre...
Definition scheduler.c:484
VALUE rb_fiber_scheduler_unblock(VALUE scheduler, VALUE blocker, VALUE fiber)
Wakes up a fiber previously blocked using rb_fiber_scheduler_block().
Definition scheduler.c:679
int rb_thread_fd_select(int nfds, rb_fdset_t *rfds, rb_fdset_t *wfds, rb_fdset_t *efds, struct timeval *timeout)
Waits for multiple file descriptors at once.
Definition thread.c:4569
#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
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
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