32#define thread_sched_dump(s) thread_sched_dump_(__FILE__, __LINE__, s)
33#define ractor_sched_dump(s) ractor_sched_dump_(__FILE__, __LINE__, s)
35#define thread_sched_lock(a, b) thread_sched_lock_(a, b, __FILE__, __LINE__)
36#define thread_sched_unlock(a, b) thread_sched_unlock_(a, b, __FILE__, __LINE__)
37#define ractor_sched_lock(a, b) ractor_sched_lock_(a, b, __FILE__, __LINE__)
38#define ractor_sched_unlock(a, b) ractor_sched_unlock_(a, b, __FILE__, __LINE__)
69static void thread_sched_wait_running_turn(
struct rb_thread_sched *sched,
rb_thread_t *th,
bool can_direct_transfer,
const rb_hrtime_t *end);
78static void ractor_sched_lock_(
rb_vm_t *vm,
rb_ractor_t *cr,
const char *file,
int line);
79static void ractor_sched_unlock_(
rb_vm_t *vm,
rb_ractor_t *cr,
const char *file,
int line);
84static void thread_sched_init_vm(
rb_vm_t *vm);
87static bool ractor_sched_timeout_arm(
rb_thread_t *th,
const rb_hrtime_t *rel);
88static bool ractor_sched_timeout_disarm(
rb_thread_t *th);
93static void ubf_clear(
rb_thread_t *th,
bool clear_serial);
96static void call_thread_start_func_2(
rb_thread_t *th);
97static void *nt_start(
void *ptr);
100static void *timer_thread_func(
void *ptr);
101static int timer_thread_set_timeout(
rb_vm_t *vm);
102static void timer_thread_check_timeslice(
rb_vm_t *vm);
103static bool timeslice_scan(
rb_vm_t *vm,
bool interrupt);
104static void timer_thread_wakeup(
void);
105static void timer_thread_wakeup_locked(
rb_vm_t *vm);
106static void timer_thread_wakeup_force(
void);
110static int mn_threads_mode = 0;
115static bool native_thread_self_can_retire_p(
void);
117#include THREAD_IMPL_SRC
120static void thread_sched_main_to_shared(
rb_thread_t *th);
125#ifndef RB_NATIVE_MUTEX_TRYLOCK_DETECTS_SELF
129#define RB_NATIVE_MUTEX_TRYLOCK_DETECTS_SELF 1
140 return th->nt->dedicated > 0;
145thread_sched_dump_(const
char *file,
int line, struct
rb_thread_sched *sched)
147 fprintf(stderr,
"@%s:%d running:%d\n", file, line, sched->running ? (
int)sched->running->serial : -1);
150 ccan_list_for_each(&sched->readyq, th, sched.node.readyq) {
151 i++;
if (i>10) rb_bug(
"too many");
152 fprintf(stderr,
" ready:%d (%sNT:%d)\n", th->serial,
153 th->nt ? (th->nt->dedicated ?
"D" :
"S") :
"x",
154 th->nt ? (int)th->nt->serial : -1);
161ractor_sched_dump_(const
char *file,
int line,
rb_vm_t *vm)
165 fprintf(stderr,
"ractor_sched_dump %s:%d\n", file, line);
168 ccan_list_for_each(&vm->ractor.sched.grq, r, threads.sched.grq_node) {
170 if (i>10) rb_bug(
"!!");
171 fprintf(stderr,
" %d ready:%"PRI_SERIALT_PREFIX
"u\n", i, rb_ractor_id(r));
180 VM_ASSERT(sched->lock_owner == NULL);
182 sched->lock_owner = th;
190 VM_ASSERT(sched->lock_owner == th);
192 sched->lock_owner = NULL;
202 RUBY_DEBUG_LOG2(file, line,
"r:%d th:%u", th ? (
int)rb_ractor_id(th->ractor) : -1, rb_th_serial(th));
204 RUBY_DEBUG_LOG2(file, line,
"th:%u", rb_th_serial(th));
207 thread_sched_set_locked(sched, th);
213 RUBY_DEBUG_LOG2(file, line,
"th:%u", rb_th_serial(th));
215 thread_sched_set_unlocked(sched, th);
227 thread_sched_set_unlocked(sched, th);
235#if RB_NATIVE_MUTEX_TRYLOCK_DETECTS_SELF
241 VM_ASSERT(sched->lock_owner == th);
244 VM_ASSERT(sched->lock_owner != NULL);
255 return rb_ractor_id(r);
266 VM_ASSERT(vm->ractor.sched.lock_owner == NULL);
267 VM_ASSERT(vm->ractor.sched.locked ==
false);
269 vm->ractor.sched.lock_owner = cr;
270 vm->ractor.sched.locked =
true;
278 VM_ASSERT(vm->ractor.sched.locked);
279 VM_ASSERT(vm->ractor.sched.lock_owner == cr);
281 vm->ractor.sched.locked =
false;
282 vm->ractor.sched.lock_owner = NULL;
293 RUBY_DEBUG_LOG2(file, line,
"cr:%"PRI_SERIALT_PREFIX
"u prev_owner:%"PRI_SERIALT_PREFIX
"u", rb_ractor_serial(cr), rb_ractor_serial(vm->ractor.sched.lock_owner));
295 RUBY_DEBUG_LOG2(file, line,
"cr:%"PRI_SERIALT_PREFIX
"u", rb_ractor_serial(cr));
298 ractor_sched_set_locked(vm, cr);
304 RUBY_DEBUG_LOG2(file, line,
"cr:%"PRI_SERIALT_PREFIX
"u", rb_ractor_serial(cr));
306 ractor_sched_set_unlocked(vm, cr);
313#if RB_NATIVE_MUTEX_TRYLOCK_DETECTS_SELF
316 VM_ASSERT(vm->ractor.sched.locked);
317 VM_ASSERT(cr == NULL || vm->ractor.sched.lock_owner == cr);
320static void ractor_sched_barrier_join_signal_locked(
rb_vm_t *vm);
332 if (nt != NULL && nt->dedicated == 0) {
335 VM_ASSERT(nt->running_th == NULL);
345 nt->running_thread = th;
346 ccan_list_add(&vm->ractor.sched.ntlist.running_dnts, &nt->running_dnts_node);
368 in_running_th = (nt->running_th == th);
370 nt->running_th = NULL;
371 serial = vm->ractor.sched.barrier_serial;
372 counted = (nt->barrier_counted_serial == serial);
373 nt->barrier_counted_serial = serial - 1;
378 if (!in_running_th) {
381 ccan_list_del_init(&nt->running_dnts_node);
382 serial = vm->ractor.sched.barrier_serial;
383 counted = (nt->barrier_counted_serial == serial);
384 nt->barrier_counted_serial = serial - 1;
399 uint32_t serial = vm->ractor.sched.barrier_serial;
405 in_running_th = (nt->running_th == th);
407 stamped = (nt->barrier_counted_serial != serial);
408 nt->barrier_counted_serial = serial;
413 if (!in_running_th) {
416 stamped = (nt->barrier_counted_serial != serial);
417 nt->barrier_counted_serial = serial;
435 if (sched->timeslice_node.next == &sched->timeslice_node) {
436 ccan_list_add_tail(&vm->ractor.sched.timeslice.scheds, &sched->timeslice_node);
446 RUBY_DEBUG_LOG(
"+:%u -:%u", rb_th_serial(add_th), rb_th_serial(del_th));
449 bool counted = ntlist_del_running(vm, del_th);
450 sched->is_running =
false;
455 ractor_sched_lock(vm, cr);
459 VM_ASSERT(vm->ractor.sched.barrier_running_cnt > 0);
460 vm->ractor.sched.barrier_running_cnt--;
462 ractor_sched_barrier_join_signal_locked(vm);
465 ractor_sched_unlock(vm, cr);
470 ntlist_add_running(vm, add_th);
478 ractor_sched_lock(vm, cr);
481 ntlist_stamp_if_uncounted(vm, add_th)) {
483 RUBY_DEBUG_LOG(
"barrier_is_waiting");
484 vm->ractor.sched.barrier_running_cnt++;
485 RUBY_VM_SET_VM_BARRIER_INTERRUPT(add_th->ec);
488 ractor_sched_unlock(vm, cr);
491 sched->is_running =
true;
494 if (!ccan_list_empty(&sched->readyq)) {
495 timeslice_sched_link(vm, sched);
496 ractor_sched_lock(vm, cr);
498 if (vm->ractor.sched.timeslice_wait_inf) {
499 timer_thread_wakeup_locked(vm);
502 ractor_sched_unlock(vm, cr);
510 ASSERT_thread_sched_locked(sched, th);
511 VM_ASSERT(sched->running == th);
514 thread_sched_setup_running_threads(sched, th->ractor, vm, th, NULL);
520 ASSERT_thread_sched_locked(sched, th);
523 thread_sched_setup_running_threads(sched, th->ractor, vm, NULL, th);
531 thread_sched_lock(sched, th);
533 thread_sched_add_running_thread(sched, th);
535 thread_sched_unlock(sched, th);
543 thread_sched_lock(sched, th);
545 thread_sched_del_running_thread(sched, th);
547 thread_sched_unlock(sched, th);
557 RUBY_DEBUG_LOG(
"th:%u->th:%u", rb_th_serial(sched->running), rb_th_serial(th));
558 VM_ASSERT(sched->running != th);
560 if (RUBY_DTRACE_RTS_SET_RUNNING_ENABLED()) {
561 RUBY_DTRACE_RTS_SET_RUNNING(sched, sched->running, th);
572 ccan_list_for_each(&sched->readyq, rth, sched.node.readyq) {
574 VM_ASSERT(th->sched.node.is_ready);
578 VM_ASSERT(!th->sched.node.is_ready);
589 ASSERT_thread_sched_locked(sched, NULL);
592 VM_ASSERT(sched->running != NULL);
594 if (ccan_list_empty(&sched->readyq)) {
598 next_th = ccan_list_pop(&sched->readyq,
rb_thread_t, sched.node.readyq);
599 VM_ASSERT(next_th->sched.node.is_ready);
600 next_th->sched.node.is_ready =
false;
602 VM_ASSERT(sched->readyq_cnt > 0);
604 ccan_list_node_init(&next_th->sched.node.readyq);
607 RUBY_DEBUG_LOG(
"next_th:%u readyq_cnt:%d", rb_th_serial(next_th), sched->readyq_cnt);
616 ASSERT_thread_sched_locked(sched, NULL);
617 RUBY_DEBUG_LOG(
"ready_th:%u readyq_cnt:%d", rb_th_serial(ready_th), sched->readyq_cnt);
619 VM_ASSERT(sched->running != NULL);
620 VM_ASSERT(!thread_sched_readyq_contain_p(sched, ready_th));
622 bool timeslice_onset = sched->is_running && ccan_list_empty(&sched->readyq);
624 ccan_list_add_tail(&sched->readyq, &ready_th->sched.node.readyq);
625 ready_th->sched.node.is_ready =
true;
628 if (timeslice_onset) {
633 timeslice_sched_link(vm, sched);
634 ractor_sched_lock(vm, NULL);
636 if (vm->ractor.sched.timeslice_wait_inf) {
637 timer_thread_wakeup_locked(vm);
640 ractor_sched_unlock(vm, NULL);
649 ASSERT_thread_sched_locked(sched, NULL);
650 VM_ASSERT(sched->running == next_th);
654 if (th_has_dedicated_nt(next_th)) {
655 RUBY_DEBUG_LOG(
"pinning th:%u", next_th->serial);
660 RUBY_DEBUG_LOG(
"th:%u is already running.", next_th->serial);
665 RUBY_DEBUG_LOG(
"th:%u (do nothing)", rb_th_serial(next_th));
668 RUBY_DEBUG_LOG(
"th:%u (enq)", rb_th_serial(next_th));
669 ractor_sched_enq(next_th->vm, next_th->ractor);
674 RUBY_DEBUG_LOG(
"no waiting threads%s",
"");
682 RUBY_DEBUG_LOG(
"th:%u running:%u redyq_cnt:%d", rb_th_serial(th), rb_th_serial(sched->running), sched->readyq_cnt);
684 VM_ASSERT(sched->running != th);
685 VM_ASSERT(!thread_sched_readyq_contain_p(sched, th));
688 if (sched->running == NULL) {
689 thread_sched_set_running(sched, th);
690 if (wakeup) thread_sched_wakeup_running_thread(sched, th, will_switch);
693 thread_sched_enq(sched, th);
705 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
707 thread_sched_lock(sched, th);
709 thread_sched_to_ready_common(sched, th,
true,
false);
711 thread_sched_unlock(sched, th);
716thread_sched_wait_running_turn(
struct rb_thread_sched *sched,
rb_thread_t *th,
bool can_direct_transfer,
const rb_hrtime_t *end)
718 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
720 ASSERT_thread_sched_locked(sched, th);
721 VM_ASSERT(th == rb_ec_thread_ptr(rb_current_ec_noinline()));
723 bool timedout =
false;
725 if (th != sched->running) {
727 if (th->has_dedicated_nt && th == sched->runnable_hot_th && (sched->running == NULL || sched->running->has_dedicated_nt)) {
728 RUBY_DEBUG_LOG(
"(nt) stealing: hot-th:%u. running:%u", rb_th_serial(th), rb_th_serial(sched->running));
734 ractor_sched_cancel_enq(th->vm, sched);
737 if (sched->running != NULL) {
739 VM_ASSERT(!thread_sched_readyq_contain_p(sched, running));
740 running->sched.node.is_ready =
true;
741 ccan_list_add(&sched->readyq, &running->sched.node.readyq);
746 if (th->sched.node.is_ready) {
747 VM_ASSERT(thread_sched_readyq_contain_p(sched, th));
748 ccan_list_del_init(&th->sched.node.readyq);
749 th->sched.node.is_ready =
false;
752 thread_sched_set_running(sched, th);
753 rb_ractor_thread_switch(th->ractor, th,
false);
755 else if (th == sched->runnable_hot_th) {
759 sched->runnable_hot_th = NULL;
760 sched->runnable_hot_th_waiting = 0;
768 while((next_th = sched->running) != th) {
769 if (th_has_dedicated_nt(th)) {
770 RUBY_DEBUG_LOG(
"(nt) sleep th:%u running:%u", rb_th_serial(th), rb_th_serial(sched->running));
772 thread_sched_set_unlocked(sched, th);
774 RUBY_DEBUG_LOG(
"nt:%d cond:%p", th->nt->serial, &th->nt->readyq);
775 rb_nativethread_cond_t *cond = &th->nt->readyq;
783 if (end && !th->sched.node.is_ready) {
784 rb_hrtime_t abs = *end;
786 if (!RB_NATIVE_COND_HRTIME_DEADLINE_P()) {
788 rb_hrtime_t now = rb_hrtime_now();
789 abs = native_cond_timeout(cond, *end > now ? *end - now : 0);
791 timedout = native_cond_timedwait(cond, &sched->lock_, &abs) == ETIMEDOUT;
797 thread_sched_set_locked(sched, th);
800 sched->running != th && !th->sched.node.is_ready) {
803 thread_sched_to_ready_common(sched, th,
false,
false);
807 if (sched->runnable_hot_th != NULL && sched->runnable_hot_th_waiting) {
808 VM_ASSERT(sched->runnable_hot_th != th);
812 thread_sched_unlock(sched, th);
813 thread_sched_lock(sched, th);
816 RUBY_DEBUG_LOG(
"(nt) wakeup %s", sched->running == th ?
"success" :
"failed");
817 if (th == sched->running) {
818 rb_ractor_thread_switch(th->ractor, th,
false);
823 if (can_direct_transfer &&
824 (next_th = sched->running) != NULL &&
828 RUBY_DEBUG_LOG(
"th:%u->%u (direct)", rb_th_serial(th), rb_th_serial(next_th));
830 thread_sched_set_unlocked(sched, th);
832 rb_ractor_set_current_ec(th->ractor, NULL);
833 thread_sched_switch(th, next_th);
835 thread_sched_set_locked(sched, th);
840 native_thread_assign(NULL, th);
842 RUBY_DEBUG_LOG(
"th:%u->%u (ractor scheduling)", rb_th_serial(th), rb_th_serial(next_th));
844 thread_sched_set_unlocked(sched, th);
846 rb_ractor_set_current_ec(th->ractor, NULL);
847 coroutine_transfer0(th->sched.context, nt->nt_context,
false);
849 thread_sched_set_locked(sched, th);
852 VM_ASSERT(rb_current_ec_noinline() == th->ec);
856 VM_ASSERT(th->nt != NULL);
857 VM_ASSERT(rb_current_ec_noinline() == th->ec);
858 VM_ASSERT(th->sched.waiting_reason.flags == thread_sched_waiting_none);
861 thread_sched_add_running_thread(sched, th);
866 sched->runnable_hot_th = NULL;
867 sched->runnable_hot_th_waiting = 0;
870 rb_ractor_thread_switch(th->ractor, th,
false);
880 RUBY_DEBUG_LOG(
"th:%u dedicated:%d", rb_th_serial(th), th_has_dedicated_nt(th));
882 VM_ASSERT(sched->running != th);
883 VM_ASSERT(th_has_dedicated_nt(th));
884 VM_ASSERT(GET_THREAD() == th);
886 native_thread_dedicated_dec(th->vm, th->ractor, th->nt);
889 thread_sched_to_ready_common(sched, th,
false,
false);
891 if (sched->running == th) {
892 thread_sched_add_running_thread(sched, th);
896 thread_sched_wait_running_turn(sched, th,
false, NULL);
912 if (sched->runnable_hot_th == th) {
913 sched->runnable_hot_th_waiting = 1;
915 thread_sched_lock(sched, th);
917 thread_sched_to_running_common(sched, th);
919 thread_sched_unlock(sched, th);
932 ASSERT_thread_sched_locked(sched, th);
934 VM_ASSERT(sched->running == th);
935 VM_ASSERT(sched->running->nt != NULL);
939 RUBY_DEBUG_LOG(
"next_th:%u", rb_th_serial(next_th));
940 VM_ASSERT(th != next_th);
942 thread_sched_set_running(sched, next_th);
943 VM_ASSERT(next_th == sched->running);
944 thread_sched_wakeup_running_thread(sched, next_th, will_switch);
947 thread_sched_del_running_thread(sched, th);
955 RUBY_DEBUG_LOG(
"th:%u DNT:%d", rb_th_serial(th), th->nt->dedicated);
963 thread_sched_wakeup_next_thread(sched, th, !th_has_dedicated_nt(th));
973 timer_thread_wake_fence(th);
975 thread_sched_lock(sched, th);
977 thread_sched_to_dead_common(sched, th);
979 thread_sched_unlock(sched, th);
988 RUBY_DEBUG_LOG(
"th:%u DNT:%d", rb_th_serial(th), th->nt->dedicated);
992 native_thread_dedicated_inc(th->vm, th->ractor, th->nt);
993 if (!yield_immediately) {
994 sched->runnable_hot_th = th;
995 sched->runnable_hot_th_waiting = 0;
997 thread_sched_wakeup_next_thread(sched, th,
false);
1006 thread_sched_lock(sched, th);
1008 thread_sched_to_waiting_common(sched, th, yield_immediately);
1010 thread_sched_unlock(sched, th);
1018 VM_ASSERT(func != NULL);
1021 if (RUBY_VM_INTERRUPTED(th->ec)) {
1022 RUBY_DEBUG_LOG(
"interrupted:0x%x", th->ec->interrupt_flag);
1028 if (!th->ec->raised_flag && RUBY_VM_INTERRUPTED(th->ec)) {
1033 VM_ASSERT(th->unblock.func == NULL);
1034 th->unblock.func = func;
1035 th->unblock.arg = arg;
1038 *event_serial = prev_serial+1;
1051 th->unblock.func = NULL;
1052 th->unblock.arg = NULL;
1061ubf_waiting(
void *ptr)
1067 th->unblock.func = NULL;
1068 th->unblock.arg = NULL;
1070 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
1072 thread_sched_lock(sched, th);
1074 if (sched->running == th || th->sched.node.is_ready) {
1078 thread_sched_to_ready_common(sched, th,
true,
false);
1085 if (sched->running != th && th->sched.waiting_timed &&
1086 th->nt != NULL && th_has_dedicated_nt(th)) {
1091 thread_sched_unlock(sched, th);
1102 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
1104 VM_ASSERT(end == NULL || th_has_dedicated_nt(th));
1106 RB_VM_SAVE_MACHINE_CONTEXT(th);
1111 thread_sched_lock(sched, th);
1114 if (ubf_set(th, ubf_waiting, (
void *)th, NULL)) {
1115 RUBY_DEBUG_LOG(
"th:%u interrupted", rb_th_serial(th));
1118 bool can_direct_transfer = !th_has_dedicated_nt(th);
1119 th->sched.waiting_timed = (end != NULL);
1121 thread_sched_wakeup_next_thread(sched, th, can_direct_transfer);
1122 thread_sched_wait_running_turn(sched, th, can_direct_transfer, end);
1123 th->sched.waiting_timed =
false;
1126 thread_sched_unlock(sched, th);
1128 ubf_clear(th,
false);
1136 RUBY_DEBUG_LOG(
"th:%d sched->readyq_cnt:%d", (
int)th->serial, sched->readyq_cnt);
1138 thread_sched_lock(sched, th);
1140 if (!ccan_list_empty(&sched->readyq)) {
1142 thread_sched_wakeup_next_thread(sched, th, !th_has_dedicated_nt(th));
1143 bool can_direct_transfer = !th_has_dedicated_nt(th);
1144 thread_sched_to_ready_common(sched, th,
false, can_direct_transfer);
1145 thread_sched_wait_running_turn(sched, th, can_direct_transfer, NULL);
1146 th->status = THREAD_RUNNABLE;
1149 VM_ASSERT(sched->readyq_cnt == 0);
1152 thread_sched_unlock(sched, th);
1161 sched->lock_owner = NULL;
1164 ccan_list_head_init(&sched->readyq);
1165 sched->readyq_cnt = 0;
1166 ccan_list_node_init(&sched->grq_node);
1167 ccan_list_node_init(&sched->timeslice_node);
1172 if (!atfork) sched->enable_mn_threads = mn_threads_enabled_p();
1179#ifdef RUBY_ASAN_ENABLED
1180 void **fake_stack = to_dead ? NULL : &transfer_from->fake_stack;
1181 __sanitizer_start_switch_fiber(fake_stack, transfer_to->stack_base, transfer_to->stack_size);
1184#if defined(COROUTINE_SANITIZE_THREAD)
1188 __tsan_switch_to_fiber(transfer_to->tsan_fiber, 0);
1192 struct
coroutine_context *returning_from = coroutine_transfer(transfer_from, transfer_to);
1196 VM_ASSERT(!to_dead);
1197#ifdef RUBY_ASAN_ENABLED
1198 __sanitizer_finish_switch_fiber(transfer_from->fake_stack,
1199 (
const void**)&returning_from->stack_base, &returning_from->stack_size);
1206 VM_ASSERT(!nt->dedicated);
1207 VM_ASSERT(next_th->nt == NULL);
1209 RUBY_DEBUG_LOG(
"next_th:%u", rb_th_serial(next_th));
1213 ractor_sched_cancel_enq(next_th->vm, TH_SCHED(next_th));
1215 ruby_thread_set_native(next_th);
1216 native_thread_assign(nt, next_th);
1218 coroutine_transfer0(current_cont, next_th->sched.context, to_dead);
1225 native_thread_assign(NULL, cth);
1226 RUBY_DEBUG_LOG(
"th:%u->%u on nt:%d", rb_th_serial(cth), rb_th_serial(next_th), nt->serial);
1229 thread_sched_switch0(cth->sched.context, next_th, nt,
false);
1232#if VM_CHECK_MODE > 0
1237 ASSERT_ractor_sched_locked(vm, cr);
1242 ccan_list_for_each(&vm->ractor.sched.grq, r, threads.sched.grq_node) {
1245 VM_ASSERT(r != prev_r);
1257rb_thread_sched_winding_begin(
rb_vm_t *vm)
1263rb_thread_sched_winding_end(
rb_vm_t *vm)
1274rb_thread_sched_wait_winding(
rb_vm_t *vm)
1277 native_thread_yield();
1291 if (sched->grq_node.next != &sched->grq_node) {
1292 ractor_sched_lock(vm, NULL);
1294 if (sched->grq_node.next != &sched->grq_node) {
1295 ccan_list_del_init(&sched->grq_node);
1296 VM_ASSERT(vm->ractor.sched.grq_cnt > 0);
1297 vm->ractor.sched.grq_cnt--;
1300 ractor_sched_unlock(vm, NULL);
1310 VM_ASSERT(sched->running != NULL);
1311 VM_ASSERT(sched->running->nt == NULL);
1313 ractor_sched_lock(vm, cr);
1322 if (sched->grq_node.next != &sched->grq_node) {
1323 rb_bug(
"ractor_sched_enq: already enqueued");
1325 ccan_list_add_tail(&vm->ractor.sched.grq, &sched->grq_node);
1326 vm->ractor.sched.grq_cnt++;
1327 VM_ASSERT(grq_size(vm, cr) == vm->ractor.sched.grq_cnt);
1329 RUBY_DEBUG_LOG(
"r:%"PRI_SERIALT_PREFIX
"u th:%u grq_cnt:%u", rb_ractor_id(r), rb_th_serial(sched->running), vm->ractor.sched.grq_cnt);
1339 timer_thread_wakeup_locked(vm);
1344 ractor_sched_unlock(vm, cr);
1350#define MINIMUM_SNT 0
1357#ifndef SNT_IDLE_RETIRE
1358#define SNT_IDLE_RETIRE 3
1364#define SNT_KEEP_MINIMUM (MINIMUM_SNT > 1 ? MINIMUM_SNT : 1)
1370 int idle_streak = 0;
1372 ractor_sched_lock(vm, cr);
1374 RUBY_DEBUG_LOG(
"empty? %d", ccan_list_empty(&vm->ractor.sched.grq));
1377 VM_ASSERT(rb_current_execution_context(
false) == NULL);
1378 VM_ASSERT(grq_size(vm, cr) == vm->ractor.sched.grq_cnt);
1380 while ((r = ccan_list_pop(&vm->ractor.sched.grq,
rb_ractor_t, threads.sched.grq_node)) == NULL) {
1381 RUBY_DEBUG_LOG(
"wait grq_cnt:%d", (
int)vm->ractor.sched.grq_cnt);
1383 if (can_retire && SNT_IDLE_RETIRE >= 0 && ++idle_streak > SNT_IDLE_RETIRE &&
1386 RUBY_DEBUG_LOG(
"retire, snt_cnt:%d", (
int)vm->ractor.sched.snt_cnt);
1390 ractor_sched_set_unlocked(vm, cr);
1392 ractor_sched_set_locked(vm, cr);
1394 RUBY_DEBUG_LOG(
"wakeup grq_cnt:%d", (
int)vm->ractor.sched.grq_cnt);
1397 VM_ASSERT(rb_current_execution_context(
false) == NULL);
1400 ccan_list_node_init(&r->threads.sched.grq_node);
1401 VM_ASSERT(vm->ractor.sched.grq_cnt > 0);
1402 vm->ractor.sched.grq_cnt--;
1403 RUBY_DEBUG_LOG(
"r:%d grq_cnt:%u", (
int)rb_ractor_id(r), vm->ractor.sched.grq_cnt);
1407 VM_ASSERT(idle_streak > SNT_IDLE_RETIRE);
1410 ractor_sched_unlock(vm, cr);
1425 RUBY_DEBUG_LOG(
"start%s",
"");
1431 if (ubf_set(th, ubf, ubf_arg, &waiter->event_serial)) {
1436 thread_sched_lock(sched, th);
1437 rb_ractor_unlock_self(cr);
1442 bool dedicated = th_has_dedicated_nt(th);
1443 const rb_hrtime_t *end_p = NULL;
1444 bool armed =
false, expired =
false;
1448 end_p = waiter->end;
1452 rb_hrtime_t now = rb_hrtime_now();
1453 rb_hrtime_t rel = *waiter->end > now ? *waiter->end - now : 0;
1455 armed = ractor_sched_timeout_arm(th, &rel);
1461 RUBY_DEBUG_LOG(
"expired before sleep%s",
"");
1463 else if (armed && th->sched.waiting_reason.flags == thread_sched_waiting_none) {
1466 th->sched.event_serial++;
1470 bool can_direct_transfer = !dedicated;
1471 RB_VM_SAVE_MACHINE_CONTEXT(th);
1472 th->status = THREAD_STOPPED_FOREVER;
1473 th->sched.waiting_timed = (end_p != NULL);
1475 thread_sched_wakeup_next_thread(sched, th, can_direct_transfer);
1477 thread_sched_wait_running_turn(sched, th, can_direct_transfer, end_p);
1478 th->sched.waiting_timed =
false;
1479 th->status = THREAD_RUNNABLE;
1482 VM_ASSERT(th->sched.waiting_reason.flags == thread_sched_waiting_none);
1485 thread_sched_unlock(sched, th);
1486 rb_ractor_lock_self(cr);
1488 ubf_clear(th,
true);
1490 RUBY_DEBUG_LOG(
"end%s",
"");
1499 RUBY_DEBUG_LOG(
"r:%u th:%d", (
unsigned int)rb_ractor_id(r), r_th->serial);
1501 thread_sched_lock(sched, r_th);
1503 if (r_th->status == THREAD_STOPPED_FOREVER) {
1509 if (r_th->sched.waiting_reason.flags != thread_sched_waiting_none) {
1510 ractor_sched_timeout_disarm(r_th);
1515 if (sched->running != r_th && !r_th->sched.node.is_ready) {
1516 r_th->sched.event_serial++;
1517 thread_sched_to_ready_common(sched, r_th,
true,
false);
1521 thread_sched_unlock(sched, r_th);
1525ractor_sched_barrier_completed_p(
rb_vm_t *vm)
1529 RUBY_DEBUG_LOG(
"run:%u wait:%u", vm->ractor.sched.barrier_running_cnt, vm->ractor.sched.barrier_joined_cnt);
1530 VM_ASSERT(vm->ractor.sched.barrier_running_cnt - 1 >= vm->ractor.sched.barrier_joined_cnt);
1532 return (vm->ractor.sched.barrier_running_cnt - vm->ractor.sched.barrier_joined_cnt) == 1;
1538 VM_ASSERT(cr == GET_RACTOR());
1539 VM_ASSERT(vm->ractor.sync.lock_owner == cr);
1540 VM_ASSERT(!vm->ractor.sched.barrier_is_waiting);
1541 VM_ASSERT(vm->ractor.sched.barrier_joined_cnt == 0);
1542 VM_ASSERT(vm->ractor.sched.barrier_ractor == NULL);
1543 VM_ASSERT(vm->ractor.sched.barrier_lock_rec == 0);
1545 RUBY_DEBUG_LOG(
"start serial:%u", vm->ractor.sched.barrier_serial);
1547 unsigned int lock_rec;
1549 ractor_sched_lock(vm, cr);
1552 vm->ractor.sched.barrier_ractor = cr;
1553 vm->ractor.sched.barrier_lock_rec = vm->ractor.sync.lock_rec;
1556 lock_rec = vm->ractor.sync.lock_rec;
1557 vm->ractor.sync.lock_rec = 0;
1558 vm->ractor.sync.lock_owner = NULL;
1567 unsigned int running_cnt = 0;
1568 uint32_t serial = vm->ractor.sched.barrier_serial;
1573 ccan_list_for_each(&vm->ractor.sched.ntlist.running_dnts, dnt, running_dnts_node) {
1574 ith = dnt->running_thread;
1575 dnt->barrier_counted_serial = serial;
1577 if (ith->ractor != cr) {
1578 RUBY_DEBUG_LOG(
"barrier request to th:%u", rb_th_serial(ith));
1579 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ith->ec);
1584 ccan_list_for_each(&vm->ractor.sched.ntlist.snts, nt, snts_node) {
1587 ith = nt->running_th;
1589 nt->barrier_counted_serial = serial;
1591 if (ith->ractor != cr) {
1592 RUBY_DEBUG_LOG(
"barrier request to th:%u", rb_th_serial(ith));
1593 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ith->ec);
1602 vm->ractor.sched.barrier_running_cnt = running_cnt;
1605 while (!ractor_sched_barrier_completed_p(vm)) {
1606 ractor_sched_set_unlocked(vm, cr);
1608 ractor_sched_set_locked(vm, cr);
1611 RUBY_DEBUG_LOG(
"completed seirial:%u", vm->ractor.sched.barrier_serial);
1614 vm->ractor.sched.barrier_serial++;
1615 vm->ractor.sched.barrier_joined_cnt = 0;
1620 vm->ractor.sync.lock_rec = lock_rec;
1621 vm->ractor.sync.lock_owner = cr;
1632 RUBY_DEBUG_LOG(
"serial:%u", (
unsigned int)vm->ractor.sched.barrier_serial - 1);
1633 VM_ASSERT(vm->ractor.sched.barrier_is_waiting);
1634 VM_ASSERT(vm->ractor.sched.barrier_ractor);
1635 VM_ASSERT(vm->ractor.sched.barrier_lock_rec > 0);
1638 vm->ractor.sched.barrier_ractor = NULL;
1639 vm->ractor.sched.barrier_lock_rec = 0;
1640 ractor_sched_unlock(vm, cr);
1644ractor_sched_barrier_join_signal_locked(
rb_vm_t *vm)
1646 if (ractor_sched_barrier_completed_p(vm)) {
1654 VM_ASSERT(vm->ractor.sched.barrier_is_waiting);
1656 unsigned int barrier_serial = vm->ractor.sched.barrier_serial;
1658 while (vm->ractor.sched.barrier_serial == barrier_serial) {
1659 RUBY_DEBUG_LOG(
"sleep serial:%u", barrier_serial);
1660 RB_VM_SAVE_MACHINE_CONTEXT(th);
1663 ractor_sched_set_unlocked(vm, cr);
1665 ractor_sched_set_locked(vm, cr);
1667 RUBY_DEBUG_LOG(
"wakeup serial:%u", barrier_serial);
1674 VM_ASSERT(cr->threads.sched.running != NULL);
1675 VM_ASSERT(cr == GET_RACTOR());
1676 VM_ASSERT(vm->ractor.sync.lock_owner == NULL);
1677 VM_ASSERT(vm->ractor.sched.barrier_is_waiting);
1679#if USE_RUBY_DEBUG_LOG || VM_CHECK_MODE > 0
1680 unsigned int barrier_serial = vm->ractor.sched.barrier_serial;
1683 RUBY_DEBUG_LOG(
"join");
1687 VM_ASSERT(vm->ractor.sched.barrier_is_waiting);
1688 VM_ASSERT(vm->ractor.sched.barrier_serial == barrier_serial);
1690 ractor_sched_lock(vm, cr);
1695 vm->ractor.sched.barrier_joined_cnt++;
1696 RUBY_DEBUG_LOG(
"waiting_cnt:%u serial:%u", vm->ractor.sched.barrier_joined_cnt, barrier_serial);
1698 ractor_sched_barrier_join_signal_locked(vm);
1699 ractor_sched_barrier_join_wait_locked(vm, cr->threads.sched.running);
1701 ractor_sched_unlock(vm, cr);
1718 ccan_list_del_init(&sched->timeslice_node);
1723#if defined(HAVE_WORKING_FORK)
1724static void rb_internal_thread_event_hooks_rw_lock_atfork(
void);
1730 rb_thread_sched_init(sched,
true);
1734 if (th_has_dedicated_nt(th)) {
1735 vm->ractor.sched.snt_cnt = 0;
1736#if USE_RUBY_DEBUG_LOG
1737 vm->ractor.sched.dnt_cnt = 1;
1741 vm->ractor.sched.snt_cnt = 1;
1742#if USE_RUBY_DEBUG_LOG
1743 vm->ractor.sched.dnt_cnt = 0;
1748#if VM_CHECK_MODE > 0
1749 vm->ractor.sched.lock_owner = NULL;
1750 vm->ractor.sched.locked =
false;
1758 ccan_list_head_init(&vm->ractor.sched.grq);
1759 vm->ractor.sched.grq_cnt = 0;
1762 vm->ractor.sched.barrier_is_waiting = 0;
1763 vm->ractor.sched.barrier_joined_cnt = 0;
1764 vm->ractor.sched.barrier_ractor = NULL;
1765 vm->ractor.sched.barrier_lock_rec = 0;
1769 vm->ractor.sched.winding_cnt = 0;
1771 ccan_list_head_init(&vm->ractor.sched.ntlist.running_dnts);
1772 ccan_list_head_init(&vm->ractor.sched.ntlist.snts);
1774 ccan_list_head_init(&vm->ractor.sched.timeslice.scheds);
1776 th->nt->running_th = NULL;
1777 th->nt->retiring =
false;
1778 native_main_thread_atfork();
1782 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
1783 ccan_list_node_init(&r->threads.sched.timeslice_node);
1785 ccan_list_for_each(&vm->ractor.terminated_set, r, vmlr_node) {
1786 ccan_list_node_init(&r->threads.sched.timeslice_node);
1789 if (th->nt && th->nt->dedicated == 0) {
1792 ccan_list_add(&vm->ractor.sched.ntlist.snts, &th->nt->snts_node);
1796 nt_machine_stack_atfork();
1798 rb_internal_thread_event_hooks_rw_lock_atfork();
1800 VM_ASSERT(sched->is_running);
1802 if (sched->running != th) {
1803 thread_sched_to_running(sched, th);
1806 thread_sched_setup_running_threads(sched, th->ractor, vm, th, NULL);
1811 th->nt->thread_id = pthread_self();
1812#ifdef RB_THREAD_T_HAS_NATIVE_ID
1813 th->nt->tid = get_native_thread_id();
1820extern int ruby_mn_threads_enabled;
1823ruby_mn_threads_params(
void)
1832 const char *mn_threads_cstr = getenv(
"RUBY_MN_THREADS");
1833 int mn_threads = (USE_MN_THREADS && mn_threads_cstr) ? atoi(mn_threads_cstr) : 0;
1835 mn_threads_mode = mn_threads;
1836 if (mn_threads > 0) {
1837 ruby_mn_threads_enabled = mn_threads;
1841 main_ractor->threads.sched.enable_mn_threads = mn_threads == 1;
1843 const char *max_cpu_cstr = getenv(
"RUBY_MAX_CPU");
1844 int max_cpu = native_thread_default_max_cpu();
1846 if (USE_MN_THREADS && max_cpu_cstr) {
1847 int given_max_cpu = atoi(max_cpu_cstr);
1848 if (given_max_cpu > 0) {
1849 max_cpu = given_max_cpu;
1853 vm->ractor.sched.max_cpu = max_cpu;
1856 if (mn_threads >= 2) {
1857 thread_sched_main_to_shared(GET_THREAD());
1865 RUBY_DEBUG_LOG(
"nt:%d %d->%d", nt->serial, nt->dedicated, nt->dedicated + 1);
1870 if (nt->dedicated == 0 && !nt->retiring) {
1875 ractor_sched_lock(vm, cr);
1877 if (vm->ractor.sched.grq_cnt > 0) {
1878 timer_thread_wakeup_locked(vm);
1881 ractor_sched_unlock(vm, cr);
1884#if USE_RUBY_DEBUG_LOG
1885 if (nt->dedicated == 0) vm->ractor.sched.dnt_cnt++;
1894 RUBY_DEBUG_LOG(
"nt:%d %d->%d", nt->serial, nt->dedicated, nt->dedicated - 1);
1895 VM_ASSERT(nt->dedicated > 0);
1898 if (nt->dedicated == 0) {
1903 const bool can_retire = native_thread_self_can_retire_p();
1906 if (!can_retire || snt < vm->ractor.sched.max_cpu || (
int)snt <= MINIMUM_SNT) {
1908 nt->retiring =
false;
1913 nt->retiring =
true;
1917#if USE_RUBY_DEBUG_LOG
1918 vm->ractor.sched.dnt_cnt--;
1926#if USE_RUBY_DEBUG_LOG
1929 RUBY_DEBUG_LOG(
"th:%d nt:%d->%d", (
int)th->serial, (
int)th->nt->serial, (
int)nt->serial);
1932 RUBY_DEBUG_LOG(
"th:%d nt:NULL->%d", (
int)th->serial, (
int)nt->serial);
1937 RUBY_DEBUG_LOG(
"th:%d nt:%d->NULL", (
int)th->serial, (
int)th->nt->serial);
1940 RUBY_DEBUG_LOG(
"th:%d nt:NULL->NULL", (
int)th->serial);
1951 th->nt = native_thread_alloc();
1952 th->nt->vm = th->vm;
1953 th->nt->running_thread = th;
1954 th->nt->dedicated = 1;
1957 size_t vm_stack_word_size = th->vm->default_params.thread_vm_stack_size /
sizeof(
VALUE);
1958 void *vm_stack = ruby_xmalloc(vm_stack_word_size *
sizeof(
VALUE));
1959 th->sched.malloc_stack =
true;
1960 rb_ec_initialize_vm_stack(th->ec, vm_stack, vm_stack_word_size);
1961 th->sched.context_stack = vm_stack;
1962 th->sched.context_stack_size = vm_stack_word_size;
1964 int err = native_thread_create0(th->nt);
1967 thread_sched_to_ready(TH_SCHED(th), th);
1981 VALUE stack_start = 0;
1982 VALUE *stack_start_addr = asan_get_real_stack_addr(&stack_start);
1984 native_thread_init_stack(th, stack_start_addr);
1985 thread_start_func_2(th, th->ec->machine.stack_start);
1994 native_thread_setup_on_thread(nt);
1996#if USE_RUBY_DEBUG_LOG && defined(RUBY_NT_SERIAL)
1997 ruby_nt_serial = nt->serial;
2000 RUBY_DEBUG_LOG(
"nt:%u", nt->serial);
2002 if (nt->dedicated) {
2007 RUBY_DEBUG_LOG(
"on dedicated th:%u", rb_th_serial(th));
2008 ruby_thread_set_native(th);
2010 thread_sched_lock(sched, th);
2012 if (sched->running == th) {
2013 thread_sched_add_running_thread(sched, th);
2015 thread_sched_wait_running_turn(sched, th,
false, NULL);
2017 thread_sched_unlock(sched, th);
2020 call_thread_start_func_2(th);
2024 coroutine_initialize_main(nt->nt_context);
2025 nt_snts_join(vm, nt);
2027 bool retired = nt_shared_loop(nt);
2028 nt_snts_leave(vm, nt);
2032 RUBY_DEBUG_LOG(
"retired nt:%u", nt->serial);
2033 native_thread_destroy_self(nt);
2046 ccan_list_add(&vm->ractor.sched.ntlist.snts, &nt->snts_node);
2057 VM_ASSERT(nt->running_th == NULL);
2060 ccan_list_del_init(&nt->snts_node);
2073 RUBY_DEBUG_LOG(
"check next");
2080 rb_ractor_t *r = ractor_sched_deq(vm, NULL, native_thread_self_can_retire_p());
2087 thread_sched_lock(sched, NULL);
2091 if (next_th && next_th->nt == NULL) {
2092 RUBY_DEBUG_LOG(
"nt:%d next_th:%d", (
int)nt->serial, (
int)next_th->serial);
2094 thread_sched_switch0(nt->nt_context, next_th, nt,
false);
2101 if (thread_sched_reclaim(dead_co)) {
2107 thread_sched_switch0(nt->nt_context, next_th, nt,
false);
2111 RUBY_DEBUG_LOG(
"no schedulable threads -- next_th:%p", next_th);
2115 thread_sched_unlock(sched, NULL);
2123 if (nt->dedicated) {
2137#ifdef RUBY_ASAN_ENABLED
2138 __sanitizer_finish_switch_fiber(self->fake_stack,
2139 (
const void**)&from->stack_base, &from->stack_size);
2148 if (!thread_sched_reclaim(dead_co)) {
2150 thread_sched_unlock(TH_SCHED(parked_th), NULL);
2155 if (!nt->dedicated) {
2156 if (nt_shared_loop(nt)) rb_bug(
"main nt retired");
2158 nt_snts_leave(nt->vm, nt);
2179 VM_ASSERT(th == vm->ractor.main_thread);
2180 VM_ASSERT(nt->dedicated == 1 && th->has_dedicated_nt);
2181 VM_ASSERT(sched->running == th);
2184 void *vm_stack, *machine_stack;
2185 int err = nt_alloc_stack(vm, &vm_stack, &machine_stack);
2187 rb_warn(
"RUBY_MN_THREADS=2: cannot allocate the main nt's stack (%s); the main thread stays dedicated", strerror(err));
2188 th->ractor->threads.sched.enable_mn_threads =
true;
2191 size_t machine_stack_size = vm->default_params.thread_machine_stack_size -
sizeof(
struct nt_machine_stack_footer);
2194 coroutine_initialize(nt->nt_context, nt_loop_co, machine_stack, machine_stack_size);
2195 nt->nt_context->argument = nt;
2198 struct rb_thread_context *tctx = ruby_xmalloc(
sizeof(
struct rb_thread_context));
2202 coroutine_initialize_main(&tctx->co);
2203 tctx->co.argument = th;
2204 th->sched.context = &tctx->co;
2206 thread_sched_lock(sched, th);
2209 thread_sched_del_running_thread(sched, th);
2211 th->has_dedicated_nt = 0;
2212 nt_snts_join(vm, nt);
2215 ractor_sched_lock(vm, th->ractor);
2222 th->ractor->threads.sched.enable_mn_threads =
true;
2224 ractor_sched_unlock(vm, th->ractor);
2225#if USE_RUBY_DEBUG_LOG
2226 vm->ractor.sched.dnt_cnt--;
2228 thread_sched_add_running_thread(sched, th);
2230 thread_sched_unlock(sched, th);
2232 RUBY_DEBUG_LOG(
"main th:%u on nt:%d is now shared", rb_th_serial(th), nt->serial);
2236static int native_thread_create_shared(
rb_thread_t *th);
2239static void nt_free_stack(
void *mstack);
2250 struct rb_thread_context *tctx = (
struct rb_thread_context *)dead_co;
2252 if (tctx != NULL && tctx->dead) {
2253 nt_free_stack(tctx->stack);
2268 timer_thread_wake_fence(th);
2274 timer_thread_wake_fence(th);
2280 const bool owns_nt = th->sched.context == NULL;
2282 if (th->sched.context != NULL) {
2287 struct rb_thread_context *tctx = (
struct rb_thread_context *)th->sched.context;
2288 nt_free_stack(tctx->stack);
2290 th->sched.context = NULL;
2293 if (th->sched.malloc_stack) {
2294 SIZED_FREE_N((
VALUE *)th->sched.context_stack, th->sched.context_stack_size);
2295 if (th->nt && owns_nt) {
2296 native_thread_destroy(th->nt);
2300 SIZED_FREE_N((
VALUE *)th->sched.context_stack, th->sched.context_stack_size);
2301 native_thread_destroy(th->nt);
2311 VM_ASSERT(th->nt == 0);
2312 RUBY_DEBUG_LOG(
"th:%d has_dnt:%d", th->serial, th->has_dedicated_nt);
2315 if (!th->ractor->threads.sched.enable_mn_threads) {
2316 th->has_dedicated_nt = 1;
2319 if (th->has_dedicated_nt) {
2320 return native_thread_create_dedicated(th);
2323 return native_thread_create_shared(th);
2328static CCAN_LIST_HEAD(ubf_list_head);
2329#ifdef RB_NATIVETHREAD_LOCK_INIT
2330static rb_nativethread_lock_t ubf_list_lock = RB_NATIVETHREAD_LOCK_INIT;
2333static rb_nativethread_lock_t ubf_list_lock;
2337ubf_list_atfork(
void)
2339 ccan_list_head_init(&ubf_list_head);
2348 ccan_list_for_each(&ubf_list_head, list_th, sched.node.ubf) {
2349 if (list_th == th)
return true;
2358 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
2359 struct ccan_list_node *node = &th->sched.node.ubf;
2361 VM_ASSERT(th->unblock.func != NULL);
2366 if (ccan_list_empty((
struct ccan_list_head*)node)) {
2367 VM_ASSERT(!ubf_list_contain_p(th));
2368 ccan_list_add(&ubf_list_head, node);
2373 timer_thread_wakeup();
2380 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
2381 struct ccan_list_node *node = &th->sched.node.ubf;
2384 VM_ASSERT(th->unblock.func == NULL);
2386 if (!ccan_list_empty((
struct ccan_list_head*)node)) {
2389 VM_ASSERT(ubf_list_contain_p(th));
2390 ccan_list_del_init(node);
2403 RUBY_DEBUG_LOG(
"th:%u thread_id:%p", rb_th_serial(th), (
void *)th->nt->thread_id);
2405 native_thread_interrupt(th);
2409ubf_select(
void *ptr)
2412 RUBY_DEBUG_LOG(
"wakeup th:%u", rb_th_serial(th));
2413 ubf_wakeup_thread(th);
2414 register_ubf_list(th);
2418ubf_threads_empty(
void)
2420 return ccan_list_empty(&ubf_list_head) != 0;
2424ubf_wakeup_all_threads(
void)
2429 ccan_list_for_each(&ubf_list_head, th, sched.node.ubf) {
2430 ubf_wakeup_thread(th);
2437#define register_ubf_list(th) (void)(th)
2438#define unregister_ubf_list(th) (void)(th)
2440static void ubf_wakeup_all_threads(
void) {
return; }
2441static bool ubf_threads_empty(
void) {
return true; }
2442#define ubf_list_atfork() do {} while (0)
2446timer_thread_set_timeout(
rb_vm_t *vm)
2453 ractor_sched_lock(vm, NULL);
2455 if ( timeslice_scan(vm,
false)
2456 || !ubf_threads_empty()
2457 || vm->ractor.sched.grq_cnt > 0
2460 RUBY_DEBUG_LOG(
"ubf:%d grq:%d",
2461 !ubf_threads_empty(),
2462 (vm->ractor.sched.grq_cnt > 0));
2465 vm->ractor.sched.timeslice_wait_inf =
false;
2468 vm->ractor.sched.timeslice_wait_inf =
true;
2471 ractor_sched_unlock(vm, NULL);
2473 timeout = timer_wheel_timeout(timeout);
2475 RUBY_DEBUG_LOG(
"timeout:%d inf:%d", timeout, (
int)vm->ractor.sched.timeslice_wait_inf);
2483timer_thread_check_signal(
rb_vm_t *vm)
2487 int signum = rb_signal_buff_size();
2488 if (UNLIKELY(signum > 0) && vm->ractor.main_thread) {
2489 RUBY_DEBUG_LOG(
"signum:%d", signum);
2490 threadptr_trap_interrupt(vm->ractor.main_thread);
2497timeslice_scan(
rb_vm_t *vm,
bool interrupt)
2504 ccan_list_for_each_safe(&vm->ractor.sched.timeslice.scheds, sched, next, timeslice_node) {
2508 if (ccan_list_empty(&sched->readyq)) {
2509 ccan_list_del_init(&sched->timeslice_node);
2511 else if (sched->is_running) {
2512 VM_ASSERT(sched->running != NULL);
2515 RUBY_DEBUG_LOG(
"timeslice th:%u", rb_th_serial(sched->running));
2516 RUBY_VM_SET_TIMER_INTERRUPT(sched->running->ec);
2534timer_thread_check_timeslice(
rb_vm_t *vm)
2537 timeslice_scan(vm,
true);
2541timer_thread_func(
void *ptr)
2544#if defined(RUBY_NT_SERIAL)
2548 RUBY_DEBUG_LOG(
"started%s",
"");
2551 timer_thread_check_signal(vm);
2552 timer_thread_check_timeout(vm);
2553 ubf_wakeup_all_threads();
2556 timer_thread_polling(vm);
2559 RUBY_DEBUG_LOG(
"terminated");
2564timer_thread_wakeup_locked(
rb_vm_t *vm)
2567 ASSERT_ractor_sched_locked(vm, NULL);
2569 if (TIMER_THREAD_CREATED_P()) {
2570 if (vm->ractor.sched.timeslice_wait_inf) {
2571 RUBY_DEBUG_LOG(
"wakeup%s",
"");
2572 timer_thread_wakeup_force();
2575 RUBY_DEBUG_LOG(
"will be wakeup...");
2581timer_thread_wakeup(
void)
2585 ractor_sched_lock(vm, NULL);
2587 timer_thread_wakeup_locked(vm);
2589 ractor_sched_unlock(vm, NULL);
2597 RUBY_DEBUG_LOG(
"rel:%d", rel ? (
int)*rel : 0);
2599 if (rel && !th_has_dedicated_nt(th)) {
2601 thread_sched_wait_events(sched, th, -1, thread_sched_waiting_timeout, rel);
2612 const rb_hrtime_t max = (rb_hrtime_t)100000000 * RB_HRTIME_PER_SEC;
2613 if (*rel > max) *rel = max;
2615 rb_hrtime_t end = rb_hrtime_add(rb_hrtime_now(), *rel);
2616 thread_sched_to_waiting_until_wakeup(sched, th, &end);
2619 thread_sched_to_waiting_until_wakeup(sched, th, NULL);
2622 RUBY_DEBUG_LOG(
"wakeup");
2632 bool is_snt = th->nt->dedicated == 0;
2633 native_thread_dedicated_inc(th->vm, th->ractor, th->nt);
2644rb_ractor_sched_wait_terminate(
rb_vm_t *vm, rb_nativethread_cond_t *cond,
unsigned long msec)
2646 ASSERT_vm_locking();
2649 unsigned int lock_rec = vm->ractor.sync.lock_rec;
2650 rb_ractor_t *lock_owner = vm->ractor.sync.lock_owner;
2652 vm->ractor.sync.lock_rec = 0;
2653 vm->ractor.sync.lock_owner = NULL;
2655 native_thread_dedicated_inc(vm, th->ractor, th->nt);
2657 native_thread_dedicated_dec(vm, th->ractor, th->nt);
2659 vm->ractor.sync.lock_rec = lock_rec;
2660 vm->ractor.sync.lock_owner = lock_owner;
2664rb_thread_malloc_stack_set(
rb_thread_t *th,
void *stack,
size_t stack_size)
2666 th->sched.malloc_stack =
true;
2667 th->sched.context_stack = stack;
2668 th->sched.context_stack_size = stack_size;
2674thread_sched_init_vm(
rb_vm_t *vm)
2681 ccan_list_head_init(&vm->ractor.sched.grq);
2683 ccan_list_head_init(&vm->ractor.sched.ntlist.running_dnts);
2684 ccan_list_head_init(&vm->ractor.sched.ntlist.snts);
2686 ccan_list_head_init(&vm->ractor.sched.timeslice.scheds);
2688#ifndef RB_NATIVETHREAD_LOCK_INIT
#define RUBY_ATOMIC_INC(var)
Atomically increments the value pointed by var.
#define RUBY_ATOMIC_CAS(var, oldval, newval)
Atomic compare-and-swap.
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_ADD(var, val)
Identical to RUBY_ATOMIC_FETCH_ADD, except for the return type.
#define RUBY_ATOMIC_FETCH_SUB(var, val)
Atomically replaces the value pointed by var with the result of subtraction of val to the old value o...
#define RUBY_ATOMIC_DEC(var)
Atomically decrements the value pointed by var.
#define RUBY_ATOMIC_LOAD(var)
Atomic load.
#define RUBY_ATOMIC_SET(var, val)
Identical to RUBY_ATOMIC_EXCHANGE, except for the return type.
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
void rb_unblock_function_t(void *)
This is the type of UBFs.
#define RUBY_INTERNAL_THREAD_EVENT_RESUMED
Triggered when a thread successfully acquired the GVL.
#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.
#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]]
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_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.