12#ifdef THREAD_SYSTEM_DEPENDENT_IMPLEMENTATION
14#include "internal/gc.h"
15#include "internal/sanitizers.h"
17#ifdef HAVE_SYS_RESOURCE_H
18#include <sys/resource.h>
20#ifdef HAVE_THR_STKSEGMENT
23#if defined(HAVE_FCNTL_H)
25#elif defined(HAVE_SYS_FCNTL_H)
28#ifdef HAVE_SYS_PRCTL_H
31#if defined(HAVE_SYS_TIME_H)
38#include <sys/syscall.h>
44# include <AvailabilityMacros.h>
47#if defined(HAVE_SYS_EVENTFD_H) && defined(HAVE_EVENTFD)
48# define USE_EVENTFD (1)
49# include <sys/eventfd.h>
51# define USE_EVENTFD (0)
54#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK) && \
55 defined(CLOCK_REALTIME) && defined(CLOCK_MONOTONIC) && \
56 defined(HAVE_CLOCK_GETTIME)
57static pthread_condattr_t condattr_mono;
58static pthread_condattr_t *condattr_monotonic = &condattr_mono;
60static const void *
const condattr_monotonic = NULL;
65#ifndef HAVE_SYS_EVENT_H
66#define HAVE_SYS_EVENT_H 0
69#ifndef HAVE_SYS_EPOLL_H
70#define HAVE_SYS_EPOLL_H 0
78 #if defined(__EMSCRIPTEN__) || defined(COROUTINE_PTHREAD_CONTEXT)
81 #define USE_MN_THREADS 0
82 #elif HAVE_SYS_EPOLL_H
83 #include <sys/epoll.h>
84 #define USE_MN_THREADS 1
85 #elif HAVE_SYS_EVENT_H
86 #include <sys/event.h>
87 #define USE_MN_THREADS 1
89 #define USE_MN_THREADS 0
93#ifdef HAVE_SCHED_YIELD
94#define native_thread_yield() (void)sched_yield()
96#define native_thread_yield() ((void)0)
101#define NATIVE_MUTEX_LOCK_DEBUG 0
102#define NATIVE_MUTEX_LOCK_DEBUG_YIELD 0
105mutex_debug(
const char *msg,
void *lock)
107 if (NATIVE_MUTEX_LOCK_DEBUG) {
109 static pthread_mutex_t dbglock = PTHREAD_MUTEX_INITIALIZER;
111 if ((r = pthread_mutex_lock(&dbglock)) != 0) {exit(EXIT_FAILURE);}
112 fprintf(stdout,
"%s: %p\n", msg, lock);
113 if ((r = pthread_mutex_unlock(&dbglock)) != 0) {exit(EXIT_FAILURE);}
121#if NATIVE_MUTEX_LOCK_DEBUG_YIELD
122 native_thread_yield();
124 mutex_debug(
"lock", lock);
125 if ((r = pthread_mutex_lock(lock)) != 0) {
134 mutex_debug(
"unlock", lock);
135 if ((r = pthread_mutex_unlock(lock)) != 0) {
144 mutex_debug(
"trylock", lock);
145 if ((r = pthread_mutex_trylock(lock)) != 0) {
159 int r = pthread_mutex_init(lock, 0);
160 mutex_debug(
"init", lock);
169 int r = pthread_mutex_destroy(lock);
170 mutex_debug(
"destroy", lock);
179 int r = pthread_cond_init(cond, condattr_monotonic);
188 int r = pthread_cond_destroy(cond);
209 r = pthread_cond_signal(cond);
210 }
while (r == EAGAIN);
221 r = pthread_cond_broadcast(cond);
222 }
while (r == EAGAIN);
231 int r = pthread_cond_wait(cond, mutex);
238native_cond_timedwait(rb_nativethread_cond_t *cond, pthread_mutex_t *mutex,
const rb_hrtime_t *abs)
250 rb_hrtime2timespec(&ts, abs);
251 r = pthread_cond_timedwait(cond, mutex, &ts);
252 }
while (r == EINTR);
254 if (r != 0 && r != ETIMEDOUT) {
262native_cond_timeout(rb_nativethread_cond_t *cond,
const rb_hrtime_t rel)
264 if (condattr_monotonic) {
265 return rb_hrtime_add(rb_hrtime_now(), rel);
271 return rb_hrtime_add(rb_timespec2hrtime(&ts), rel);
278 rb_hrtime_t hrmsec = native_cond_timeout(cond, RB_HRTIME_PER_MSEC * msec);
279 native_cond_timedwait(cond, mutex, &hrmsec);
284static rb_internal_thread_event_hook_t *rb_internal_thread_event_hooks = NULL;
285static void rb_thread_execute_hooks(
rb_event_flag_t event, rb_thread_t *th);
306#define RB_INTERNAL_THREAD_HOOK(event, th) \
307 if (UNLIKELY(rb_internal_thread_event_hooks)) { \
308 fprintf(stderr, "[thread=%"PRIxVALUE"] %s in %s (%s:%d)\n", th->self, event_name(event), __func__, __FILE__, __LINE__); \
309 rb_thread_execute_hooks(event, th); \
312#define RB_INTERNAL_THREAD_HOOK(event, th) if (UNLIKELY(rb_internal_thread_event_hooks)) { rb_thread_execute_hooks(event, th); }
315static rb_serial_t current_fork_gen = 1;
317#if defined(SIGVTALRM) && !defined(__EMSCRIPTEN__)
318# define USE_UBF_LIST 1
321static void threadptr_trap_interrupt(rb_thread_t *);
323static void native_thread_dedicated_inc(rb_vm_t *vm, rb_ractor_t *cr,
struct rb_native_thread *nt);
324static void native_thread_dedicated_dec(rb_vm_t *vm, rb_ractor_t *cr,
struct rb_native_thread *nt);
325static void native_thread_assign(
struct rb_native_thread *nt, rb_thread_t *th);
327static void ractor_sched_enq(rb_vm_t *vm, rb_ractor_t *r);
328static void timer_thread_wakeup(
void);
329static void timer_thread_wakeup_locked(rb_vm_t *vm);
330static void timer_thread_wakeup_force(
void);
331static void thread_sched_switch(rb_thread_t *cth, rb_thread_t *next_th);
335#define thread_sched_dump(s) thread_sched_dump_(__FILE__, __LINE__, s)
338th_has_dedicated_nt(
const rb_thread_t *th)
341 return th->nt->dedicated > 0;
346thread_sched_dump_(const
char *file,
int line, struct
rb_thread_sched *sched)
348 fprintf(stderr,
"@%s:%d running:%d\n", file, line, sched->running ? (
int)sched->running->serial : -1);
351 ccan_list_for_each(&sched->readyq, th, sched.node.readyq) {
352 i++;
if (i>10) rb_bug(
"too many");
353 fprintf(stderr,
" ready:%d (%sNT:%d)\n", th->serial,
354 th->nt ? (th->nt->dedicated ?
"D" :
"S") :
"x",
355 th->nt ? (
int)th->nt->serial : -1);
359#define ractor_sched_dump(s) ractor_sched_dump_(__FILE__, __LINE__, s)
363ractor_sched_dump_(const
char *file,
int line, rb_vm_t *vm)
367 fprintf(stderr,
"ractor_sched_dump %s:%d\n", file, line);
370 ccan_list_for_each(&vm->ractor.sched.grq, r, threads.sched.grq_node) {
372 if (i>10) rb_bug(
"!!");
373 fprintf(stderr,
" %d ready:%d\n", i, rb_ractor_id(r));
377#define thread_sched_lock(a, b) thread_sched_lock_(a, b, __FILE__, __LINE__)
378#define thread_sched_unlock(a, b) thread_sched_unlock_(a, b, __FILE__, __LINE__)
384 VM_ASSERT(sched->lock_owner == NULL);
386 sched->lock_owner = th;
391thread_sched_set_unlocked(
struct rb_thread_sched *sched, rb_thread_t *th)
394 VM_ASSERT(sched->lock_owner == th);
396 sched->lock_owner = NULL;
401thread_sched_lock_(
struct rb_thread_sched *sched, rb_thread_t *th,
const char *file,
int line)
406 RUBY_DEBUG_LOG2(file, line,
"r:%d th:%u", th ? (
int)rb_ractor_id(th->ractor) : -1, rb_th_serial(th));
408 RUBY_DEBUG_LOG2(file, line,
"th:%u", rb_th_serial(th));
411 thread_sched_set_locked(sched, th);
415thread_sched_unlock_(
struct rb_thread_sched *sched, rb_thread_t *th,
const char *file,
int line)
417 RUBY_DEBUG_LOG2(file, line,
"th:%u", rb_th_serial(th));
419 thread_sched_set_unlocked(sched, th);
425ASSERT_thread_sched_locked(
struct rb_thread_sched *sched, rb_thread_t *th)
431 VM_ASSERT(sched->lock_owner == th);
434 VM_ASSERT(sched->lock_owner != NULL);
439#define ractor_sched_lock(a, b) ractor_sched_lock_(a, b, __FILE__, __LINE__)
440#define ractor_sched_unlock(a, b) ractor_sched_unlock_(a, b, __FILE__, __LINE__)
444rb_ractor_serial(const rb_ractor_t *r)
447 return rb_ractor_id(r);
455ractor_sched_set_locked(rb_vm_t *vm, rb_ractor_t *cr)
458 VM_ASSERT(vm->ractor.sched.lock_owner == NULL);
459 VM_ASSERT(vm->ractor.sched.locked ==
false);
461 vm->ractor.sched.lock_owner = cr;
462 vm->ractor.sched.locked =
true;
467ractor_sched_set_unlocked(rb_vm_t *vm, rb_ractor_t *cr)
470 VM_ASSERT(vm->ractor.sched.locked);
471 VM_ASSERT(vm->ractor.sched.lock_owner == cr);
473 vm->ractor.sched.locked =
false;
474 vm->ractor.sched.lock_owner = NULL;
479ractor_sched_lock_(rb_vm_t *vm, rb_ractor_t *cr,
const char *file,
int line)
484 RUBY_DEBUG_LOG2(file, line,
"cr:%u prev_owner:%u", rb_ractor_serial(cr), rb_ractor_serial(vm->ractor.sched.lock_owner));
486 RUBY_DEBUG_LOG2(file, line,
"cr:%u", rb_ractor_serial(cr));
489 ractor_sched_set_locked(vm, cr);
493ractor_sched_unlock_(rb_vm_t *vm, rb_ractor_t *cr,
const char *file,
int line)
495 RUBY_DEBUG_LOG2(file, line,
"cr:%u", rb_ractor_serial(cr));
497 ractor_sched_set_unlocked(vm, cr);
502ASSERT_ractor_sched_locked(rb_vm_t *vm, rb_ractor_t *cr)
505 VM_ASSERT(vm->ractor.sched.locked);
506 VM_ASSERT(cr == NULL || vm->ractor.sched.lock_owner == cr);
511ractor_sched_running_threads_contain_p(rb_vm_t *vm, rb_thread_t *th)
514 ccan_list_for_each(&vm->ractor.sched.running_threads, rth, sched.node.running_threads) {
515 if (rth == th)
return true;
522ractor_sched_running_threads_size(rb_vm_t *vm)
526 ccan_list_for_each(&vm->ractor.sched.running_threads, th, sched.node.running_threads) {
534ractor_sched_timeslice_threads_size(rb_vm_t *vm)
538 ccan_list_for_each(&vm->ractor.sched.timeslice_threads, th, sched.node.timeslice_threads) {
546ractor_sched_timeslice_threads_contain_p(rb_vm_t *vm, rb_thread_t *th)
549 ccan_list_for_each(&vm->ractor.sched.timeslice_threads, rth, sched.node.timeslice_threads) {
550 if (rth == th)
return true;
555static void ractor_sched_barrier_join_signal_locked(rb_vm_t *vm);
559thread_sched_setup_running_threads(
struct rb_thread_sched *sched, rb_ractor_t *cr, rb_vm_t *vm,
560 rb_thread_t *add_th, rb_thread_t *del_th, rb_thread_t *add_timeslice_th)
562#if USE_RUBY_DEBUG_LOG
563 unsigned int prev_running_cnt = vm->ractor.sched.running_cnt;
566 rb_thread_t *del_timeslice_th;
568 if (del_th && sched->is_running_timeslice) {
569 del_timeslice_th = del_th;
570 sched->is_running_timeslice =
false;
573 del_timeslice_th = NULL;
576 RUBY_DEBUG_LOG(
"+:%u -:%u +ts:%u -ts:%u",
577 rb_th_serial(add_th), rb_th_serial(del_th),
578 rb_th_serial(add_timeslice_th), rb_th_serial(del_timeslice_th));
580 ractor_sched_lock(vm, cr);
584 VM_ASSERT(ractor_sched_running_threads_contain_p(vm, del_th));
585 VM_ASSERT(del_timeslice_th != NULL ||
586 !ractor_sched_timeslice_threads_contain_p(vm, del_th));
588 ccan_list_del_init(&del_th->sched.node.running_threads);
589 vm->ractor.sched.running_cnt--;
591 if (UNLIKELY(vm->ractor.sched.barrier_waiting)) {
592 ractor_sched_barrier_join_signal_locked(vm);
594 sched->is_running =
false;
598 if (vm->ractor.sched.barrier_waiting) {
600 RUBY_DEBUG_LOG(
"barrier_waiting");
601 RUBY_VM_SET_VM_BARRIER_INTERRUPT(add_th->ec);
604 VM_ASSERT(!ractor_sched_running_threads_contain_p(vm, add_th));
605 VM_ASSERT(!ractor_sched_timeslice_threads_contain_p(vm, add_th));
607 ccan_list_add(&vm->ractor.sched.running_threads, &add_th->sched.node.running_threads);
608 vm->ractor.sched.running_cnt++;
609 sched->is_running =
true;
612 if (add_timeslice_th) {
614 int was_empty = ccan_list_empty(&vm->ractor.sched.timeslice_threads);
615 VM_ASSERT(!ractor_sched_timeslice_threads_contain_p(vm, add_timeslice_th));
616 ccan_list_add(&vm->ractor.sched.timeslice_threads, &add_timeslice_th->sched.node.timeslice_threads);
617 sched->is_running_timeslice =
true;
619 timer_thread_wakeup_locked(vm);
623 if (del_timeslice_th) {
624 VM_ASSERT(ractor_sched_timeslice_threads_contain_p(vm, del_timeslice_th));
625 ccan_list_del_init(&del_timeslice_th->sched.node.timeslice_threads);
628 VM_ASSERT(ractor_sched_running_threads_size(vm) == vm->ractor.sched.running_cnt);
629 VM_ASSERT(ractor_sched_timeslice_threads_size(vm) <= vm->ractor.sched.running_cnt);
631 ractor_sched_unlock(vm, cr);
636 RUBY_DEBUG_LOG(
"run:%u->%u", prev_running_cnt, vm->ractor.sched.running_cnt);
640thread_sched_add_running_thread(
struct rb_thread_sched *sched, rb_thread_t *th)
642 ASSERT_thread_sched_locked(sched, th);
643 VM_ASSERT(sched->running == th);
645 rb_vm_t *vm = th->vm;
646 thread_sched_setup_running_threads(sched, th->ractor, vm, th, NULL, ccan_list_empty(&sched->readyq) ? NULL : th);
650thread_sched_del_running_thread(
struct rb_thread_sched *sched, rb_thread_t *th)
652 ASSERT_thread_sched_locked(sched, th);
654 rb_vm_t *vm = th->vm;
655 thread_sched_setup_running_threads(sched, th->ractor, vm, NULL, th, NULL);
659rb_add_running_thread(rb_thread_t *th)
663 thread_sched_lock(sched, th);
665 thread_sched_add_running_thread(sched, th);
667 thread_sched_unlock(sched, th);
671rb_del_running_thread(rb_thread_t *th)
675 thread_sched_lock(sched, th);
677 thread_sched_del_running_thread(sched, th);
679 thread_sched_unlock(sched, th);
687thread_sched_set_running(
struct rb_thread_sched *sched, rb_thread_t *th)
689 RUBY_DEBUG_LOG(
"th:%u->th:%u", rb_th_serial(sched->running), rb_th_serial(th));
690 VM_ASSERT(sched->running != th);
697thread_sched_readyq_contain_p(struct
rb_thread_sched *sched, rb_thread_t *th)
700 ccan_list_for_each(&sched->readyq, rth, sched.node.readyq) {
702 VM_ASSERT(th->sched.node.is_ready);
706 VM_ASSERT(!th->sched.node.is_ready);
717 ASSERT_thread_sched_locked(sched, NULL);
718 rb_thread_t *next_th;
720 VM_ASSERT(sched->running != NULL);
722 if (ccan_list_empty(&sched->readyq)) {
726 next_th = ccan_list_pop(&sched->readyq, rb_thread_t, sched.node.readyq);
727 VM_ASSERT(next_th->sched.node.is_ready);
728 next_th->sched.node.is_ready =
false;
730 VM_ASSERT(sched->readyq_cnt > 0);
732 ccan_list_node_init(&next_th->sched.node.readyq);
735 RUBY_DEBUG_LOG(
"next_th:%u readyq_cnt:%d", rb_th_serial(next_th), sched->readyq_cnt);
744 ASSERT_thread_sched_locked(sched, NULL);
745 RUBY_DEBUG_LOG(
"ready_th:%u readyq_cnt:%d", rb_th_serial(ready_th), sched->readyq_cnt);
747 VM_ASSERT(sched->running != NULL);
748 VM_ASSERT(!thread_sched_readyq_contain_p(sched, ready_th));
750 if (sched->is_running) {
751 if (ccan_list_empty(&sched->readyq)) {
753 thread_sched_setup_running_threads(sched, ready_th->ractor, ready_th->vm, NULL, NULL, sched->running);
761 ccan_list_add_tail(&sched->readyq, &ready_th->sched.node.readyq);
762 ready_th->sched.node.is_ready =
true;
769thread_sched_wakeup_running_thread(
struct rb_thread_sched *sched, rb_thread_t *next_th,
bool will_switch)
771 ASSERT_thread_sched_locked(sched, NULL);
772 VM_ASSERT(sched->running == next_th);
776 if (th_has_dedicated_nt(next_th)) {
777 RUBY_DEBUG_LOG(
"pinning th:%u", next_th->serial);
782 RUBY_DEBUG_LOG(
"th:%u is already running.", next_th->serial);
787 RUBY_DEBUG_LOG(
"th:%u (do nothing)", rb_th_serial(next_th));
790 RUBY_DEBUG_LOG(
"th:%u (enq)", rb_th_serial(next_th));
791 ractor_sched_enq(next_th->vm, next_th->ractor);
796 RUBY_DEBUG_LOG(
"no waiting threads%s",
"");
802thread_sched_to_ready_common(
struct rb_thread_sched *sched, rb_thread_t *th,
bool wakeup,
bool will_switch)
804 RUBY_DEBUG_LOG(
"th:%u running:%u redyq_cnt:%d", rb_th_serial(th), rb_th_serial(sched->running), sched->readyq_cnt);
806 VM_ASSERT(sched->running != th);
807 VM_ASSERT(!thread_sched_readyq_contain_p(sched, th));
810 if (sched->running == NULL) {
811 thread_sched_set_running(sched, th);
812 if (wakeup) thread_sched_wakeup_running_thread(sched, th, will_switch);
815 thread_sched_enq(sched, th);
827 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
829 thread_sched_lock(sched, th);
831 thread_sched_to_ready_common(sched, th,
true,
false);
833 thread_sched_unlock(sched, th);
838thread_sched_wait_running_turn(
struct rb_thread_sched *sched, rb_thread_t *th,
bool can_direct_transfer)
840 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
842 ASSERT_thread_sched_locked(sched, th);
843 VM_ASSERT(th == rb_ec_thread_ptr(rb_current_ec_noinline()));
845 if (th != sched->running) {
847 if (th->has_dedicated_nt && th == sched->runnable_hot_th && (sched->running == NULL || sched->running->has_dedicated_nt)) {
848 RUBY_DEBUG_LOG(
"(nt) stealing: hot-th:%u. running:%u", rb_th_serial(th), rb_th_serial(sched->running));
851 if (sched->running != NULL) {
852 rb_thread_t *running = sched->running;
853 VM_ASSERT(!thread_sched_readyq_contain_p(sched, running));
854 running->sched.node.is_ready =
true;
855 ccan_list_add(&sched->readyq, &running->sched.node.readyq);
860 if (th->sched.node.is_ready) {
861 VM_ASSERT(thread_sched_readyq_contain_p(sched, th));
862 ccan_list_del_init(&th->sched.node.readyq);
863 th->sched.node.is_ready =
false;
866 thread_sched_set_running(sched, th);
867 rb_ractor_thread_switch(th->ractor, th,
false);
874 rb_thread_t *next_th;
875 while((next_th = sched->running) != th) {
876 if (th_has_dedicated_nt(th)) {
877 RUBY_DEBUG_LOG(
"(nt) sleep th:%u running:%u", rb_th_serial(th), rb_th_serial(sched->running));
879 thread_sched_set_unlocked(sched, th);
881 RUBY_DEBUG_LOG(
"nt:%d cond:%p", th->nt->serial, &th->nt->cond.readyq);
884 thread_sched_set_locked(sched, th);
886 if (sched->runnable_hot_th != NULL && sched->runnable_hot_th_waiting) {
887 VM_ASSERT(sched->runnable_hot_th != th);
891 thread_sched_unlock(sched, th);
892 thread_sched_lock(sched, th);
895 RUBY_DEBUG_LOG(
"(nt) wakeup %s", sched->running == th ?
"success" :
"failed");
896 if (th == sched->running) {
897 rb_ractor_thread_switch(th->ractor, th,
false);
902 if (can_direct_transfer &&
903 (next_th = sched->running) != NULL &&
907 RUBY_DEBUG_LOG(
"th:%u->%u (direct)", rb_th_serial(th), rb_th_serial(next_th));
909 thread_sched_set_unlocked(sched, th);
911 rb_ractor_set_current_ec(th->ractor, NULL);
912 thread_sched_switch(th, next_th);
914 thread_sched_set_locked(sched, th);
919 native_thread_assign(NULL, th);
921 RUBY_DEBUG_LOG(
"th:%u->%u (ractor scheduling)", rb_th_serial(th), rb_th_serial(next_th));
923 thread_sched_set_unlocked(sched, th);
925 rb_ractor_set_current_ec(th->ractor, NULL);
926 coroutine_transfer0(th->sched.context, nt->nt_context,
false);
928 thread_sched_set_locked(sched, th);
931 VM_ASSERT(rb_current_ec_noinline() == th->ec);
935 VM_ASSERT(th->nt != NULL);
936 VM_ASSERT(rb_current_ec_noinline() == th->ec);
937 VM_ASSERT(th->sched.waiting_reason.flags == thread_sched_waiting_none);
940 thread_sched_add_running_thread(sched, th);
945 sched->runnable_hot_th = NULL;
946 sched->runnable_hot_th_waiting = 0;
954thread_sched_to_running_common(
struct rb_thread_sched *sched, rb_thread_t *th)
956 RUBY_DEBUG_LOG(
"th:%u dedicated:%d", rb_th_serial(th), th_has_dedicated_nt(th));
958 VM_ASSERT(sched->running != th);
959 VM_ASSERT(th_has_dedicated_nt(th));
960 VM_ASSERT(GET_THREAD() == th);
962 native_thread_dedicated_dec(th->vm, th->ractor, th->nt);
965 thread_sched_to_ready_common(sched, th,
false,
false);
967 if (sched->running == th) {
968 thread_sched_add_running_thread(sched, th);
972 thread_sched_wait_running_turn(sched, th,
false);
988 if (sched->runnable_hot_th == th) {
989 sched->runnable_hot_th_waiting = 1;
991 thread_sched_lock(sched, th);
993 thread_sched_to_running_common(sched, th);
995 thread_sched_unlock(sched, th);
1006thread_sched_wakeup_next_thread(
struct rb_thread_sched *sched, rb_thread_t *th,
bool will_switch)
1008 ASSERT_thread_sched_locked(sched, th);
1010 VM_ASSERT(sched->running == th);
1011 VM_ASSERT(sched->running->nt != NULL);
1013 rb_thread_t *next_th = thread_sched_deq(sched);
1015 RUBY_DEBUG_LOG(
"next_th:%u", rb_th_serial(next_th));
1016 VM_ASSERT(th != next_th);
1018 thread_sched_set_running(sched, next_th);
1019 VM_ASSERT(next_th == sched->running);
1020 thread_sched_wakeup_running_thread(sched, next_th, will_switch);
1022 if (th != next_th) {
1023 thread_sched_del_running_thread(sched, th);
1029thread_sched_to_dead_common(
struct rb_thread_sched *sched, rb_thread_t *th)
1031 RUBY_DEBUG_LOG(
"th:%u DNT:%d", rb_th_serial(th), th->nt->dedicated);
1035 thread_sched_wakeup_next_thread(sched, th, !th_has_dedicated_nt(th));
1044 thread_sched_lock(sched, th);
1046 thread_sched_to_dead_common(sched, th);
1048 thread_sched_unlock(sched, th);
1055thread_sched_to_waiting_common(
struct rb_thread_sched *sched, rb_thread_t *th,
bool yield_immediately)
1057 RUBY_DEBUG_LOG(
"th:%u DNT:%d", rb_th_serial(th), th->nt->dedicated);
1061 native_thread_dedicated_inc(th->vm, th->ractor, th->nt);
1062 if (!yield_immediately) {
1063 sched->runnable_hot_th = th;
1064 sched->runnable_hot_th_waiting = 0;
1066 thread_sched_wakeup_next_thread(sched, th,
false);
1073thread_sched_to_waiting(
struct rb_thread_sched *sched, rb_thread_t *th,
bool yield_immediately)
1075 thread_sched_lock(sched, th);
1077 thread_sched_to_waiting_common(sched, th, yield_immediately);
1079 thread_sched_unlock(sched, th);
1087 VM_ASSERT(func != NULL);
1090 if (RUBY_VM_INTERRUPTED(th->ec)) {
1091 RUBY_DEBUG_LOG(
"interrupted:0x%x", th->ec->interrupt_flag);
1097 if (!th->ec->raised_flag && RUBY_VM_INTERRUPTED(th->ec)) {
1102 VM_ASSERT(th->unblock.func == NULL);
1103 th->unblock.func = func;
1104 th->unblock.arg = arg;
1112ubf_clear(rb_thread_t *th)
1114 if (th->unblock.func) {
1117 th->unblock.func = NULL;
1118 th->unblock.arg = NULL;
1125ubf_waiting(
void *ptr)
1127 rb_thread_t *th = (rb_thread_t *)ptr;
1131 th->unblock.func = NULL;
1132 th->unblock.arg = NULL;
1134 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
1136 thread_sched_lock(sched, th);
1138 if (sched->running == th) {
1142 thread_sched_to_ready_common(sched, th,
true,
false);
1145 thread_sched_unlock(sched, th);
1152thread_sched_to_waiting_until_wakeup(
struct rb_thread_sched *sched, rb_thread_t *th)
1154 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
1156 RB_VM_SAVE_MACHINE_CONTEXT(th);
1161 thread_sched_lock(sched, th);
1164 if (ubf_set(th, ubf_waiting, (
void *)th)) {
1165 RUBY_DEBUG_LOG(
"th:%u interrupted", rb_th_serial(th));
1168 bool can_direct_transfer = !th_has_dedicated_nt(th);
1170 thread_sched_wakeup_next_thread(sched, th, can_direct_transfer);
1171 thread_sched_wait_running_turn(sched, th, can_direct_transfer);
1174 thread_sched_unlock(sched, th);
1184 RUBY_DEBUG_LOG(
"th:%d sched->readyq_cnt:%d", (
int)th->serial, sched->readyq_cnt);
1186 thread_sched_lock(sched, th);
1188 if (!ccan_list_empty(&sched->readyq)) {
1190 thread_sched_wakeup_next_thread(sched, th, !th_has_dedicated_nt(th));
1191 bool can_direct_transfer = !th_has_dedicated_nt(th);
1192 thread_sched_to_ready_common(sched, th,
false, can_direct_transfer);
1193 thread_sched_wait_running_turn(sched, th, can_direct_transfer);
1194 th->status = THREAD_RUNNABLE;
1197 VM_ASSERT(sched->readyq_cnt == 0);
1200 thread_sched_unlock(sched, th);
1209 sched->lock_owner = NULL;
1212 ccan_list_head_init(&sched->readyq);
1213 sched->readyq_cnt = 0;
1216 if (!atfork) sched->enable_mn_threads =
true;
1223#ifdef RUBY_ASAN_ENABLED
1224 void **fake_stack = to_dead ? NULL : &transfer_from->fake_stack;
1225 __sanitizer_start_switch_fiber(fake_stack, transfer_to->stack_base, transfer_to->stack_size);
1229 struct
coroutine_context *returning_from = coroutine_transfer(transfer_from, transfer_to);
1233 VM_ASSERT(!to_dead);
1234#ifdef RUBY_ASAN_ENABLED
1235 __sanitizer_finish_switch_fiber(transfer_from->fake_stack,
1236 (
const void**)&returning_from->stack_base, &returning_from->stack_size);
1244 VM_ASSERT(!nt->dedicated);
1245 VM_ASSERT(next_th->nt == NULL);
1247 RUBY_DEBUG_LOG(
"next_th:%u", rb_th_serial(next_th));
1249 ruby_thread_set_native(next_th);
1250 native_thread_assign(nt, next_th);
1252 coroutine_transfer0(current_cont, next_th->sched.context, to_dead);
1256thread_sched_switch(rb_thread_t *cth, rb_thread_t *next_th)
1259 native_thread_assign(NULL, cth);
1260 RUBY_DEBUG_LOG(
"th:%u->%u on nt:%d", rb_th_serial(cth), rb_th_serial(next_th), nt->serial);
1261 thread_sched_switch0(cth->sched.context, next_th, nt, cth->status == THREAD_KILLED);
1264#if VM_CHECK_MODE > 0
1267grq_size(rb_vm_t *vm, rb_ractor_t *cr)
1269 ASSERT_ractor_sched_locked(vm, cr);
1271 rb_ractor_t *r, *prev_r = NULL;
1274 ccan_list_for_each(&vm->ractor.sched.grq, r, threads.sched.grq_node) {
1277 VM_ASSERT(r != prev_r);
1285ractor_sched_enq(rb_vm_t *vm, rb_ractor_t *r)
1288 rb_ractor_t *cr = NULL;
1290 VM_ASSERT(sched->running != NULL);
1291 VM_ASSERT(sched->running->nt == NULL);
1293 ractor_sched_lock(vm, cr);
1295#if VM_CHECK_MODE > 0
1298 ccan_list_for_each(&vm->ractor.sched.grq,
tr, threads.sched.grq_node) {
1303 ccan_list_add_tail(&vm->ractor.sched.grq, &sched->grq_node);
1304 vm->ractor.sched.grq_cnt++;
1305 VM_ASSERT(grq_size(vm, cr) == vm->ractor.sched.grq_cnt);
1307 RUBY_DEBUG_LOG(
"r:%u th:%u grq_cnt:%u", rb_ractor_id(r), rb_th_serial(sched->running), vm->ractor.sched.grq_cnt);
1313 ractor_sched_unlock(vm, cr);
1317#ifndef SNT_KEEP_SECONDS
1318#define SNT_KEEP_SECONDS 0
1323#define MINIMUM_SNT 0
1327ractor_sched_deq(rb_vm_t *vm, rb_ractor_t *cr)
1331 ractor_sched_lock(vm, cr);
1333 RUBY_DEBUG_LOG(
"empty? %d", ccan_list_empty(&vm->ractor.sched.grq));
1336 VM_ASSERT(rb_current_execution_context(
false) == NULL);
1337 VM_ASSERT(grq_size(vm, cr) == vm->ractor.sched.grq_cnt);
1339 while ((r = ccan_list_pop(&vm->ractor.sched.grq, rb_ractor_t, threads.sched.grq_node)) == NULL) {
1340 RUBY_DEBUG_LOG(
"wait grq_cnt:%d", (
int)vm->ractor.sched.grq_cnt);
1342#if SNT_KEEP_SECONDS > 0
1343 rb_hrtime_t abs = rb_hrtime_add(rb_hrtime_now(), RB_HRTIME_PER_SEC * SNT_KEEP_SECONDS);
1344 if (native_cond_timedwait(&vm->ractor.sched.cond, &vm->ractor.sched.lock, &abs) == ETIMEDOUT) {
1345 RUBY_DEBUG_LOG(
"timeout, grq_cnt:%d", (
int)vm->ractor.sched.grq_cnt);
1346 VM_ASSERT(r == NULL);
1347 vm->ractor.sched.snt_cnt--;
1348 vm->ractor.sched.running_cnt--;
1352 RUBY_DEBUG_LOG(
"wakeup grq_cnt:%d", (
int)vm->ractor.sched.grq_cnt);
1355 ractor_sched_set_unlocked(vm, cr);
1357 ractor_sched_set_locked(vm, cr);
1359 RUBY_DEBUG_LOG(
"wakeup grq_cnt:%d", (
int)vm->ractor.sched.grq_cnt);
1363 VM_ASSERT(rb_current_execution_context(
false) == NULL);
1366 VM_ASSERT(vm->ractor.sched.grq_cnt > 0);
1367 vm->ractor.sched.grq_cnt--;
1368 RUBY_DEBUG_LOG(
"r:%d grq_cnt:%u", (
int)rb_ractor_id(r), vm->ractor.sched.grq_cnt);
1371 VM_ASSERT(SNT_KEEP_SECONDS > 0);
1375 ractor_sched_unlock(vm, cr);
1380void rb_ractor_lock_self(rb_ractor_t *r);
1381void rb_ractor_unlock_self(rb_ractor_t *r);
1386rb_ractor_sched_wait(rb_execution_context_t *ec, rb_ractor_t *cr,
rb_unblock_function_t *ubf,
void *ubf_arg)
1390 RUBY_DEBUG_LOG(
"start%s",
"");
1392 rb_thread_t *
volatile th = rb_ec_thread_ptr(ec);
1395 if (ubf_set(th, ubf, ubf_arg)) {
1400 thread_sched_lock(sched, th);
1401 rb_ractor_unlock_self(cr);
1404 bool can_direct_transfer = !th_has_dedicated_nt(th);
1405 RB_VM_SAVE_MACHINE_CONTEXT(th);
1406 th->status = THREAD_STOPPED_FOREVER;
1408 thread_sched_wakeup_next_thread(sched, th, can_direct_transfer);
1410 thread_sched_wait_running_turn(sched, th, can_direct_transfer);
1411 th->status = THREAD_RUNNABLE;
1413 thread_sched_unlock(sched, th);
1414 rb_ractor_lock_self(cr);
1418 RUBY_DEBUG_LOG(
"end%s",
"");
1422rb_ractor_sched_wakeup(rb_ractor_t *r, rb_thread_t *r_th)
1427 RUBY_DEBUG_LOG(
"r:%u th:%d", (
unsigned int)rb_ractor_id(r), r_th->serial);
1429 thread_sched_lock(sched, r_th);
1431 if (r_th->status == THREAD_STOPPED_FOREVER) {
1432 thread_sched_to_ready_common(sched, r_th,
true,
false);
1435 thread_sched_unlock(sched, r_th);
1439ractor_sched_barrier_completed_p(rb_vm_t *vm)
1441 RUBY_DEBUG_LOG(
"run:%u wait:%u", vm->ractor.sched.running_cnt, vm->ractor.sched.barrier_waiting_cnt);
1442 VM_ASSERT(vm->ractor.sched.running_cnt - 1 >= vm->ractor.sched.barrier_waiting_cnt);
1444 return (vm->ractor.sched.running_cnt - vm->ractor.sched.barrier_waiting_cnt) == 1;
1448rb_ractor_sched_barrier_start(rb_vm_t *vm, rb_ractor_t *cr)
1450 VM_ASSERT(cr == GET_RACTOR());
1451 VM_ASSERT(vm->ractor.sync.lock_owner == cr);
1452 VM_ASSERT(!vm->ractor.sched.barrier_waiting);
1453 VM_ASSERT(vm->ractor.sched.barrier_waiting_cnt == 0);
1454 VM_ASSERT(vm->ractor.sched.barrier_ractor == NULL);
1455 VM_ASSERT(vm->ractor.sched.barrier_lock_rec == 0);
1457 RUBY_DEBUG_LOG(
"start serial:%u", vm->ractor.sched.barrier_serial);
1459 unsigned int lock_rec;
1461 ractor_sched_lock(vm, cr);
1463 vm->ractor.sched.barrier_waiting =
true;
1464 vm->ractor.sched.barrier_ractor = cr;
1465 vm->ractor.sched.barrier_lock_rec = vm->ractor.sync.lock_rec;
1468 lock_rec = vm->ractor.sync.lock_rec;
1469 vm->ractor.sync.lock_rec = 0;
1470 vm->ractor.sync.lock_owner = NULL;
1475 ccan_list_for_each(&vm->ractor.sched.running_threads, ith, sched.node.running_threads) {
1476 if (ith->ractor != cr) {
1477 RUBY_DEBUG_LOG(
"barrier request to th:%u", rb_th_serial(ith));
1478 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ith->ec);
1483 while (!ractor_sched_barrier_completed_p(vm)) {
1484 ractor_sched_set_unlocked(vm, cr);
1486 ractor_sched_set_locked(vm, cr);
1489 RUBY_DEBUG_LOG(
"completed seirial:%u", vm->ractor.sched.barrier_serial);
1492 vm->ractor.sched.barrier_serial++;
1493 vm->ractor.sched.barrier_waiting_cnt = 0;
1498 vm->ractor.sync.lock_rec = lock_rec;
1499 vm->ractor.sync.lock_owner = cr;
1508rb_ractor_sched_barrier_end(rb_vm_t *vm, rb_ractor_t *cr)
1510 RUBY_DEBUG_LOG(
"serial:%u", (
unsigned int)vm->ractor.sched.barrier_serial - 1);
1511 VM_ASSERT(vm->ractor.sched.barrier_waiting);
1512 VM_ASSERT(vm->ractor.sched.barrier_ractor);
1513 VM_ASSERT(vm->ractor.sched.barrier_lock_rec > 0);
1515 vm->ractor.sched.barrier_waiting =
false;
1516 vm->ractor.sched.barrier_ractor = NULL;
1517 vm->ractor.sched.barrier_lock_rec = 0;
1518 ractor_sched_unlock(vm, cr);
1522ractor_sched_barrier_join_signal_locked(rb_vm_t *vm)
1524 if (ractor_sched_barrier_completed_p(vm)) {
1530ractor_sched_barrier_join_wait_locked(rb_vm_t *vm, rb_thread_t *th)
1532 VM_ASSERT(vm->ractor.sched.barrier_waiting);
1534 unsigned int barrier_serial = vm->ractor.sched.barrier_serial;
1536 while (vm->ractor.sched.barrier_serial == barrier_serial) {
1537 RUBY_DEBUG_LOG(
"sleep serial:%u", barrier_serial);
1538 RB_VM_SAVE_MACHINE_CONTEXT(th);
1540 rb_ractor_t *cr = th->ractor;
1541 ractor_sched_set_unlocked(vm, cr);
1543 ractor_sched_set_locked(vm, cr);
1545 RUBY_DEBUG_LOG(
"wakeup serial:%u", barrier_serial);
1550rb_ractor_sched_barrier_join(rb_vm_t *vm, rb_ractor_t *cr)
1552 VM_ASSERT(cr->threads.sched.running != NULL);
1553 VM_ASSERT(cr == GET_RACTOR());
1554 VM_ASSERT(vm->ractor.sync.lock_owner == NULL);
1555 VM_ASSERT(vm->ractor.sched.barrier_waiting);
1557#if USE_RUBY_DEBUG_LOG || VM_CHECK_MODE > 0
1558 unsigned int barrier_serial = vm->ractor.sched.barrier_serial;
1561 RUBY_DEBUG_LOG(
"join");
1565 VM_ASSERT(vm->ractor.sched.barrier_waiting);
1566 VM_ASSERT(vm->ractor.sched.barrier_serial == barrier_serial);
1568 ractor_sched_lock(vm, cr);
1571 vm->ractor.sched.barrier_waiting_cnt++;
1572 RUBY_DEBUG_LOG(
"waiting_cnt:%u serial:%u", vm->ractor.sched.barrier_waiting_cnt, barrier_serial);
1574 ractor_sched_barrier_join_signal_locked(vm);
1575 ractor_sched_barrier_join_wait_locked(vm, cr->threads.sched.running);
1577 ractor_sched_unlock(vm, cr);
1587static void clear_thread_cache_altstack(
void);
1600 clear_thread_cache_altstack();
1604#ifdef RB_THREAD_T_HAS_NATIVE_ID
1606get_native_thread_id(
void)
1609 return (
int)syscall(SYS_gettid);
1610#elif defined(__FreeBSD__)
1611 return pthread_getthreadid_np();
1616#if defined(HAVE_WORKING_FORK)
1617void rb_internal_thread_event_hooks_rw_lock_atfork(
void);
1623 rb_thread_sched_init(sched,
true);
1624 rb_thread_t *th = GET_THREAD();
1625 rb_vm_t *vm = GET_VM();
1627 if (th_has_dedicated_nt(th)) {
1628 vm->ractor.sched.snt_cnt = 0;
1631 vm->ractor.sched.snt_cnt = 1;
1633 vm->ractor.sched.running_cnt = 0;
1636#if VM_CHECK_MODE > 0
1637 vm->ractor.sched.lock_owner = NULL;
1638 vm->ractor.sched.locked =
false;
1646 ccan_list_head_init(&vm->ractor.sched.grq);
1647 ccan_list_head_init(&vm->ractor.sched.timeslice_threads);
1648 ccan_list_head_init(&vm->ractor.sched.running_threads);
1650 rb_internal_thread_event_hooks_rw_lock_atfork();
1652 VM_ASSERT(sched->is_running);
1653 sched->is_running_timeslice =
false;
1655 if (sched->running != th) {
1656 thread_sched_to_running(sched, th);
1659 thread_sched_setup_running_threads(sched, th->ractor, vm, th, NULL, NULL);
1662#ifdef RB_THREAD_T_HAS_NATIVE_ID
1664 th->nt->tid = get_native_thread_id();
1671#ifdef RB_THREAD_LOCAL_SPECIFIER
1672static RB_THREAD_LOCAL_SPECIFIER rb_thread_t *ruby_native_thread;
1674static pthread_key_t ruby_native_thread_key;
1686ruby_thread_from_native(
void)
1688#ifdef RB_THREAD_LOCAL_SPECIFIER
1689 return ruby_native_thread;
1691 return pthread_getspecific(ruby_native_thread_key);
1696ruby_thread_set_native(rb_thread_t *th)
1700 ccan_list_node_init(&th->sched.node.ubf);
1707 rb_ractor_set_current_ec(th->ractor, th->ec);
1709#ifdef RB_THREAD_LOCAL_SPECIFIER
1710 ruby_native_thread = th;
1713 return pthread_setspecific(ruby_native_thread_key, th) == 0;
1721Init_native_thread(rb_thread_t *main_th)
1723#if defined(HAVE_PTHREAD_CONDATTR_SETCLOCK)
1724 if (condattr_monotonic) {
1725 int r = pthread_condattr_init(condattr_monotonic);
1727 r = pthread_condattr_setclock(condattr_monotonic, CLOCK_MONOTONIC);
1729 if (r) condattr_monotonic = NULL;
1733#ifndef RB_THREAD_LOCAL_SPECIFIER
1734 if (pthread_key_create(&ruby_native_thread_key, 0) == EAGAIN) {
1735 rb_bug(
"pthread_key_create failed (ruby_native_thread_key)");
1737 if (pthread_key_create(&ruby_current_ec_key, 0) == EAGAIN) {
1738 rb_bug(
"pthread_key_create failed (ruby_current_ec_key)");
1741 ruby_posix_signal(SIGVTALRM, null_func);
1744 rb_vm_t *vm = main_th->vm;
1750 ccan_list_head_init(&vm->ractor.sched.grq);
1751 ccan_list_head_init(&vm->ractor.sched.timeslice_threads);
1752 ccan_list_head_init(&vm->ractor.sched.running_threads);
1755 main_th->nt->thread_id = pthread_self();
1756 main_th->nt->serial = 1;
1757#ifdef RUBY_NT_SERIAL
1760 ruby_thread_set_native(main_th);
1761 native_thread_setup(main_th->nt);
1762 native_thread_setup_on_thread(main_th->nt);
1764 TH_SCHED(main_th)->running = main_th;
1765 main_th->has_dedicated_nt = 1;
1767 thread_sched_setup_running_threads(TH_SCHED(main_th), main_th->ractor, vm, main_th, NULL, NULL);
1770 main_th->nt->dedicated = 1;
1771 main_th->nt->vm = vm;
1774 vm->ractor.sched.dnt_cnt = 1;
1777extern int ruby_mn_threads_enabled;
1780ruby_mn_threads_params(
void)
1782 rb_vm_t *vm = GET_VM();
1783 rb_ractor_t *main_ractor = GET_RACTOR();
1785 const char *mn_threads_cstr = getenv(
"RUBY_MN_THREADS");
1786 bool enable_mn_threads =
false;
1788 if (USE_MN_THREADS && mn_threads_cstr && (enable_mn_threads = atoi(mn_threads_cstr) > 0)) {
1790 ruby_mn_threads_enabled = 1;
1792 main_ractor->threads.sched.enable_mn_threads = enable_mn_threads;
1794 const char *max_cpu_cstr = getenv(
"RUBY_MAX_CPU");
1795 const int default_max_cpu = 8;
1796 int max_cpu = default_max_cpu;
1798 if (USE_MN_THREADS && max_cpu_cstr) {
1799 int given_max_cpu = atoi(max_cpu_cstr);
1800 if (given_max_cpu > 0) {
1801 max_cpu = given_max_cpu;
1805 vm->ractor.sched.max_cpu = max_cpu;
1809native_thread_dedicated_inc(rb_vm_t *vm, rb_ractor_t *cr,
struct rb_native_thread *nt)
1811 RUBY_DEBUG_LOG(
"nt:%d %d->%d", nt->serial, nt->dedicated, nt->dedicated + 1);
1813 if (nt->dedicated == 0) {
1814 ractor_sched_lock(vm, cr);
1816 vm->ractor.sched.snt_cnt--;
1817 vm->ractor.sched.dnt_cnt++;
1819 ractor_sched_unlock(vm, cr);
1826native_thread_dedicated_dec(rb_vm_t *vm, rb_ractor_t *cr,
struct rb_native_thread *nt)
1828 RUBY_DEBUG_LOG(
"nt:%d %d->%d", nt->serial, nt->dedicated, nt->dedicated - 1);
1829 VM_ASSERT(nt->dedicated > 0);
1832 if (nt->dedicated == 0) {
1833 ractor_sched_lock(vm, cr);
1835 nt->vm->ractor.sched.snt_cnt++;
1836 nt->vm->ractor.sched.dnt_cnt--;
1838 ractor_sched_unlock(vm, cr);
1845#if USE_RUBY_DEBUG_LOG
1848 RUBY_DEBUG_LOG(
"th:%d nt:%d->%d", (
int)th->serial, (
int)th->nt->serial, (
int)nt->serial);
1851 RUBY_DEBUG_LOG(
"th:%d nt:NULL->%d", (
int)th->serial, (
int)nt->serial);
1856 RUBY_DEBUG_LOG(
"th:%d nt:%d->NULL", (
int)th->serial, (
int)th->nt->serial);
1859 RUBY_DEBUG_LOG(
"th:%d nt:NULL->NULL", (
int)th->serial);
1882 RB_ALTSTACK_FREE(nt->altstack);
1894 if (&nt->cond.readyq != &nt->cond.intr) {
1898 native_thread_destroy_atfork(nt);
1902#if defined HAVE_PTHREAD_GETATTR_NP || defined HAVE_PTHREAD_ATTR_GET_NP
1903#define STACKADDR_AVAILABLE 1
1904#elif defined HAVE_PTHREAD_GET_STACKADDR_NP && defined HAVE_PTHREAD_GET_STACKSIZE_NP
1905#define STACKADDR_AVAILABLE 1
1906#undef MAINSTACKADDR_AVAILABLE
1907#define MAINSTACKADDR_AVAILABLE 1
1908void *pthread_get_stackaddr_np(pthread_t);
1909size_t pthread_get_stacksize_np(pthread_t);
1910#elif defined HAVE_THR_STKSEGMENT || defined HAVE_PTHREAD_STACKSEG_NP
1911#define STACKADDR_AVAILABLE 1
1912#elif defined HAVE_PTHREAD_GETTHRDS_NP
1913#define STACKADDR_AVAILABLE 1
1914#elif defined __HAIKU__
1915#define STACKADDR_AVAILABLE 1
1918#ifndef MAINSTACKADDR_AVAILABLE
1919# ifdef STACKADDR_AVAILABLE
1920# define MAINSTACKADDR_AVAILABLE 1
1922# define MAINSTACKADDR_AVAILABLE 0
1925#if MAINSTACKADDR_AVAILABLE && !defined(get_main_stack)
1926# define get_main_stack(addr, size) get_stack(addr, size)
1929#ifdef STACKADDR_AVAILABLE
1934get_stack(
void **addr,
size_t *size)
1936#define CHECK_ERR(expr) \
1937 {int err = (expr); if (err) return err;}
1938#ifdef HAVE_PTHREAD_GETATTR_NP
1939 pthread_attr_t attr;
1941 STACK_GROW_DIR_DETECTION;
1942 CHECK_ERR(pthread_getattr_np(pthread_self(), &attr));
1943# ifdef HAVE_PTHREAD_ATTR_GETSTACK
1944 CHECK_ERR(pthread_attr_getstack(&attr, addr, size));
1945 STACK_DIR_UPPER((
void)0, (
void)(*addr = (
char *)*addr + *size));
1947 CHECK_ERR(pthread_attr_getstackaddr(&attr, addr));
1948 CHECK_ERR(pthread_attr_getstacksize(&attr, size));
1950# ifdef HAVE_PTHREAD_ATTR_GETGUARDSIZE
1951 CHECK_ERR(pthread_attr_getguardsize(&attr, &guard));
1953 guard = getpagesize();
1956 pthread_attr_destroy(&attr);
1957#elif defined HAVE_PTHREAD_ATTR_GET_NP
1958 pthread_attr_t attr;
1959 CHECK_ERR(pthread_attr_init(&attr));
1960 CHECK_ERR(pthread_attr_get_np(pthread_self(), &attr));
1961# ifdef HAVE_PTHREAD_ATTR_GETSTACK
1962 CHECK_ERR(pthread_attr_getstack(&attr, addr, size));
1964 CHECK_ERR(pthread_attr_getstackaddr(&attr, addr));
1965 CHECK_ERR(pthread_attr_getstacksize(&attr, size));
1967 STACK_DIR_UPPER((
void)0, (
void)(*addr = (
char *)*addr + *size));
1968 pthread_attr_destroy(&attr);
1969#elif (defined HAVE_PTHREAD_GET_STACKADDR_NP && defined HAVE_PTHREAD_GET_STACKSIZE_NP)
1970 pthread_t th = pthread_self();
1971 *addr = pthread_get_stackaddr_np(th);
1972 *size = pthread_get_stacksize_np(th);
1973#elif defined HAVE_THR_STKSEGMENT || defined HAVE_PTHREAD_STACKSEG_NP
1975# if defined HAVE_THR_STKSEGMENT
1976 CHECK_ERR(thr_stksegment(&stk));
1978 CHECK_ERR(pthread_stackseg_np(pthread_self(), &stk));
1981 *size = stk.ss_size;
1982#elif defined HAVE_PTHREAD_GETTHRDS_NP
1983 pthread_t th = pthread_self();
1984 struct __pthrdsinfo thinfo;
1986 int regsiz=
sizeof(reg);
1987 CHECK_ERR(pthread_getthrds_np(&th, PTHRDSINFO_QUERY_ALL,
1988 &thinfo,
sizeof(thinfo),
1990 *addr = thinfo.__pi_stackaddr;
1994 *size = thinfo.__pi_stackend - thinfo.__pi_stackaddr;
1995 STACK_DIR_UPPER((
void)0, (
void)(*addr = (
char *)*addr + *size));
1996#elif defined __HAIKU__
1998 STACK_GROW_DIR_DETECTION;
1999 CHECK_ERR(get_thread_info(find_thread(NULL), &info));
2000 *addr = info.stack_base;
2001 *size = (uintptr_t)info.stack_end - (uintptr_t)info.stack_base;
2002 STACK_DIR_UPPER((
void)0, (
void)(*addr = (
char *)*addr + *size));
2004#error STACKADDR_AVAILABLE is defined but not implemented.
2012 rb_nativethread_id_t id;
2013 size_t stack_maxsize;
2015} native_main_thread;
2017#ifdef STACK_END_ADDRESS
2018extern void *STACK_END_ADDRESS;
2022 RUBY_STACK_SPACE_LIMIT = 1024 * 1024,
2023 RUBY_STACK_SPACE_RATIO = 5
2027space_size(
size_t stack_size)
2029 size_t space_size = stack_size / RUBY_STACK_SPACE_RATIO;
2030 if (space_size > RUBY_STACK_SPACE_LIMIT) {
2031 return RUBY_STACK_SPACE_LIMIT;
2039native_thread_init_main_thread_stack(
void *addr)
2041 native_main_thread.id = pthread_self();
2042#ifdef RUBY_ASAN_ENABLED
2043 addr = asan_get_real_stack_addr((
void *)addr);
2046#if MAINSTACKADDR_AVAILABLE
2047 if (native_main_thread.stack_maxsize)
return;
2051 if (get_main_stack(&stackaddr, &size) == 0) {
2052 native_main_thread.stack_maxsize = size;
2053 native_main_thread.stack_start = stackaddr;
2058#ifdef STACK_END_ADDRESS
2059 native_main_thread.stack_start = STACK_END_ADDRESS;
2061 if (!native_main_thread.stack_start ||
2062 STACK_UPPER((
VALUE *)(
void *)&addr,
2063 native_main_thread.stack_start > (
VALUE *)addr,
2064 native_main_thread.stack_start < (
VALUE *)addr)) {
2065 native_main_thread.stack_start = (
VALUE *)addr;
2069#if defined(HAVE_GETRLIMIT)
2070#if defined(PTHREAD_STACK_DEFAULT)
2071# if PTHREAD_STACK_DEFAULT < RUBY_STACK_SPACE*5
2072# error "PTHREAD_STACK_DEFAULT is too small"
2074 size_t size = PTHREAD_STACK_DEFAULT;
2076 size_t size = RUBY_VM_THREAD_VM_STACK_SIZE;
2079 int pagesize = getpagesize();
2081 STACK_GROW_DIR_DETECTION;
2082 if (getrlimit(RLIMIT_STACK, &rlim) == 0) {
2083 size = (size_t)rlim.rlim_cur;
2085 addr = native_main_thread.stack_start;
2086 if (IS_STACK_DIR_UPPER()) {
2087 space = ((size_t)((
char *)addr + size) / pagesize) * pagesize - (size_t)addr;
2090 space = (size_t)addr - ((
size_t)((
char *)addr - size) / pagesize + 1) * pagesize;
2092 native_main_thread.stack_maxsize = space;
2096#if MAINSTACKADDR_AVAILABLE
2103 STACK_GROW_DIR_DETECTION;
2105 if (IS_STACK_DIR_UPPER()) {
2106 start = native_main_thread.stack_start;
2107 end = (
char *)native_main_thread.stack_start + native_main_thread.stack_maxsize;
2110 start = (
char *)native_main_thread.stack_start - native_main_thread.stack_maxsize;
2111 end = native_main_thread.stack_start;
2114 if ((
void *)addr < start || (
void *)addr > end) {
2116 native_main_thread.stack_start = (
VALUE *)addr;
2117 native_main_thread.stack_maxsize = 0;
2122#define CHECK_ERR(expr) \
2123 {int err = (expr); if (err) {rb_bug_errno(#expr, err);}}
2126native_thread_init_stack(rb_thread_t *th,
void *local_in_parent_frame)
2128 rb_nativethread_id_t curr = pthread_self();
2129#ifdef RUBY_ASAN_ENABLED
2130 local_in_parent_frame = asan_get_real_stack_addr(local_in_parent_frame);
2131 th->ec->machine.asan_fake_stack_handle = asan_get_thread_fake_stack_handle();
2134 if (!native_main_thread.id) {
2137 native_thread_init_main_thread_stack(local_in_parent_frame);
2140 if (pthread_equal(curr, native_main_thread.id)) {
2141 th->ec->machine.stack_start = native_main_thread.stack_start;
2142 th->ec->machine.stack_maxsize = native_main_thread.stack_maxsize;
2145#ifdef STACKADDR_AVAILABLE
2146 if (th_has_dedicated_nt(th)) {
2150 if (get_stack(&start, &size) == 0) {
2151 uintptr_t diff = (uintptr_t)start - (uintptr_t)local_in_parent_frame;
2152 th->ec->machine.stack_start = local_in_parent_frame;
2153 th->ec->machine.stack_maxsize = size - diff;
2157 rb_raise(
rb_eNotImpError,
"ruby engine can initialize only in the main thread");
2166 struct rb_native_thread *nt;
2176 pthread_attr_t attr;
2178 const size_t stack_size = nt->vm->default_params.thread_machine_stack_size;
2179 const size_t space = space_size(stack_size);
2181 nt->machine_stack_maxsize = stack_size - space;
2183#ifdef USE_SIGALTSTACK
2184 nt->altstack = rb_allocate_sigaltstack();
2187 CHECK_ERR(pthread_attr_init(&attr));
2189# ifdef PTHREAD_STACK_MIN
2190 RUBY_DEBUG_LOG(
"stack size: %lu", (
unsigned long)stack_size);
2191 CHECK_ERR(pthread_attr_setstacksize(&attr, stack_size));
2194# ifdef HAVE_PTHREAD_ATTR_SETINHERITSCHED
2195 CHECK_ERR(pthread_attr_setinheritsched(&attr, PTHREAD_INHERIT_SCHED));
2197 CHECK_ERR(pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED));
2199 err = pthread_create(&nt->thread_id, &attr, nt_start, nt);
2201 RUBY_DEBUG_LOG(
"nt:%d err:%d", (
int)nt->serial, err);
2203 CHECK_ERR(pthread_attr_destroy(&attr));
2214 if (&nt->cond.readyq != &nt->cond.intr) {
2223#ifdef RB_THREAD_T_HAS_NATIVE_ID
2224 nt->tid = get_native_thread_id();
2228 RB_ALTSTACK_INIT(nt->altstack, nt->altstack);
2232native_thread_alloc(
void)
2235 native_thread_setup(nt);
2241#if USE_RUBY_DEBUG_LOG
2249native_thread_create_dedicated(rb_thread_t *th)
2251 th->nt = native_thread_alloc();
2252 th->nt->vm = th->vm;
2253 th->nt->running_thread = th;
2254 th->nt->dedicated = 1;
2257 size_t vm_stack_word_size = th->vm->default_params.thread_vm_stack_size /
sizeof(
VALUE);
2259 th->sched.malloc_stack =
true;
2260 rb_ec_initialize_vm_stack(th->ec, vm_stack, vm_stack_word_size);
2261 th->sched.context_stack = vm_stack;
2264 int err = native_thread_create0(th->nt);
2267 thread_sched_to_ready(TH_SCHED(th), th);
2273call_thread_start_func_2(rb_thread_t *th)
2281 VALUE stack_start = 0;
2282 VALUE *stack_start_addr = asan_get_real_stack_addr(&stack_start);
2284 native_thread_init_stack(th, stack_start_addr);
2285 thread_start_func_2(th, th->ec->machine.stack_start);
2292 rb_vm_t *vm = nt->vm;
2294 native_thread_setup_on_thread(nt);
2297#ifdef RB_THREAD_T_HAS_NATIVE_ID
2298 nt->tid = get_native_thread_id();
2301#if USE_RUBY_DEBUG_LOG && defined(RUBY_NT_SERIAL)
2302 ruby_nt_serial = nt->serial;
2305 RUBY_DEBUG_LOG(
"nt:%u", nt->serial);
2307 if (!nt->dedicated) {
2308 coroutine_initialize_main(nt->nt_context);
2312 if (nt->dedicated) {
2314 rb_thread_t *th = nt->running_thread;
2317 RUBY_DEBUG_LOG(
"on dedicated th:%u", rb_th_serial(th));
2318 ruby_thread_set_native(th);
2320 thread_sched_lock(sched, th);
2322 if (sched->running == th) {
2323 thread_sched_add_running_thread(sched, th);
2325 thread_sched_wait_running_turn(sched, th,
false);
2327 thread_sched_unlock(sched, th);
2330 call_thread_start_func_2(th);
2334 RUBY_DEBUG_LOG(
"check next");
2335 rb_ractor_t *r = ractor_sched_deq(vm, NULL);
2340 thread_sched_lock(sched, NULL);
2342 rb_thread_t *next_th = sched->running;
2344 if (next_th && next_th->nt == NULL) {
2345 RUBY_DEBUG_LOG(
"nt:%d next_th:%d", (
int)nt->serial, (
int)next_th->serial);
2346 thread_sched_switch0(nt->nt_context, next_th, nt,
false);
2349 RUBY_DEBUG_LOG(
"no schedulable threads -- next_th:%p", next_th);
2352 thread_sched_unlock(sched, NULL);
2359 if (nt->dedicated) {
2369static int native_thread_create_shared(rb_thread_t *th);
2372static void nt_free_stack(
void *mstack);
2376rb_threadptr_remove(rb_thread_t *th)
2379 if (th->sched.malloc_stack) {
2384 rb_vm_t *vm = th->vm;
2385 th->sched.finished =
false;
2388 ccan_list_add(&vm->ractor.sched.zombie_threads, &th->sched.node.zombie_threads);
2395rb_threadptr_sched_free(rb_thread_t *th)
2398 if (th->sched.malloc_stack) {
2401 native_thread_destroy(th->nt);
2404 nt_free_stack(th->sched.context_stack);
2409 th->sched.context = NULL;
2413 native_thread_destroy(th->nt);
2420rb_thread_sched_mark_zombies(rb_vm_t *vm)
2422 if (!ccan_list_empty(&vm->ractor.sched.zombie_threads)) {
2423 rb_thread_t *zombie_th, *next_zombie_th;
2424 ccan_list_for_each_safe(&vm->ractor.sched.zombie_threads, zombie_th, next_zombie_th, sched.node.zombie_threads) {
2425 if (zombie_th->sched.finished) {
2426 ccan_list_del_init(&zombie_th->sched.node.zombie_threads);
2429 rb_gc_mark(zombie_th->self);
2436native_thread_create(rb_thread_t *th)
2438 VM_ASSERT(th->nt == 0);
2439 RUBY_DEBUG_LOG(
"th:%d has_dnt:%d", th->serial, th->has_dedicated_nt);
2442 if (!th->ractor->threads.sched.enable_mn_threads) {
2443 th->has_dedicated_nt = 1;
2446 if (th->has_dedicated_nt) {
2447 return native_thread_create_dedicated(th);
2450 return native_thread_create_shared(th);
2454#if USE_NATIVE_THREAD_PRIORITY
2457native_thread_apply_priority(rb_thread_t *th)
2459#if defined(_POSIX_PRIORITY_SCHEDULING) && (_POSIX_PRIORITY_SCHEDULING > 0)
2460 struct sched_param sp;
2462 int priority = 0 - th->priority;
2464 pthread_getschedparam(th->nt->thread_id, &policy, &sp);
2465 max = sched_get_priority_max(policy);
2466 min = sched_get_priority_min(policy);
2468 if (min > priority) {
2471 else if (max < priority) {
2475 sp.sched_priority = priority;
2476 pthread_setschedparam(th->nt->thread_id, policy, &sp);
2487 return rb_fd_select(n, readfds, writefds, exceptfds, timeout);
2491ubf_pthread_cond_signal(
void *ptr)
2493 rb_thread_t *th = (rb_thread_t *)ptr;
2494 RUBY_DEBUG_LOG(
"th:%u on nt:%d", rb_th_serial(th), (
int)th->nt->serial);
2499native_cond_sleep(rb_thread_t *th, rb_hrtime_t *rel)
2501 rb_nativethread_lock_t *lock = &th->interrupt_lock;
2502 rb_nativethread_cond_t *cond = &th->nt->cond.intr;
2512 const rb_hrtime_t max = (rb_hrtime_t)100000000 * RB_HRTIME_PER_SEC;
2514 THREAD_BLOCKING_BEGIN(th);
2517 th->unblock.func = ubf_pthread_cond_signal;
2518 th->unblock.arg = th;
2520 if (RUBY_VM_INTERRUPTED(th->ec)) {
2522 RUBY_DEBUG_LOG(
"interrupted before sleep th:%u", rb_th_serial(th));
2535 end = native_cond_timeout(cond, *rel);
2536 native_cond_timedwait(cond, lock, &end);
2539 th->unblock.func = 0;
2543 THREAD_BLOCKING_END(th);
2545 RUBY_DEBUG_LOG(
"done th:%u", rb_th_serial(th));
2549static CCAN_LIST_HEAD(ubf_list_head);
2550static rb_nativethread_lock_t ubf_list_lock = RB_NATIVETHREAD_LOCK_INIT;
2553ubf_list_atfork(
void)
2555 ccan_list_head_init(&ubf_list_head);
2561ubf_list_contain_p(rb_thread_t *th)
2563 rb_thread_t *list_th;
2564 ccan_list_for_each(&ubf_list_head, list_th, sched.node.ubf) {
2565 if (list_th == th)
return true;
2572register_ubf_list(rb_thread_t *th)
2574 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
2575 struct ccan_list_node *node = &th->sched.node.ubf;
2577 VM_ASSERT(th->unblock.func != NULL);
2582 if (ccan_list_empty((
struct ccan_list_head*)node)) {
2583 VM_ASSERT(!ubf_list_contain_p(th));
2584 ccan_list_add(&ubf_list_head, node);
2589 timer_thread_wakeup();
2594unregister_ubf_list(rb_thread_t *th)
2596 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
2597 struct ccan_list_node *node = &th->sched.node.ubf;
2600 VM_ASSERT(th->unblock.func == NULL);
2602 if (!ccan_list_empty((
struct ccan_list_head*)node)) {
2605 VM_ASSERT(ubf_list_contain_p(th));
2606 ccan_list_del_init(node);
2617ubf_wakeup_thread(rb_thread_t *th)
2619 RUBY_DEBUG_LOG(
"th:%u thread_id:%p", rb_th_serial(th), (
void *)th->nt->thread_id);
2621 pthread_kill(th->nt->thread_id, SIGVTALRM);
2625ubf_select(
void *ptr)
2627 rb_thread_t *th = (rb_thread_t *)ptr;
2628 RUBY_DEBUG_LOG(
"wakeup th:%u", rb_th_serial(th));
2629 ubf_wakeup_thread(th);
2630 register_ubf_list(th);
2634ubf_threads_empty(
void)
2636 return ccan_list_empty(&ubf_list_head) != 0;
2640ubf_wakeup_all_threads(
void)
2645 ccan_list_for_each(&ubf_list_head, th, sched.node.ubf) {
2646 ubf_wakeup_thread(th);
2653#define register_ubf_list(th) (void)(th)
2654#define unregister_ubf_list(th) (void)(th)
2656static void ubf_wakeup_all_threads(
void) {
return; }
2657static bool ubf_threads_empty(
void) {
return true; }
2658#define ubf_list_atfork() do {} while (0)
2662#define WRITE_CONST(fd, str) (void)(write((fd),(str),sizeof(str)-1)<0)
2665rb_thread_wakeup_timer_thread(
int sig)
2671 timer_thread_wakeup_force();
2675 rb_vm_t *vm = GET_VM();
2676 rb_thread_t *main_th = vm->ractor.main_thread;
2679 volatile rb_execution_context_t *main_th_ec = ACCESS_ONCE(rb_execution_context_t *, main_th->ec);
2682 RUBY_VM_SET_TRAP_INTERRUPT(main_th_ec);
2684 if (vm->ubf_async_safe && main_th->unblock.func) {
2685 (main_th->unblock.func)(main_th->unblock.arg);
2692#define CLOSE_INVALIDATE_PAIR(expr) \
2693 close_invalidate_pair(expr,"close_invalidate: "#expr)
2695close_invalidate(
int *fdp,
const char *msg)
2700 if (close(fd) < 0) {
2701 async_bug_fd(msg,
errno, fd);
2706close_invalidate_pair(
int fds[2],
const char *msg)
2708 if (USE_EVENTFD && fds[0] == fds[1]) {
2710 close_invalidate(&fds[0], msg);
2713 close_invalidate(&fds[1], msg);
2714 close_invalidate(&fds[0], msg);
2724 oflags = fcntl(fd, F_GETFL);
2727 oflags |= O_NONBLOCK;
2728 err = fcntl(fd, F_SETFL, oflags);
2735setup_communication_pipe_internal(
int pipes[2])
2739 if (pipes[0] > 0 || pipes[1] > 0) {
2740 VM_ASSERT(pipes[0] > 0);
2741 VM_ASSERT(pipes[1] > 0);
2749#if USE_EVENTFD && defined(EFD_NONBLOCK) && defined(EFD_CLOEXEC)
2750 pipes[0] = pipes[1] = eventfd(0, EFD_NONBLOCK|EFD_CLOEXEC);
2752 if (pipes[0] >= 0) {
2760 rb_bug(
"can not create communication pipe");
2764 set_nonblock(pipes[0]);
2765 set_nonblock(pipes[1]);
2768#if !defined(SET_CURRENT_THREAD_NAME) && defined(__linux__) && defined(PR_SET_NAME)
2769# define SET_CURRENT_THREAD_NAME(name) prctl(PR_SET_NAME, name)
2774#if defined(__linux__)
2776#elif defined(__APPLE__)
2784static VALUE threadptr_invoke_proc_location(rb_thread_t *th);
2787native_set_thread_name(rb_thread_t *th)
2789#ifdef SET_CURRENT_THREAD_NAME
2791 if (!
NIL_P(loc = th->name)) {
2792 SET_CURRENT_THREAD_NAME(RSTRING_PTR(loc));
2794 else if ((loc = threadptr_invoke_proc_location(th)) !=
Qnil) {
2796 char buf[THREAD_NAME_MAX];
2801 p = strrchr(name,
'/');
2809 if (
len >=
sizeof(buf)) {
2810 buf[
sizeof(buf)-2] =
'*';
2811 buf[
sizeof(buf)-1] =
'\0';
2813 SET_CURRENT_THREAD_NAME(buf);
2819native_set_another_thread_name(rb_nativethread_id_t thread_id,
VALUE name)
2821#if defined SET_ANOTHER_THREAD_NAME || defined SET_CURRENT_THREAD_NAME
2822 char buf[THREAD_NAME_MAX];
2824# if !defined SET_ANOTHER_THREAD_NAME
2825 if (!pthread_equal(pthread_self(), thread_id))
return;
2830 if (n >= (
int)
sizeof(buf)) {
2831 memcpy(buf, s,
sizeof(buf)-1);
2832 buf[
sizeof(buf)-1] =
'\0';
2836# if defined SET_ANOTHER_THREAD_NAME
2837 SET_ANOTHER_THREAD_NAME(thread_id, s);
2838# elif defined SET_CURRENT_THREAD_NAME
2839 SET_CURRENT_THREAD_NAME(s);
2844#if defined(RB_THREAD_T_HAS_NATIVE_ID) || defined(__APPLE__)
2846native_thread_native_thread_id(rb_thread_t *target_th)
2848 if (!target_th->nt)
return Qnil;
2850#ifdef RB_THREAD_T_HAS_NATIVE_ID
2851 int tid = target_th->nt->tid;
2852 if (tid == 0)
return Qnil;
2854#elif defined(__APPLE__)
2860# if (!defined(MAC_OS_X_VERSION_10_6) || \
2861 (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6) || \
2862 defined(__POWERPC__) )
2863 const bool no_pthread_threadid_np =
true;
2864# define NO_PTHREAD_MACH_THREAD_NP 1
2865# elif MAC_OS_X_VERSION_MIN_REQUIRED >= MAC_OS_X_VERSION_10_6
2866 const bool no_pthread_threadid_np =
false;
2868# if !(defined(__has_attribute) && __has_attribute(availability))
2870 __attribute__((weak))
int pthread_threadid_np(pthread_t, uint64_t*);
2873 const bool no_pthread_threadid_np = !&pthread_threadid_np;
2875 if (no_pthread_threadid_np) {
2876 return ULL2NUM(pthread_mach_thread_np(pthread_self()));
2878# ifndef NO_PTHREAD_MACH_THREAD_NP
2879 int e = pthread_threadid_np(target_th->nt->thread_id, &tid);
2881 return ULL2NUM((
unsigned long long)tid);
2885# define USE_NATIVE_THREAD_NATIVE_THREAD_ID 1
2887# define USE_NATIVE_THREAD_NATIVE_THREAD_ID 0
2891 rb_serial_t created_fork_gen;
2892 pthread_t pthread_id;
2896#if (HAVE_SYS_EPOLL_H || HAVE_SYS_EVENT_H) && USE_MN_THREADS
2899#if HAVE_SYS_EPOLL_H && USE_MN_THREADS
2900#define EPOLL_EVENTS_MAX 0x10
2901 struct epoll_event finished_events[EPOLL_EVENTS_MAX];
2902#elif HAVE_SYS_EVENT_H && USE_MN_THREADS
2903#define KQUEUE_EVENTS_MAX 0x10
2904 struct kevent finished_events[KQUEUE_EVENTS_MAX];
2908 struct ccan_list_head waiting;
2909 pthread_mutex_t waiting_lock;
2911 .created_fork_gen = 0,
2914#define TIMER_THREAD_CREATED_P() (timer_th.created_fork_gen == current_fork_gen)
2916static void timer_thread_check_timeslice(rb_vm_t *vm);
2917static int timer_thread_set_timeout(rb_vm_t *vm);
2918static void timer_thread_wakeup_thread(rb_thread_t *th, uint32_t event_serial);
2920#include "thread_pthread_mn.c"
2926 return (rb_thread_t *)((size_t)w - offsetof(rb_thread_t, sched.waiting_reason));
2934timer_thread_set_timeout(rb_vm_t *vm)
2941 ractor_sched_lock(vm, NULL);
2943 if ( !ccan_list_empty(&vm->ractor.sched.timeslice_threads)
2944 || !ubf_threads_empty()
2945 || vm->ractor.sched.grq_cnt > 0
2948 RUBY_DEBUG_LOG(
"timeslice:%d ubf:%d grq:%d",
2949 !ccan_list_empty(&vm->ractor.sched.timeslice_threads),
2950 !ubf_threads_empty(),
2951 (vm->ractor.sched.grq_cnt > 0));
2954 vm->ractor.sched.timeslice_wait_inf =
false;
2957 vm->ractor.sched.timeslice_wait_inf =
true;
2960 ractor_sched_unlock(vm, NULL);
2967 rb_thread_t *th = thread_sched_waiting_thread(w);
2969 if (th && (th->sched.waiting_reason.flags & thread_sched_waiting_timeout)) {
2970 rb_hrtime_t now = rb_hrtime_now();
2971 rb_hrtime_t hrrel = rb_hrtime_sub(th->sched.waiting_reason.data.timeout, now);
2973 RUBY_DEBUG_LOG(
"th:%u now:%lu rel:%lu", rb_th_serial(th), (
unsigned long)now, (
unsigned long)hrrel);
2976 int thread_timeout = (int)((hrrel + RB_HRTIME_PER_MSEC - 1) / RB_HRTIME_PER_MSEC);
2979 if (timeout < 0 || thread_timeout < timeout) {
2980 timeout = thread_timeout;
2986 RUBY_DEBUG_LOG(
"timeout:%d inf:%d", timeout, (
int)vm->ractor.sched.timeslice_wait_inf);
2994timer_thread_check_signal(rb_vm_t *vm)
2998 int signum = rb_signal_buff_size();
2999 if (UNLIKELY(signum > 0) && vm->ractor.main_thread) {
3000 RUBY_DEBUG_LOG(
"signum:%d", signum);
3001 threadptr_trap_interrupt(vm->ractor.main_thread);
3006timer_thread_check_exceed(rb_hrtime_t abs, rb_hrtime_t now)
3011 else if (abs - now < RB_HRTIME_PER_MSEC) {
3020timer_thread_deq_wakeup(rb_vm_t *vm, rb_hrtime_t now, uint32_t *event_serial)
3025 (w->flags & thread_sched_waiting_timeout) &&
3026 timer_thread_check_exceed(w->data.timeout, now)) {
3028 RUBY_DEBUG_LOG(
"wakeup th:%u", rb_th_serial(thread_sched_waiting_thread(w)));
3031 ccan_list_del_init(&w->node);
3034 w->flags = thread_sched_waiting_none;
3037 rb_thread_t *th = thread_sched_waiting_thread(w);
3038 *event_serial = w->data.event_serial;
3046timer_thread_wakeup_thread_locked(
struct rb_thread_sched *sched, rb_thread_t *th, uint32_t event_serial)
3048 if (sched->running != th && th->sched.event_serial == event_serial) {
3049 thread_sched_to_ready_common(sched, th,
true,
false);
3054timer_thread_wakeup_thread(rb_thread_t *th, uint32_t event_serial)
3056 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
3059 thread_sched_lock(sched, th);
3061 timer_thread_wakeup_thread_locked(sched, th, event_serial);
3063 thread_sched_unlock(sched, th);
3067timer_thread_check_timeout(rb_vm_t *vm)
3069 rb_hrtime_t now = rb_hrtime_now();
3071 uint32_t event_serial;
3075 while ((th = timer_thread_deq_wakeup(vm, now, &event_serial)) != NULL) {
3077 timer_thread_wakeup_thread(th, event_serial);
3085timer_thread_check_timeslice(rb_vm_t *vm)
3089 ccan_list_for_each(&vm->ractor.sched.timeslice_threads, th, sched.node.timeslice_threads) {
3090 RUBY_DEBUG_LOG(
"timeslice th:%u", rb_th_serial(th));
3091 RUBY_VM_SET_TIMER_INTERRUPT(th->ec);
3099 pthread_sigmask(0, NULL, &oldmask);
3100 if (sigismember(&oldmask, SIGVTALRM)) {
3104 RUBY_DEBUG_LOG(
"ok");
3109timer_thread_func(
void *ptr)
3111 rb_vm_t *vm = (rb_vm_t *)ptr;
3112#if defined(RUBY_NT_SERIAL)
3116 RUBY_DEBUG_LOG(
"started%s",
"");
3119 timer_thread_check_signal(vm);
3120 timer_thread_check_timeout(vm);
3121 ubf_wakeup_all_threads();
3124 timer_thread_polling(vm);
3127 RUBY_DEBUG_LOG(
"terminated");
3133signal_communication_pipe(
int fd)
3136 const uint64_t buff = 1;
3138 const char buff =
'!';
3145 if ((result = write(fd, &buff,
sizeof(buff))) <= 0) {
3148 case EINTR:
goto retry;
3150#if defined(EWOULDBLOCK) && EWOULDBLOCK != EAGAIN
3155 async_bug_fd(
"rb_thread_wakeup_timer_thread: write", e, fd);
3158 if (TT_DEBUG) WRITE_CONST(2,
"rb_thread_wakeup_timer_thread: write\n");
3166timer_thread_wakeup_force(
void)
3169 signal_communication_pipe(timer_th.comm_fds[1]);
3173timer_thread_wakeup_locked(rb_vm_t *vm)
3176 ASSERT_ractor_sched_locked(vm, NULL);
3178 if (timer_th.created_fork_gen == current_fork_gen) {
3179 if (vm->ractor.sched.timeslice_wait_inf) {
3180 RUBY_DEBUG_LOG(
"wakeup with fd:%d", timer_th.comm_fds[1]);
3181 timer_thread_wakeup_force();
3184 RUBY_DEBUG_LOG(
"will be wakeup...");
3190timer_thread_wakeup(
void)
3192 rb_vm_t *vm = GET_VM();
3194 ractor_sched_lock(vm, NULL);
3196 timer_thread_wakeup_locked(vm);
3198 ractor_sched_unlock(vm, NULL);
3202rb_thread_create_timer_thread(
void)
3204 rb_serial_t created_fork_gen = timer_th.created_fork_gen;
3206 RUBY_DEBUG_LOG(
"fork_gen create:%d current:%d", (
int)created_fork_gen, (
int)current_fork_gen);
3208 timer_th.created_fork_gen = current_fork_gen;
3210 if (created_fork_gen != current_fork_gen) {
3211 if (created_fork_gen != 0) {
3212 RUBY_DEBUG_LOG(
"forked child process");
3214 CLOSE_INVALIDATE_PAIR(timer_th.comm_fds);
3215#if HAVE_SYS_EPOLL_H && USE_MN_THREADS
3216 close_invalidate(&timer_th.event_fd,
"close event_fd");
3221 ccan_list_head_init(&timer_th.waiting);
3225 setup_communication_pipe_internal(timer_th.comm_fds);
3228 timer_thread_setup_mn();
3231 pthread_create(&timer_th.pthread_id, NULL, timer_thread_func, GET_VM());
3235native_stop_timer_thread(
void)
3239 RUBY_DEBUG_LOG(
"wakeup send %d", timer_th.comm_fds[1]);
3240 timer_thread_wakeup_force();
3241 RUBY_DEBUG_LOG(
"wakeup sent");
3242 pthread_join(timer_th.pthread_id, NULL);
3244 if (TT_DEBUG) fprintf(stderr,
"stop timer thread\n");
3250native_reset_timer_thread(
void)
3255#ifdef HAVE_SIGALTSTACK
3257ruby_stack_overflowed_p(
const rb_thread_t *th,
const void *addr)
3261 const size_t water_mark = 1024 * 1024;
3262 STACK_GROW_DIR_DETECTION;
3265 size = th->ec->machine.stack_maxsize;
3266 base = (
char *)th->ec->machine.stack_start - STACK_DIR_UPPER(0, size);
3268#ifdef STACKADDR_AVAILABLE
3269 else if (get_stack(&base, &size) == 0) {
3271 if (pthread_equal(th->nt->thread_id, native_main_thread.id)) {
3273 if (getrlimit(RLIMIT_STACK, &rlim) == 0 && rlim.rlim_cur > size) {
3274 size = (size_t)rlim.rlim_cur;
3278 base = (
char *)base + STACK_DIR_UPPER(+size, -size);
3285 size /= RUBY_STACK_SPACE_RATIO;
3286 if (size > water_mark) size = water_mark;
3287 if (IS_STACK_DIR_UPPER()) {
3288 if (size > ~(
size_t)base+1) size = ~(size_t)base+1;
3289 if (addr > base && addr <= (
void *)((
char *)base + size))
return 1;
3292 if (size > (
size_t)base) size = (size_t)base;
3293 if (addr > (
void *)((
char *)base - size) && addr <= base)
return 1;
3303 if (fd < 0)
return 0;
3305 if (fd == timer_th.comm_fds[0] ||
3306 fd == timer_th.comm_fds[1]
3307#if (HAVE_SYS_EPOLL_H || HAVE_SYS_EVENT_H) && USE_MN_THREADS
3308 || fd == timer_th.event_fd
3311 goto check_fork_gen;
3316 if (timer_th.created_fork_gen == current_fork_gen) {
3328 return pthread_self();
3331#if defined(USE_POLL) && !defined(HAVE_PPOLL)
3334ruby_ppoll(
struct pollfd *fds, nfds_t nfds,
3335 const struct timespec *ts,
const sigset_t *sigmask)
3342 if (ts->tv_sec > INT_MAX/1000)
3343 timeout_ms = INT_MAX;
3345 tmp = (int)(ts->tv_sec * 1000);
3347 tmp2 = (int)((ts->tv_nsec + 999999L) / (1000L * 1000L));
3348 if (INT_MAX - tmp < tmp2)
3349 timeout_ms = INT_MAX;
3351 timeout_ms = (int)(tmp + tmp2);
3357 return poll(fds, nfds, timeout_ms);
3359# define ppoll(fds,nfds,ts,sigmask) ruby_ppoll((fds),(nfds),(ts),(sigmask))
3372#define THREAD_BLOCKING_YIELD(th) do { \
3373 const rb_thread_t *next_th; \
3374 struct rb_thread_sched *sched = TH_SCHED(th); \
3375 RB_VM_SAVE_MACHINE_CONTEXT(th); \
3376 thread_sched_to_waiting(sched, (th)); \
3377 next_th = sched->running; \
3378 rb_native_mutex_unlock(&sched->lock_); \
3379 native_thread_yield(); \
3380 if (!next_th && rb_ractor_living_thread_num(th->ractor) > 1) { \
3381 native_thread_yield(); \
3385native_sleep(rb_thread_t *th, rb_hrtime_t *rel)
3389 RUBY_DEBUG_LOG(
"rel:%d", rel ? (
int)*rel : 0);
3391 if (th_has_dedicated_nt(th)) {
3392 native_cond_sleep(th, rel);
3395 thread_sched_wait_events(sched, th, -1, thread_sched_waiting_timeout, rel);
3399 thread_sched_to_waiting_until_wakeup(sched, th);
3402 RUBY_DEBUG_LOG(
"wakeup");
3406static pthread_rwlock_t rb_thread_fork_rw_lock = PTHREAD_RWLOCK_INITIALIZER;
3409rb_thread_release_fork_lock(
void)
3412 if ((r = pthread_rwlock_unlock(&rb_thread_fork_rw_lock))) {
3418rb_thread_reset_fork_lock(
void)
3421 if ((r = pthread_rwlock_destroy(&rb_thread_fork_rw_lock))) {
3425 if ((r = pthread_rwlock_init(&rb_thread_fork_rw_lock, NULL))) {
3431rb_thread_prevent_fork(
void *(*func)(
void *),
void *data)
3434 if ((r = pthread_rwlock_rdlock(&rb_thread_fork_rw_lock))) {
3437 void *result = func(data);
3438 rb_thread_release_fork_lock();
3443rb_thread_acquire_fork_lock(
void)
3446 if ((r = pthread_rwlock_wrlock(&rb_thread_fork_rw_lock))) {
3453struct rb_internal_thread_event_hook {
3454 rb_internal_thread_event_callback callback;
3458 struct rb_internal_thread_event_hook *next;
3461static pthread_rwlock_t rb_internal_thread_event_hooks_rw_lock = PTHREAD_RWLOCK_INITIALIZER;
3463#if defined(HAVE_WORKING_FORK)
3465rb_internal_thread_event_hooks_rw_lock_atfork(
void)
3474 rb_internal_thread_event_hooks_rw_lock =
3475 (pthread_rwlock_t)PTHREAD_RWLOCK_INITIALIZER;
3479rb_internal_thread_event_hook_t *
3482 rb_internal_thread_event_hook_t *hook =
ALLOC_N(rb_internal_thread_event_hook_t, 1);
3483 hook->callback = callback;
3484 hook->user_data = user_data;
3485 hook->event = internal_event;
3488 if ((r = pthread_rwlock_wrlock(&rb_internal_thread_event_hooks_rw_lock))) {
3492 hook->next = rb_internal_thread_event_hooks;
3493 ATOMIC_PTR_EXCHANGE(rb_internal_thread_event_hooks, hook);
3495 if ((r = pthread_rwlock_unlock(&rb_internal_thread_event_hooks_rw_lock))) {
3505 if ((r = pthread_rwlock_wrlock(&rb_internal_thread_event_hooks_rw_lock))) {
3509 bool success = FALSE;
3511 if (rb_internal_thread_event_hooks == hook) {
3512 ATOMIC_PTR_EXCHANGE(rb_internal_thread_event_hooks, hook->next);
3516 rb_internal_thread_event_hook_t *h = rb_internal_thread_event_hooks;
3519 if (h->next == hook) {
3520 h->next = hook->next;
3524 }
while ((h = h->next));
3527 if ((r = pthread_rwlock_unlock(&rb_internal_thread_event_hooks_rw_lock))) {
3541 if ((r = pthread_rwlock_rdlock(&rb_internal_thread_event_hooks_rw_lock))) {
3545 if (rb_internal_thread_event_hooks) {
3546 rb_internal_thread_event_hook_t *h = rb_internal_thread_event_hooks;
3548 if (h->event & event) {
3549 rb_internal_thread_event_data_t event_data = {
3552 (*h->callback)(event, &event_data, h->user_data);
3554 }
while((h = h->next));
3556 if ((r = pthread_rwlock_unlock(&rb_internal_thread_event_hooks_rw_lock))) {
3566 rb_thread_t *th = GET_THREAD();
3567 bool is_snt = th->nt->dedicated == 0;
3568 native_thread_dedicated_inc(th->vm, th->ractor, th->nt);
3574rb_thread_malloc_stack_set(rb_thread_t *th,
void *stack)
3576 th->sched.malloc_stack =
true;
3577 th->sched.context_stack = stack;
std::atomic< unsigned > rb_atomic_t
Type that is eligible for atomic operations.
#define RUBY_ATOMIC_FETCH_ADD(var, val)
Atomically replaces the value pointed by var with the result of addition of val to the old value of v...
#define RUBY_ATOMIC_LOAD(var)
Atomic load.
#define RUBY_ATOMIC_SET(var, val)
Identical to RUBY_ATOMIC_EXCHANGE, except for the return type.
uint32_t rb_event_flag_t
Represents event(s).
#define INT2FIX
Old name of RB_INT2FIX.
#define ZALLOC
Old name of RB_ZALLOC.
#define ALLOC_N
Old name of RB_ALLOC_N.
#define ULL2NUM
Old name of RB_ULL2NUM.
#define NUM2INT
Old name of RB_NUM2INT.
#define Qnil
Old name of RUBY_Qnil.
#define NIL_P
Old name of RB_NIL_P.
VALUE rb_eNotImpError
NotImplementedError exception.
void rb_syserr_fail(int e, const char *mesg)
Raises appropriate exception that represents a C errno.
void rb_bug_errno(const char *mesg, int errno_arg)
This is a wrapper of rb_bug() which automatically constructs appropriate message from the passed errn...
int rb_cloexec_pipe(int fildes[2])
Opens a pipe with closing on exec.
void rb_update_max_fd(int fd)
Informs the interpreter that the passed fd can be the max.
int rb_reserved_fd_p(int fd)
Queries if the given FD is reserved or not.
void rb_unblock_function_t(void *)
This is the type of UBFs.
void rb_timespec_now(struct timespec *ts)
Fills the current time into the given struct.
int len
Length of the buffer.
#define RUBY_INTERNAL_THREAD_EVENT_RESUMED
Triggered when a thread successfully acquired the GVL.
rb_internal_thread_event_hook_t * rb_internal_thread_add_event_hook(rb_internal_thread_event_callback func, rb_event_flag_t events, void *data)
Registers a thread event hook function.
#define RUBY_INTERNAL_THREAD_EVENT_EXITED
Triggered when a thread exits.
#define RUBY_INTERNAL_THREAD_EVENT_SUSPENDED
Triggered when a thread released the GVL.
bool rb_thread_lock_native_thread(void)
Declare the current Ruby thread should acquire a dedicated native thread on M:N thread scheduler.
#define RUBY_INTERNAL_THREAD_EVENT_STARTED
Triggered when a new thread is started.
bool rb_internal_thread_remove_event_hook(rb_internal_thread_event_hook_t *hook)
Unregister the passed hook.
#define RUBY_INTERNAL_THREAD_EVENT_READY
Triggered when a thread attempt to acquire the GVL.
#define RBIMPL_ATTR_MAYBE_UNUSED()
Wraps (or simulates) [[maybe_unused]].
#define RB_GC_GUARD(v)
Prevents premature destruction of local objects.
#define rb_fd_select
Waits for multiple file descriptors at once.
#define RARRAY_AREF(a, i)
#define RSTRING_GETMEM(str, ptrvar, lenvar)
Convenient macro to obtain the contents and length at once.
#define errno
Ractor-aware version of errno.
The data structure which wraps the fd_set bitmap used by select(2).
rb_nativethread_id_t rb_nativethread_self(void)
Queries the ID of the native thread that is calling this function.
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_lock.
void rb_native_cond_initialize(rb_nativethread_cond_t *cond)
Fills the passed condition variable with an initial value.
int rb_native_mutex_trylock(rb_nativethread_lock_t *lock)
Identical to rb_native_mutex_lock(), except it doesn't block in case rb_native_mutex_lock() would.
void rb_native_cond_broadcast(rb_nativethread_cond_t *cond)
Signals a condition variable.
void rb_native_mutex_initialize(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_initialize.
void rb_native_mutex_unlock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_unlock.
void rb_native_mutex_destroy(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_destroy.
void rb_native_cond_destroy(rb_nativethread_cond_t *cond)
Destroys the passed condition variable.
void rb_native_cond_signal(rb_nativethread_cond_t *cond)
Signals a condition variable.
void rb_native_cond_wait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex)
Waits for the passed condition variable to be signalled.
void rb_native_cond_timedwait(rb_nativethread_cond_t *cond, rb_nativethread_lock_t *mutex, unsigned long msec)
Identical to rb_native_cond_wait(), except it additionally takes timeout in msec resolution.
uintptr_t VALUE
Type that represents a Ruby object.
void ruby_xfree(void *ptr)
Deallocates a storage instance.
void * ruby_xmalloc(size_t size)
Allocates a storage instance.