Coverage Report

Created: 2026-03-31 07:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ruby/thread.c
Line
Count
Source
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
88
#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"
87
#include "ruby/fiber/scheduler.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
0
#define RUBY_THREAD_PRIORITY_MAX 3
109
0
#define RUBY_THREAD_PRIORITY_MIN -3
110
#endif
111
112
static VALUE rb_cThreadShield;
113
static VALUE cThGroup;
114
115
static VALUE sym_immediate;
116
static VALUE sym_on_blocking;
117
static VALUE sym_never;
118
119
static uint32_t thread_default_quantum_ms = 100;
120
121
0
#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
124
static inline VALUE
125
rb_thread_local_storage(VALUE thread)
126
0
{
127
0
    if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
128
0
        rb_ivar_set(thread, idLocals, rb_hash_new());
129
0
        RB_FL_SET_RAW(thread, THREAD_LOCAL_STORAGE_INITIALISED);
130
0
    }
131
0
    return rb_ivar_get(thread, idLocals);
132
0
}
133
134
enum SLEEP_FLAGS {
135
    SLEEP_DEADLOCKABLE   = 0x01,
136
    SLEEP_SPURIOUS_CHECK = 0x02,
137
    SLEEP_ALLOW_SPURIOUS = 0x04,
138
    SLEEP_NO_CHECKINTS   = 0x08,
139
};
140
141
static void sleep_forever(rb_thread_t *th, unsigned int fl);
142
static int sleep_hrtime(rb_thread_t *, rb_hrtime_t, unsigned int fl);
143
144
static void rb_thread_sleep_deadly_allow_spurious_wakeup(VALUE blocker, VALUE timeout, rb_hrtime_t end);
145
static int rb_threadptr_dead(rb_thread_t *th);
146
static void rb_check_deadlock(rb_ractor_t *r);
147
static int rb_threadptr_pending_interrupt_empty_p(const rb_thread_t *th);
148
static const char *thread_status_name(rb_thread_t *th, int detail);
149
static int hrtime_update_expire(rb_hrtime_t *, const rb_hrtime_t);
150
NORETURN(static void async_bug_fd(const char *mesg, int errno_arg, int fd));
151
MAYBE_UNUSED(static int consume_communication_pipe(int fd));
152
153
static rb_atomic_t system_working = 1;
154
static rb_internal_thread_specific_key_t specific_key_count;
155
156
/********************************************************************************/
157
158
#define THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
159
160
struct rb_blocking_region_buffer {
161
    enum rb_thread_status prev_status;
162
};
163
164
static int unblock_function_set(rb_thread_t *th, rb_unblock_function_t *func, void *arg, int fail_if_interrupted);
165
static void unblock_function_clear(rb_thread_t *th);
166
167
static 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);
169
static inline void blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region);
170
171
0
#define THREAD_BLOCKING_BEGIN(th) do { \
172
0
  struct rb_thread_sched * const sched = TH_SCHED(th); \
173
0
  RB_VM_SAVE_MACHINE_CONTEXT(th); \
174
0
  thread_sched_to_waiting((sched), (th), true);
175
176
#define THREAD_BLOCKING_END(th) \
177
0
  thread_sched_to_running((sched), (th)); \
178
0
  rb_ractor_thread_switch(th->ractor, th, false); \
179
0
} while(0)
180
181
#ifdef __GNUC__
182
#ifdef HAVE_BUILTIN___BUILTIN_CHOOSE_EXPR_CONSTANT_P
183
0
#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
35
#define BLOCKING_REGION(th, exec, ubf, ubfarg, fail_if_interrupted) do { \
191
35
    struct rb_blocking_region_buffer __region; \
192
35
    if (blocking_region_begin(th, &__region, (ubf), (ubfarg), fail_if_interrupted) || \
193
35
        /* always return true unless fail_if_interrupted */ \
194
35
        !only_if_constant(fail_if_interrupted, TRUE)) { \
195
35
        /* Important that this is inlined into the macro, and not part of \
196
35
         * blocking_region_begin - see bug #20493 */ \
197
35
        RB_VM_SAVE_MACHINE_CONTEXT(th); \
198
35
        thread_sched_to_waiting(TH_SCHED(th), th, false); \
199
35
        exec; \
200
35
        blocking_region_end(th, &__region); \
201
35
    }; \
202
35
} 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
41.1M
#define RUBY_VM_CHECK_INTS_BLOCKING(ec) vm_check_ints_blocking(ec)
209
static inline int
210
vm_check_ints_blocking(rb_execution_context_t *ec)
211
41.1M
{
212
#ifdef RUBY_ASSERT_CRITICAL_SECTION
213
    VM_ASSERT(ruby_assert_critical_section_entered == 0);
214
#endif
215
216
41.1M
    rb_thread_t *th = rb_ec_thread_ptr(ec);
217
218
41.1M
    if (LIKELY(rb_threadptr_pending_interrupt_empty_p(th))) {
219
41.1M
        if (LIKELY(!RUBY_VM_INTERRUPTED_ANY(ec))) return FALSE;
220
41.1M
    }
221
0
    else {
222
0
        th->pending_interrupt_queue_checked = 0;
223
0
        RUBY_VM_SET_INTERRUPT(ec);
224
0
    }
225
226
0
    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
0
    if (result || RUBY_VM_INTERRUPTED(ec)) {
230
0
        VALUE scheduler = rb_fiber_scheduler_current_for_threadptr(th);
231
0
        if (scheduler != Qnil) {
232
0
            rb_fiber_scheduler_yield(scheduler);
233
0
        }
234
0
    }
235
236
0
    return result;
237
41.1M
}
238
239
int
240
rb_vm_check_ints_blocking(rb_execution_context_t *ec)
241
0
{
242
0
    return vm_check_ints_blocking(ec);
243
0
}
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
261
static void
262
timeout_prepare(rb_hrtime_t **to, rb_hrtime_t *rel, rb_hrtime_t *end,
263
                const struct timeval *timeout)
264
0
{
265
0
    if (timeout) {
266
0
        *rel = rb_timeval2hrtime(timeout);
267
0
        *end = rb_hrtime_add(rb_hrtime_now(), *rel);
268
0
        *to = rel;
269
0
    }
270
0
    else {
271
0
        *to = 0;
272
0
    }
273
0
}
274
275
MAYBE_UNUSED(NOINLINE(static int thread_start_func_2(rb_thread_t *th, VALUE *stack_start)));
276
MAYBE_UNUSED(static bool th_has_dedicated_nt(const rb_thread_t *th));
277
MAYBE_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
296
void
297
rb_nativethread_lock_initialize(rb_nativethread_lock_t *lock)
298
0
{
299
0
    rb_native_mutex_initialize(lock);
300
0
}
301
302
void
303
rb_nativethread_lock_destroy(rb_nativethread_lock_t *lock)
304
0
{
305
0
    rb_native_mutex_destroy(lock);
306
0
}
307
308
void
309
rb_nativethread_lock_lock(rb_nativethread_lock_t *lock)
310
0
{
311
0
    rb_native_mutex_lock(lock);
312
0
}
313
314
void
315
rb_nativethread_lock_unlock(rb_nativethread_lock_t *lock)
316
0
{
317
0
    rb_native_mutex_unlock(lock);
318
0
}
319
320
static int
321
unblock_function_set(rb_thread_t *th, rb_unblock_function_t *func, void *arg, int fail_if_interrupted)
322
35
{
323
35
    do {
324
35
        if (fail_if_interrupted) {
325
0
            if (RUBY_VM_INTERRUPTED_ANY(th->ec)) {
326
0
                return FALSE;
327
0
            }
328
0
        }
329
35
        else {
330
35
            RUBY_VM_CHECK_INTS(th->ec);
331
35
        }
332
333
35
        rb_native_mutex_lock(&th->interrupt_lock);
334
35
    } while (!th->ec->raised_flag && RUBY_VM_INTERRUPTED_ANY(th->ec) &&
335
0
             (rb_native_mutex_unlock(&th->interrupt_lock), TRUE));
336
337
35
    VM_ASSERT(th->unblock.func == NULL);
338
339
35
    th->unblock.func = func;
340
35
    th->unblock.arg = arg;
341
35
    rb_native_mutex_unlock(&th->interrupt_lock);
342
343
35
    return TRUE;
344
35
}
345
346
static void
347
unblock_function_clear(rb_thread_t *th)
348
35
{
349
35
    rb_native_mutex_lock(&th->interrupt_lock);
350
35
    th->unblock.func = 0;
351
35
    rb_native_mutex_unlock(&th->interrupt_lock);
352
35
}
353
354
static void
355
threadptr_set_interrupt_locked(rb_thread_t *th, bool trap)
356
0
{
357
    // th->interrupt_lock should be acquired here
358
359
0
    RUBY_DEBUG_LOG("th:%u trap:%d", rb_th_serial(th), trap);
360
361
0
    if (trap) {
362
0
        RUBY_VM_SET_TRAP_INTERRUPT(th->ec);
363
0
    }
364
0
    else {
365
0
        RUBY_VM_SET_INTERRUPT(th->ec);
366
0
    }
367
368
0
    if (th->unblock.func != NULL) {
369
0
        (th->unblock.func)(th->unblock.arg);
370
0
    }
371
0
    else {
372
        /* none */
373
0
    }
374
0
}
375
376
static void
377
threadptr_set_interrupt(rb_thread_t *th, int trap)
378
0
{
379
0
    rb_native_mutex_lock(&th->interrupt_lock);
380
0
    {
381
0
        threadptr_set_interrupt_locked(th, trap);
382
0
    }
383
0
    rb_native_mutex_unlock(&th->interrupt_lock);
384
0
}
385
386
/* Set interrupt flag on another thread or current thread, and call its UBF if it has one set */
387
void
388
rb_threadptr_interrupt(rb_thread_t *th)
389
0
{
390
0
    RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
391
0
    threadptr_set_interrupt(th, false);
392
0
}
393
394
static void
395
threadptr_trap_interrupt(rb_thread_t *th)
396
0
{
397
0
    threadptr_set_interrupt(th, true);
398
0
}
399
400
static void
401
terminate_all(rb_ractor_t *r, const rb_thread_t *main_thread)
402
0
{
403
0
    rb_thread_t *th = 0;
404
405
0
    ccan_list_for_each(&r->threads.set, th, lt_node) {
406
0
        if (th != main_thread) {
407
0
            RUBY_DEBUG_LOG("terminate start th:%u status:%s", rb_th_serial(th), thread_status_name(th, TRUE));
408
409
0
            rb_threadptr_pending_interrupt_enque(th, RUBY_FATAL_THREAD_TERMINATED);
410
0
            rb_threadptr_interrupt(th);
411
412
0
            RUBY_DEBUG_LOG("terminate done th:%u status:%s", rb_th_serial(th), thread_status_name(th, TRUE));
413
0
        }
414
0
        else {
415
0
            RUBY_DEBUG_LOG("main thread th:%u", rb_th_serial(th));
416
0
        }
417
0
    }
418
0
}
419
420
static void
421
rb_threadptr_join_list_wakeup(rb_thread_t *thread)
422
0
{
423
0
    while (thread->join_list) {
424
0
        struct rb_waiting_list *join_list = thread->join_list;
425
426
        // Consume the entry from the join list:
427
0
        thread->join_list = join_list->next;
428
429
0
        rb_thread_t *target_thread = join_list->thread;
430
431
0
        if (target_thread->scheduler != Qnil && join_list->fiber) {
432
0
            rb_fiber_scheduler_unblock(target_thread->scheduler, target_thread->self, rb_fiberptr_self(join_list->fiber));
433
0
        }
434
0
        else {
435
0
            rb_threadptr_interrupt(target_thread);
436
437
0
            switch (target_thread->status) {
438
0
              case THREAD_STOPPED:
439
0
              case THREAD_STOPPED_FOREVER:
440
0
                target_thread->status = THREAD_RUNNABLE;
441
0
                break;
442
0
              default:
443
0
                break;
444
0
            }
445
0
        }
446
0
    }
447
0
}
448
449
void
450
rb_threadptr_unlock_all_locking_mutexes(rb_thread_t *th)
451
0
{
452
0
    while (th->keeping_mutexes) {
453
0
        rb_mutex_t *mutex = th->keeping_mutexes;
454
0
        th->keeping_mutexes = mutex->next_mutex;
455
456
        // rb_warn("mutex #<%p> was not unlocked by thread #<%p>", (void *)mutex, (void*)th);
457
0
        VM_ASSERT(mutex->ec_serial);
458
0
        const char *error_message = rb_mutex_unlock_th(mutex, th, 0);
459
0
        if (error_message) rb_bug("invalid keeping_mutexes: %s", error_message);
460
0
    }
461
0
}
462
463
void
464
rb_thread_terminate_all(rb_thread_t *th)
465
0
{
466
0
    rb_ractor_t *cr = th->ractor;
467
0
    rb_execution_context_t * volatile ec = th->ec;
468
0
    volatile int sleeping = 0;
469
470
0
    if (cr->threads.main != th) {
471
0
        rb_bug("rb_thread_terminate_all: called by child thread (%p, %p)",
472
0
               (void *)cr->threads.main, (void *)th);
473
0
    }
474
475
    /* unlock all locking mutexes */
476
0
    rb_threadptr_unlock_all_locking_mutexes(th);
477
478
0
    EC_PUSH_TAG(ec);
479
0
    if (EC_EXEC_TAG() == TAG_NONE) {
480
0
      retry:
481
0
        RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
482
483
0
        terminate_all(cr, th);
484
485
0
        while (rb_ractor_living_thread_num(cr) > 1) {
486
0
            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
0
            sleeping = 1;
492
0
            native_sleep(th, &rel);
493
0
            RUBY_VM_CHECK_INTS_BLOCKING(ec);
494
0
            sleeping = 0;
495
0
        }
496
0
    }
497
0
    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
0
        if (sleeping) {
504
0
            sleeping = 0;
505
0
            goto retry;
506
0
        }
507
0
    }
508
0
    EC_POP_TAG();
509
0
}
510
511
void rb_threadptr_root_fiber_terminate(rb_thread_t *th);
512
static void threadptr_interrupt_exec_cleanup(rb_thread_t *th);
513
514
static void
515
thread_cleanup_func_before_exec(void *th_ptr)
516
0
{
517
0
    rb_thread_t *th = th_ptr;
518
0
    th->status = THREAD_KILLED;
519
520
    // The thread stack doesn't exist in the forked process:
521
0
    th->ec->machine.stack_start = th->ec->machine.stack_end = NULL;
522
523
0
    threadptr_interrupt_exec_cleanup(th);
524
0
    rb_threadptr_root_fiber_terminate(th);
525
0
}
526
527
static void
528
thread_cleanup_func(void *th_ptr, int atfork)
529
0
{
530
0
    rb_thread_t *th = th_ptr;
531
532
0
    th->locking_mutex = Qfalse;
533
0
    thread_cleanup_func_before_exec(th_ptr);
534
535
0
    if (atfork) {
536
0
        native_thread_destroy_atfork(th->nt);
537
0
        th->nt = NULL;
538
0
        return;
539
0
    }
540
541
0
    rb_native_mutex_destroy(&th->interrupt_lock);
542
0
}
543
544
void
545
rb_thread_free_native_thread(void *th_ptr)
546
0
{
547
0
    rb_thread_t *th = th_ptr;
548
549
0
    native_thread_destroy_atfork(th->nt);
550
0
    th->nt = NULL;
551
0
}
552
553
static VALUE rb_threadptr_raise(rb_thread_t *, int, VALUE *);
554
static VALUE rb_thread_to_s(VALUE thread);
555
556
void
557
ruby_thread_init_stack(rb_thread_t *th, void *local_in_parent_frame)
558
9
{
559
9
    native_thread_init_stack(th, local_in_parent_frame);
560
9
}
561
562
const VALUE *
563
rb_vm_proc_local_ep(VALUE proc)
564
0
{
565
0
    const VALUE *ep = vm_proc_ep(proc);
566
567
0
    if (ep) {
568
0
        return rb_vm_ep_local_ep(ep);
569
0
    }
570
0
    else {
571
0
        return NULL;
572
0
    }
573
0
}
574
575
// for ractor, defined in vm.c
576
VALUE 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
579
static VALUE
580
thread_do_start_proc(rb_thread_t *th)
581
0
{
582
0
    VALUE args = th->invoke_arg.proc.args;
583
0
    const VALUE *args_ptr;
584
0
    int args_len;
585
0
    VALUE procval = th->invoke_arg.proc.proc;
586
0
    rb_proc_t *proc;
587
0
    GetProcPtr(procval, proc);
588
589
0
    th->ec->errinfo = Qnil;
590
0
    th->ec->root_lep = rb_vm_proc_local_ep(procval);
591
0
    th->ec->root_svar = Qfalse;
592
593
0
    vm_check_ints_blocking(th->ec);
594
595
0
    if (th->invoke_type == thread_invoke_type_ractor_proc) {
596
0
        VALUE self = rb_ractor_self(th->ractor);
597
0
        th->thgroup = th->ractor->thgroup_default = rb_obj_alloc(cThGroup);
598
599
0
        VM_ASSERT(FIXNUM_P(args));
600
0
        args_len = FIX2INT(args);
601
0
        args_ptr = ALLOCA_N(VALUE, args_len);
602
0
        rb_ractor_receive_parameters(th->ec, th->ractor, args_len, (VALUE *)args_ptr);
603
0
        vm_check_ints_blocking(th->ec);
604
605
0
        return rb_vm_invoke_proc_with_self(
606
0
            th->ec, proc, self,
607
0
            args_len, args_ptr,
608
0
            th->invoke_arg.proc.kw_splat,
609
0
            VM_BLOCK_HANDLER_NONE
610
0
        );
611
0
    }
612
0
    else {
613
0
        args_len = RARRAY_LENINT(args);
614
0
        if (args_len < 8) {
615
            /* free proc.args if the length is enough small */
616
0
            args_ptr = ALLOCA_N(VALUE, args_len);
617
0
            MEMCPY((VALUE *)args_ptr, RARRAY_CONST_PTR(args), VALUE, args_len);
618
0
            th->invoke_arg.proc.args = Qnil;
619
0
        }
620
0
        else {
621
0
            args_ptr = RARRAY_CONST_PTR(args);
622
0
        }
623
624
0
        vm_check_ints_blocking(th->ec);
625
626
0
        return rb_vm_invoke_proc(
627
0
            th->ec, proc,
628
0
            args_len, args_ptr,
629
0
            th->invoke_arg.proc.kw_splat,
630
0
            VM_BLOCK_HANDLER_NONE
631
0
        );
632
0
    }
633
0
}
634
635
static VALUE
636
thread_do_start(rb_thread_t *th)
637
0
{
638
0
    native_set_thread_name(th);
639
0
    VALUE result = Qundef;
640
641
0
    switch (th->invoke_type) {
642
0
      case thread_invoke_type_proc:
643
0
        result = thread_do_start_proc(th);
644
0
        break;
645
646
0
      case thread_invoke_type_ractor_proc:
647
0
        result = thread_do_start_proc(th);
648
0
        rb_ractor_atexit(th->ec, result);
649
0
        break;
650
651
0
      case thread_invoke_type_func:
652
0
        result = (*th->invoke_arg.func.func)(th->invoke_arg.func.arg);
653
0
        break;
654
655
0
      case thread_invoke_type_none:
656
0
        rb_bug("unreachable");
657
0
    }
658
659
0
    return result;
660
0
}
661
662
void rb_ec_clear_current_thread_trace_func(const rb_execution_context_t *ec);
663
664
static int
665
thread_start_func_2(rb_thread_t *th, VALUE *stack_start)
666
0
{
667
0
    RUBY_DEBUG_LOG("th:%u", rb_th_serial(th));
668
0
    VM_ASSERT(th != th->vm->ractor.main_thread);
669
670
0
    enum ruby_tag_type state;
671
0
    VALUE errinfo = Qnil;
672
0
    rb_thread_t *ractor_main_th = th->ractor->threads.main;
673
674
    // setup ractor
675
0
    if (rb_ractor_status_p(th->ractor, ractor_blocking)) {
676
0
        RB_VM_LOCK();
677
0
        {
678
0
            rb_vm_ractor_blocking_cnt_dec(th->vm, th->ractor, __FILE__, __LINE__);
679
0
            rb_ractor_t *r = th->ractor;
680
0
            r->r_stdin = rb_io_prep_stdin();
681
0
            r->r_stdout = rb_io_prep_stdout();
682
0
            r->r_stderr = rb_io_prep_stderr();
683
0
        }
684
0
        RB_VM_UNLOCK();
685
0
    }
686
687
    // Ensure that we are not joinable.
688
0
    VM_ASSERT(UNDEF_P(th->value));
689
690
0
    int fiber_scheduler_closed = 0, event_thread_end_hooked = 0;
691
0
    VALUE result = Qundef;
692
693
0
    EC_PUSH_TAG(th->ec);
694
695
0
    if ((state = EC_EXEC_TAG()) == TAG_NONE) {
696
0
        EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_BEGIN, th->self, 0, 0, 0, Qundef);
697
698
0
        result = thread_do_start(th);
699
0
    }
700
701
0
    if (!fiber_scheduler_closed) {
702
0
        fiber_scheduler_closed = 1;
703
0
        rb_fiber_scheduler_set(Qnil);
704
0
    }
705
706
0
    if (!event_thread_end_hooked) {
707
0
        event_thread_end_hooked = 1;
708
0
        EXEC_EVENT_HOOK(th->ec, RUBY_EVENT_THREAD_END, th->self, 0, 0, 0, Qundef);
709
0
    }
710
711
0
    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
0
        th->value = result;
714
0
    }
715
0
    else {
716
0
        errinfo = th->ec->errinfo;
717
718
0
        VALUE exc = rb_vm_make_jump_tag_but_local_jump(state, Qundef);
719
0
        if (!NIL_P(exc)) errinfo = exc;
720
721
0
        if (state == TAG_FATAL) {
722
0
            if (th->invoke_type == thread_invoke_type_ractor_proc) {
723
0
                rb_ractor_atexit(th->ec, Qnil);
724
0
            }
725
            /* fatal error within this thread, need to stop whole script */
726
0
        }
727
0
        else if (rb_obj_is_kind_of(errinfo, rb_eSystemExit)) {
728
0
            if (th->invoke_type == thread_invoke_type_ractor_proc) {
729
0
                rb_ractor_atexit_exception(th->ec);
730
0
            }
731
732
            /* exit on main_thread. */
733
0
        }
734
0
        else {
735
0
            if (th->report_on_exception) {
736
0
                VALUE mesg = rb_thread_to_s(th->self);
737
0
                rb_str_cat_cstr(mesg, " terminated with exception (report_on_exception is true):\n");
738
0
                rb_write_error_str(mesg);
739
0
                rb_ec_error_print(th->ec, errinfo);
740
0
            }
741
742
0
            if (th->invoke_type == thread_invoke_type_ractor_proc) {
743
0
                rb_ractor_atexit_exception(th->ec);
744
0
            }
745
746
0
            if (th->vm->thread_abort_on_exception ||
747
0
                th->abort_on_exception || RTEST(ruby_debug)) {
748
                /* exit on main_thread */
749
0
            }
750
0
            else {
751
0
                errinfo = Qnil;
752
0
            }
753
0
        }
754
0
        th->value = Qnil;
755
0
    }
756
757
    // The thread is effectively finished and can be joined.
758
0
    VM_ASSERT(!UNDEF_P(th->value));
759
760
0
    rb_threadptr_join_list_wakeup(th);
761
0
    rb_threadptr_unlock_all_locking_mutexes(th);
762
763
0
    if (th->invoke_type == thread_invoke_type_ractor_proc) {
764
0
        rb_thread_terminate_all(th);
765
0
        rb_ractor_teardown(th->ec);
766
0
    }
767
768
0
    th->status = THREAD_KILLED;
769
0
    RUBY_DEBUG_LOG("killed th:%u", rb_th_serial(th));
770
771
0
    if (th->vm->ractor.main_thread == th) {
772
0
        ruby_stop(0);
773
0
    }
774
775
0
    if (RB_TYPE_P(errinfo, T_OBJECT)) {
776
        /* treat with normal error object */
777
0
        rb_threadptr_raise(ractor_main_th, 1, &errinfo);
778
0
    }
779
780
0
    EC_POP_TAG();
781
782
0
    rb_ec_clear_current_thread_trace_func(th->ec);
783
784
    /* locking_mutex must be Qfalse */
785
0
    if (th->locking_mutex != Qfalse) {
786
0
        rb_bug("thread_start_func_2: locking_mutex must not be set (%p:%"PRIxVALUE")",
787
0
               (void *)th, th->locking_mutex);
788
0
    }
789
790
0
    if (ractor_main_th->status == THREAD_KILLED &&
791
0
        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
0
        rb_threadptr_interrupt(ractor_main_th);
794
0
    }
795
796
0
    rb_check_deadlock(th->ractor);
797
798
0
    rb_fiber_close(th->ec->fiber_ptr);
799
800
0
    thread_cleanup_func(th, FALSE);
801
0
    VM_ASSERT(th->ec->vm_stack == NULL);
802
803
0
    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
0
        thread_sched_to_dead(TH_SCHED(th), th);
808
0
        rb_ractor_living_threads_remove(th->ractor, th);
809
0
    }
810
0
    else {
811
0
        rb_ractor_living_threads_remove(th->ractor, th);
812
0
        thread_sched_to_dead(TH_SCHED(th), th);
813
0
    }
814
815
0
    return 0;
816
0
}
817
818
struct thread_create_params {
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
832
static void thread_specific_storage_alloc(rb_thread_t *th);
833
834
static VALUE
835
thread_create_core(VALUE thval, struct thread_create_params *params)
836
0
{
837
0
    rb_execution_context_t *ec = GET_EC();
838
0
    rb_thread_t *th = rb_thread_ptr(thval), *current_th = rb_ec_thread_ptr(ec);
839
0
    int err;
840
841
0
    thread_specific_storage_alloc(th);
842
843
0
    if (OBJ_FROZEN(current_th->thgroup)) {
844
0
        rb_raise(rb_eThreadError,
845
0
                 "can't start a new thread (frozen ThreadGroup)");
846
0
    }
847
848
0
    rb_fiber_inherit_storage(ec, th->ec->fiber_ptr);
849
850
0
    switch (params->type) {
851
0
      case thread_invoke_type_proc:
852
0
        th->invoke_type = thread_invoke_type_proc;
853
0
        th->invoke_arg.proc.args = params->args;
854
0
        th->invoke_arg.proc.proc = params->proc;
855
0
        th->invoke_arg.proc.kw_splat = rb_keyword_given_p();
856
0
        break;
857
858
0
      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
0
        th->invoke_type = thread_invoke_type_ractor_proc;
863
0
        th->ractor = params->g;
864
0
        th->ec->ractor_id = rb_ractor_id(th->ractor);
865
0
        th->ractor->threads.main = th;
866
0
        th->invoke_arg.proc.proc = rb_proc_isolate_bang(params->proc, Qnil);
867
0
        th->invoke_arg.proc.args = INT2FIX(RARRAY_LENINT(params->args));
868
0
        th->invoke_arg.proc.kw_splat = rb_keyword_given_p();
869
0
        rb_ractor_send_parameters(ec, params->g, params->args);
870
0
        break;
871
872
0
      case thread_invoke_type_func:
873
0
        th->invoke_type = thread_invoke_type_func;
874
0
        th->invoke_arg.func.func = params->fn;
875
0
        th->invoke_arg.func.arg = (void *)params->args;
876
0
        break;
877
878
0
      default:
879
0
        rb_bug("unreachable");
880
0
    }
881
882
0
    th->priority = current_th->priority;
883
0
    th->thgroup = current_th->thgroup;
884
885
0
    th->pending_interrupt_queue = rb_ary_hidden_new(0);
886
0
    th->pending_interrupt_queue_checked = 0;
887
0
    th->pending_interrupt_mask_stack = rb_ary_dup(current_th->pending_interrupt_mask_stack);
888
0
    RBASIC_CLEAR_CLASS(th->pending_interrupt_mask_stack);
889
890
0
    rb_native_mutex_initialize(&th->interrupt_lock);
891
892
0
    RUBY_DEBUG_LOG("r:%u th:%u", rb_ractor_id(th->ractor), rb_th_serial(th));
893
894
0
    rb_ractor_living_threads_insert(th->ractor, th);
895
896
    /* kick thread */
897
0
    err = native_thread_create(th);
898
0
    if (err) {
899
0
        th->status = THREAD_KILLED;
900
0
        rb_ractor_living_threads_remove(th->ractor, th);
901
0
        rb_raise(rb_eThreadError, "can't create Thread: %s", strerror(err));
902
0
    }
903
0
    return thval;
904
0
}
905
906
0
#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
 */
928
static VALUE
929
thread_s_new(int argc, VALUE *argv, VALUE klass)
930
0
{
931
0
    rb_thread_t *th;
932
0
    VALUE thread = rb_thread_alloc(klass);
933
934
0
    if (GET_RACTOR()->threads.main->status == THREAD_KILLED) {
935
0
        rb_raise(rb_eThreadError, "can't alloc thread");
936
0
    }
937
938
0
    rb_obj_call_init_kw(thread, argc, argv, RB_PASS_CALLED_KEYWORDS);
939
0
    th = rb_thread_ptr(thread);
940
0
    if (!threadptr_initialized(th)) {
941
0
        rb_raise(rb_eThreadError, "uninitialized thread - check '%"PRIsVALUE"#initialize'",
942
0
                 klass);
943
0
    }
944
0
    return thread;
945
0
}
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
957
static VALUE
958
thread_start(VALUE klass, VALUE args)
959
0
{
960
0
    struct thread_create_params params = {
961
0
        .type = thread_invoke_type_proc,
962
0
        .args = args,
963
0
        .proc = rb_block_proc(),
964
0
    };
965
0
    return thread_create_core(rb_thread_alloc(klass), &params);
966
0
}
967
968
static VALUE
969
threadptr_invoke_proc_location(rb_thread_t *th)
970
0
{
971
0
    if (th->invoke_type == thread_invoke_type_proc) {
972
0
        return rb_proc_location(th->invoke_arg.proc.proc);
973
0
    }
974
0
    else {
975
0
        return Qnil;
976
0
    }
977
0
}
978
979
/* :nodoc: */
980
static VALUE
981
thread_initialize(VALUE thread, VALUE args)
982
0
{
983
0
    rb_thread_t *th = rb_thread_ptr(thread);
984
985
0
    if (!rb_block_given_p()) {
986
0
        rb_raise(rb_eThreadError, "must be called with a block");
987
0
    }
988
0
    else if (th->invoke_type != thread_invoke_type_none) {
989
0
        VALUE loc = threadptr_invoke_proc_location(th);
990
0
        if (!NIL_P(loc)) {
991
0
            rb_raise(rb_eThreadError,
992
0
                     "already initialized thread - %"PRIsVALUE":%"PRIsVALUE,
993
0
                     RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
994
0
        }
995
0
        else {
996
0
            rb_raise(rb_eThreadError, "already initialized thread");
997
0
        }
998
0
    }
999
0
    else {
1000
0
        struct thread_create_params params = {
1001
0
            .type = thread_invoke_type_proc,
1002
0
            .args = args,
1003
0
            .proc = rb_block_proc(),
1004
0
        };
1005
0
        return thread_create_core(thread, &params);
1006
0
    }
1007
0
}
1008
1009
VALUE
1010
rb_thread_create(VALUE (*fn)(void *), void *arg)
1011
0
{
1012
0
    struct thread_create_params params = {
1013
0
        .type = thread_invoke_type_func,
1014
0
        .fn = fn,
1015
0
        .args = (VALUE)arg,
1016
0
    };
1017
0
    return thread_create_core(rb_thread_alloc(rb_cThread), &params);
1018
0
}
1019
1020
VALUE
1021
rb_thread_create_ractor(rb_ractor_t *r, VALUE args, VALUE proc)
1022
0
{
1023
0
    struct thread_create_params params = {
1024
0
        .type = thread_invoke_type_ractor_proc,
1025
0
        .g = r,
1026
0
        .args = args,
1027
0
        .proc = proc,
1028
0
    };
1029
0
    return thread_create_core(rb_thread_alloc(rb_cThread), &params);
1030
0
}
1031
1032
1033
struct join_arg {
1034
    struct rb_waiting_list *waiter;
1035
    rb_thread_t *target;
1036
    VALUE timeout;
1037
    rb_hrtime_t *limit;
1038
};
1039
1040
static VALUE
1041
remove_from_join_list(VALUE arg)
1042
0
{
1043
0
    struct join_arg *p = (struct join_arg *)arg;
1044
0
    rb_thread_t *target_thread = p->target;
1045
1046
0
    if (target_thread->status != THREAD_KILLED) {
1047
0
        struct rb_waiting_list **join_list = &target_thread->join_list;
1048
1049
0
        while (*join_list) {
1050
0
            if (*join_list == p->waiter) {
1051
0
                *join_list = (*join_list)->next;
1052
0
                break;
1053
0
            }
1054
1055
0
            join_list = &(*join_list)->next;
1056
0
        }
1057
0
    }
1058
1059
0
    return Qnil;
1060
0
}
1061
1062
static int
1063
thread_finished(rb_thread_t *th)
1064
0
{
1065
0
    return th->status == THREAD_KILLED || !UNDEF_P(th->value);
1066
0
}
1067
1068
static VALUE
1069
thread_join_sleep(VALUE arg)
1070
0
{
1071
0
    struct join_arg *p = (struct join_arg *)arg;
1072
0
    rb_thread_t *target_th = p->target, *th = p->waiter->thread;
1073
0
    rb_hrtime_t end = 0, *limit = p->limit;
1074
1075
0
    if (limit) {
1076
0
        end = rb_hrtime_add(*limit, rb_hrtime_now());
1077
0
    }
1078
1079
0
    while (!thread_finished(target_th)) {
1080
0
        VALUE scheduler = rb_fiber_scheduler_current_for_threadptr(th);
1081
1082
0
        if (!limit) {
1083
0
            if (scheduler != Qnil) {
1084
0
                rb_fiber_scheduler_block(scheduler, target_th->self, Qnil);
1085
0
            }
1086
0
            else {
1087
0
                sleep_forever(th, SLEEP_DEADLOCKABLE | SLEEP_ALLOW_SPURIOUS | SLEEP_NO_CHECKINTS);
1088
0
            }
1089
0
        }
1090
0
        else {
1091
0
            if (hrtime_update_expire(limit, end)) {
1092
0
                RUBY_DEBUG_LOG("timeout target_th:%u", rb_th_serial(target_th));
1093
0
                return Qfalse;
1094
0
            }
1095
1096
0
            if (scheduler != Qnil) {
1097
0
                VALUE timeout = rb_float_new(hrtime2double(*limit));
1098
0
                rb_fiber_scheduler_block(scheduler, target_th->self, timeout);
1099
0
            }
1100
0
            else {
1101
0
                th->status = THREAD_STOPPED;
1102
0
                native_sleep(th, limit);
1103
0
            }
1104
0
        }
1105
0
        RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1106
0
        th->status = THREAD_RUNNABLE;
1107
1108
0
        RUBY_DEBUG_LOG("interrupted target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1109
0
    }
1110
1111
0
    return Qtrue;
1112
0
}
1113
1114
static VALUE
1115
thread_join(rb_thread_t *target_th, VALUE timeout, rb_hrtime_t *limit)
1116
0
{
1117
0
    rb_execution_context_t *ec = GET_EC();
1118
0
    rb_thread_t *th = ec->thread_ptr;
1119
0
    rb_fiber_t *fiber = ec->fiber_ptr;
1120
1121
0
    if (th == target_th) {
1122
0
        rb_raise(rb_eThreadError, "Target thread must not be current thread");
1123
0
    }
1124
1125
0
    if (th->ractor->threads.main == target_th) {
1126
0
        rb_raise(rb_eThreadError, "Target thread must not be main thread");
1127
0
    }
1128
1129
0
    RUBY_DEBUG_LOG("target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1130
1131
0
    if (target_th->status != THREAD_KILLED) {
1132
0
        struct rb_waiting_list waiter;
1133
0
        waiter.next = target_th->join_list;
1134
0
        waiter.thread = th;
1135
0
        waiter.fiber = rb_fiberptr_blocking(fiber) ? NULL : fiber;
1136
0
        target_th->join_list = &waiter;
1137
1138
0
        struct join_arg arg;
1139
0
        arg.waiter = &waiter;
1140
0
        arg.target = target_th;
1141
0
        arg.timeout = timeout;
1142
0
        arg.limit = limit;
1143
1144
0
        if (!rb_ensure(thread_join_sleep, (VALUE)&arg, remove_from_join_list, (VALUE)&arg)) {
1145
0
            return Qnil;
1146
0
        }
1147
0
    }
1148
1149
0
    RUBY_DEBUG_LOG("success target_th:%u status:%s", rb_th_serial(target_th), thread_status_name(target_th, TRUE));
1150
1151
0
    if (target_th->ec->errinfo != Qnil) {
1152
0
        VALUE err = target_th->ec->errinfo;
1153
1154
0
        if (FIXNUM_P(err)) {
1155
0
            switch (err) {
1156
0
              case INT2FIX(TAG_FATAL):
1157
0
                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
0
                break;
1161
0
              default:
1162
0
                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
0
                    break;
1165
0
                }
1166
0
                rb_bug("thread_join: Fixnum (%d) should not reach here.", FIX2INT(err));
1167
0
            }
1168
0
        }
1169
0
        else if (THROW_DATA_P(target_th->ec->errinfo)) {
1170
0
            rb_bug("thread_join: THROW_DATA should not reach here.");
1171
0
        }
1172
0
        else {
1173
            /* normal exception */
1174
0
            rb_exc_raise(err);
1175
0
        }
1176
0
    }
1177
0
    return target_th->self;
1178
0
}
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
1219
static VALUE
1220
thread_join_m(int argc, VALUE *argv, VALUE self)
1221
0
{
1222
0
    VALUE timeout = Qnil;
1223
0
    rb_hrtime_t rel = 0, *limit = 0;
1224
1225
0
    if (rb_check_arity(argc, 0, 1)) {
1226
0
        timeout = argv[0];
1227
0
    }
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
0
    if (NIL_P(timeout)) {
1235
        /* unlimited */
1236
0
    }
1237
0
    else if (FIXNUM_P(timeout)) {
1238
0
        rel = rb_sec2hrtime(NUM2TIMET(timeout));
1239
0
        limit = &rel;
1240
0
    }
1241
0
    else {
1242
0
        limit = double2hrtime(&rel, rb_num2dbl(timeout));
1243
0
    }
1244
1245
0
    return thread_join(rb_thread_ptr(self), timeout, limit);
1246
0
}
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
1262
static VALUE
1263
thread_value(VALUE self)
1264
0
{
1265
0
    rb_thread_t *th = rb_thread_ptr(self);
1266
0
    thread_join(th, Qnil, 0);
1267
0
    if (UNDEF_P(th->value)) {
1268
        // If the thread is dead because we forked th->value is still Qundef.
1269
0
        return Qnil;
1270
0
    }
1271
0
    return th->value;
1272
0
}
1273
1274
/*
1275
 * Thread Scheduling
1276
 */
1277
1278
static void
1279
getclockofday(struct timespec *ts)
1280
19.0M
{
1281
19.0M
#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_MONOTONIC)
1282
19.0M
    if (clock_gettime(CLOCK_MONOTONIC, ts) == 0)
1283
19.0M
        return;
1284
0
#endif
1285
0
    rb_timespec_now(ts);
1286
0
}
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
 */
1292
NOINLINE(rb_hrtime_t rb_hrtime_now(void));
1293
rb_hrtime_t
1294
rb_hrtime_now(void)
1295
19.0M
{
1296
19.0M
    struct timespec ts;
1297
1298
19.0M
    getclockofday(&ts);
1299
19.0M
    return rb_timespec2hrtime(&ts);
1300
19.0M
}
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
 */
1306
COMPILER_WARNING_PUSH
1307
#if defined(__GNUC__) && __GNUC__ == 7 && __GNUC_MINOR__ <= 3
1308
COMPILER_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
 */
1318
static int
1319
hrtime_update_expire(rb_hrtime_t *timeout, const rb_hrtime_t end)
1320
0
{
1321
0
    rb_hrtime_t now = rb_hrtime_now();
1322
1323
0
    if (now > end) return 1;
1324
1325
0
    RUBY_DEBUG_LOG("%"PRIu64" > %"PRIu64"", (uint64_t)end, (uint64_t)now);
1326
1327
0
    *timeout = end - now;
1328
0
    return 0;
1329
0
}
1330
COMPILER_WARNING_POP
1331
1332
static int
1333
sleep_hrtime(rb_thread_t *th, rb_hrtime_t rel, unsigned int fl)
1334
0
{
1335
0
    enum rb_thread_status prev_status = th->status;
1336
0
    int woke;
1337
0
    rb_hrtime_t end = rb_hrtime_add(rb_hrtime_now(), rel);
1338
1339
0
    th->status = THREAD_STOPPED;
1340
0
    RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1341
0
    while (th->status == THREAD_STOPPED) {
1342
0
        native_sleep(th, &rel);
1343
0
        woke = vm_check_ints_blocking(th->ec);
1344
0
        if (woke && !(fl & SLEEP_SPURIOUS_CHECK))
1345
0
            break;
1346
0
        if (hrtime_update_expire(&rel, end))
1347
0
            break;
1348
0
        woke = 1;
1349
0
    }
1350
0
    th->status = prev_status;
1351
0
    return woke;
1352
0
}
1353
1354
static int
1355
sleep_hrtime_until(rb_thread_t *th, rb_hrtime_t end, unsigned int fl)
1356
0
{
1357
0
    enum rb_thread_status prev_status = th->status;
1358
0
    int woke;
1359
0
    rb_hrtime_t rel = rb_hrtime_sub(end, rb_hrtime_now());
1360
1361
0
    th->status = THREAD_STOPPED;
1362
0
    RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1363
0
    while (th->status == THREAD_STOPPED) {
1364
0
        native_sleep(th, &rel);
1365
0
        woke = vm_check_ints_blocking(th->ec);
1366
0
        if (woke && !(fl & SLEEP_SPURIOUS_CHECK))
1367
0
            break;
1368
0
        if (hrtime_update_expire(&rel, end))
1369
0
            break;
1370
0
        woke = 1;
1371
0
    }
1372
0
    th->status = prev_status;
1373
0
    return woke;
1374
0
}
1375
1376
static void
1377
sleep_forever(rb_thread_t *th, unsigned int fl)
1378
0
{
1379
0
    enum rb_thread_status prev_status = th->status;
1380
0
    enum rb_thread_status status;
1381
0
    int woke;
1382
1383
0
    status  = fl & SLEEP_DEADLOCKABLE ? THREAD_STOPPED_FOREVER : THREAD_STOPPED;
1384
0
    th->status = status;
1385
1386
0
    if (!(fl & SLEEP_NO_CHECKINTS)) RUBY_VM_CHECK_INTS_BLOCKING(th->ec);
1387
1388
0
    while (th->status == status) {
1389
0
        if (fl & SLEEP_DEADLOCKABLE) {
1390
0
            rb_ractor_sleeper_threads_inc(th->ractor);
1391
0
            rb_check_deadlock(th->ractor);
1392
0
        }
1393
0
        {
1394
0
            native_sleep(th, 0);
1395
0
        }
1396
0
        if (fl & SLEEP_DEADLOCKABLE) {
1397
0
            rb_ractor_sleeper_threads_dec(th->ractor);
1398
0
        }
1399
0
        if (fl & SLEEP_ALLOW_SPURIOUS) {
1400
0
            break;
1401
0
        }
1402
1403
0
        woke = vm_check_ints_blocking(th->ec);
1404
1405
0
        if (woke && !(fl & SLEEP_SPURIOUS_CHECK)) {
1406
0
            break;
1407
0
        }
1408
0
    }
1409
0
    th->status = prev_status;
1410
0
}
1411
1412
void
1413
rb_thread_sleep_forever(void)
1414
0
{
1415
0
    RUBY_DEBUG_LOG("forever");
1416
0
    sleep_forever(GET_THREAD(), SLEEP_SPURIOUS_CHECK);
1417
0
}
1418
1419
void
1420
rb_thread_sleep_deadly(void)
1421
0
{
1422
0
    RUBY_DEBUG_LOG("deadly");
1423
0
    sleep_forever(GET_THREAD(), SLEEP_DEADLOCKABLE|SLEEP_SPURIOUS_CHECK);
1424
0
}
1425
1426
static void
1427
rb_thread_sleep_deadly_allow_spurious_wakeup(VALUE blocker, VALUE timeout, rb_hrtime_t end)
1428
0
{
1429
0
    rb_thread_t *th = GET_THREAD();
1430
0
    VALUE scheduler = rb_fiber_scheduler_current_for_threadptr(th);
1431
0
    if (scheduler != Qnil) {
1432
0
        rb_fiber_scheduler_block(scheduler, blocker, timeout);
1433
0
    }
1434
0
    else {
1435
0
        RUBY_DEBUG_LOG("...");
1436
0
        if (end) {
1437
0
            sleep_hrtime_until(th, end, SLEEP_SPURIOUS_CHECK);
1438
0
        }
1439
0
        else {
1440
0
            sleep_forever(th, SLEEP_DEADLOCKABLE);
1441
0
        }
1442
0
    }
1443
0
}
1444
1445
void
1446
rb_thread_wait_for(struct timeval time)
1447
0
{
1448
0
    rb_thread_t *th = GET_THREAD();
1449
1450
0
    sleep_hrtime(th, rb_timeval2hrtime(&time), SLEEP_SPURIOUS_CHECK);
1451
0
}
1452
1453
void
1454
rb_ec_check_ints(rb_execution_context_t *ec)
1455
41.1M
{
1456
41.1M
    RUBY_VM_CHECK_INTS_BLOCKING(ec);
1457
41.1M
}
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
1466
void
1467
rb_thread_check_ints(void)
1468
41.1M
{
1469
41.1M
    rb_ec_check_ints(GET_EC());
1470
41.1M
}
1471
1472
/*
1473
 * Hidden API for tcl/tk wrapper.
1474
 * There is no guarantee to perpetuate it.
1475
 */
1476
int
1477
rb_thread_check_trap_pending(void)
1478
0
{
1479
0
    return rb_signal_buff_size() != 0;
1480
0
}
1481
1482
/* This function can be called in blocking region. */
1483
int
1484
rb_thread_interrupted(VALUE thval)
1485
0
{
1486
0
    return (int)RUBY_VM_INTERRUPTED(rb_thread_ptr(thval)->ec);
1487
0
}
1488
1489
void
1490
rb_thread_sleep(int sec)
1491
0
{
1492
0
    rb_thread_wait_for(rb_time_timeval(INT2FIX(sec)));
1493
0
}
1494
1495
static void
1496
rb_thread_schedule_limits(uint32_t limits_us)
1497
0
{
1498
0
    if (!rb_thread_alone()) {
1499
0
        rb_thread_t *th = GET_THREAD();
1500
0
        RUBY_DEBUG_LOG("us:%u", (unsigned int)limits_us);
1501
1502
0
        if (th->running_time_us >= limits_us) {
1503
0
            RUBY_DEBUG_LOG("switch %s", "start");
1504
1505
0
            RB_VM_SAVE_MACHINE_CONTEXT(th);
1506
0
            thread_sched_yield(TH_SCHED(th), th);
1507
0
            rb_ractor_thread_switch(th->ractor, th, true);
1508
1509
0
            RUBY_DEBUG_LOG("switch %s", "done");
1510
0
        }
1511
0
    }
1512
0
}
1513
1514
void
1515
rb_thread_schedule(void)
1516
0
{
1517
0
    rb_thread_schedule_limits(0);
1518
0
    RUBY_VM_CHECK_INTS(GET_EC());
1519
0
}
1520
1521
/* blocking region */
1522
1523
static inline int
1524
blocking_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
35
{
1527
#ifdef RUBY_ASSERT_CRITICAL_SECTION
1528
    VM_ASSERT(ruby_assert_critical_section_entered == 0);
1529
#endif
1530
35
    VM_ASSERT(th == GET_THREAD());
1531
1532
35
    region->prev_status = th->status;
1533
35
    if (unblock_function_set(th, ubf, arg, fail_if_interrupted)) {
1534
35
        th->blocking_region_buffer = region;
1535
35
        th->status = THREAD_STOPPED;
1536
35
        rb_ractor_blocking_threads_inc(th->ractor, __FILE__, __LINE__);
1537
1538
35
        RUBY_DEBUG_LOG("thread_id:%p", (void *)th->nt->thread_id);
1539
35
        return TRUE;
1540
35
    }
1541
0
    else {
1542
0
        return FALSE;
1543
0
    }
1544
35
}
1545
1546
static inline void
1547
blocking_region_end(rb_thread_t *th, struct rb_blocking_region_buffer *region)
1548
35
{
1549
    /* entry to ubf_list still permitted at this point, make it impossible: */
1550
35
    unblock_function_clear(th);
1551
    /* entry to ubf_list impossible at this point, so unregister is safe: */
1552
35
    unregister_ubf_list(th);
1553
1554
35
    thread_sched_to_running(TH_SCHED(th), th);
1555
35
    rb_ractor_thread_switch(th->ractor, th, false);
1556
1557
35
    th->blocking_region_buffer = 0;
1558
35
    rb_ractor_blocking_threads_dec(th->ractor, __FILE__, __LINE__);
1559
35
    if (th->status == THREAD_STOPPED) {
1560
35
        th->status = region->prev_status;
1561
35
    }
1562
1563
35
    RUBY_DEBUG_LOG("end");
1564
1565
35
#ifndef _WIN32
1566
    // GET_THREAD() clears WSAGetLastError()
1567
35
    VM_ASSERT(th == GET_THREAD());
1568
35
#endif
1569
35
}
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
 */
1581
bool
1582
rb_thread_resolve_unblock_function(rb_unblock_function_t **unblock_function, void **data2, struct rb_thread_struct *thread)
1583
35
{
1584
35
    rb_unblock_function_t *ubf = *unblock_function;
1585
1586
35
    if ((ubf == RUBY_UBF_IO) || (ubf == RUBY_UBF_PROCESS)) {
1587
35
        *unblock_function = ubf_select;
1588
35
        *data2 = thread;
1589
35
        return true;
1590
35
    }
1591
0
    return false;
1592
35
}
1593
1594
void *
1595
rb_nogvl(void *(*func)(void *), void *data1,
1596
         rb_unblock_function_t *ubf, void *data2,
1597
         int flags)
1598
35
{
1599
35
    if (flags & RB_NOGVL_OFFLOAD_SAFE) {
1600
35
        VALUE scheduler = rb_fiber_scheduler_current();
1601
35
        if (scheduler != Qnil) {
1602
0
            struct rb_fiber_scheduler_blocking_operation_state state = {0};
1603
1604
0
            VALUE result = rb_fiber_scheduler_blocking_operation_wait(scheduler, func, data1, ubf, data2, flags, &state);
1605
1606
0
            if (!UNDEF_P(result)) {
1607
0
                rb_errno_set(state.saved_errno);
1608
0
                return state.result;
1609
0
            }
1610
0
        }
1611
35
    }
1612
1613
35
    void *val = 0;
1614
35
    rb_execution_context_t *ec = GET_EC();
1615
35
    rb_thread_t *th = rb_ec_thread_ptr(ec);
1616
35
    rb_vm_t *vm = rb_ec_vm_ptr(ec);
1617
35
    bool is_main_thread = vm->ractor.main_thread == th;
1618
35
    int saved_errno = 0;
1619
1620
35
    rb_thread_resolve_unblock_function(&ubf, &data2, th);
1621
1622
35
    if (ubf && rb_ractor_living_thread_num(th->ractor) == 1 && is_main_thread) {
1623
35
        if (flags & RB_NOGVL_UBF_ASYNC_SAFE) {
1624
0
            vm->ubf_async_safe = 1;
1625
0
        }
1626
35
    }
1627
1628
35
    rb_vm_t *volatile saved_vm = vm;
1629
35
    BLOCKING_REGION(th, {
1630
35
        val = func(data1);
1631
35
        saved_errno = rb_errno();
1632
35
    }, ubf, data2, flags & RB_NOGVL_INTR_FAIL);
1633
35
    vm = saved_vm;
1634
1635
35
    if (is_main_thread) vm->ubf_async_safe = 0;
1636
1637
35
    if ((flags & RB_NOGVL_INTR_FAIL) == 0) {
1638
35
        RUBY_VM_CHECK_INTS_BLOCKING(ec);
1639
35
    }
1640
1641
35
    rb_errno_set(saved_errno);
1642
1643
35
    return val;
1644
35
}
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
 */
1731
void *
1732
rb_thread_call_without_gvl2(void *(*func)(void *), void *data1,
1733
                            rb_unblock_function_t *ubf, void *data2)
1734
0
{
1735
0
    return rb_nogvl(func, data1, ubf, data2, RB_NOGVL_INTR_FAIL);
1736
0
}
1737
1738
void *
1739
rb_thread_call_without_gvl(void *(*func)(void *data), void *data1,
1740
                            rb_unblock_function_t *ubf, void *data2)
1741
0
{
1742
0
    return rb_nogvl(func, data1, ubf, data2, 0);
1743
0
}
1744
1745
static int
1746
waitfd_to_waiting_flag(int wfd_event)
1747
0
{
1748
0
    return wfd_event << 1;
1749
0
}
1750
1751
static struct ccan_list_head *
1752
rb_io_blocking_operations(struct rb_io *io)
1753
0
{
1754
0
    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
0
    if (io->fork_generation != fork_generation) {
1759
0
        ccan_list_head_init(&io->blocking_operations);
1760
0
        io->fork_generation = fork_generation;
1761
0
    }
1762
1763
0
    return &io->blocking_operations;
1764
0
}
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
 */
1776
static void
1777
rb_io_blocking_operation_enter(struct rb_io *io, struct rb_io_blocking_operation *blocking_operation)
1778
0
{
1779
0
    ccan_list_add(rb_io_blocking_operations(io), &blocking_operation->list);
1780
0
}
1781
1782
static void
1783
rb_io_blocking_operation_pop(struct rb_io *io, struct rb_io_blocking_operation *blocking_operation)
1784
0
{
1785
0
    ccan_list_del(&blocking_operation->list);
1786
0
}
1787
1788
struct io_blocking_operation_arguments {
1789
    struct rb_io *io;
1790
    struct rb_io_blocking_operation *blocking_operation;
1791
};
1792
1793
static VALUE
1794
io_blocking_operation_exit(VALUE _arguments)
1795
0
{
1796
0
    struct io_blocking_operation_arguments *arguments = (void*)_arguments;
1797
0
    struct rb_io_blocking_operation *blocking_operation = arguments->blocking_operation;
1798
1799
0
    rb_io_blocking_operation_pop(arguments->io, blocking_operation);
1800
1801
0
    rb_io_t *io = arguments->io;
1802
0
    rb_thread_t *thread = io->closing_ec->thread_ptr;
1803
0
    rb_fiber_t *fiber = io->closing_ec->fiber_ptr;
1804
1805
0
    if (thread->scheduler != Qnil) {
1806
        // This can cause spurious wakeups...
1807
0
        rb_fiber_scheduler_unblock(thread->scheduler, io->self, rb_fiberptr_self(fiber));
1808
0
    }
1809
0
    else {
1810
0
        rb_thread_wakeup(thread->self);
1811
0
    }
1812
1813
0
    return Qnil;
1814
0
}
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
 */
1826
static void
1827
rb_io_blocking_operation_exit(struct rb_io *io, struct rb_io_blocking_operation *blocking_operation)
1828
0
{
1829
0
    VALUE wakeup_mutex = io->wakeup_mutex;
1830
1831
    // Indicate that the blocking operation is no longer active:
1832
0
    blocking_operation->ec = NULL;
1833
1834
0
    if (RB_TEST(wakeup_mutex)) {
1835
0
        struct io_blocking_operation_arguments arguments = {
1836
0
            .io = io,
1837
0
            .blocking_operation = blocking_operation
1838
0
        };
1839
1840
0
        rb_mutex_synchronize(wakeup_mutex, io_blocking_operation_exit, (VALUE)&arguments);
1841
0
    }
1842
0
    else {
1843
        // If there's no wakeup_mutex, we can safely remove the operation directly:
1844
0
        rb_io_blocking_operation_pop(io, blocking_operation);
1845
0
    }
1846
0
}
1847
1848
static VALUE
1849
rb_thread_io_blocking_operation_ensure(VALUE _argument)
1850
0
{
1851
0
    struct io_blocking_operation_arguments *arguments = (void*)_argument;
1852
1853
0
    rb_io_blocking_operation_exit(arguments->io, arguments->blocking_operation);
1854
1855
0
    return Qnil;
1856
0
}
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
 */
1871
VALUE
1872
rb_thread_io_blocking_operation(VALUE self, VALUE(*function)(VALUE), VALUE argument)
1873
0
{
1874
0
    struct rb_io *io;
1875
0
    RB_IO_POINTER(self, io);
1876
1877
0
    rb_execution_context_t *ec = GET_EC();
1878
0
    struct rb_io_blocking_operation blocking_operation = {
1879
0
        .ec = ec,
1880
0
    };
1881
0
    rb_io_blocking_operation_enter(io, &blocking_operation);
1882
1883
0
    struct io_blocking_operation_arguments io_blocking_operation_arguments = {
1884
0
        .io = io,
1885
0
        .blocking_operation = &blocking_operation
1886
0
    };
1887
1888
0
    return rb_ensure(function, argument, rb_thread_io_blocking_operation_ensure, (VALUE)&io_blocking_operation_arguments);
1889
0
}
1890
1891
static bool
1892
thread_io_mn_schedulable(rb_thread_t *th, int events, const struct timeval *timeout)
1893
0
{
1894
0
#if defined(USE_MN_THREADS) && USE_MN_THREADS
1895
0
    return !th_has_dedicated_nt(th) && (events || timeout) && th->blocking;
1896
#else
1897
    return false;
1898
#endif
1899
0
}
1900
1901
// true if need retry
1902
static bool
1903
thread_io_wait_events(rb_thread_t *th, int fd, int events, const struct timeval *timeout)
1904
0
{
1905
0
#if defined(USE_MN_THREADS) && USE_MN_THREADS
1906
0
    if (thread_io_mn_schedulable(th, events, timeout)) {
1907
0
        rb_hrtime_t rel, *prel;
1908
1909
0
        if (timeout) {
1910
0
            rel = rb_timeval2hrtime(timeout);
1911
0
            prel = &rel;
1912
0
        }
1913
0
        else {
1914
0
            prel = NULL;
1915
0
        }
1916
1917
0
        VM_ASSERT(prel || (events & (RB_WAITFD_IN | RB_WAITFD_OUT)));
1918
1919
0
        if (thread_sched_wait_events(TH_SCHED(th), th, fd, waitfd_to_waiting_flag(events), prel)) {
1920
            // timeout
1921
0
            return false;
1922
0
        }
1923
0
        else {
1924
0
            return true;
1925
0
        }
1926
0
    }
1927
0
#endif // defined(USE_MN_THREADS) && USE_MN_THREADS
1928
0
    return false;
1929
0
}
1930
1931
// assume read/write
1932
static bool
1933
blocking_call_retryable_p(int r, int eno)
1934
0
{
1935
0
    if (r != -1) return false;
1936
1937
0
    switch (eno) {
1938
0
      case EAGAIN:
1939
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
1940
      case EWOULDBLOCK:
1941
#endif
1942
0
        return true;
1943
0
      default:
1944
0
        return false;
1945
0
    }
1946
0
}
1947
1948
bool
1949
rb_thread_mn_schedulable(VALUE thval)
1950
0
{
1951
0
    rb_thread_t *th = rb_thread_ptr(thval);
1952
0
    return th->mn_schedulable;
1953
0
}
1954
1955
VALUE
1956
rb_thread_io_blocking_call(struct rb_io* io, rb_blocking_function_t *func, void *data1, int events)
1957
0
{
1958
0
    rb_execution_context_t * volatile ec = GET_EC();
1959
0
    rb_thread_t * volatile th = rb_ec_thread_ptr(ec);
1960
1961
0
    RUBY_DEBUG_LOG("th:%u fd:%d ev:%d", rb_th_serial(th), io->fd, events);
1962
1963
0
    volatile VALUE val = Qundef; /* shouldn't be used */
1964
0
    volatile int saved_errno = 0;
1965
0
    enum ruby_tag_type state;
1966
0
    volatile bool prev_mn_schedulable = th->mn_schedulable;
1967
0
    th->mn_schedulable = thread_io_mn_schedulable(th, events, NULL);
1968
1969
0
    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
0
    errno = 0;
1976
1977
0
    struct rb_io_blocking_operation blocking_operation = {
1978
0
        .ec = ec,
1979
0
    };
1980
0
    rb_io_blocking_operation_enter(io, &blocking_operation);
1981
1982
0
    {
1983
0
        EC_PUSH_TAG(ec);
1984
0
        if ((state = EC_EXEC_TAG()) == TAG_NONE) {
1985
0
            volatile enum ruby_tag_type saved_state = state; /* for BLOCKING_REGION */
1986
0
          retry:
1987
0
            BLOCKING_REGION(th, {
1988
0
                val = func(data1);
1989
0
                saved_errno = errno;
1990
0
            }, ubf_select, th, FALSE);
1991
1992
0
            RUBY_ASSERT(th == rb_ec_thread_ptr(ec));
1993
0
            if (events &&
1994
0
                blocking_call_retryable_p((int)val, saved_errno) &&
1995
0
                thread_io_wait_events(th, fd, events, NULL)) {
1996
0
                RUBY_VM_CHECK_INTS_BLOCKING(ec);
1997
0
                goto retry;
1998
0
            }
1999
2000
0
            RUBY_VM_CHECK_INTS_BLOCKING(ec);
2001
2002
0
            state = saved_state;
2003
0
        }
2004
0
        EC_POP_TAG();
2005
2006
0
        th = rb_ec_thread_ptr(ec);
2007
0
        th->mn_schedulable = prev_mn_schedulable;
2008
0
    }
2009
2010
0
    rb_io_blocking_operation_exit(io, &blocking_operation);
2011
2012
0
    if (state) {
2013
0
        EC_JUMP_TAG(ec, state);
2014
0
    }
2015
2016
    // If the error was a timeout, we raise a specific exception for that:
2017
0
    if (saved_errno == ETIMEDOUT) {
2018
0
        rb_raise(rb_eIOTimeoutError, "Blocking operation timed out!");
2019
0
    }
2020
2021
0
    errno = saved_errno;
2022
2023
0
    return val;
2024
0
}
2025
2026
VALUE
2027
rb_thread_io_blocking_region(struct rb_io *io, rb_blocking_function_t *func, void *data1)
2028
0
{
2029
0
    return rb_thread_io_blocking_call(io, func, data1, 0);
2030
0
}
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
 */
2063
void *
2064
rb_thread_call_with_gvl(void *(*func)(void *), void *data1)
2065
0
{
2066
0
    rb_thread_t *th = ruby_thread_from_native();
2067
0
    struct rb_blocking_region_buffer *brb;
2068
0
    struct rb_unblock_callback prev_unblock;
2069
0
    void *r;
2070
2071
0
    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
0
        bp();
2077
0
        fprintf(stderr, "[BUG] rb_thread_call_with_gvl() is called by non-ruby thread\n");
2078
0
        exit(EXIT_FAILURE);
2079
0
    }
2080
2081
0
    brb = (struct rb_blocking_region_buffer *)th->blocking_region_buffer;
2082
0
    prev_unblock = th->unblock;
2083
2084
0
    if (brb == 0) {
2085
        /* the GVL is already acquired, call method directly */
2086
0
        return (*func)(data1);
2087
0
    }
2088
2089
0
    blocking_region_end(th, brb);
2090
    /* enter to Ruby world: You can access Ruby values, methods and so on. */
2091
0
    r = (*func)(data1);
2092
    /* leave from Ruby world: You can not access Ruby values, etc. */
2093
0
    int released = blocking_region_begin(th, brb, prev_unblock.func, prev_unblock.arg, FALSE);
2094
0
    RUBY_ASSERT_ALWAYS(released);
2095
0
    RB_VM_SAVE_MACHINE_CONTEXT(th);
2096
0
    thread_sched_to_waiting(TH_SCHED(th), th, true);
2097
0
    return r;
2098
0
}
2099
2100
/*
2101
 * ruby_thread_has_gvl_p - check if current native thread has GVL.
2102
 */
2103
2104
int
2105
ruby_thread_has_gvl_p(void)
2106
12
{
2107
12
    rb_thread_t *th = ruby_thread_from_native();
2108
2109
12
    if (th && th->blocking_region_buffer == 0) {
2110
12
        return 1;
2111
12
    }
2112
0
    else {
2113
0
        return 0;
2114
0
    }
2115
12
}
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
2125
static VALUE
2126
thread_s_pass(VALUE klass)
2127
0
{
2128
0
    rb_thread_schedule();
2129
0
    return Qnil;
2130
0
}
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
2150
void
2151
rb_threadptr_pending_interrupt_clear(rb_thread_t *th)
2152
0
{
2153
0
    rb_ary_clear(th->pending_interrupt_queue);
2154
0
}
2155
2156
void
2157
rb_threadptr_pending_interrupt_enque(rb_thread_t *th, VALUE v)
2158
0
{
2159
0
    rb_ary_push(th->pending_interrupt_queue, v);
2160
0
    th->pending_interrupt_queue_checked = 0;
2161
0
}
2162
2163
static void
2164
threadptr_check_pending_interrupt_queue(rb_thread_t *th)
2165
0
{
2166
0
    if (!th->pending_interrupt_queue) {
2167
0
        rb_raise(rb_eThreadError, "uninitialized thread");
2168
0
    }
2169
0
}
2170
2171
enum handle_interrupt_timing {
2172
    INTERRUPT_NONE,
2173
    INTERRUPT_IMMEDIATE,
2174
    INTERRUPT_ON_BLOCKING,
2175
    INTERRUPT_NEVER
2176
};
2177
2178
static enum handle_interrupt_timing
2179
rb_threadptr_pending_interrupt_from_symbol(rb_thread_t *th, VALUE sym)
2180
0
{
2181
0
    if (sym == sym_immediate) {
2182
0
        return INTERRUPT_IMMEDIATE;
2183
0
    }
2184
0
    else if (sym == sym_on_blocking) {
2185
0
        return INTERRUPT_ON_BLOCKING;
2186
0
    }
2187
0
    else if (sym == sym_never) {
2188
0
        return INTERRUPT_NEVER;
2189
0
    }
2190
0
    else {
2191
0
        rb_raise(rb_eThreadError, "unknown mask signature");
2192
0
    }
2193
0
}
2194
2195
static enum handle_interrupt_timing
2196
rb_threadptr_pending_interrupt_check_mask(rb_thread_t *th, VALUE err)
2197
0
{
2198
0
    VALUE mask;
2199
0
    long mask_stack_len = RARRAY_LEN(th->pending_interrupt_mask_stack);
2200
0
    const VALUE *mask_stack = RARRAY_CONST_PTR(th->pending_interrupt_mask_stack);
2201
0
    VALUE mod;
2202
0
    long i;
2203
2204
0
    for (i=0; i<mask_stack_len; i++) {
2205
0
        mask = mask_stack[mask_stack_len-(i+1)];
2206
2207
0
        if (SYMBOL_P(mask)) {
2208
            /* do not match RUBY_FATAL_THREAD_KILLED etc */
2209
0
            if (err != rb_cInteger) {
2210
0
                return rb_threadptr_pending_interrupt_from_symbol(th, mask);
2211
0
            }
2212
0
            else {
2213
0
                continue;
2214
0
            }
2215
0
        }
2216
2217
0
        for (mod = err; mod; mod = RCLASS_SUPER(mod)) {
2218
0
            VALUE klass = mod;
2219
0
            VALUE sym;
2220
2221
0
            if (BUILTIN_TYPE(mod) == T_ICLASS) {
2222
0
                klass = RBASIC(mod)->klass;
2223
0
            }
2224
0
            else if (mod != RCLASS_ORIGIN(mod)) {
2225
0
                continue;
2226
0
            }
2227
2228
0
            if ((sym = rb_hash_aref(mask, klass)) != Qnil) {
2229
0
                return rb_threadptr_pending_interrupt_from_symbol(th, sym);
2230
0
            }
2231
0
        }
2232
        /* try to next mask */
2233
0
    }
2234
0
    return INTERRUPT_NONE;
2235
0
}
2236
2237
static int
2238
rb_threadptr_pending_interrupt_empty_p(const rb_thread_t *th)
2239
41.1M
{
2240
41.1M
    return RARRAY_LEN(th->pending_interrupt_queue) == 0;
2241
41.1M
}
2242
2243
static int
2244
rb_threadptr_pending_interrupt_include_p(rb_thread_t *th, VALUE err)
2245
0
{
2246
0
    int i;
2247
0
    for (i=0; i<RARRAY_LEN(th->pending_interrupt_queue); i++) {
2248
0
        VALUE e = RARRAY_AREF(th->pending_interrupt_queue, i);
2249
0
        if (rb_obj_is_kind_of(e, err)) {
2250
0
            return TRUE;
2251
0
        }
2252
0
    }
2253
0
    return FALSE;
2254
0
}
2255
2256
static VALUE
2257
rb_threadptr_pending_interrupt_deque(rb_thread_t *th, enum handle_interrupt_timing timing)
2258
0
{
2259
0
#if 1 /* 1 to enable Thread#handle_interrupt, 0 to ignore it */
2260
0
    int i;
2261
2262
0
    for (i=0; i<RARRAY_LEN(th->pending_interrupt_queue); i++) {
2263
0
        VALUE err = RARRAY_AREF(th->pending_interrupt_queue, i);
2264
2265
0
        enum handle_interrupt_timing mask_timing = rb_threadptr_pending_interrupt_check_mask(th, CLASS_OF(err));
2266
2267
0
        switch (mask_timing) {
2268
0
          case INTERRUPT_ON_BLOCKING:
2269
0
            if (timing != INTERRUPT_ON_BLOCKING) {
2270
0
                break;
2271
0
            }
2272
            /* fall through */
2273
0
          case INTERRUPT_NONE: /* default: IMMEDIATE */
2274
0
          case INTERRUPT_IMMEDIATE:
2275
0
            rb_ary_delete_at(th->pending_interrupt_queue, i);
2276
0
            return err;
2277
0
          case INTERRUPT_NEVER:
2278
0
            break;
2279
0
        }
2280
0
    }
2281
2282
0
    th->pending_interrupt_queue_checked = 1;
2283
0
    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
0
}
2292
2293
static int
2294
threadptr_pending_interrupt_active_p(rb_thread_t *th)
2295
0
{
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
0
    if (th->pending_interrupt_queue_checked) {
2302
0
        return 0;
2303
0
    }
2304
2305
0
    if (rb_threadptr_pending_interrupt_empty_p(th)) {
2306
0
        return 0;
2307
0
    }
2308
2309
0
    return 1;
2310
0
}
2311
2312
static int
2313
handle_interrupt_arg_check_i(VALUE key, VALUE val, VALUE args)
2314
0
{
2315
0
    VALUE *maskp = (VALUE *)args;
2316
2317
0
    if (val != sym_immediate && val != sym_on_blocking && val != sym_never) {
2318
0
        rb_raise(rb_eArgError, "unknown mask signature");
2319
0
    }
2320
2321
0
    if (key == rb_eException && (UNDEF_P(*maskp) || NIL_P(*maskp))) {
2322
0
        *maskp = val;
2323
0
        return ST_CONTINUE;
2324
0
    }
2325
2326
0
    if (RTEST(*maskp)) {
2327
0
        if (!RB_TYPE_P(*maskp, T_HASH)) {
2328
0
            VALUE prev = *maskp;
2329
0
            *maskp = rb_ident_hash_new();
2330
0
            if (SYMBOL_P(prev)) {
2331
0
                rb_hash_aset(*maskp, rb_eException, prev);
2332
0
            }
2333
0
        }
2334
0
        rb_hash_aset(*maskp, key, val);
2335
0
    }
2336
0
    else {
2337
0
        *maskp = Qfalse;
2338
0
    }
2339
2340
0
    return ST_CONTINUE;
2341
0
}
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
 */
2427
static VALUE
2428
rb_thread_s_handle_interrupt(VALUE self, VALUE mask_arg)
2429
0
{
2430
0
    VALUE mask = Qundef;
2431
0
    rb_execution_context_t * volatile ec = GET_EC();
2432
0
    rb_thread_t * volatile th = rb_ec_thread_ptr(ec);
2433
0
    volatile VALUE r = Qnil;
2434
0
    enum ruby_tag_type state;
2435
2436
0
    if (!rb_block_given_p()) {
2437
0
        rb_raise(rb_eArgError, "block is needed.");
2438
0
    }
2439
2440
0
    mask_arg = rb_to_hash_type(mask_arg);
2441
2442
0
    if (OBJ_FROZEN(mask_arg) && rb_hash_compare_by_id_p(mask_arg)) {
2443
0
        mask = Qnil;
2444
0
    }
2445
2446
0
    rb_hash_foreach(mask_arg, handle_interrupt_arg_check_i, (VALUE)&mask);
2447
2448
0
    if (UNDEF_P(mask)) {
2449
0
        return rb_yield(Qnil);
2450
0
    }
2451
2452
0
    if (!RTEST(mask)) {
2453
0
        mask = mask_arg;
2454
0
    }
2455
0
    else if (RB_TYPE_P(mask, T_HASH)) {
2456
0
        OBJ_FREEZE(mask);
2457
0
    }
2458
2459
0
    rb_ary_push(th->pending_interrupt_mask_stack, mask);
2460
0
    if (!rb_threadptr_pending_interrupt_empty_p(th)) {
2461
0
        th->pending_interrupt_queue_checked = 0;
2462
0
        RUBY_VM_SET_INTERRUPT(th->ec);
2463
0
    }
2464
2465
0
    EC_PUSH_TAG(th->ec);
2466
0
    if ((state = EC_EXEC_TAG()) == TAG_NONE) {
2467
0
        r = rb_yield(Qnil);
2468
0
    }
2469
0
    EC_POP_TAG();
2470
2471
0
    rb_ary_pop(th->pending_interrupt_mask_stack);
2472
0
    if (!rb_threadptr_pending_interrupt_empty_p(th)) {
2473
0
        th->pending_interrupt_queue_checked = 0;
2474
0
        RUBY_VM_SET_INTERRUPT(th->ec);
2475
0
    }
2476
2477
0
    RUBY_VM_CHECK_INTS(th->ec);
2478
2479
0
    if (state) {
2480
0
        EC_JUMP_TAG(th->ec, state);
2481
0
    }
2482
2483
0
    return r;
2484
0
}
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
 */
2496
static VALUE
2497
rb_thread_pending_interrupt_p(int argc, VALUE *argv, VALUE target_thread)
2498
0
{
2499
0
    rb_thread_t *target_th = rb_thread_ptr(target_thread);
2500
2501
0
    if (!target_th->pending_interrupt_queue) {
2502
0
        return Qfalse;
2503
0
    }
2504
0
    if (rb_threadptr_pending_interrupt_empty_p(target_th)) {
2505
0
        return Qfalse;
2506
0
    }
2507
0
    if (rb_check_arity(argc, 0, 1)) {
2508
0
        VALUE err = argv[0];
2509
0
        if (!rb_obj_is_kind_of(err, rb_cModule)) {
2510
0
            rb_raise(rb_eTypeError, "class or module required for rescue clause");
2511
0
        }
2512
0
        return RBOOL(rb_threadptr_pending_interrupt_include_p(target_th, err));
2513
0
    }
2514
0
    else {
2515
0
        return Qtrue;
2516
0
    }
2517
0
}
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
2576
static VALUE
2577
rb_thread_s_pending_interrupt_p(int argc, VALUE *argv, VALUE self)
2578
0
{
2579
0
    return rb_thread_pending_interrupt_p(argc, argv, GET_THREAD()->self);
2580
0
}
2581
2582
NORETURN(static void rb_threadptr_to_kill(rb_thread_t *th));
2583
2584
static void
2585
rb_threadptr_to_kill(rb_thread_t *th)
2586
0
{
2587
0
    VM_ASSERT(GET_THREAD() == th);
2588
0
    rb_threadptr_pending_interrupt_clear(th);
2589
0
    th->status = THREAD_RUNNABLE;
2590
0
    th->to_kill = 1;
2591
0
    th->ec->errinfo = INT2FIX(TAG_FATAL);
2592
0
    EC_JUMP_TAG(th->ec, TAG_FATAL);
2593
0
}
2594
2595
static inline rb_atomic_t
2596
threadptr_get_interrupts(rb_thread_t *th)
2597
0
{
2598
0
    rb_execution_context_t *ec = th->ec;
2599
0
    rb_atomic_t interrupt;
2600
0
    rb_atomic_t old;
2601
2602
0
    old = ATOMIC_LOAD_RELAXED(ec->interrupt_flag);
2603
0
    do {
2604
0
        interrupt = old;
2605
0
        old = ATOMIC_CAS(ec->interrupt_flag, interrupt, interrupt & ec->interrupt_mask);
2606
0
    } while (old != interrupt);
2607
0
    return interrupt & (rb_atomic_t)~ec->interrupt_mask;
2608
0
}
2609
2610
static 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
2617
int
2618
rb_threadptr_execute_interrupts(rb_thread_t *th, int blocking_timing)
2619
0
{
2620
0
    rb_atomic_t interrupt;
2621
0
    int postponed_job_interrupt = 0;
2622
0
    int ret = FALSE;
2623
2624
0
    VM_ASSERT(GET_THREAD() == th);
2625
2626
0
    if (th->ec->raised_flag) return ret;
2627
2628
0
    while ((interrupt = threadptr_get_interrupts(th)) != 0) {
2629
0
        int sig;
2630
0
        int timer_interrupt;
2631
0
        int pending_interrupt;
2632
0
        int trap_interrupt;
2633
0
        int terminate_interrupt;
2634
2635
0
        timer_interrupt = interrupt & TIMER_INTERRUPT_MASK;
2636
0
        pending_interrupt = interrupt & PENDING_INTERRUPT_MASK;
2637
0
        postponed_job_interrupt = interrupt & POSTPONED_JOB_INTERRUPT_MASK;
2638
0
        trap_interrupt = interrupt & TRAP_INTERRUPT_MASK;
2639
0
        terminate_interrupt = interrupt & TERMINATE_INTERRUPT_MASK; // request from other ractors
2640
2641
0
        if (interrupt & VM_BARRIER_INTERRUPT_MASK) {
2642
0
            RB_VM_LOCKING();
2643
0
        }
2644
2645
0
        if (postponed_job_interrupt) {
2646
0
            rb_postponed_job_flush(th->vm);
2647
0
        }
2648
2649
0
        if (trap_interrupt) {
2650
            /* signal handling */
2651
0
            if (th == th->vm->ractor.main_thread) {
2652
0
                enum rb_thread_status prev_status = th->status;
2653
2654
0
                th->status = THREAD_RUNNABLE;
2655
0
                {
2656
0
                    while ((sig = rb_get_next_signal()) != 0) {
2657
0
                        ret |= rb_signal_exec(th, sig);
2658
0
                    }
2659
0
                }
2660
0
                th->status = prev_status;
2661
0
            }
2662
2663
0
            if (!ccan_list_empty(&th->interrupt_exec_tasks)) {
2664
0
                enum rb_thread_status prev_status = th->status;
2665
2666
0
                th->status = THREAD_RUNNABLE;
2667
0
                {
2668
0
                    threadptr_interrupt_exec_exec(th);
2669
0
                }
2670
0
                th->status = prev_status;
2671
0
            }
2672
0
        }
2673
2674
        /* exception from another thread */
2675
0
        if (pending_interrupt && threadptr_pending_interrupt_active_p(th)) {
2676
0
            VALUE err = rb_threadptr_pending_interrupt_deque(th, blocking_timing ? INTERRUPT_ON_BLOCKING : INTERRUPT_NONE);
2677
0
            RUBY_DEBUG_LOG("err:%"PRIdVALUE, err);
2678
0
            ret = TRUE;
2679
2680
0
            if (UNDEF_P(err)) {
2681
                /* no error */
2682
0
            }
2683
0
            else if (err == RUBY_FATAL_THREAD_KILLED        /* Thread#kill received */   ||
2684
0
                     err == RUBY_FATAL_THREAD_TERMINATED   /* Terminate thread */       ||
2685
0
                     err == INT2FIX(TAG_FATAL) /* Thread.exit etc. */         ) {
2686
0
                terminate_interrupt = 1;
2687
0
            }
2688
0
            else {
2689
0
                if (err == th->vm->special_exceptions[ruby_error_stream_closed]) {
2690
                    /* the only special exception to be queued across thread */
2691
0
                    err = ruby_vm_special_exception_copy(err);
2692
0
                }
2693
                /* set runnable if th was slept. */
2694
0
                if (th->status == THREAD_STOPPED ||
2695
0
                    th->status == THREAD_STOPPED_FOREVER)
2696
0
                    th->status = THREAD_RUNNABLE;
2697
0
                rb_exc_raise(err);
2698
0
            }
2699
0
        }
2700
2701
0
        if (terminate_interrupt) {
2702
0
            rb_threadptr_to_kill(th);
2703
0
        }
2704
2705
0
        if (timer_interrupt) {
2706
0
            uint32_t limits_us = thread_default_quantum_ms * 1000;
2707
2708
0
            if (th->priority > 0)
2709
0
                limits_us <<= th->priority;
2710
0
            else
2711
0
                limits_us >>= -th->priority;
2712
2713
0
            if (th->status == THREAD_RUNNABLE)
2714
0
                th->running_time_us += 10 * 1000; // 10ms = 10_000us // TODO: use macro
2715
2716
0
            VM_ASSERT(th->ec->cfp);
2717
0
            EXEC_EVENT_HOOK(th->ec, RUBY_INTERNAL_EVENT_SWITCH, th->ec->cfp->self,
2718
0
                            0, 0, 0, Qundef);
2719
2720
0
            rb_thread_schedule_limits(limits_us);
2721
0
        }
2722
0
    }
2723
0
    return ret;
2724
0
}
2725
2726
void
2727
rb_thread_execute_interrupts(VALUE thval)
2728
0
{
2729
0
    rb_threadptr_execute_interrupts(rb_thread_ptr(thval), 1);
2730
0
}
2731
2732
static void
2733
rb_threadptr_ready(rb_thread_t *th)
2734
0
{
2735
0
    rb_threadptr_interrupt(th);
2736
0
}
2737
2738
static VALUE
2739
rb_threadptr_raise(rb_thread_t *target_th, int argc, VALUE *argv)
2740
0
{
2741
0
    VALUE exc;
2742
2743
0
    if (rb_threadptr_dead(target_th)) {
2744
0
        return Qnil;
2745
0
    }
2746
2747
0
    if (argc == 0) {
2748
0
        exc = rb_exc_new(rb_eRuntimeError, 0, 0);
2749
0
    }
2750
0
    else {
2751
0
        exc = rb_make_exception(argc, argv);
2752
0
    }
2753
2754
    /* making an exception object can switch thread,
2755
       so we need to check thread deadness again */
2756
0
    if (rb_threadptr_dead(target_th)) {
2757
0
        return Qnil;
2758
0
    }
2759
2760
0
    rb_ec_setup_exception(GET_EC(), exc, Qundef);
2761
0
    rb_threadptr_pending_interrupt_enque(target_th, exc);
2762
0
    rb_threadptr_interrupt(target_th);
2763
2764
0
    return Qnil;
2765
0
}
2766
2767
void
2768
rb_threadptr_signal_raise(rb_thread_t *th, int sig)
2769
0
{
2770
0
    VALUE argv[2];
2771
2772
0
    argv[0] = rb_eSignal;
2773
0
    argv[1] = INT2FIX(sig);
2774
0
    rb_threadptr_raise(th->vm->ractor.main_thread, 2, argv);
2775
0
}
2776
2777
void
2778
rb_threadptr_signal_exit(rb_thread_t *th)
2779
0
{
2780
0
    VALUE argv[2];
2781
2782
0
    argv[0] = rb_eSystemExit;
2783
0
    argv[1] = rb_str_new2("exit");
2784
2785
    // TODO: check signal raise deliverly
2786
0
    rb_threadptr_raise(th->vm->ractor.main_thread, 2, argv);
2787
0
}
2788
2789
int
2790
rb_ec_set_raised(rb_execution_context_t *ec)
2791
38.1k
{
2792
38.1k
    if (ec->raised_flag & RAISED_EXCEPTION) {
2793
0
        return 1;
2794
0
    }
2795
38.1k
    ec->raised_flag |= RAISED_EXCEPTION;
2796
38.1k
    return 0;
2797
38.1k
}
2798
2799
int
2800
rb_ec_reset_raised(rb_execution_context_t *ec)
2801
35.9k
{
2802
35.9k
    if (!(ec->raised_flag & RAISED_EXCEPTION)) {
2803
16.8k
        return 0;
2804
16.8k
    }
2805
19.0k
    ec->raised_flag &= ~RAISED_EXCEPTION;
2806
19.0k
    return 1;
2807
35.9k
}
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
 */
2829
static VALUE
2830
thread_io_close_notify_all(VALUE _io)
2831
0
{
2832
0
    struct rb_io *io = (struct rb_io *)_io;
2833
2834
0
    size_t count = 0;
2835
0
    rb_vm_t *vm = io->closing_ec->thread_ptr->vm;
2836
0
    VALUE error = vm->special_exceptions[ruby_error_stream_closed];
2837
2838
0
    struct rb_io_blocking_operation *blocking_operation;
2839
0
    ccan_list_for_each(rb_io_blocking_operations(io), blocking_operation, list) {
2840
0
        rb_execution_context_t *ec = blocking_operation->ec;
2841
2842
        // If the operation is in progress, we need to interrupt it:
2843
0
        if (ec) {
2844
0
            rb_thread_t *thread = ec->thread_ptr;
2845
2846
0
            VALUE result = RUBY_Qundef;
2847
0
            if (thread->scheduler != Qnil) {
2848
0
                result = rb_fiber_scheduler_fiber_interrupt(thread->scheduler, rb_fiberptr_self(ec->fiber_ptr), error);
2849
0
            }
2850
2851
0
            if (result == RUBY_Qundef) {
2852
                // If the thread is not the current thread, we need to enqueue an error:
2853
0
                rb_threadptr_pending_interrupt_enque(thread, error);
2854
0
                rb_threadptr_interrupt(thread);
2855
0
            }
2856
0
        }
2857
2858
0
        count += 1;
2859
0
    }
2860
2861
0
    return (VALUE)count;
2862
0
}
2863
2864
size_t
2865
rb_thread_io_close_interrupt(struct rb_io *io)
2866
0
{
2867
    // We guard this operation based on `io->closing_ec` -> only one thread will ever enter this function.
2868
0
    if (io->closing_ec) {
2869
0
        return 0;
2870
0
    }
2871
2872
    // If there are no blocking operations, we are done:
2873
0
    if (ccan_list_empty(rb_io_blocking_operations(io))) {
2874
0
        return 0;
2875
0
    }
2876
2877
    // Otherwise, we are now closing the IO:
2878
0
    rb_execution_context_t *ec = GET_EC();
2879
0
    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
0
    io->wakeup_mutex = rb_mutex_new();
2883
0
    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
0
    VALUE result = rb_mutex_synchronize(io->wakeup_mutex, thread_io_close_notify_all, (VALUE)io);
2887
2888
0
    return (size_t)result;
2889
0
}
2890
2891
void
2892
rb_thread_io_close_wait(struct rb_io* io)
2893
0
{
2894
0
    VALUE wakeup_mutex = io->wakeup_mutex;
2895
2896
0
    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
0
        return;
2899
0
    }
2900
2901
0
    rb_mutex_lock(wakeup_mutex);
2902
0
    while (!ccan_list_empty(rb_io_blocking_operations(io))) {
2903
0
        rb_mutex_sleep(wakeup_mutex, Qnil);
2904
0
    }
2905
0
    rb_mutex_unlock(wakeup_mutex);
2906
2907
    // We are done closing:
2908
0
    io->wakeup_mutex = Qnil;
2909
0
    io->closing_ec = NULL;
2910
0
}
2911
2912
void
2913
rb_thread_fd_close(int fd)
2914
0
{
2915
0
    rb_warn("rb_thread_fd_close is deprecated (and is now a no-op).");
2916
0
}
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
2938
static VALUE
2939
thread_raise_m(int argc, VALUE *argv, VALUE self)
2940
0
{
2941
0
    rb_thread_t *target_th = rb_thread_ptr(self);
2942
0
    const rb_thread_t *current_th = GET_THREAD();
2943
2944
0
    threadptr_check_pending_interrupt_queue(target_th);
2945
2946
0
    if (rb_threadptr_dead(target_th)) {
2947
0
        return Qnil;
2948
0
    }
2949
2950
0
    VALUE exception = rb_exception_setup(argc, argv);
2951
0
    rb_threadptr_pending_interrupt_enque(target_th, exception);
2952
0
    rb_threadptr_interrupt(target_th);
2953
2954
    /* To perform Thread.current.raise as Kernel.raise */
2955
0
    if (current_th == target_th) {
2956
0
        RUBY_VM_CHECK_INTS(target_th->ec);
2957
0
    }
2958
0
    return Qnil;
2959
0
}
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
2976
VALUE
2977
rb_thread_kill(VALUE thread)
2978
0
{
2979
0
    rb_thread_t *target_th = rb_thread_ptr(thread);
2980
2981
0
    if (target_th->to_kill || target_th->status == THREAD_KILLED) {
2982
0
        return thread;
2983
0
    }
2984
0
    if (target_th == target_th->vm->ractor.main_thread) {
2985
0
        rb_exit(EXIT_SUCCESS);
2986
0
    }
2987
2988
0
    RUBY_DEBUG_LOG("target_th:%u", rb_th_serial(target_th));
2989
2990
0
    if (target_th == GET_THREAD()) {
2991
        /* kill myself immediately */
2992
0
        rb_threadptr_to_kill(target_th);
2993
0
    }
2994
0
    else {
2995
0
        threadptr_check_pending_interrupt_queue(target_th);
2996
0
        rb_threadptr_pending_interrupt_enque(target_th, RUBY_FATAL_THREAD_KILLED);
2997
0
        rb_threadptr_interrupt(target_th);
2998
0
    }
2999
3000
0
    return thread;
3001
0
}
3002
3003
int
3004
rb_thread_to_be_killed(VALUE thread)
3005
0
{
3006
0
    rb_thread_t *target_th = rb_thread_ptr(thread);
3007
3008
0
    if (target_th->to_kill || target_th->status == THREAD_KILLED) {
3009
0
        return TRUE;
3010
0
    }
3011
0
    return FALSE;
3012
0
}
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
3028
static VALUE
3029
rb_thread_s_kill(VALUE obj, VALUE th)
3030
0
{
3031
0
    return rb_thread_kill(th);
3032
0
}
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
3047
static VALUE
3048
rb_thread_exit(VALUE _)
3049
0
{
3050
0
    rb_thread_t *th = GET_THREAD();
3051
0
    return rb_thread_kill(th->self);
3052
0
}
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
3071
VALUE
3072
rb_thread_wakeup(VALUE thread)
3073
0
{
3074
0
    if (!RTEST(rb_thread_wakeup_alive(thread))) {
3075
0
        rb_raise(rb_eThreadError, "killed thread");
3076
0
    }
3077
0
    return thread;
3078
0
}
3079
3080
VALUE
3081
rb_thread_wakeup_alive(VALUE thread)
3082
0
{
3083
0
    rb_thread_t *target_th = rb_thread_ptr(thread);
3084
0
    if (target_th->status == THREAD_KILLED) return Qnil;
3085
3086
0
    rb_threadptr_ready(target_th);
3087
3088
0
    if (target_th->status == THREAD_STOPPED ||
3089
0
        target_th->status == THREAD_STOPPED_FOREVER) {
3090
0
        target_th->status = THREAD_RUNNABLE;
3091
0
    }
3092
3093
0
    return thread;
3094
0
}
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
3118
VALUE
3119
rb_thread_run(VALUE thread)
3120
0
{
3121
0
    rb_thread_wakeup(thread);
3122
0
    rb_thread_schedule();
3123
0
    return thread;
3124
0
}
3125
3126
3127
VALUE
3128
rb_thread_stop(void)
3129
0
{
3130
0
    if (rb_thread_alone()) {
3131
0
        rb_raise(rb_eThreadError,
3132
0
                 "stopping only thread\n\tnote: use sleep to stop forever");
3133
0
    }
3134
0
    rb_thread_sleep_deadly();
3135
0
    return Qnil;
3136
0
}
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
3153
static VALUE
3154
thread_stop(VALUE _)
3155
0
{
3156
0
    return rb_thread_stop();
3157
0
}
3158
3159
/********************************************************************/
3160
3161
VALUE
3162
rb_thread_list(void)
3163
0
{
3164
    // TODO
3165
0
    return rb_ractor_thread_list();
3166
0
}
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
3188
static VALUE
3189
thread_list(VALUE _)
3190
0
{
3191
0
    return rb_thread_list();
3192
0
}
3193
3194
VALUE
3195
rb_thread_current(void)
3196
0
{
3197
0
    return GET_THREAD()->self;
3198
0
}
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
3209
static VALUE
3210
thread_s_current(VALUE klass)
3211
0
{
3212
0
    return rb_thread_current();
3213
0
}
3214
3215
VALUE
3216
rb_thread_main(void)
3217
0
{
3218
0
    return GET_RACTOR()->threads.main->self;
3219
0
}
3220
3221
/*
3222
 *  call-seq:
3223
 *     Thread.main   -> thread
3224
 *
3225
 *  Returns the main thread.
3226
 */
3227
3228
static VALUE
3229
rb_thread_s_main(VALUE klass)
3230
0
{
3231
0
    return rb_thread_main();
3232
0
}
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
3255
static VALUE
3256
rb_thread_s_abort_exc(VALUE _)
3257
0
{
3258
0
    return RBOOL(GET_THREAD()->vm->thread_abort_on_exception);
3259
0
}
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
3292
static VALUE
3293
rb_thread_s_abort_exc_set(VALUE self, VALUE val)
3294
0
{
3295
0
    GET_THREAD()->vm->thread_abort_on_exception = RTEST(val);
3296
0
    return val;
3297
0
}
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
3315
static VALUE
3316
rb_thread_abort_exc(VALUE thread)
3317
0
{
3318
0
    return RBOOL(rb_thread_ptr(thread)->abort_on_exception);
3319
0
}
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
3335
static VALUE
3336
rb_thread_abort_exc_set(VALUE thread, VALUE val)
3337
0
{
3338
0
    rb_thread_ptr(thread)->abort_on_exception = RTEST(val);
3339
0
    return val;
3340
0
}
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
3385
static VALUE
3386
rb_thread_s_report_exc(VALUE _)
3387
0
{
3388
0
    return RBOOL(GET_THREAD()->vm->thread_report_on_exception);
3389
0
}
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
3422
static VALUE
3423
rb_thread_s_report_exc_set(VALUE self, VALUE val)
3424
0
{
3425
0
    GET_THREAD()->vm->thread_report_on_exception = RTEST(val);
3426
0
    return val;
3427
0
}
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
3441
static VALUE
3442
rb_thread_s_ignore_deadlock(VALUE _)
3443
0
{
3444
0
    return RBOOL(GET_THREAD()->vm->thread_ignore_deadlock);
3445
0
}
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
3468
static VALUE
3469
rb_thread_s_ignore_deadlock_set(VALUE self, VALUE val)
3470
0
{
3471
0
    GET_THREAD()->vm->thread_ignore_deadlock = RTEST(val);
3472
0
    return val;
3473
0
}
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
3492
static VALUE
3493
rb_thread_report_exc(VALUE thread)
3494
0
{
3495
0
    return RBOOL(rb_thread_ptr(thread)->report_on_exception);
3496
0
}
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
3512
static VALUE
3513
rb_thread_report_exc_set(VALUE thread, VALUE val)
3514
0
{
3515
0
    rb_thread_ptr(thread)->report_on_exception = RTEST(val);
3516
0
    return val;
3517
0
}
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
3529
VALUE
3530
rb_thread_group(VALUE thread)
3531
0
{
3532
0
    return rb_thread_ptr(thread)->thgroup;
3533
0
}
3534
3535
static const char *
3536
thread_status_name(rb_thread_t *th, int detail)
3537
0
{
3538
0
    switch (th->status) {
3539
0
      case THREAD_RUNNABLE:
3540
0
        return th->to_kill ? "aborting" : "run";
3541
0
      case THREAD_STOPPED_FOREVER:
3542
0
        if (detail) return "sleep_forever";
3543
0
      case THREAD_STOPPED:
3544
0
        return "sleep";
3545
0
      case THREAD_KILLED:
3546
0
        return "dead";
3547
0
      default:
3548
0
        return "unknown";
3549
0
    }
3550
0
}
3551
3552
static int
3553
rb_threadptr_dead(rb_thread_t *th)
3554
0
{
3555
0
    return th->status == THREAD_KILLED;
3556
0
}
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
3590
static VALUE
3591
rb_thread_status(VALUE thread)
3592
0
{
3593
0
    rb_thread_t *target_th = rb_thread_ptr(thread);
3594
3595
0
    if (rb_threadptr_dead(target_th)) {
3596
0
        if (!NIL_P(target_th->ec->errinfo) &&
3597
0
            !FIXNUM_P(target_th->ec->errinfo)) {
3598
0
            return Qnil;
3599
0
        }
3600
0
        else {
3601
0
            return Qfalse;
3602
0
        }
3603
0
    }
3604
0
    else {
3605
0
        return rb_str_new2(thread_status_name(target_th, FALSE));
3606
0
    }
3607
0
}
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
3624
static VALUE
3625
rb_thread_alive_p(VALUE thread)
3626
0
{
3627
0
    return RBOOL(!thread_finished(rb_thread_ptr(thread)));
3628
0
}
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
3644
static VALUE
3645
rb_thread_stop_p(VALUE thread)
3646
0
{
3647
0
    rb_thread_t *th = rb_thread_ptr(thread);
3648
3649
0
    if (rb_threadptr_dead(th)) {
3650
0
        return Qtrue;
3651
0
    }
3652
0
    return RBOOL(th->status == THREAD_STOPPED || th->status == THREAD_STOPPED_FOREVER);
3653
0
}
3654
3655
/*
3656
 * call-seq:
3657
 *   thr.name   -> string
3658
 *
3659
 * show the name of the thread.
3660
 */
3661
3662
static VALUE
3663
rb_thread_getname(VALUE thread)
3664
0
{
3665
0
    return rb_thread_ptr(thread)->name;
3666
0
}
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
3676
static VALUE
3677
rb_thread_setname(VALUE thread, VALUE name)
3678
0
{
3679
0
    rb_thread_t *target_th = rb_thread_ptr(thread);
3680
3681
0
    if (!NIL_P(name)) {
3682
0
        rb_encoding *enc;
3683
0
        StringValueCStr(name);
3684
0
        enc = rb_enc_get(name);
3685
0
        if (!rb_enc_asciicompat(enc)) {
3686
0
            rb_raise(rb_eArgError, "ASCII incompatible encoding (%s)",
3687
0
                     rb_enc_name(enc));
3688
0
        }
3689
0
        name = rb_str_new_frozen(name);
3690
0
    }
3691
0
    target_th->name = name;
3692
0
    if (threadptr_initialized(target_th) && target_th->has_dedicated_nt) {
3693
0
        native_set_another_thread_name(target_th->nt->thread_id, name);
3694
0
    }
3695
0
    return name;
3696
0
}
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
3721
static VALUE
3722
rb_thread_native_thread_id(VALUE thread)
3723
0
{
3724
0
    rb_thread_t *target_th = rb_thread_ptr(thread);
3725
0
    if (rb_threadptr_dead(target_th)) return Qnil;
3726
0
    return native_thread_native_thread_id(target_th);
3727
0
}
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
3739
static VALUE
3740
rb_thread_to_s(VALUE thread)
3741
0
{
3742
0
    VALUE cname = rb_class_path(rb_obj_class(thread));
3743
0
    rb_thread_t *target_th = rb_thread_ptr(thread);
3744
0
    const char *status;
3745
0
    VALUE str, loc;
3746
3747
0
    status = thread_status_name(target_th, TRUE);
3748
0
    str = rb_sprintf("#<%"PRIsVALUE":%p", cname, (void *)thread);
3749
0
    if (!NIL_P(target_th->name)) {
3750
0
        rb_str_catf(str, "@%"PRIsVALUE, target_th->name);
3751
0
    }
3752
0
    if ((loc = threadptr_invoke_proc_location(target_th)) != Qnil) {
3753
0
        rb_str_catf(str, " %"PRIsVALUE":%"PRIsVALUE,
3754
0
                    RARRAY_AREF(loc, 0), RARRAY_AREF(loc, 1));
3755
0
    }
3756
0
    rb_str_catf(str, " %s>", status);
3757
3758
0
    return str;
3759
0
}
3760
3761
/* variables for recursive traversals */
3762
0
#define recursive_key id__recursive_key__
3763
3764
static VALUE
3765
threadptr_local_aref(rb_thread_t *th, ID id)
3766
0
{
3767
0
    if (id == recursive_key) {
3768
0
        return th->ec->local_storage_recursive_hash;
3769
0
    }
3770
0
    else {
3771
0
        VALUE val;
3772
0
        struct rb_id_table *local_storage = th->ec->local_storage;
3773
3774
0
        if (local_storage != NULL && rb_id_table_lookup(local_storage, id, &val)) {
3775
0
            return val;
3776
0
        }
3777
0
        else {
3778
0
            return Qnil;
3779
0
        }
3780
0
    }
3781
0
}
3782
3783
VALUE
3784
rb_thread_local_aref(VALUE thread, ID id)
3785
0
{
3786
0
    return threadptr_local_aref(rb_thread_ptr(thread), id);
3787
0
}
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
3849
static VALUE
3850
rb_thread_aref(VALUE thread, VALUE key)
3851
0
{
3852
0
    ID id = rb_check_id(&key);
3853
0
    if (!id) return Qnil;
3854
0
    return rb_thread_local_aref(thread, id);
3855
0
}
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
 */
3870
static VALUE
3871
rb_thread_fetch(int argc, VALUE *argv, VALUE self)
3872
0
{
3873
0
    VALUE key, val;
3874
0
    ID id;
3875
0
    rb_thread_t *target_th = rb_thread_ptr(self);
3876
0
    int block_given;
3877
3878
0
    rb_check_arity(argc, 1, 2);
3879
0
    key = argv[0];
3880
3881
0
    block_given = rb_block_given_p();
3882
0
    if (block_given && argc == 2) {
3883
0
        rb_warn("block supersedes default value argument");
3884
0
    }
3885
3886
0
    id = rb_check_id(&key);
3887
3888
0
    if (id == recursive_key) {
3889
0
        return target_th->ec->local_storage_recursive_hash;
3890
0
    }
3891
0
    else if (id && target_th->ec->local_storage &&
3892
0
             rb_id_table_lookup(target_th->ec->local_storage, id, &val)) {
3893
0
        return val;
3894
0
    }
3895
0
    else if (block_given) {
3896
0
        return rb_yield(key);
3897
0
    }
3898
0
    else if (argc == 1) {
3899
0
        rb_key_err_raise(rb_sprintf("key not found: %+"PRIsVALUE, key), self, key);
3900
0
    }
3901
0
    else {
3902
0
        return argv[1];
3903
0
    }
3904
0
}
3905
3906
static VALUE
3907
threadptr_local_aset(rb_thread_t *th, ID id, VALUE val)
3908
0
{
3909
0
    if (id == recursive_key) {
3910
0
        th->ec->local_storage_recursive_hash = val;
3911
0
        return val;
3912
0
    }
3913
0
    else {
3914
0
        struct rb_id_table *local_storage = th->ec->local_storage;
3915
3916
0
        if (NIL_P(val)) {
3917
0
            if (!local_storage) return Qnil;
3918
0
            rb_id_table_delete(local_storage, id);
3919
0
            return Qnil;
3920
0
        }
3921
0
        else {
3922
0
            if (local_storage == NULL) {
3923
0
                th->ec->local_storage = local_storage = rb_id_table_create(0);
3924
0
            }
3925
0
            rb_id_table_insert(local_storage, id, val);
3926
0
            return val;
3927
0
        }
3928
0
    }
3929
0
}
3930
3931
VALUE
3932
rb_thread_local_aset(VALUE thread, ID id, VALUE val)
3933
0
{
3934
0
    if (OBJ_FROZEN(thread)) {
3935
0
        rb_frozen_error_raise(thread, "can't modify frozen thread locals");
3936
0
    }
3937
3938
0
    return threadptr_local_aset(rb_thread_ptr(thread), id, val);
3939
0
}
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
3954
static VALUE
3955
rb_thread_aset(VALUE self, VALUE id, VALUE val)
3956
0
{
3957
0
    return rb_thread_local_aset(self, rb_to_id(id), val);
3958
0
}
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
3988
static VALUE
3989
rb_thread_variable_get(VALUE thread, VALUE key)
3990
0
{
3991
0
    VALUE locals;
3992
0
    VALUE symbol = rb_to_symbol(key);
3993
3994
0
    if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
3995
0
        return Qnil;
3996
0
    }
3997
0
    locals = rb_thread_local_storage(thread);
3998
0
    return rb_hash_aref(locals, symbol);
3999
0
}
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
4010
static VALUE
4011
rb_thread_variable_set(VALUE thread, VALUE key, VALUE val)
4012
0
{
4013
0
    VALUE locals;
4014
4015
0
    if (OBJ_FROZEN(thread)) {
4016
0
        rb_frozen_error_raise(thread, "can't modify frozen thread locals");
4017
0
    }
4018
4019
0
    locals = rb_thread_local_storage(thread);
4020
0
    return rb_hash_aset(locals, rb_to_symbol(key), val);
4021
0
}
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
4036
static VALUE
4037
rb_thread_key_p(VALUE self, VALUE key)
4038
0
{
4039
0
    VALUE val;
4040
0
    ID id = rb_check_id(&key);
4041
0
    struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
4042
4043
0
    if (!id || local_storage == NULL) {
4044
0
        return Qfalse;
4045
0
    }
4046
0
    return RBOOL(rb_id_table_lookup(local_storage, id, &val));
4047
0
}
4048
4049
static enum rb_id_table_iterator_result
4050
thread_keys_i(ID key, VALUE value, void *ary)
4051
0
{
4052
0
    rb_ary_push((VALUE)ary, ID2SYM(key));
4053
0
    return ID_TABLE_CONTINUE;
4054
0
}
4055
4056
int
4057
rb_thread_alone(void)
4058
0
{
4059
    // TODO
4060
0
    return rb_ractor_living_thread_num(GET_RACTOR()) == 1;
4061
0
}
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
4077
static VALUE
4078
rb_thread_keys(VALUE self)
4079
0
{
4080
0
    struct rb_id_table *local_storage = rb_thread_ptr(self)->ec->local_storage;
4081
0
    VALUE ary = rb_ary_new();
4082
4083
0
    if (local_storage) {
4084
0
        rb_id_table_foreach(local_storage, thread_keys_i, (void *)ary);
4085
0
    }
4086
0
    return ary;
4087
0
}
4088
4089
static int
4090
keys_i(VALUE key, VALUE value, VALUE ary)
4091
0
{
4092
0
    rb_ary_push(ary, key);
4093
0
    return ST_CONTINUE;
4094
0
}
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
4113
static VALUE
4114
rb_thread_variables(VALUE thread)
4115
0
{
4116
0
    VALUE locals;
4117
0
    VALUE ary;
4118
4119
0
    ary = rb_ary_new();
4120
0
    if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4121
0
        return ary;
4122
0
    }
4123
0
    locals = rb_thread_local_storage(thread);
4124
0
    rb_hash_foreach(locals, keys_i, ary);
4125
4126
0
    return ary;
4127
0
}
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
4145
static VALUE
4146
rb_thread_variable_p(VALUE thread, VALUE key)
4147
0
{
4148
0
    VALUE locals;
4149
0
    VALUE symbol = rb_to_symbol(key);
4150
4151
0
    if (LIKELY(!THREAD_LOCAL_STORAGE_INITIALISED_P(thread))) {
4152
0
        return Qfalse;
4153
0
    }
4154
0
    locals = rb_thread_local_storage(thread);
4155
4156
0
    return RBOOL(rb_hash_lookup(locals, symbol) != Qnil);
4157
0
}
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
4174
static VALUE
4175
rb_thread_priority(VALUE thread)
4176
0
{
4177
0
    return INT2NUM(rb_thread_ptr(thread)->priority);
4178
0
}
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
4207
static VALUE
4208
rb_thread_priority_set(VALUE thread, VALUE prio)
4209
0
{
4210
0
    rb_thread_t *target_th = rb_thread_ptr(thread);
4211
0
    int priority;
4212
4213
#if USE_NATIVE_THREAD_PRIORITY
4214
    target_th->priority = NUM2INT(prio);
4215
    native_thread_apply_priority(th);
4216
#else
4217
0
    priority = NUM2INT(prio);
4218
0
    if (priority > RUBY_THREAD_PRIORITY_MAX) {
4219
0
        priority = RUBY_THREAD_PRIORITY_MAX;
4220
0
    }
4221
0
    else if (priority < RUBY_THREAD_PRIORITY_MIN) {
4222
0
        priority = RUBY_THREAD_PRIORITY_MIN;
4223
0
    }
4224
0
    target_th->priority = (int8_t)priority;
4225
0
#endif
4226
0
    return INT2NUM(target_th->priority);
4227
0
}
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
4262
void
4263
rb_fd_init(rb_fdset_t *fds)
4264
0
{
4265
0
    fds->maxfd = 0;
4266
0
    fds->fdset = ALLOC(fd_set);
4267
0
    FD_ZERO(fds->fdset);
4268
0
}
4269
4270
static inline size_t
4271
fdset_memsize(int maxfd)
4272
0
{
4273
0
    size_t o = howmany(maxfd, NFDBITS) * sizeof(fd_mask);
4274
0
    if (o < sizeof(fd_set)) {
4275
0
        return sizeof(fd_set);
4276
0
    }
4277
0
    return o;
4278
0
}
4279
4280
void
4281
rb_fd_init_copy(rb_fdset_t *dst, rb_fdset_t *src)
4282
0
{
4283
0
    size_t size = fdset_memsize(rb_fd_max(src));
4284
0
    dst->maxfd = src->maxfd;
4285
0
    dst->fdset = xmalloc(size);
4286
0
    memcpy(dst->fdset, src->fdset, size);
4287
0
}
4288
4289
void
4290
rb_fd_term(rb_fdset_t *fds)
4291
0
{
4292
0
    ruby_sized_xfree(fds->fdset, fdset_memsize(fds->maxfd));
4293
0
    fds->maxfd = 0;
4294
0
    fds->fdset = 0;
4295
0
}
4296
4297
void
4298
rb_fd_zero(rb_fdset_t *fds)
4299
0
{
4300
0
    if (fds->fdset)
4301
0
        MEMZERO(fds->fdset, fd_mask, howmany(fds->maxfd, NFDBITS));
4302
0
}
4303
4304
static void
4305
rb_fd_resize(int n, rb_fdset_t *fds)
4306
0
{
4307
0
    size_t m = fdset_memsize(n + 1);
4308
0
    size_t o = fdset_memsize(fds->maxfd);
4309
4310
0
    if (m > o) {
4311
0
        fds->fdset = ruby_sized_xrealloc(fds->fdset, m, o);
4312
0
        memset((char *)fds->fdset + o, 0, m - o);
4313
0
    }
4314
0
    if (n >= fds->maxfd) fds->maxfd = n + 1;
4315
0
}
4316
4317
void
4318
rb_fd_set(int n, rb_fdset_t *fds)
4319
0
{
4320
0
    rb_fd_resize(n, fds);
4321
0
    FD_SET(n, fds->fdset);
4322
0
}
4323
4324
void
4325
rb_fd_clr(int n, rb_fdset_t *fds)
4326
0
{
4327
0
    if (n >= fds->maxfd) return;
4328
0
    FD_CLR(n, fds->fdset);
4329
0
}
4330
4331
int
4332
rb_fd_isset(int n, const rb_fdset_t *fds)
4333
0
{
4334
0
    if (n >= fds->maxfd) return 0;
4335
0
    return FD_ISSET(n, fds->fdset) != 0; /* "!= 0" avoids FreeBSD PR 91421 */
4336
0
}
4337
4338
void
4339
rb_fd_copy(rb_fdset_t *dst, const fd_set *src, int max)
4340
0
{
4341
0
    size_t size = fdset_memsize(max);
4342
0
    dst->fdset = ruby_sized_xrealloc(dst->fdset, size, fdset_memsize(dst->maxfd));
4343
0
    dst->maxfd = max;
4344
0
    memcpy(dst->fdset, src, size);
4345
0
}
4346
4347
void
4348
rb_fd_dup(rb_fdset_t *dst, const rb_fdset_t *src)
4349
0
{
4350
0
    size_t size = fdset_memsize(rb_fd_max(src));
4351
0
    dst->fdset = ruby_sized_xrealloc(dst->fdset, size, fdset_memsize(dst->maxfd));
4352
0
    dst->maxfd = src->maxfd;
4353
0
    memcpy(dst->fdset, src->fdset, size);
4354
0
}
4355
4356
int
4357
rb_fd_select(int n, rb_fdset_t *readfds, rb_fdset_t *writefds, rb_fdset_t *exceptfds, struct timeval *timeout)
4358
0
{
4359
0
    fd_set *r = NULL, *w = NULL, *e = NULL;
4360
0
    if (readfds) {
4361
0
        rb_fd_resize(n - 1, readfds);
4362
0
        r = rb_fd_ptr(readfds);
4363
0
    }
4364
0
    if (writefds) {
4365
0
        rb_fd_resize(n - 1, writefds);
4366
0
        w = rb_fd_ptr(writefds);
4367
0
    }
4368
0
    if (exceptfds) {
4369
0
        rb_fd_resize(n - 1, exceptfds);
4370
0
        e = rb_fd_ptr(exceptfds);
4371
0
    }
4372
0
    return select(n, r, w, e, timeout);
4373
0
}
4374
4375
0
#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
4389
void
4390
rb_fd_init(rb_fdset_t *set)
4391
{
4392
    set->capa = FD_SETSIZE;
4393
    set->fdset = ALLOC(fd_set);
4394
    FD_ZERO(set->fdset);
4395
}
4396
4397
void
4398
rb_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
4404
static inline size_t
4405
fdset_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
4413
void
4414
rb_fd_term(rb_fdset_t *set)
4415
{
4416
    ruby_sized_xfree(set->fdset, fdset_memsize(set->capa));
4417
    set->fdset = NULL;
4418
    set->capa = 0;
4419
}
4420
4421
void
4422
rb_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
4459
static int
4460
wait_retryable(volatile int *result, int errnum, rb_hrtime_t *rel, rb_hrtime_t end)
4461
0
{
4462
0
    int r = *result;
4463
0
    if (r < 0) {
4464
0
        switch (errnum) {
4465
0
          case EINTR:
4466
0
#ifdef ERESTART
4467
0
          case ERESTART:
4468
0
#endif
4469
0
            *result = 0;
4470
0
            if (rel && hrtime_update_expire(rel, end)) {
4471
0
                *rel = 0;
4472
0
            }
4473
0
            return TRUE;
4474
0
        }
4475
0
        return FALSE;
4476
0
    }
4477
0
    else if (r == 0) {
4478
        /* check for spurious wakeup */
4479
0
        if (rel) {
4480
0
            return !hrtime_update_expire(rel, end);
4481
0
        }
4482
0
        return TRUE;
4483
0
    }
4484
0
    return FALSE;
4485
0
}
4486
4487
struct 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
4499
static VALUE
4500
select_set_free(VALUE p)
4501
0
{
4502
0
    struct select_set *set = (struct select_set *)p;
4503
4504
0
    rb_fd_term(&set->orig_rset);
4505
0
    rb_fd_term(&set->orig_wset);
4506
0
    rb_fd_term(&set->orig_eset);
4507
4508
0
    return Qfalse;
4509
0
}
4510
4511
static VALUE
4512
do_select(VALUE p)
4513
0
{
4514
0
    struct select_set *set = (struct select_set *)p;
4515
0
    volatile int result = 0;
4516
0
    int lerrno;
4517
0
    rb_hrtime_t *to, rel, end = 0;
4518
4519
0
    timeout_prepare(&to, &rel, &end, set->timeout);
4520
0
    volatile rb_hrtime_t endtime = end;
4521
0
#define restore_fdset(dst, src) \
4522
0
    ((dst) ? rb_fd_dup(dst, src) : (void)0)
4523
0
#define do_select_update() \
4524
0
    (restore_fdset(set->rset, &set->orig_rset), \
4525
0
     restore_fdset(set->wset, &set->orig_wset), \
4526
0
     restore_fdset(set->eset, &set->orig_eset), \
4527
0
     TRUE)
4528
4529
0
    do {
4530
0
        lerrno = 0;
4531
4532
0
        BLOCKING_REGION(set->th, {
4533
0
            struct timeval tv;
4534
4535
0
            if (!RUBY_VM_INTERRUPTED(set->th->ec)) {
4536
0
                result = native_fd_select(set->max,
4537
0
                                          set->rset, set->wset, set->eset,
4538
0
                                          rb_hrtime2timeval(&tv, to), set->th);
4539
0
                if (result < 0) lerrno = errno;
4540
0
            }
4541
0
        }, ubf_select, set->th, TRUE);
4542
4543
0
        RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec); /* may raise */
4544
0
    } while (wait_retryable(&result, lerrno, to, endtime) && do_select_update());
4545
4546
0
    RUBY_VM_CHECK_INTS_BLOCKING(set->th->ec);
4547
4548
0
    if (result < 0) {
4549
0
        errno = lerrno;
4550
0
    }
4551
4552
0
    return (VALUE)result;
4553
0
}
4554
4555
int
4556
rb_thread_fd_select(int max, rb_fdset_t * read, rb_fdset_t * write, rb_fdset_t * except,
4557
                    struct timeval *timeout)
4558
0
{
4559
0
    struct select_set set;
4560
4561
0
    set.th = GET_THREAD();
4562
0
    RUBY_VM_CHECK_INTS_BLOCKING(set.th->ec);
4563
0
    set.max = max;
4564
0
    set.rset = read;
4565
0
    set.wset = write;
4566
0
    set.eset = except;
4567
0
    set.timeout = timeout;
4568
4569
0
    if (!set.rset && !set.wset && !set.eset) {
4570
0
        if (!timeout) {
4571
0
            rb_thread_sleep_forever();
4572
0
            return 0;
4573
0
        }
4574
0
        rb_thread_wait_for(*timeout);
4575
0
        return 0;
4576
0
    }
4577
4578
0
#define fd_init_copy(f) do { \
4579
0
        if (set.f) { \
4580
0
            rb_fd_resize(set.max - 1, set.f); \
4581
0
            if (&set.orig_##f != set.f) { /* sigwait_fd */ \
4582
0
                rb_fd_init_copy(&set.orig_##f, set.f); \
4583
0
            } \
4584
0
        } \
4585
0
        else { \
4586
0
            rb_fd_no_init(&set.orig_##f); \
4587
0
        } \
4588
0
    } while (0)
4589
0
    fd_init_copy(rset);
4590
0
    fd_init_copy(wset);
4591
0
    fd_init_copy(eset);
4592
0
#undef fd_init_copy
4593
4594
0
    return (int)rb_ensure(do_select, (VALUE)&set, select_set_free, (VALUE)&set);
4595
0
}
4596
4597
#ifdef USE_POLL
4598
4599
/* The same with linux kernel. TODO: make platform independent definition. */
4600
0
#define POLLIN_SET (POLLRDNORM | POLLRDBAND | POLLIN | POLLHUP | POLLERR)
4601
0
#define POLLOUT_SET (POLLWRBAND | POLLWRNORM | POLLOUT | POLLERR)
4602
0
#define POLLEX_SET (POLLPRI)
4603
4604
#ifndef POLLERR_SET /* defined for FreeBSD for now */
4605
0
#  define POLLERR_SET (0)
4606
#endif
4607
4608
static int
4609
wait_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
0
{
4612
0
    struct timespec ts;
4613
0
    volatile int result = 0;
4614
4615
0
    *lerrno = 0;
4616
0
    BLOCKING_REGION(th, {
4617
0
        if (!RUBY_VM_INTERRUPTED(th->ec)) {
4618
0
            result = ppoll(fds, nfds, rb_hrtime2timespec(&ts, to), 0);
4619
0
            if (result < 0) *lerrno = errno;
4620
0
        }
4621
0
    }, ubf_select, th, TRUE);
4622
0
    return result;
4623
0
}
4624
4625
/*
4626
 * returns a mask of events
4627
 */
4628
static int
4629
thread_io_wait(rb_thread_t *th, struct rb_io *io, int fd, int events, struct timeval *timeout)
4630
0
{
4631
0
    struct pollfd fds[1] = {{
4632
0
        .fd = fd,
4633
0
        .events = (short)events,
4634
0
        .revents = 0,
4635
0
    }};
4636
0
    volatile int result = 0;
4637
0
    nfds_t nfds;
4638
0
    struct rb_io_blocking_operation blocking_operation;
4639
0
    enum ruby_tag_type state;
4640
0
    volatile int lerrno;
4641
4642
0
    RUBY_ASSERT(th);
4643
0
    rb_execution_context_t *ec = th->ec;
4644
4645
0
    if (io) {
4646
0
        blocking_operation.ec = ec;
4647
0
        rb_io_blocking_operation_enter(io, &blocking_operation);
4648
0
    }
4649
4650
0
    if (timeout == NULL && thread_io_wait_events(th, fd, events, NULL)) {
4651
        // fd is readable
4652
0
        state = 0;
4653
0
        fds[0].revents = events;
4654
0
        errno = 0;
4655
0
    }
4656
0
    else {
4657
0
        EC_PUSH_TAG(ec);
4658
0
        if ((state = EC_EXEC_TAG()) == TAG_NONE) {
4659
0
            rb_hrtime_t *to, rel, end = 0;
4660
0
            RUBY_VM_CHECK_INTS_BLOCKING(ec);
4661
0
            timeout_prepare(&to, &rel, &end, timeout);
4662
0
            do {
4663
0
                nfds = numberof(fds);
4664
0
                result = wait_for_single_fd_blocking_region(th, fds, nfds, to, &lerrno);
4665
4666
0
                RUBY_VM_CHECK_INTS_BLOCKING(ec);
4667
0
            } while (wait_retryable(&result, lerrno, to, end));
4668
4669
0
            RUBY_VM_CHECK_INTS_BLOCKING(ec);
4670
0
        }
4671
4672
0
        EC_POP_TAG();
4673
0
    }
4674
4675
0
    if (io) {
4676
0
        rb_io_blocking_operation_exit(io, &blocking_operation);
4677
0
    }
4678
4679
0
    if (state) {
4680
0
        EC_JUMP_TAG(ec, state);
4681
0
    }
4682
4683
0
    if (result < 0) {
4684
0
        errno = lerrno;
4685
0
        return -1;
4686
0
    }
4687
4688
0
    if (fds[0].revents & POLLNVAL) {
4689
0
        errno = EBADF;
4690
0
        return -1;
4691
0
    }
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
0
    result = 0;
4698
0
    if (fds[0].revents & POLLIN_SET)
4699
0
        result |= RB_WAITFD_IN;
4700
0
    if (fds[0].revents & POLLOUT_SET)
4701
0
        result |= RB_WAITFD_OUT;
4702
0
    if (fds[0].revents & POLLEX_SET)
4703
0
        result |= RB_WAITFD_PRI;
4704
4705
    /* all requested events are ready if there is an error */
4706
0
    if (fds[0].revents & POLLERR_SET)
4707
0
        result |= events;
4708
4709
0
    return result;
4710
0
}
4711
#else /* ! USE_POLL - implement rb_io_poll_fd() using select() */
4712
struct 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
4726
static VALUE
4727
select_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
4748
static VALUE
4749
select_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
4764
static rb_fdset_t *
4765
init_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
4776
static int
4777
thread_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
4810
int
4811
rb_thread_wait_for_single_fd(rb_thread_t *th, int fd, int events, struct timeval *timeout)
4812
0
{
4813
0
    return thread_io_wait(th, NULL, fd, events, timeout);
4814
0
}
4815
4816
int
4817
rb_thread_io_wait(rb_thread_t *th, struct rb_io *io, int events, struct timeval * timeout)
4818
0
{
4819
0
    return thread_io_wait(th, io, io->fd, events, timeout);
4820
0
}
4821
4822
/*
4823
 * for GC
4824
 */
4825
4826
#ifdef USE_CONSERVATIVE_STACK_END
4827
void
4828
rb_gc_set_stack_end(VALUE **stack_end_p)
4829
{
4830
    VALUE stack_end;
4831
COMPILER_WARNING_PUSH
4832
#if RBIMPL_COMPILER_IS(GCC)
4833
COMPILER_WARNING_IGNORED(-Wdangling-pointer);
4834
#endif
4835
    *stack_end_p = &stack_end;
4836
COMPILER_WARNING_POP
4837
}
4838
#endif
4839
4840
/*
4841
 *
4842
 */
4843
4844
void
4845
rb_threadptr_check_signal(rb_thread_t *mth)
4846
0
{
4847
    /* mth must be main_thread */
4848
0
    if (rb_signal_buff_size() > 0) {
4849
        /* wakeup main thread */
4850
0
        threadptr_trap_interrupt(mth);
4851
0
    }
4852
0
}
4853
4854
static void
4855
async_bug_fd(const char *mesg, int errno_arg, int fd)
4856
0
{
4857
0
    char buff[64];
4858
0
    size_t n = strlcpy(buff, mesg, sizeof(buff));
4859
0
    if (n < sizeof(buff)-3) {
4860
0
        ruby_snprintf(buff+n, sizeof(buff)-n, "(%d)", fd);
4861
0
    }
4862
0
    rb_async_bug_errno(buff, errno_arg);
4863
0
}
4864
4865
/* VM-dependent API is not available for this function */
4866
static int
4867
consume_communication_pipe(int fd)
4868
0
{
4869
0
#if USE_EVENTFD
4870
0
    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
0
    ssize_t result;
4876
0
    int ret = FALSE; /* for rb_sigwait_sleep */
4877
4878
0
    while (1) {
4879
0
        result = read(fd, buff, sizeof(buff));
4880
0
#if USE_EVENTFD
4881
0
        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
0
        if (result > 0) {
4886
0
            ret = TRUE;
4887
0
            if (USE_EVENTFD || result < (ssize_t)sizeof(buff)) {
4888
0
                return ret;
4889
0
            }
4890
0
        }
4891
0
        else if (result == 0) {
4892
0
            return ret;
4893
0
        }
4894
0
        else if (result < 0) {
4895
0
            int e = errno;
4896
0
            switch (e) {
4897
0
              case EINTR:
4898
0
                continue; /* retry */
4899
0
              case EAGAIN:
4900
#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
4901
              case EWOULDBLOCK:
4902
#endif
4903
0
                return ret;
4904
0
              default:
4905
0
                async_bug_fd("consume_communication_pipe: read", e, fd);
4906
0
            }
4907
0
        }
4908
0
    }
4909
0
}
4910
4911
void
4912
rb_thread_stop_timer_thread(void)
4913
0
{
4914
0
    if (TIMER_THREAD_CREATED_P() && native_stop_timer_thread()) {
4915
0
        native_reset_timer_thread();
4916
0
    }
4917
0
}
4918
4919
void
4920
rb_thread_reset_timer_thread(void)
4921
0
{
4922
0
    native_reset_timer_thread();
4923
0
}
4924
4925
void
4926
rb_thread_start_timer_thread(void)
4927
0
{
4928
0
    system_working = 1;
4929
0
    rb_thread_create_timer_thread();
4930
0
}
4931
4932
static int
4933
clear_coverage_i(st_data_t key, st_data_t val, st_data_t dummy)
4934
0
{
4935
0
    int i;
4936
0
    VALUE coverage = (VALUE)val;
4937
0
    VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
4938
0
    VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
4939
4940
0
    if (lines) {
4941
0
        if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
4942
0
            rb_ary_clear(lines);
4943
0
        }
4944
0
        else {
4945
0
            int i;
4946
0
            for (i = 0; i < RARRAY_LEN(lines); i++) {
4947
0
                if (RARRAY_AREF(lines, i) != Qnil)
4948
0
                    RARRAY_ASET(lines, i, INT2FIX(0));
4949
0
            }
4950
0
        }
4951
0
    }
4952
0
    if (branches) {
4953
0
        VALUE counters = RARRAY_AREF(branches, 1);
4954
0
        for (i = 0; i < RARRAY_LEN(counters); i++) {
4955
0
            RARRAY_ASET(counters, i, INT2FIX(0));
4956
0
        }
4957
0
    }
4958
4959
0
    return ST_CONTINUE;
4960
0
}
4961
4962
void
4963
rb_clear_coverages(void)
4964
0
{
4965
0
    VALUE coverages = rb_get_coverages();
4966
0
    if (RTEST(coverages)) {
4967
0
        rb_hash_foreach(coverages, clear_coverage_i, 0);
4968
0
    }
4969
0
}
4970
4971
#if defined(HAVE_WORKING_FORK)
4972
4973
static void
4974
rb_thread_atfork_internal(rb_thread_t *th, void (*atfork)(rb_thread_t *, const rb_thread_t *))
4975
0
{
4976
0
    rb_thread_t *i = 0;
4977
0
    rb_vm_t *vm = th->vm;
4978
0
    rb_ractor_t *r = th->ractor;
4979
0
    vm->ractor.main_ractor = r;
4980
0
    vm->ractor.main_thread = th;
4981
0
    r->threads.main = th;
4982
0
    r->status_ = ractor_created;
4983
4984
0
    thread_sched_atfork(TH_SCHED(th));
4985
0
    ubf_list_atfork();
4986
0
    rb_signal_atfork();
4987
4988
    // OK. Only this thread accesses:
4989
0
    ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
4990
0
        if (r != vm->ractor.main_ractor) {
4991
0
            rb_ractor_terminate_atfork(vm, r);
4992
0
        }
4993
0
        ccan_list_for_each(&r->threads.set, i, lt_node) {
4994
0
            atfork(i, th);
4995
0
        }
4996
0
    }
4997
0
    rb_vm_living_threads_init(vm);
4998
4999
0
    rb_ractor_atfork(vm, th);
5000
0
    rb_vm_postponed_job_atfork();
5001
5002
    /* may be held by any thread in parent */
5003
0
    rb_native_mutex_initialize(&th->interrupt_lock);
5004
0
    ccan_list_head_init(&th->interrupt_exec_tasks);
5005
5006
0
    vm->fork_gen++;
5007
0
    rb_ractor_sleeper_threads_clear(th->ractor);
5008
0
    rb_clear_coverages();
5009
5010
    // restart timer thread (timer threads access to `vm->waitpid_lock` and so on.
5011
0
    rb_thread_reset_timer_thread();
5012
0
    rb_thread_start_timer_thread();
5013
5014
0
    VM_ASSERT(vm->ractor.blocking_cnt == 0);
5015
0
    VM_ASSERT(vm->ractor.cnt == 1);
5016
0
}
5017
5018
static void
5019
terminate_atfork_i(rb_thread_t *th, const rb_thread_t *current_th)
5020
0
{
5021
0
    if (th != current_th) {
5022
        // Clear the scheduler as it is no longer operational:
5023
0
        th->scheduler = Qnil;
5024
5025
0
        rb_native_mutex_initialize(&th->interrupt_lock);
5026
0
        rb_mutex_abandon_keeping_mutexes(th);
5027
0
        rb_mutex_abandon_locking_mutex(th);
5028
0
        thread_cleanup_func(th, TRUE);
5029
0
    }
5030
0
}
5031
5032
void rb_fiber_atfork(rb_thread_t *);
5033
void
5034
rb_thread_atfork(void)
5035
0
{
5036
0
    rb_thread_t *th = GET_THREAD();
5037
0
    rb_threadptr_pending_interrupt_clear(th);
5038
0
    rb_thread_atfork_internal(th, terminate_atfork_i);
5039
0
    th->join_list = NULL;
5040
0
    th->scheduler = Qnil;
5041
0
    rb_fiber_atfork(th);
5042
5043
    /* We don't want reproduce CVE-2003-0900. */
5044
0
    rb_reset_random_seed();
5045
0
}
5046
5047
static void
5048
terminate_atfork_before_exec_i(rb_thread_t *th, const rb_thread_t *current_th)
5049
0
{
5050
0
    if (th != current_th) {
5051
0
        thread_cleanup_func_before_exec(th);
5052
0
    }
5053
0
}
5054
5055
void
5056
rb_thread_atfork_before_exec(void)
5057
0
{
5058
0
    rb_thread_t *th = GET_THREAD();
5059
0
    rb_thread_atfork_internal(th, terminate_atfork_before_exec_i);
5060
0
}
5061
#else
5062
void
5063
rb_thread_atfork(void)
5064
{
5065
}
5066
5067
void
5068
rb_thread_atfork_before_exec(void)
5069
{
5070
}
5071
#endif
5072
5073
struct thgroup {
5074
    int enclosed;
5075
};
5076
5077
static const rb_data_type_t thgroup_data_type = {
5078
    "thgroup",
5079
    {
5080
        0,
5081
        RUBY_TYPED_DEFAULT_FREE,
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
 */
5106
static VALUE
5107
thgroup_s_alloc(VALUE klass)
5108
9
{
5109
9
    VALUE group;
5110
9
    struct thgroup *data;
5111
5112
9
    group = TypedData_Make_Struct(klass, struct thgroup, &thgroup_data_type, data);
5113
9
    data->enclosed = 0;
5114
5115
9
    return group;
5116
9
}
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
5127
static VALUE
5128
thgroup_list(VALUE group)
5129
0
{
5130
0
    VALUE ary = rb_ary_new();
5131
0
    rb_thread_t *th = 0;
5132
0
    rb_ractor_t *r = GET_RACTOR();
5133
5134
0
    ccan_list_for_each(&r->threads.set, th, lt_node) {
5135
0
        if (th->thgroup == group) {
5136
0
            rb_ary_push(ary, th->self);
5137
0
        }
5138
0
    }
5139
0
    return ary;
5140
0
}
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
5159
static VALUE
5160
thgroup_enclose(VALUE group)
5161
0
{
5162
0
    struct thgroup *data;
5163
5164
0
    TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
5165
0
    data->enclosed = 1;
5166
5167
0
    return group;
5168
0
}
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
5178
static VALUE
5179
thgroup_enclosed_p(VALUE group)
5180
0
{
5181
0
    struct thgroup *data;
5182
5183
0
    TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
5184
0
    return RBOOL(data->enclosed);
5185
0
}
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
5214
static VALUE
5215
thgroup_add(VALUE group, VALUE thread)
5216
0
{
5217
0
    rb_thread_t *target_th = rb_thread_ptr(thread);
5218
0
    struct thgroup *data;
5219
5220
0
    if (OBJ_FROZEN(group)) {
5221
0
        rb_raise(rb_eThreadError, "can't move to the frozen thread group");
5222
0
    }
5223
0
    TypedData_Get_Struct(group, struct thgroup, &thgroup_data_type, data);
5224
0
    if (data->enclosed) {
5225
0
        rb_raise(rb_eThreadError, "can't move to the enclosed thread group");
5226
0
    }
5227
5228
0
    if (OBJ_FROZEN(target_th->thgroup)) {
5229
0
        rb_raise(rb_eThreadError, "can't move from the frozen thread group");
5230
0
    }
5231
0
    TypedData_Get_Struct(target_th->thgroup, struct thgroup, &thgroup_data_type, data);
5232
0
    if (data->enclosed) {
5233
0
        rb_raise(rb_eThreadError,
5234
0
                 "can't move from the enclosed thread group");
5235
0
    }
5236
5237
0
    target_th->thgroup = group;
5238
0
    return group;
5239
0
}
5240
5241
/*
5242
 * Document-class: ThreadShield
5243
 */
5244
static void
5245
thread_shield_mark(void *ptr)
5246
0
{
5247
0
    rb_gc_mark((VALUE)ptr);
5248
0
}
5249
5250
static const rb_data_type_t thread_shield_data_type = {
5251
    "thread_shield",
5252
    {thread_shield_mark, 0, 0,},
5253
    0, 0, RUBY_TYPED_FREE_IMMEDIATELY
5254
};
5255
5256
static VALUE
5257
thread_shield_alloc(VALUE klass)
5258
0
{
5259
0
    return TypedData_Wrap_Struct(klass, &thread_shield_data_type, (void *)mutex_alloc(0));
5260
0
}
5261
5262
0
#define GetThreadShieldPtr(obj) ((VALUE)rb_check_typeddata((obj), &thread_shield_data_type))
5263
0
#define THREAD_SHIELD_WAITING_MASK (((FL_USER19-1)&~(FL_USER0-1))|FL_USER19)
5264
0
#define THREAD_SHIELD_WAITING_SHIFT (FL_USHIFT)
5265
0
#define THREAD_SHIELD_WAITING_MAX (THREAD_SHIELD_WAITING_MASK>>THREAD_SHIELD_WAITING_SHIFT)
5266
STATIC_ASSERT(THREAD_SHIELD_WAITING_MAX, THREAD_SHIELD_WAITING_MAX <= UINT_MAX);
5267
static inline unsigned int
5268
rb_thread_shield_waiting(VALUE b)
5269
0
{
5270
0
    return ((RBASIC(b)->flags&THREAD_SHIELD_WAITING_MASK)>>THREAD_SHIELD_WAITING_SHIFT);
5271
0
}
5272
5273
static inline void
5274
rb_thread_shield_waiting_inc(VALUE b)
5275
0
{
5276
0
    unsigned int w = rb_thread_shield_waiting(b);
5277
0
    w++;
5278
0
    if (w > THREAD_SHIELD_WAITING_MAX)
5279
0
        rb_raise(rb_eRuntimeError, "waiting count overflow");
5280
0
    RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
5281
0
    RBASIC(b)->flags |= ((VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
5282
0
}
5283
5284
static inline void
5285
rb_thread_shield_waiting_dec(VALUE b)
5286
0
{
5287
0
    unsigned int w = rb_thread_shield_waiting(b);
5288
0
    if (!w) rb_raise(rb_eRuntimeError, "waiting count underflow");
5289
0
    w--;
5290
0
    RBASIC(b)->flags &= ~THREAD_SHIELD_WAITING_MASK;
5291
0
    RBASIC(b)->flags |= ((VALUE)w << THREAD_SHIELD_WAITING_SHIFT);
5292
0
}
5293
5294
VALUE
5295
rb_thread_shield_new(void)
5296
0
{
5297
0
    VALUE thread_shield = thread_shield_alloc(rb_cThreadShield);
5298
0
    rb_mutex_lock((VALUE)DATA_PTR(thread_shield));
5299
0
    return thread_shield;
5300
0
}
5301
5302
bool
5303
rb_thread_shield_owned(VALUE self)
5304
0
{
5305
0
    VALUE mutex = GetThreadShieldPtr(self);
5306
0
    if (!mutex) return false;
5307
5308
0
    rb_mutex_t *m = mutex_ptr(mutex);
5309
5310
0
    return m->ec_serial == rb_ec_serial(GET_EC());
5311
0
}
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
 */
5321
VALUE
5322
rb_thread_shield_wait(VALUE self)
5323
0
{
5324
0
    VALUE mutex = GetThreadShieldPtr(self);
5325
0
    rb_mutex_t *m;
5326
5327
0
    if (!mutex) return Qfalse;
5328
0
    m = mutex_ptr(mutex);
5329
0
    if (m->ec_serial == rb_ec_serial(GET_EC())) return Qnil;
5330
0
    rb_thread_shield_waiting_inc(self);
5331
0
    rb_mutex_lock(mutex);
5332
0
    rb_thread_shield_waiting_dec(self);
5333
0
    if (DATA_PTR(self)) return Qtrue;
5334
0
    rb_mutex_unlock(mutex);
5335
0
    return rb_thread_shield_waiting(self) > 0 ? Qnil : Qfalse;
5336
0
}
5337
5338
static VALUE
5339
thread_shield_get_mutex(VALUE self)
5340
0
{
5341
0
    VALUE mutex = GetThreadShieldPtr(self);
5342
0
    if (!mutex)
5343
0
        rb_raise(rb_eThreadError, "destroyed thread shield - %p", (void *)self);
5344
0
    return mutex;
5345
0
}
5346
5347
/*
5348
 * Release a thread shield, and return true if it has waiting threads.
5349
 */
5350
VALUE
5351
rb_thread_shield_release(VALUE self)
5352
0
{
5353
0
    VALUE mutex = thread_shield_get_mutex(self);
5354
0
    rb_mutex_unlock(mutex);
5355
0
    return RBOOL(rb_thread_shield_waiting(self) > 0);
5356
0
}
5357
5358
/*
5359
 * Release and destroy a thread shield, and return true if it has waiting threads.
5360
 */
5361
VALUE
5362
rb_thread_shield_destroy(VALUE self)
5363
0
{
5364
0
    VALUE mutex = thread_shield_get_mutex(self);
5365
0
    DATA_PTR(self) = 0;
5366
0
    rb_mutex_unlock(mutex);
5367
0
    return RBOOL(rb_thread_shield_waiting(self) > 0);
5368
0
}
5369
5370
static VALUE
5371
threadptr_recursive_hash(rb_thread_t *th)
5372
309
{
5373
309
    return th->ec->local_storage_recursive_hash;
5374
309
}
5375
5376
static void
5377
threadptr_recursive_hash_set(rb_thread_t *th, VALUE hash)
5378
1
{
5379
1
    th->ec->local_storage_recursive_hash = hash;
5380
1
}
5381
5382
ID rb_frame_last_func(void);
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
5390
static VALUE
5391
recursive_list_access(VALUE sym)
5392
309
{
5393
309
    rb_thread_t *th = GET_THREAD();
5394
309
    VALUE hash = threadptr_recursive_hash(th);
5395
309
    VALUE list;
5396
309
    if (NIL_P(hash) || !RB_TYPE_P(hash, T_HASH)) {
5397
1
        hash = rb_ident_hash_new();
5398
1
        threadptr_recursive_hash_set(th, hash);
5399
1
        list = Qnil;
5400
1
    }
5401
308
    else {
5402
308
        list = rb_hash_aref(hash, sym);
5403
308
    }
5404
309
    if (NIL_P(list) || !RB_TYPE_P(list, T_HASH)) {
5405
2
        list = rb_ident_hash_new();
5406
2
        rb_hash_aset(hash, sym, list);
5407
2
    }
5408
309
    return list;
5409
309
}
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
5417
static bool
5418
recursive_check(VALUE list, VALUE obj, VALUE paired_obj_id)
5419
309
{
5420
309
#if SIZEOF_LONG == SIZEOF_VOIDP
5421
309
  #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
309
    VALUE pair_list = rb_hash_lookup2(list, obj, Qundef);
5428
309
    if (UNDEF_P(pair_list))
5429
309
        return false;
5430
0
    if (paired_obj_id) {
5431
0
        if (!RB_TYPE_P(pair_list, T_HASH)) {
5432
0
            if (!OBJ_ID_EQL(paired_obj_id, pair_list))
5433
0
                return false;
5434
0
        }
5435
0
        else {
5436
0
            if (NIL_P(rb_hash_lookup(pair_list, paired_obj_id)))
5437
0
                return false;
5438
0
        }
5439
0
    }
5440
0
    return true;
5441
0
}
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
5452
static void
5453
recursive_push(VALUE list, VALUE obj, VALUE paired_obj)
5454
309
{
5455
309
    VALUE pair_list;
5456
5457
309
    if (!paired_obj) {
5458
108
        rb_hash_aset(list, obj, Qtrue);
5459
108
    }
5460
201
    else if (UNDEF_P(pair_list = rb_hash_lookup2(list, obj, Qundef))) {
5461
201
        rb_hash_aset(list, obj, paired_obj);
5462
201
    }
5463
0
    else {
5464
0
        if (!RB_TYPE_P(pair_list, T_HASH)){
5465
0
            VALUE other_paired_obj = pair_list;
5466
0
            pair_list = rb_hash_new();
5467
0
            rb_hash_aset(pair_list, other_paired_obj, Qtrue);
5468
0
            rb_hash_aset(list, obj, pair_list);
5469
0
        }
5470
0
        rb_hash_aset(pair_list, paired_obj, Qtrue);
5471
0
    }
5472
309
}
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
5482
static int
5483
recursive_pop(VALUE list, VALUE obj, VALUE paired_obj)
5484
309
{
5485
309
    if (paired_obj) {
5486
201
        VALUE pair_list = rb_hash_lookup2(list, obj, Qundef);
5487
201
        if (UNDEF_P(pair_list)) {
5488
0
            return 0;
5489
0
        }
5490
201
        if (RB_TYPE_P(pair_list, T_HASH)) {
5491
0
            rb_hash_delete_entry(pair_list, paired_obj);
5492
0
            if (!RHASH_EMPTY_P(pair_list)) {
5493
0
                return 1; /* keep hash until is empty */
5494
0
            }
5495
0
        }
5496
201
    }
5497
309
    rb_hash_delete_entry(list, obj);
5498
309
    return 1;
5499
309
}
5500
5501
struct exec_recursive_params {
5502
    VALUE (*func) (VALUE, VALUE, int);
5503
    VALUE list;
5504
    VALUE obj;
5505
    VALUE pairid;
5506
    VALUE arg;
5507
};
5508
5509
static VALUE
5510
exec_recursive_i(RB_BLOCK_CALL_FUNC_ARGLIST(tag, data))
5511
0
{
5512
0
    struct exec_recursive_params *p = (void *)data;
5513
0
    return (*p->func)(p->obj, p->arg, FALSE);
5514
0
}
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
5527
static VALUE
5528
exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE pairid, VALUE arg, int outer, ID mid)
5529
309
{
5530
309
    VALUE result = Qundef;
5531
309
    const VALUE sym = mid ? ID2SYM(mid) : ID2SYM(idNULL);
5532
309
    struct exec_recursive_params p;
5533
309
    int outermost;
5534
309
    p.list = recursive_list_access(sym);
5535
309
    p.obj = obj;
5536
309
    p.pairid = pairid;
5537
309
    p.arg = arg;
5538
309
    outermost = outer && !recursive_check(p.list, ID2SYM(recursive_key), 0);
5539
5540
309
    if (recursive_check(p.list, p.obj, pairid)) {
5541
0
        if (outer && !outermost) {
5542
0
            rb_throw_obj(p.list, p.list);
5543
0
        }
5544
0
        return (*func)(obj, arg, TRUE);
5545
0
    }
5546
309
    else {
5547
309
        enum ruby_tag_type state;
5548
5549
309
        p.func = func;
5550
5551
309
        if (outermost) {
5552
0
            recursive_push(p.list, ID2SYM(recursive_key), 0);
5553
0
            recursive_push(p.list, p.obj, p.pairid);
5554
0
            result = rb_catch_protect(p.list, exec_recursive_i, (VALUE)&p, &state);
5555
0
            if (!recursive_pop(p.list, p.obj, p.pairid)) goto invalid;
5556
0
            if (!recursive_pop(p.list, ID2SYM(recursive_key), 0)) goto invalid;
5557
0
            if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
5558
0
            if (result == p.list) {
5559
0
                result = (*func)(obj, arg, TRUE);
5560
0
            }
5561
0
        }
5562
309
        else {
5563
309
            volatile VALUE ret = Qundef;
5564
309
            recursive_push(p.list, p.obj, p.pairid);
5565
309
            EC_PUSH_TAG(GET_EC());
5566
309
            if ((state = EC_EXEC_TAG()) == TAG_NONE) {
5567
309
                ret = (*func)(obj, arg, FALSE);
5568
309
            }
5569
309
            EC_POP_TAG();
5570
309
            if (!recursive_pop(p.list, p.obj, p.pairid)) {
5571
0
                goto invalid;
5572
0
            }
5573
309
            if (state != TAG_NONE) EC_JUMP_TAG(GET_EC(), state);
5574
309
            result = ret;
5575
309
        }
5576
309
    }
5577
309
    *(volatile struct exec_recursive_params *)&p;
5578
309
    return result;
5579
5580
0
  invalid:
5581
0
    rb_raise(rb_eTypeError, "invalid inspect_tbl pair_list "
5582
0
             "for %+"PRIsVALUE" in %+"PRIsVALUE,
5583
0
             sym, rb_thread_current());
5584
309
    UNREACHABLE_RETURN(Qundef);
5585
309
}
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
5592
VALUE
5593
rb_exec_recursive(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
5594
108
{
5595
108
    return exec_recursive(func, obj, 0, arg, 0, rb_frame_last_func());
5596
108
}
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
5603
VALUE
5604
rb_exec_recursive_paired(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
5605
201
{
5606
201
    return exec_recursive(func, obj, rb_memory_id(paired_obj), arg, 0, rb_frame_last_func());
5607
201
}
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
5615
VALUE
5616
rb_exec_recursive_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg)
5617
0
{
5618
0
    return exec_recursive(func, obj, 0, arg, 1, rb_frame_last_func());
5619
0
}
5620
5621
VALUE
5622
rb_exec_recursive_outer_mid(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE arg, ID mid)
5623
0
{
5624
0
    return exec_recursive(func, obj, 0, arg, 1, mid);
5625
0
}
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
5633
VALUE
5634
rb_exec_recursive_paired_outer(VALUE (*func) (VALUE, VALUE, int), VALUE obj, VALUE paired_obj, VALUE arg)
5635
0
{
5636
0
    return exec_recursive(func, obj, rb_memory_id(paired_obj), arg, 1, rb_frame_last_func());
5637
0
}
5638
5639
/*
5640
 *  call-seq:
5641
 *     thread.backtrace    -> array or nil
5642
 *
5643
 *  Returns the current backtrace of the target thread.
5644
 *
5645
 */
5646
5647
static VALUE
5648
rb_thread_backtrace_m(int argc, VALUE *argv, VALUE thval)
5649
0
{
5650
0
    return rb_vm_thread_backtrace(argc, argv, thval);
5651
0
}
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
 */
5664
static VALUE
5665
rb_thread_backtrace_locations_m(int argc, VALUE *argv, VALUE thval)
5666
0
{
5667
0
    return rb_vm_thread_backtrace_locations(argc, argv, thval);
5668
0
}
5669
5670
void
5671
Init_Thread_Mutex(void)
5672
9
{
5673
9
    rb_thread_t *th = GET_THREAD();
5674
5675
9
    rb_native_mutex_initialize(&th->vm->workqueue_lock);
5676
9
    rb_native_mutex_initialize(&th->interrupt_lock);
5677
9
}
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
5694
void
5695
Init_Thread(void)
5696
9
{
5697
9
    rb_thread_t *th = GET_THREAD();
5698
5699
9
    sym_never = ID2SYM(rb_intern_const("never"));
5700
9
    sym_immediate = ID2SYM(rb_intern_const("immediate"));
5701
9
    sym_on_blocking = ID2SYM(rb_intern_const("on_blocking"));
5702
5703
9
    rb_define_singleton_method(rb_cThread, "new", thread_s_new, -1);
5704
9
    rb_define_singleton_method(rb_cThread, "start", thread_start, -2);
5705
9
    rb_define_singleton_method(rb_cThread, "fork", thread_start, -2);
5706
9
    rb_define_singleton_method(rb_cThread, "main", rb_thread_s_main, 0);
5707
9
    rb_define_singleton_method(rb_cThread, "current", thread_s_current, 0);
5708
9
    rb_define_singleton_method(rb_cThread, "stop", thread_stop, 0);
5709
9
    rb_define_singleton_method(rb_cThread, "kill", rb_thread_s_kill, 1);
5710
9
    rb_define_singleton_method(rb_cThread, "exit", rb_thread_exit, 0);
5711
9
    rb_define_singleton_method(rb_cThread, "pass", thread_s_pass, 0);
5712
9
    rb_define_singleton_method(rb_cThread, "list", thread_list, 0);
5713
9
    rb_define_singleton_method(rb_cThread, "abort_on_exception", rb_thread_s_abort_exc, 0);
5714
9
    rb_define_singleton_method(rb_cThread, "abort_on_exception=", rb_thread_s_abort_exc_set, 1);
5715
9
    rb_define_singleton_method(rb_cThread, "report_on_exception", rb_thread_s_report_exc, 0);
5716
9
    rb_define_singleton_method(rb_cThread, "report_on_exception=", rb_thread_s_report_exc_set, 1);
5717
9
    rb_define_singleton_method(rb_cThread, "ignore_deadlock", rb_thread_s_ignore_deadlock, 0);
5718
9
    rb_define_singleton_method(rb_cThread, "ignore_deadlock=", rb_thread_s_ignore_deadlock_set, 1);
5719
9
    rb_define_singleton_method(rb_cThread, "handle_interrupt", rb_thread_s_handle_interrupt, 1);
5720
9
    rb_define_singleton_method(rb_cThread, "pending_interrupt?", rb_thread_s_pending_interrupt_p, -1);
5721
9
    rb_define_method(rb_cThread, "pending_interrupt?", rb_thread_pending_interrupt_p, -1);
5722
5723
9
    rb_define_method(rb_cThread, "initialize", thread_initialize, -2);
5724
9
    rb_define_method(rb_cThread, "raise", thread_raise_m, -1);
5725
9
    rb_define_method(rb_cThread, "join", thread_join_m, -1);
5726
9
    rb_define_method(rb_cThread, "value", thread_value, 0);
5727
9
    rb_define_method(rb_cThread, "kill", rb_thread_kill, 0);
5728
9
    rb_define_method(rb_cThread, "terminate", rb_thread_kill, 0);
5729
9
    rb_define_method(rb_cThread, "exit", rb_thread_kill, 0);
5730
9
    rb_define_method(rb_cThread, "run", rb_thread_run, 0);
5731
9
    rb_define_method(rb_cThread, "wakeup", rb_thread_wakeup, 0);
5732
9
    rb_define_method(rb_cThread, "[]", rb_thread_aref, 1);
5733
9
    rb_define_method(rb_cThread, "[]=", rb_thread_aset, 2);
5734
9
    rb_define_method(rb_cThread, "fetch", rb_thread_fetch, -1);
5735
9
    rb_define_method(rb_cThread, "key?", rb_thread_key_p, 1);
5736
9
    rb_define_method(rb_cThread, "keys", rb_thread_keys, 0);
5737
9
    rb_define_method(rb_cThread, "priority", rb_thread_priority, 0);
5738
9
    rb_define_method(rb_cThread, "priority=", rb_thread_priority_set, 1);
5739
9
    rb_define_method(rb_cThread, "status", rb_thread_status, 0);
5740
9
    rb_define_method(rb_cThread, "thread_variable_get", rb_thread_variable_get, 1);
5741
9
    rb_define_method(rb_cThread, "thread_variable_set", rb_thread_variable_set, 2);
5742
9
    rb_define_method(rb_cThread, "thread_variables", rb_thread_variables, 0);
5743
9
    rb_define_method(rb_cThread, "thread_variable?", rb_thread_variable_p, 1);
5744
9
    rb_define_method(rb_cThread, "alive?", rb_thread_alive_p, 0);
5745
9
    rb_define_method(rb_cThread, "stop?", rb_thread_stop_p, 0);
5746
9
    rb_define_method(rb_cThread, "abort_on_exception", rb_thread_abort_exc, 0);
5747
9
    rb_define_method(rb_cThread, "abort_on_exception=", rb_thread_abort_exc_set, 1);
5748
9
    rb_define_method(rb_cThread, "report_on_exception", rb_thread_report_exc, 0);
5749
9
    rb_define_method(rb_cThread, "report_on_exception=", rb_thread_report_exc_set, 1);
5750
9
    rb_define_method(rb_cThread, "group", rb_thread_group, 0);
5751
9
    rb_define_method(rb_cThread, "backtrace", rb_thread_backtrace_m, -1);
5752
9
    rb_define_method(rb_cThread, "backtrace_locations", rb_thread_backtrace_locations_m, -1);
5753
5754
9
    rb_define_method(rb_cThread, "name", rb_thread_getname, 0);
5755
9
    rb_define_method(rb_cThread, "name=", rb_thread_setname, 1);
5756
9
    rb_define_method(rb_cThread, "native_thread_id", rb_thread_native_thread_id, 0);
5757
9
    rb_define_method(rb_cThread, "to_s", rb_thread_to_s, 0);
5758
9
    rb_define_alias(rb_cThread, "inspect", "to_s");
5759
5760
9
    rb_vm_register_special_exception(ruby_error_stream_closed, rb_eIOError,
5761
9
                                     "stream closed in another thread");
5762
5763
9
    cThGroup = rb_define_class("ThreadGroup", rb_cObject);
5764
9
    rb_define_alloc_func(cThGroup, thgroup_s_alloc);
5765
9
    rb_define_method(cThGroup, "list", thgroup_list, 0);
5766
9
    rb_define_method(cThGroup, "enclose", thgroup_enclose, 0);
5767
9
    rb_define_method(cThGroup, "enclosed?", thgroup_enclosed_p, 0);
5768
9
    rb_define_method(cThGroup, "add", thgroup_add, 1);
5769
5770
9
    const char * ptr = getenv("RUBY_THREAD_TIMESLICE");
5771
5772
9
    if (ptr) {
5773
0
        long quantum = strtol(ptr, NULL, 0);
5774
0
        if (quantum > 0 && !(SIZEOF_LONG > 4 && quantum > UINT32_MAX)) {
5775
0
            thread_default_quantum_ms = (uint32_t)quantum;
5776
0
        }
5777
0
        else if (0) {
5778
0
            fprintf(stderr, "Ignored RUBY_THREAD_TIMESLICE=%s\n", ptr);
5779
0
        }
5780
0
    }
5781
5782
9
    {
5783
9
        th->thgroup = th->ractor->thgroup_default = rb_obj_alloc(cThGroup);
5784
9
        rb_define_const(cThGroup, "Default", th->thgroup);
5785
9
    }
5786
5787
9
    rb_eThreadError = rb_define_class("ThreadError", rb_eStandardError);
5788
5789
    /* init thread core */
5790
9
    {
5791
        /* main thread setting */
5792
9
        {
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
9
            th->pending_interrupt_queue = rb_ary_hidden_new(0);
5802
9
            th->pending_interrupt_queue_checked = 0;
5803
9
            th->pending_interrupt_mask_stack = rb_ary_hidden_new(0);
5804
9
        }
5805
9
    }
5806
5807
9
    rb_thread_create_timer_thread();
5808
5809
9
    Init_thread_sync();
5810
5811
    // TODO: Suppress unused function warning for now
5812
    // if (0) rb_thread_sched_destroy(NULL);
5813
9
}
5814
5815
int
5816
ruby_native_thread_p(void)
5817
12
{
5818
12
    rb_thread_t *th = ruby_thread_from_native();
5819
5820
12
    return th != 0;
5821
12
}
5822
5823
#ifdef NON_SCALAR_THREAD_ID
5824
  #define thread_id_str(th) (NULL)
5825
#else
5826
0
  #define thread_id_str(th) ((void *)(uintptr_t)(th)->nt->thread_id)
5827
#endif
5828
5829
static void
5830
debug_deadlock_check(rb_ractor_t *r, VALUE msg)
5831
0
{
5832
0
    rb_thread_t *th = 0;
5833
0
    VALUE sep = rb_str_new_cstr("\n   ");
5834
5835
0
    rb_str_catf(msg, "\n%d threads, %d sleeps current:%p main thread:%p\n",
5836
0
                rb_ractor_living_thread_num(r), rb_ractor_sleeper_thread_num(r),
5837
0
                (void *)GET_THREAD(), (void *)r->threads.main);
5838
5839
0
    ccan_list_for_each(&r->threads.set, th, lt_node) {
5840
0
        rb_str_catf(msg, "* %+"PRIsVALUE"\n   rb_thread_t:%p "
5841
0
                    "native:%p int:%u",
5842
0
                    th->self, (void *)th, th->nt ? thread_id_str(th) : "N/A", th->ec->interrupt_flag);
5843
5844
0
        if (th->locking_mutex) {
5845
0
            rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
5846
0
            rb_str_catf(msg, " mutex:%llu cond:%"PRIuSIZE,
5847
0
                        (unsigned long long)mutex->ec_serial, rb_mutex_num_waiting(mutex));
5848
0
        }
5849
5850
0
        {
5851
0
            struct rb_waiting_list *list = th->join_list;
5852
0
            while (list) {
5853
0
                rb_str_catf(msg, "\n    depended by: tb_thread_id:%p", (void *)list->thread);
5854
0
                list = list->next;
5855
0
            }
5856
0
        }
5857
0
        rb_str_catf(msg, "\n   ");
5858
0
        rb_str_concat(msg, rb_ary_join(rb_ec_backtrace_str_ary(th->ec, RUBY_BACKTRACE_START, RUBY_ALL_BACKTRACE_LINES), sep));
5859
0
        rb_str_catf(msg, "\n");
5860
0
    }
5861
0
}
5862
5863
static void
5864
rb_check_deadlock(rb_ractor_t *r)
5865
0
{
5866
0
    if (GET_THREAD()->vm->thread_ignore_deadlock) return;
5867
5868
0
#ifdef RUBY_THREAD_PTHREAD_H
5869
0
    if (r->threads.sched.readyq_cnt > 0) return;
5870
0
#endif
5871
5872
0
    int sleeper_num = rb_ractor_sleeper_thread_num(r);
5873
0
    int ltnum = rb_ractor_living_thread_num(r);
5874
5875
0
    if (ltnum > sleeper_num) return;
5876
0
    if (ltnum < sleeper_num) rb_bug("sleeper must not be more than vm_living_thread_num(vm)");
5877
5878
0
    int found = 0;
5879
0
    rb_thread_t *th = NULL;
5880
5881
0
    ccan_list_for_each(&r->threads.set, th, lt_node) {
5882
0
        if (th->status != THREAD_STOPPED_FOREVER || RUBY_VM_INTERRUPTED(th->ec)) {
5883
0
            found = 1;
5884
0
        }
5885
0
        else if (th->locking_mutex) {
5886
0
            rb_mutex_t *mutex = mutex_ptr(th->locking_mutex);
5887
0
            if (mutex->ec_serial == rb_ec_serial(th->ec) || (!mutex->ec_serial && !ccan_list_empty(&mutex->waitq))) {
5888
0
                found = 1;
5889
0
            }
5890
0
        }
5891
0
        if (found)
5892
0
          break;
5893
0
    }
5894
5895
0
    if (!found) {
5896
0
        VALUE argv[2];
5897
0
        argv[0] = rb_eFatal;
5898
0
        argv[1] = rb_str_new2("No live threads left. Deadlock?");
5899
0
        debug_deadlock_check(r, argv[1]);
5900
0
        rb_ractor_sleeper_threads_dec(GET_RACTOR());
5901
0
        rb_threadptr_raise(r->threads.main, 2, argv);
5902
0
    }
5903
0
}
5904
5905
static void
5906
update_line_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
5907
0
{
5908
0
    const rb_control_frame_t *cfp = GET_EC()->cfp;
5909
0
    VALUE coverage = rb_iseq_coverage(CFP_ISEQ(cfp));
5910
0
    if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
5911
0
        VALUE lines = RARRAY_AREF(coverage, COVERAGE_INDEX_LINES);
5912
0
        if (lines) {
5913
0
            long line = rb_sourceline() - 1;
5914
0
            VM_ASSERT(line >= 0);
5915
0
            long count;
5916
0
            VALUE num;
5917
0
            void rb_iseq_clear_event_flags(const rb_iseq_t *iseq, size_t pos, rb_event_flag_t reset);
5918
0
            if (GET_VM()->coverage_mode & COVERAGE_TARGET_ONESHOT_LINES) {
5919
0
                rb_iseq_clear_event_flags(CFP_ISEQ(cfp), CFP_PC(cfp) - ISEQ_BODY(CFP_ISEQ(cfp))->iseq_encoded - 1, RUBY_EVENT_COVERAGE_LINE);
5920
0
                rb_ary_push(lines, LONG2FIX(line + 1));
5921
0
                return;
5922
0
            }
5923
0
            if (line >= RARRAY_LEN(lines)) { /* no longer tracked */
5924
0
                return;
5925
0
            }
5926
0
            num = RARRAY_AREF(lines, line);
5927
0
            if (!FIXNUM_P(num)) return;
5928
0
            count = FIX2LONG(num) + 1;
5929
0
            if (POSFIXABLE(count)) {
5930
0
                RARRAY_ASET(lines, line, LONG2FIX(count));
5931
0
            }
5932
0
        }
5933
0
    }
5934
0
}
5935
5936
static void
5937
update_branch_coverage(VALUE data, const rb_trace_arg_t *trace_arg)
5938
0
{
5939
0
    const rb_control_frame_t *cfp = GET_EC()->cfp;
5940
0
    VALUE coverage = rb_iseq_coverage(CFP_ISEQ(cfp));
5941
0
    if (RB_TYPE_P(coverage, T_ARRAY) && !RBASIC_CLASS(coverage)) {
5942
0
        VALUE branches = RARRAY_AREF(coverage, COVERAGE_INDEX_BRANCHES);
5943
0
        if (branches) {
5944
0
            long pc = CFP_PC(cfp) - ISEQ_BODY(CFP_ISEQ(cfp))->iseq_encoded - 1;
5945
0
            long idx = FIX2INT(RARRAY_AREF(ISEQ_PC2BRANCHINDEX(CFP_ISEQ(cfp)), pc)), count;
5946
0
            VALUE counters = RARRAY_AREF(branches, 1);
5947
0
            VALUE num = RARRAY_AREF(counters, idx);
5948
0
            count = FIX2LONG(num) + 1;
5949
0
            if (POSFIXABLE(count)) {
5950
0
                RARRAY_ASET(counters, idx, LONG2FIX(count));
5951
0
            }
5952
0
        }
5953
0
    }
5954
0
}
5955
5956
const rb_method_entry_t *
5957
rb_resolve_me_location(const rb_method_entry_t *me, VALUE resolved_location[5])
5958
0
{
5959
0
    VALUE path, beg_pos_lineno, beg_pos_column, end_pos_lineno, end_pos_column;
5960
5961
0
    if (!me->def) return NULL; // negative cme
5962
5963
0
  retry:
5964
0
    switch (me->def->type) {
5965
0
      case VM_METHOD_TYPE_ISEQ: {
5966
0
        const rb_iseq_t *iseq = me->def->body.iseq.iseqptr;
5967
0
        rb_iseq_location_t *loc = &ISEQ_BODY(iseq)->location;
5968
0
        path = rb_iseq_path(iseq);
5969
0
        beg_pos_lineno = INT2FIX(loc->code_location.beg_pos.lineno);
5970
0
        beg_pos_column = INT2FIX(loc->code_location.beg_pos.column);
5971
0
        end_pos_lineno = INT2FIX(loc->code_location.end_pos.lineno);
5972
0
        end_pos_column = INT2FIX(loc->code_location.end_pos.column);
5973
0
        break;
5974
0
      }
5975
0
      case VM_METHOD_TYPE_BMETHOD: {
5976
0
        const rb_iseq_t *iseq = rb_proc_get_iseq(me->def->body.bmethod.proc, 0);
5977
0
        if (iseq) {
5978
0
            rb_iseq_location_t *loc;
5979
0
            rb_iseq_check(iseq);
5980
0
            path = rb_iseq_path(iseq);
5981
0
            loc = &ISEQ_BODY(iseq)->location;
5982
0
            beg_pos_lineno = INT2FIX(loc->code_location.beg_pos.lineno);
5983
0
            beg_pos_column = INT2FIX(loc->code_location.beg_pos.column);
5984
0
            end_pos_lineno = INT2FIX(loc->code_location.end_pos.lineno);
5985
0
            end_pos_column = INT2FIX(loc->code_location.end_pos.column);
5986
0
            break;
5987
0
        }
5988
0
        return NULL;
5989
0
      }
5990
0
      case VM_METHOD_TYPE_ALIAS:
5991
0
        me = me->def->body.alias.original_me;
5992
0
        goto retry;
5993
0
      case VM_METHOD_TYPE_REFINED:
5994
0
        me = me->def->body.refined.orig_me;
5995
0
        if (!me) return NULL;
5996
0
        goto retry;
5997
0
      default:
5998
0
        return NULL;
5999
0
    }
6000
6001
    /* found */
6002
0
    if (RB_TYPE_P(path, T_ARRAY)) {
6003
0
        path = rb_ary_entry(path, 1);
6004
0
        if (!RB_TYPE_P(path, T_STRING)) return NULL; /* just for the case... */
6005
0
    }
6006
0
    if (resolved_location) {
6007
0
        resolved_location[0] = path;
6008
0
        resolved_location[1] = beg_pos_lineno;
6009
0
        resolved_location[2] = beg_pos_column;
6010
0
        resolved_location[3] = end_pos_lineno;
6011
0
        resolved_location[4] = end_pos_column;
6012
0
    }
6013
0
    return me;
6014
0
}
6015
6016
static void
6017
update_method_coverage(VALUE me2counter, rb_trace_arg_t *trace_arg)
6018
0
{
6019
0
    const rb_control_frame_t *cfp = GET_EC()->cfp;
6020
0
    const rb_callable_method_entry_t *cme = rb_vm_frame_method_entry(cfp);
6021
0
    const rb_method_entry_t *me = (const rb_method_entry_t *)cme;
6022
0
    VALUE rcount;
6023
0
    long count;
6024
6025
0
    me = rb_resolve_me_location(me, 0);
6026
0
    if (!me) return;
6027
6028
0
    rcount = rb_hash_aref(me2counter, (VALUE) me);
6029
0
    count = FIXNUM_P(rcount) ? FIX2LONG(rcount) + 1 : 1;
6030
0
    if (POSFIXABLE(count)) {
6031
0
        rb_hash_aset(me2counter, (VALUE) me, LONG2FIX(count));
6032
0
    }
6033
0
}
6034
6035
VALUE
6036
rb_get_coverages(void)
6037
9
{
6038
9
    return GET_VM()->coverages;
6039
9
}
6040
6041
int
6042
rb_get_coverage_mode(void)
6043
0
{
6044
0
    return GET_VM()->coverage_mode;
6045
0
}
6046
6047
void
6048
rb_set_coverages(VALUE coverages, int mode, VALUE me2counter)
6049
0
{
6050
0
    GET_VM()->coverages = coverages;
6051
0
    GET_VM()->me2counter = me2counter;
6052
0
    GET_VM()->coverage_mode = mode;
6053
0
}
6054
6055
void
6056
rb_resume_coverages(void)
6057
0
{
6058
0
    int mode = GET_VM()->coverage_mode;
6059
0
    VALUE me2counter = GET_VM()->me2counter;
6060
0
    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
0
    if (mode & COVERAGE_TARGET_BRANCHES) {
6062
0
        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
0
    }
6064
0
    if (mode & COVERAGE_TARGET_METHODS) {
6065
0
        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
0
    }
6067
0
}
6068
6069
void
6070
rb_suspend_coverages(void)
6071
0
{
6072
0
    rb_remove_event_hook((rb_event_hook_func_t) update_line_coverage);
6073
0
    if (GET_VM()->coverage_mode & COVERAGE_TARGET_BRANCHES) {
6074
0
        rb_remove_event_hook((rb_event_hook_func_t) update_branch_coverage);
6075
0
    }
6076
0
    if (GET_VM()->coverage_mode & COVERAGE_TARGET_METHODS) {
6077
0
        rb_remove_event_hook((rb_event_hook_func_t) update_method_coverage);
6078
0
    }
6079
0
}
6080
6081
/* Make coverage arrays empty so old covered files are no longer tracked. */
6082
void
6083
rb_reset_coverages(void)
6084
0
{
6085
0
    rb_clear_coverages();
6086
0
    rb_iseq_remove_coverage_all();
6087
0
    GET_VM()->coverages = Qfalse;
6088
0
}
6089
6090
VALUE
6091
rb_default_coverage(int n)
6092
0
{
6093
0
    VALUE coverage = rb_ary_hidden_new_fill(3);
6094
0
    VALUE lines = Qfalse, branches = Qfalse;
6095
0
    int mode = GET_VM()->coverage_mode;
6096
6097
0
    if (mode & COVERAGE_TARGET_LINES) {
6098
0
        lines = n > 0 ? rb_ary_hidden_new_fill(n) : rb_ary_hidden_new(0);
6099
0
    }
6100
0
    RARRAY_ASET(coverage, COVERAGE_INDEX_LINES, lines);
6101
6102
0
    if (mode & COVERAGE_TARGET_BRANCHES) {
6103
0
        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
0
        VALUE structure = rb_hash_new();
6126
0
        rb_obj_hide(structure);
6127
0
        RARRAY_ASET(branches, 0, structure);
6128
        /* branch execution counters */
6129
0
        RARRAY_ASET(branches, 1, rb_ary_hidden_new(0));
6130
0
    }
6131
0
    RARRAY_ASET(coverage, COVERAGE_INDEX_BRANCHES, branches);
6132
6133
0
    return coverage;
6134
0
}
6135
6136
static VALUE
6137
uninterruptible_exit(VALUE v)
6138
0
{
6139
0
    rb_thread_t *cur_th = GET_THREAD();
6140
0
    rb_ary_pop(cur_th->pending_interrupt_mask_stack);
6141
6142
0
    cur_th->pending_interrupt_queue_checked = 0;
6143
0
    if (!rb_threadptr_pending_interrupt_empty_p(cur_th)) {
6144
0
        RUBY_VM_SET_INTERRUPT(cur_th->ec);
6145
0
    }
6146
0
    return Qnil;
6147
0
}
6148
6149
VALUE
6150
rb_uninterruptible(VALUE (*b_proc)(VALUE), VALUE data)
6151
0
{
6152
0
    VALUE interrupt_mask = rb_ident_hash_new();
6153
0
    rb_thread_t *cur_th = GET_THREAD();
6154
6155
0
    rb_hash_aset(interrupt_mask, rb_cObject, sym_never);
6156
0
    OBJ_FREEZE(interrupt_mask);
6157
0
    rb_ary_push(cur_th->pending_interrupt_mask_stack, interrupt_mask);
6158
6159
0
    VALUE ret = rb_ensure(b_proc, data, uninterruptible_exit, Qnil);
6160
6161
0
    RUBY_VM_CHECK_INTS(cur_th->ec);
6162
0
    return ret;
6163
0
}
6164
6165
static void
6166
thread_specific_storage_alloc(rb_thread_t *th)
6167
0
{
6168
0
    VM_ASSERT(th->specific_storage == NULL);
6169
6170
0
    if (UNLIKELY(specific_key_count > 0)) {
6171
0
        th->specific_storage = ZALLOC_N(void *, RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6172
0
    }
6173
0
}
6174
6175
rb_internal_thread_specific_key_t
6176
rb_internal_thread_specific_key_create(void)
6177
0
{
6178
0
    rb_vm_t *vm = GET_VM();
6179
6180
0
    if (specific_key_count == 0 && vm->ractor.cnt > 1) {
6181
0
        rb_raise(rb_eThreadError, "The first rb_internal_thread_specific_key_create() is called with multiple ractors");
6182
0
    }
6183
0
    else if (specific_key_count > RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX) {
6184
0
        rb_raise(rb_eThreadError, "rb_internal_thread_specific_key_create() is called more than %d times", RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6185
0
    }
6186
0
    else {
6187
0
        rb_internal_thread_specific_key_t key = specific_key_count++;
6188
6189
0
        if (key == 0) {
6190
            // allocate
6191
0
            rb_ractor_t *cr = GET_RACTOR();
6192
0
            rb_thread_t *th;
6193
6194
0
            ccan_list_for_each(&cr->threads.set, th, lt_node) {
6195
0
                thread_specific_storage_alloc(th);
6196
0
            }
6197
0
        }
6198
0
        return key;
6199
0
    }
6200
0
}
6201
6202
// async and native thread safe.
6203
void *
6204
rb_internal_thread_specific_get(VALUE thread_val, rb_internal_thread_specific_key_t key)
6205
0
{
6206
0
    rb_thread_t *th = DATA_PTR(thread_val);
6207
6208
0
    VM_ASSERT(rb_thread_ptr(thread_val) == th);
6209
0
    VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6210
0
    VM_ASSERT(th->specific_storage);
6211
6212
0
    return th->specific_storage[key];
6213
0
}
6214
6215
// async and native thread safe.
6216
void
6217
rb_internal_thread_specific_set(VALUE thread_val, rb_internal_thread_specific_key_t key, void *data)
6218
0
{
6219
0
    rb_thread_t *th = DATA_PTR(thread_val);
6220
6221
0
    VM_ASSERT(rb_thread_ptr(thread_val) == th);
6222
0
    VM_ASSERT(key < RB_INTERNAL_THREAD_SPECIFIC_KEY_MAX);
6223
0
    VM_ASSERT(th->specific_storage);
6224
6225
0
    th->specific_storage[key] = data;
6226
0
}
6227
6228
// interrupt_exec
6229
6230
struct rb_interrupt_exec_task {
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
6238
void
6239
rb_threadptr_interrupt_exec_task_mark(rb_thread_t *th)
6240
45.5k
{
6241
45.5k
    struct rb_interrupt_exec_task *task;
6242
6243
45.5k
    ccan_list_for_each(&th->interrupt_exec_tasks, task, node) {
6244
0
        if (task->flags & rb_interrupt_exec_flag_value_data) {
6245
0
            rb_gc_mark((VALUE)task->data);
6246
0
        }
6247
0
    }
6248
45.5k
}
6249
6250
// native thread safe
6251
// th should be available
6252
void
6253
rb_threadptr_interrupt_exec(rb_thread_t *th, rb_interrupt_exec_func_t *func, void *data, enum rb_interrupt_exec_flag flags)
6254
0
{
6255
    // should not use ALLOC
6256
0
    struct rb_interrupt_exec_task *task = ALLOC(struct rb_interrupt_exec_task);
6257
0
    *task = (struct rb_interrupt_exec_task) {
6258
0
        .flags = flags,
6259
0
        .func = func,
6260
0
        .data = data,
6261
0
    };
6262
6263
0
    rb_native_mutex_lock(&th->interrupt_lock);
6264
0
    {
6265
0
        ccan_list_add_tail(&th->interrupt_exec_tasks, &task->node);
6266
0
        threadptr_set_interrupt_locked(th, true);
6267
0
    }
6268
0
    rb_native_mutex_unlock(&th->interrupt_lock);
6269
0
}
6270
6271
static void
6272
threadptr_interrupt_exec_exec(rb_thread_t *th)
6273
0
{
6274
0
    while (1) {
6275
0
        struct rb_interrupt_exec_task *task;
6276
6277
0
        rb_native_mutex_lock(&th->interrupt_lock);
6278
0
        {
6279
0
            task = ccan_list_pop(&th->interrupt_exec_tasks, struct rb_interrupt_exec_task, node);
6280
0
        }
6281
0
        rb_native_mutex_unlock(&th->interrupt_lock);
6282
6283
0
        RUBY_DEBUG_LOG("task:%p", task);
6284
6285
0
        if (task) {
6286
0
            if (task->flags & rb_interrupt_exec_flag_new_thread) {
6287
0
                rb_thread_create(task->func, task->data);
6288
0
            }
6289
0
            else {
6290
0
                (*task->func)(task->data);
6291
0
            }
6292
0
            SIZED_FREE(task);
6293
0
        }
6294
0
        else {
6295
0
            break;
6296
0
        }
6297
0
    }
6298
0
}
6299
6300
static void
6301
threadptr_interrupt_exec_cleanup(rb_thread_t *th)
6302
0
{
6303
0
    rb_native_mutex_lock(&th->interrupt_lock);
6304
0
    {
6305
0
        struct rb_interrupt_exec_task *task;
6306
6307
0
        while ((task = ccan_list_pop(&th->interrupt_exec_tasks, struct rb_interrupt_exec_task, node)) != NULL) {
6308
0
            SIZED_FREE(task);
6309
0
        }
6310
0
    }
6311
0
    rb_native_mutex_unlock(&th->interrupt_lock);
6312
0
}
6313
6314
// native thread safe
6315
// func/data should be native thread safe
6316
void
6317
rb_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
0
{
6320
0
    RUBY_DEBUG_LOG("flags:%d", (int)flags);
6321
6322
0
    rb_thread_t *main_th = target_r->threads.main;
6323
0
    rb_threadptr_interrupt_exec(main_th, func, data, flags | rb_interrupt_exec_flag_new_thread);
6324
0
}
6325