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