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;
877 RUBY_DEBUG_LOG(
"th:%u dedicated:%d", rb_th_serial(th), th_has_dedicated_nt(th));
879 VM_ASSERT(sched->running != th);
880 VM_ASSERT(th_has_dedicated_nt(th));
881 VM_ASSERT(GET_THREAD() == th);
883 native_thread_dedicated_dec(th->vm, th->ractor, th->nt);
886 thread_sched_to_ready_common(sched, th,
false,
false);
888 if (sched->running == th) {
889 thread_sched_add_running_thread(sched, th);
893 thread_sched_wait_running_turn(sched, th,
false, NULL);
909 if (sched->runnable_hot_th == th) {
910 sched->runnable_hot_th_waiting = 1;
912 thread_sched_lock(sched, th);
914 thread_sched_to_running_common(sched, th);
916 thread_sched_unlock(sched, th);
929 ASSERT_thread_sched_locked(sched, th);
931 VM_ASSERT(sched->running == th);
932 VM_ASSERT(sched->running->nt != NULL);
936 RUBY_DEBUG_LOG(
"next_th:%u", rb_th_serial(next_th));
937 VM_ASSERT(th != next_th);
939 thread_sched_set_running(sched, next_th);
940 VM_ASSERT(next_th == sched->running);
941 thread_sched_wakeup_running_thread(sched, next_th, will_switch);
944 thread_sched_del_running_thread(sched, th);
952 RUBY_DEBUG_LOG(
"th:%u DNT:%d", rb_th_serial(th), th->nt->dedicated);
960 thread_sched_wakeup_next_thread(sched, th, !th_has_dedicated_nt(th));
970 timer_thread_wake_fence(th);
972 thread_sched_lock(sched, th);
974 thread_sched_to_dead_common(sched, th);
976 thread_sched_unlock(sched, th);
985 RUBY_DEBUG_LOG(
"th:%u DNT:%d", rb_th_serial(th), th->nt->dedicated);
989 native_thread_dedicated_inc(th->vm, th->ractor, th->nt);
990 if (!yield_immediately) {
991 sched->runnable_hot_th = th;
992 sched->runnable_hot_th_waiting = 0;
994 thread_sched_wakeup_next_thread(sched, th,
false);
1003 thread_sched_lock(sched, th);
1005 thread_sched_to_waiting_common(sched, th, yield_immediately);
1007 thread_sched_unlock(sched, th);
1015 VM_ASSERT(func != NULL);
1018 if (RUBY_VM_INTERRUPTED(th->ec)) {
1019 RUBY_DEBUG_LOG(
"interrupted:0x%x", th->ec->interrupt_flag);
1025 if (!th->ec->raised_flag && RUBY_VM_INTERRUPTED(th->ec)) {
1030 VM_ASSERT(th->unblock.func == NULL);
1031 th->unblock.func = func;
1032 th->unblock.arg = arg;
1035 *event_serial = prev_serial+1;
1048 th->unblock.func = NULL;
1049 th->unblock.arg = NULL;
1058ubf_waiting(
void *ptr)
1064 th->unblock.func = NULL;
1065 th->unblock.arg = NULL;
1067 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
1069 thread_sched_lock(sched, th);
1071 if (sched->running == th || th->sched.node.is_ready) {
1075 thread_sched_to_ready_common(sched, th,
true,
false);
1082 if (sched->running != th && th->sched.waiting_timed &&
1083 th->nt != NULL && th_has_dedicated_nt(th)) {
1088 thread_sched_unlock(sched, th);
1099 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
1101 VM_ASSERT(end == NULL || th_has_dedicated_nt(th));
1103 RB_VM_SAVE_MACHINE_CONTEXT(th);
1108 thread_sched_lock(sched, th);
1111 if (ubf_set(th, ubf_waiting, (
void *)th, NULL)) {
1112 RUBY_DEBUG_LOG(
"th:%u interrupted", rb_th_serial(th));
1115 bool can_direct_transfer = !th_has_dedicated_nt(th);
1116 th->sched.waiting_timed = (end != NULL);
1118 thread_sched_wakeup_next_thread(sched, th, can_direct_transfer);
1119 thread_sched_wait_running_turn(sched, th, can_direct_transfer, end);
1120 th->sched.waiting_timed =
false;
1123 thread_sched_unlock(sched, th);
1125 ubf_clear(th,
false);
1133 RUBY_DEBUG_LOG(
"th:%d sched->readyq_cnt:%d", (
int)th->serial, sched->readyq_cnt);
1135 thread_sched_lock(sched, th);
1137 if (!ccan_list_empty(&sched->readyq)) {
1139 thread_sched_wakeup_next_thread(sched, th, !th_has_dedicated_nt(th));
1140 bool can_direct_transfer = !th_has_dedicated_nt(th);
1141 thread_sched_to_ready_common(sched, th,
false, can_direct_transfer);
1142 thread_sched_wait_running_turn(sched, th, can_direct_transfer, NULL);
1143 th->status = THREAD_RUNNABLE;
1146 VM_ASSERT(sched->readyq_cnt == 0);
1149 thread_sched_unlock(sched, th);
1158 sched->lock_owner = NULL;
1161 ccan_list_head_init(&sched->readyq);
1162 sched->readyq_cnt = 0;
1163 ccan_list_node_init(&sched->grq_node);
1164 ccan_list_node_init(&sched->timeslice_node);
1169 if (!atfork) sched->enable_mn_threads = mn_threads_enabled_p();
1176#ifdef RUBY_ASAN_ENABLED
1177 void **fake_stack = to_dead ? NULL : &transfer_from->fake_stack;
1178 __sanitizer_start_switch_fiber(fake_stack, transfer_to->stack_base, transfer_to->stack_size);
1181#if defined(COROUTINE_SANITIZE_THREAD)
1185 __tsan_switch_to_fiber(transfer_to->tsan_fiber, 0);
1189 struct
coroutine_context *returning_from = coroutine_transfer(transfer_from, transfer_to);
1193 VM_ASSERT(!to_dead);
1194#ifdef RUBY_ASAN_ENABLED
1195 __sanitizer_finish_switch_fiber(transfer_from->fake_stack,
1196 (
const void**)&returning_from->stack_base, &returning_from->stack_size);
1203 VM_ASSERT(!nt->dedicated);
1204 VM_ASSERT(next_th->nt == NULL);
1206 RUBY_DEBUG_LOG(
"next_th:%u", rb_th_serial(next_th));
1210 ractor_sched_cancel_enq(next_th->vm, TH_SCHED(next_th));
1212 ruby_thread_set_native(next_th);
1213 native_thread_assign(nt, next_th);
1215 coroutine_transfer0(current_cont, next_th->sched.context, to_dead);
1222 native_thread_assign(NULL, cth);
1223 RUBY_DEBUG_LOG(
"th:%u->%u on nt:%d", rb_th_serial(cth), rb_th_serial(next_th), nt->serial);
1226 thread_sched_switch0(cth->sched.context, next_th, nt,
false);
1229#if VM_CHECK_MODE > 0
1234 ASSERT_ractor_sched_locked(vm, cr);
1239 ccan_list_for_each(&vm->ractor.sched.grq, r, threads.sched.grq_node) {
1242 VM_ASSERT(r != prev_r);
1254rb_thread_sched_winding_begin(
rb_vm_t *vm)
1260rb_thread_sched_winding_end(
rb_vm_t *vm)
1271rb_thread_sched_wait_winding(
rb_vm_t *vm)
1274 native_thread_yield();
1288 if (sched->grq_node.next != &sched->grq_node) {
1289 ractor_sched_lock(vm, NULL);
1291 if (sched->grq_node.next != &sched->grq_node) {
1292 ccan_list_del_init(&sched->grq_node);
1293 VM_ASSERT(vm->ractor.sched.grq_cnt > 0);
1294 vm->ractor.sched.grq_cnt--;
1297 ractor_sched_unlock(vm, NULL);
1307 VM_ASSERT(sched->running != NULL);
1308 VM_ASSERT(sched->running->nt == NULL);
1310 ractor_sched_lock(vm, cr);
1319 if (sched->grq_node.next != &sched->grq_node) {
1320 rb_bug(
"ractor_sched_enq: already enqueued");
1322 ccan_list_add_tail(&vm->ractor.sched.grq, &sched->grq_node);
1323 vm->ractor.sched.grq_cnt++;
1324 VM_ASSERT(grq_size(vm, cr) == vm->ractor.sched.grq_cnt);
1326 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);
1336 timer_thread_wakeup_locked(vm);
1341 ractor_sched_unlock(vm, cr);
1347#define MINIMUM_SNT 0
1354#ifndef SNT_IDLE_RETIRE
1355#define SNT_IDLE_RETIRE 3
1361#define SNT_KEEP_MINIMUM (MINIMUM_SNT > 1 ? MINIMUM_SNT : 1)
1367 int idle_streak = 0;
1369 ractor_sched_lock(vm, cr);
1371 RUBY_DEBUG_LOG(
"empty? %d", ccan_list_empty(&vm->ractor.sched.grq));
1374 VM_ASSERT(rb_current_execution_context(
false) == NULL);
1375 VM_ASSERT(grq_size(vm, cr) == vm->ractor.sched.grq_cnt);
1377 while ((r = ccan_list_pop(&vm->ractor.sched.grq,
rb_ractor_t, threads.sched.grq_node)) == NULL) {
1378 RUBY_DEBUG_LOG(
"wait grq_cnt:%d", (
int)vm->ractor.sched.grq_cnt);
1380 if (can_retire && SNT_IDLE_RETIRE >= 0 && ++idle_streak > SNT_IDLE_RETIRE &&
1383 RUBY_DEBUG_LOG(
"retire, snt_cnt:%d", (
int)vm->ractor.sched.snt_cnt);
1387 ractor_sched_set_unlocked(vm, cr);
1389 ractor_sched_set_locked(vm, cr);
1391 RUBY_DEBUG_LOG(
"wakeup grq_cnt:%d", (
int)vm->ractor.sched.grq_cnt);
1394 VM_ASSERT(rb_current_execution_context(
false) == NULL);
1397 ccan_list_node_init(&r->threads.sched.grq_node);
1398 VM_ASSERT(vm->ractor.sched.grq_cnt > 0);
1399 vm->ractor.sched.grq_cnt--;
1400 RUBY_DEBUG_LOG(
"r:%d grq_cnt:%u", (
int)rb_ractor_id(r), vm->ractor.sched.grq_cnt);
1404 VM_ASSERT(idle_streak > SNT_IDLE_RETIRE);
1407 ractor_sched_unlock(vm, cr);
1422 RUBY_DEBUG_LOG(
"start%s",
"");
1428 if (ubf_set(th, ubf, ubf_arg, &waiter->event_serial)) {
1433 thread_sched_lock(sched, th);
1434 rb_ractor_unlock_self(cr);
1439 bool dedicated = th_has_dedicated_nt(th);
1440 const rb_hrtime_t *end_p = NULL;
1441 bool armed =
false, expired =
false;
1445 end_p = waiter->end;
1449 rb_hrtime_t now = rb_hrtime_now();
1450 rb_hrtime_t rel = *waiter->end > now ? *waiter->end - now : 0;
1452 armed = ractor_sched_timeout_arm(th, &rel);
1458 RUBY_DEBUG_LOG(
"expired before sleep%s",
"");
1460 else if (armed && th->sched.waiting_reason.flags == thread_sched_waiting_none) {
1463 th->sched.event_serial++;
1467 bool can_direct_transfer = !dedicated;
1468 RB_VM_SAVE_MACHINE_CONTEXT(th);
1469 th->status = THREAD_STOPPED_FOREVER;
1470 th->sched.waiting_timed = (end_p != NULL);
1472 thread_sched_wakeup_next_thread(sched, th, can_direct_transfer);
1474 thread_sched_wait_running_turn(sched, th, can_direct_transfer, end_p);
1475 th->sched.waiting_timed =
false;
1476 th->status = THREAD_RUNNABLE;
1479 VM_ASSERT(th->sched.waiting_reason.flags == thread_sched_waiting_none);
1482 thread_sched_unlock(sched, th);
1483 rb_ractor_lock_self(cr);
1485 ubf_clear(th,
true);
1487 RUBY_DEBUG_LOG(
"end%s",
"");
1496 RUBY_DEBUG_LOG(
"r:%u th:%d", (
unsigned int)rb_ractor_id(r), r_th->serial);
1498 thread_sched_lock(sched, r_th);
1500 if (r_th->status == THREAD_STOPPED_FOREVER) {
1506 if (r_th->sched.waiting_reason.flags != thread_sched_waiting_none) {
1507 ractor_sched_timeout_disarm(r_th);
1512 if (sched->running != r_th && !r_th->sched.node.is_ready) {
1513 r_th->sched.event_serial++;
1514 thread_sched_to_ready_common(sched, r_th,
true,
false);
1518 thread_sched_unlock(sched, r_th);
1522ractor_sched_barrier_completed_p(
rb_vm_t *vm)
1526 RUBY_DEBUG_LOG(
"run:%u wait:%u", vm->ractor.sched.barrier_running_cnt, vm->ractor.sched.barrier_joined_cnt);
1527 VM_ASSERT(vm->ractor.sched.barrier_running_cnt - 1 >= vm->ractor.sched.barrier_joined_cnt);
1529 return (vm->ractor.sched.barrier_running_cnt - vm->ractor.sched.barrier_joined_cnt) == 1;
1535 VM_ASSERT(cr == GET_RACTOR());
1536 VM_ASSERT(vm->ractor.sync.lock_owner == cr);
1537 VM_ASSERT(!vm->ractor.sched.barrier_is_waiting);
1538 VM_ASSERT(vm->ractor.sched.barrier_joined_cnt == 0);
1539 VM_ASSERT(vm->ractor.sched.barrier_ractor == NULL);
1540 VM_ASSERT(vm->ractor.sched.barrier_lock_rec == 0);
1542 RUBY_DEBUG_LOG(
"start serial:%u", vm->ractor.sched.barrier_serial);
1544 unsigned int lock_rec;
1546 ractor_sched_lock(vm, cr);
1549 vm->ractor.sched.barrier_ractor = cr;
1550 vm->ractor.sched.barrier_lock_rec = vm->ractor.sync.lock_rec;
1553 lock_rec = vm->ractor.sync.lock_rec;
1554 vm->ractor.sync.lock_rec = 0;
1555 vm->ractor.sync.lock_owner = NULL;
1564 unsigned int running_cnt = 0;
1565 uint32_t serial = vm->ractor.sched.barrier_serial;
1570 ccan_list_for_each(&vm->ractor.sched.ntlist.running_dnts, dnt, running_dnts_node) {
1571 ith = dnt->running_thread;
1572 dnt->barrier_counted_serial = serial;
1574 if (ith->ractor != cr) {
1575 RUBY_DEBUG_LOG(
"barrier request to th:%u", rb_th_serial(ith));
1576 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ith->ec);
1581 ccan_list_for_each(&vm->ractor.sched.ntlist.snts, nt, snts_node) {
1584 ith = nt->running_th;
1586 nt->barrier_counted_serial = serial;
1588 if (ith->ractor != cr) {
1589 RUBY_DEBUG_LOG(
"barrier request to th:%u", rb_th_serial(ith));
1590 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ith->ec);
1599 vm->ractor.sched.barrier_running_cnt = running_cnt;
1602 while (!ractor_sched_barrier_completed_p(vm)) {
1603 ractor_sched_set_unlocked(vm, cr);
1605 ractor_sched_set_locked(vm, cr);
1608 RUBY_DEBUG_LOG(
"completed seirial:%u", vm->ractor.sched.barrier_serial);
1611 vm->ractor.sched.barrier_serial++;
1612 vm->ractor.sched.barrier_joined_cnt = 0;
1617 vm->ractor.sync.lock_rec = lock_rec;
1618 vm->ractor.sync.lock_owner = cr;
1629 RUBY_DEBUG_LOG(
"serial:%u", (
unsigned int)vm->ractor.sched.barrier_serial - 1);
1630 VM_ASSERT(vm->ractor.sched.barrier_is_waiting);
1631 VM_ASSERT(vm->ractor.sched.barrier_ractor);
1632 VM_ASSERT(vm->ractor.sched.barrier_lock_rec > 0);
1635 vm->ractor.sched.barrier_ractor = NULL;
1636 vm->ractor.sched.barrier_lock_rec = 0;
1637 ractor_sched_unlock(vm, cr);
1641ractor_sched_barrier_join_signal_locked(
rb_vm_t *vm)
1643 if (ractor_sched_barrier_completed_p(vm)) {
1651 VM_ASSERT(vm->ractor.sched.barrier_is_waiting);
1653 unsigned int barrier_serial = vm->ractor.sched.barrier_serial;
1655 while (vm->ractor.sched.barrier_serial == barrier_serial) {
1656 RUBY_DEBUG_LOG(
"sleep serial:%u", barrier_serial);
1657 RB_VM_SAVE_MACHINE_CONTEXT(th);
1660 ractor_sched_set_unlocked(vm, cr);
1662 ractor_sched_set_locked(vm, cr);
1664 RUBY_DEBUG_LOG(
"wakeup serial:%u", barrier_serial);
1671 VM_ASSERT(cr->threads.sched.running != NULL);
1672 VM_ASSERT(cr == GET_RACTOR());
1673 VM_ASSERT(vm->ractor.sync.lock_owner == NULL);
1674 VM_ASSERT(vm->ractor.sched.barrier_is_waiting);
1676#if USE_RUBY_DEBUG_LOG || VM_CHECK_MODE > 0
1677 unsigned int barrier_serial = vm->ractor.sched.barrier_serial;
1680 RUBY_DEBUG_LOG(
"join");
1684 VM_ASSERT(vm->ractor.sched.barrier_is_waiting);
1685 VM_ASSERT(vm->ractor.sched.barrier_serial == barrier_serial);
1687 ractor_sched_lock(vm, cr);
1692 vm->ractor.sched.barrier_joined_cnt++;
1693 RUBY_DEBUG_LOG(
"waiting_cnt:%u serial:%u", vm->ractor.sched.barrier_joined_cnt, barrier_serial);
1695 ractor_sched_barrier_join_signal_locked(vm);
1696 ractor_sched_barrier_join_wait_locked(vm, cr->threads.sched.running);
1698 ractor_sched_unlock(vm, cr);
1715 ccan_list_del_init(&sched->timeslice_node);
1720#if defined(HAVE_WORKING_FORK)
1721static void rb_internal_thread_event_hooks_rw_lock_atfork(
void);
1727 rb_thread_sched_init(sched,
true);
1731 if (th_has_dedicated_nt(th)) {
1732 vm->ractor.sched.snt_cnt = 0;
1733#if USE_RUBY_DEBUG_LOG
1734 vm->ractor.sched.dnt_cnt = 1;
1738 vm->ractor.sched.snt_cnt = 1;
1739#if USE_RUBY_DEBUG_LOG
1740 vm->ractor.sched.dnt_cnt = 0;
1745#if VM_CHECK_MODE > 0
1746 vm->ractor.sched.lock_owner = NULL;
1747 vm->ractor.sched.locked =
false;
1755 ccan_list_head_init(&vm->ractor.sched.grq);
1756 vm->ractor.sched.grq_cnt = 0;
1759 vm->ractor.sched.barrier_is_waiting = 0;
1760 vm->ractor.sched.barrier_joined_cnt = 0;
1761 vm->ractor.sched.barrier_ractor = NULL;
1762 vm->ractor.sched.barrier_lock_rec = 0;
1766 vm->ractor.sched.winding_cnt = 0;
1768 ccan_list_head_init(&vm->ractor.sched.ntlist.running_dnts);
1769 ccan_list_head_init(&vm->ractor.sched.ntlist.snts);
1771 ccan_list_head_init(&vm->ractor.sched.timeslice.scheds);
1773 th->nt->running_th = NULL;
1774 th->nt->retiring =
false;
1775 native_main_thread_atfork();
1779 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
1780 ccan_list_node_init(&r->threads.sched.timeslice_node);
1782 ccan_list_for_each(&vm->ractor.terminated_set, r, vmlr_node) {
1783 ccan_list_node_init(&r->threads.sched.timeslice_node);
1786 if (th->nt && th->nt->dedicated == 0) {
1789 ccan_list_add(&vm->ractor.sched.ntlist.snts, &th->nt->snts_node);
1793 nt_machine_stack_atfork();
1795 rb_internal_thread_event_hooks_rw_lock_atfork();
1797 VM_ASSERT(sched->is_running);
1799 if (sched->running != th) {
1800 thread_sched_to_running(sched, th);
1803 thread_sched_setup_running_threads(sched, th->ractor, vm, th, NULL);
1806#ifdef RB_THREAD_T_HAS_NATIVE_ID
1808 th->nt->tid = get_native_thread_id();
1815extern int ruby_mn_threads_enabled;
1818ruby_mn_threads_params(
void)
1827 const char *mn_threads_cstr = getenv(
"RUBY_MN_THREADS");
1828 int mn_threads = (USE_MN_THREADS && mn_threads_cstr) ? atoi(mn_threads_cstr) : 0;
1830 mn_threads_mode = mn_threads;
1831 if (mn_threads > 0) {
1832 ruby_mn_threads_enabled = mn_threads;
1836 main_ractor->threads.sched.enable_mn_threads = mn_threads == 1;
1838 const char *max_cpu_cstr = getenv(
"RUBY_MAX_CPU");
1839 int max_cpu = native_thread_default_max_cpu();
1841 if (USE_MN_THREADS && max_cpu_cstr) {
1842 int given_max_cpu = atoi(max_cpu_cstr);
1843 if (given_max_cpu > 0) {
1844 max_cpu = given_max_cpu;
1848 vm->ractor.sched.max_cpu = max_cpu;
1851 if (mn_threads >= 2) {
1852 thread_sched_main_to_shared(GET_THREAD());
1860 RUBY_DEBUG_LOG(
"nt:%d %d->%d", nt->serial, nt->dedicated, nt->dedicated + 1);
1862 if (nt->dedicated == 0) {
1867 ractor_sched_lock(vm, cr);
1869 if (vm->ractor.sched.grq_cnt > 0) {
1870 timer_thread_wakeup_locked(vm);
1873 ractor_sched_unlock(vm, cr);
1875#if USE_RUBY_DEBUG_LOG
1876 vm->ractor.sched.dnt_cnt++;
1886 RUBY_DEBUG_LOG(
"nt:%d %d->%d", nt->serial, nt->dedicated, nt->dedicated - 1);
1887 VM_ASSERT(nt->dedicated > 0);
1890 if (nt->dedicated == 0) {
1895 const bool can_retire = native_thread_self_can_retire_p();
1898 if (!can_retire || snt < vm->ractor.sched.max_cpu || (
int)snt <= MINIMUM_SNT) {
1899 if (
RUBY_ATOMIC_CAS(vm->ractor.sched.snt_cnt, snt, snt + 1) == snt)
break;
1902 nt->retiring =
true;
1906#if USE_RUBY_DEBUG_LOG
1907 vm->ractor.sched.dnt_cnt--;
1915#if USE_RUBY_DEBUG_LOG
1918 RUBY_DEBUG_LOG(
"th:%d nt:%d->%d", (
int)th->serial, (
int)th->nt->serial, (
int)nt->serial);
1921 RUBY_DEBUG_LOG(
"th:%d nt:NULL->%d", (
int)th->serial, (
int)nt->serial);
1926 RUBY_DEBUG_LOG(
"th:%d nt:%d->NULL", (
int)th->serial, (
int)th->nt->serial);
1929 RUBY_DEBUG_LOG(
"th:%d nt:NULL->NULL", (
int)th->serial);
1940 th->nt = native_thread_alloc();
1941 th->nt->vm = th->vm;
1942 th->nt->running_thread = th;
1943 th->nt->dedicated = 1;
1946 size_t vm_stack_word_size = th->vm->default_params.thread_vm_stack_size /
sizeof(
VALUE);
1947 void *vm_stack = ruby_xmalloc(vm_stack_word_size *
sizeof(
VALUE));
1948 th->sched.malloc_stack =
true;
1949 rb_ec_initialize_vm_stack(th->ec, vm_stack, vm_stack_word_size);
1950 th->sched.context_stack = vm_stack;
1951 th->sched.context_stack_size = vm_stack_word_size;
1953 int err = native_thread_create0(th->nt);
1956 thread_sched_to_ready(TH_SCHED(th), th);
1970 VALUE stack_start = 0;
1971 VALUE *stack_start_addr = asan_get_real_stack_addr(&stack_start);
1973 native_thread_init_stack(th, stack_start_addr);
1974 thread_start_func_2(th, th->ec->machine.stack_start);
1983 native_thread_setup_on_thread(nt);
1986#ifdef RB_THREAD_T_HAS_NATIVE_ID
1987 nt->tid = get_native_thread_id();
1990#if USE_RUBY_DEBUG_LOG && defined(RUBY_NT_SERIAL)
1991 ruby_nt_serial = nt->serial;
1994 RUBY_DEBUG_LOG(
"nt:%u", nt->serial);
1996 if (nt->dedicated) {
2001 RUBY_DEBUG_LOG(
"on dedicated th:%u", rb_th_serial(th));
2002 ruby_thread_set_native(th);
2004 thread_sched_lock(sched, th);
2006 if (sched->running == th) {
2007 thread_sched_add_running_thread(sched, th);
2009 thread_sched_wait_running_turn(sched, th,
false, NULL);
2011 thread_sched_unlock(sched, th);
2014 call_thread_start_func_2(th);
2018 coroutine_initialize_main(nt->nt_context);
2019 nt_snts_join(vm, nt);
2021 bool retired = nt_shared_loop(nt);
2022 nt_snts_leave(vm, nt);
2026 RUBY_DEBUG_LOG(
"retired nt:%u", nt->serial);
2027 native_thread_destroy_self(nt);
2040 ccan_list_add(&vm->ractor.sched.ntlist.snts, &nt->snts_node);
2051 VM_ASSERT(nt->running_th == NULL);
2054 ccan_list_del_init(&nt->snts_node);
2067 RUBY_DEBUG_LOG(
"check next");
2074 rb_ractor_t *r = ractor_sched_deq(vm, NULL, native_thread_self_can_retire_p());
2081 thread_sched_lock(sched, NULL);
2085 if (next_th && next_th->nt == NULL) {
2086 RUBY_DEBUG_LOG(
"nt:%d next_th:%d", (
int)nt->serial, (
int)next_th->serial);
2088 thread_sched_switch0(nt->nt_context, next_th, nt,
false);
2095 if (thread_sched_reclaim(dead_co)) {
2101 thread_sched_switch0(nt->nt_context, next_th, nt,
false);
2105 RUBY_DEBUG_LOG(
"no schedulable threads -- next_th:%p", next_th);
2109 thread_sched_unlock(sched, NULL);
2117 if (nt->dedicated) {
2131#ifdef RUBY_ASAN_ENABLED
2132 __sanitizer_finish_switch_fiber(self->fake_stack,
2133 (
const void**)&from->stack_base, &from->stack_size);
2142 if (!thread_sched_reclaim(dead_co)) {
2144 thread_sched_unlock(TH_SCHED(parked_th), NULL);
2149 if (!nt->dedicated) {
2150 if (nt_shared_loop(nt)) rb_bug(
"main nt retired");
2152 nt_snts_leave(nt->vm, nt);
2173 VM_ASSERT(th == vm->ractor.main_thread);
2174 VM_ASSERT(nt->dedicated == 1 && th->has_dedicated_nt);
2175 VM_ASSERT(sched->running == th);
2178 void *vm_stack, *machine_stack;
2179 int err = nt_alloc_stack(vm, &vm_stack, &machine_stack);
2181 rb_warn(
"RUBY_MN_THREADS=2: cannot allocate the main nt's stack (%s); the main thread stays dedicated", strerror(err));
2182 th->ractor->threads.sched.enable_mn_threads =
true;
2185 size_t machine_stack_size = vm->default_params.thread_machine_stack_size -
sizeof(
struct nt_machine_stack_footer);
2188 coroutine_initialize(nt->nt_context, nt_loop_co, machine_stack, machine_stack_size);
2189 nt->nt_context->argument = nt;
2192 struct rb_thread_context *tctx = ruby_xmalloc(
sizeof(
struct rb_thread_context));
2196 coroutine_initialize_main(&tctx->co);
2197 tctx->co.argument = th;
2198 th->sched.context = &tctx->co;
2200 thread_sched_lock(sched, th);
2203 thread_sched_del_running_thread(sched, th);
2205 th->has_dedicated_nt = 0;
2206 nt_snts_join(vm, nt);
2209 ractor_sched_lock(vm, th->ractor);
2216 th->ractor->threads.sched.enable_mn_threads =
true;
2218 ractor_sched_unlock(vm, th->ractor);
2219#if USE_RUBY_DEBUG_LOG
2220 vm->ractor.sched.dnt_cnt--;
2222 thread_sched_add_running_thread(sched, th);
2224 thread_sched_unlock(sched, th);
2226 RUBY_DEBUG_LOG(
"main th:%u on nt:%d is now shared", rb_th_serial(th), nt->serial);
2230static int native_thread_create_shared(
rb_thread_t *th);
2233static void nt_free_stack(
void *mstack);
2244 struct rb_thread_context *tctx = (
struct rb_thread_context *)dead_co;
2246 if (tctx != NULL && tctx->dead) {
2247 nt_free_stack(tctx->stack);
2262 timer_thread_wake_fence(th);
2268 timer_thread_wake_fence(th);
2274 const bool owns_nt = th->sched.context == NULL;
2276 if (th->sched.context != NULL) {
2281 struct rb_thread_context *tctx = (
struct rb_thread_context *)th->sched.context;
2282 nt_free_stack(tctx->stack);
2284 th->sched.context = NULL;
2287 if (th->sched.malloc_stack) {
2288 SIZED_FREE_N((
VALUE *)th->sched.context_stack, th->sched.context_stack_size);
2289 if (th->nt && owns_nt) {
2290 native_thread_destroy(th->nt);
2294 SIZED_FREE_N((
VALUE *)th->sched.context_stack, th->sched.context_stack_size);
2295 native_thread_destroy(th->nt);
2305 VM_ASSERT(th->nt == 0);
2306 RUBY_DEBUG_LOG(
"th:%d has_dnt:%d", th->serial, th->has_dedicated_nt);
2309 if (!th->ractor->threads.sched.enable_mn_threads) {
2310 th->has_dedicated_nt = 1;
2313 if (th->has_dedicated_nt) {
2314 return native_thread_create_dedicated(th);
2317 return native_thread_create_shared(th);
2322static CCAN_LIST_HEAD(ubf_list_head);
2323#ifdef RB_NATIVETHREAD_LOCK_INIT
2324static rb_nativethread_lock_t ubf_list_lock = RB_NATIVETHREAD_LOCK_INIT;
2327static rb_nativethread_lock_t ubf_list_lock;
2331ubf_list_atfork(
void)
2333 ccan_list_head_init(&ubf_list_head);
2342 ccan_list_for_each(&ubf_list_head, list_th, sched.node.ubf) {
2343 if (list_th == th)
return true;
2352 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
2353 struct ccan_list_node *node = &th->sched.node.ubf;
2355 VM_ASSERT(th->unblock.func != NULL);
2360 if (ccan_list_empty((
struct ccan_list_head*)node)) {
2361 VM_ASSERT(!ubf_list_contain_p(th));
2362 ccan_list_add(&ubf_list_head, node);
2367 timer_thread_wakeup();
2374 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
2375 struct ccan_list_node *node = &th->sched.node.ubf;
2378 VM_ASSERT(th->unblock.func == NULL);
2380 if (!ccan_list_empty((
struct ccan_list_head*)node)) {
2383 VM_ASSERT(ubf_list_contain_p(th));
2384 ccan_list_del_init(node);
2397 RUBY_DEBUG_LOG(
"th:%u thread_id:%p", rb_th_serial(th), (
void *)th->nt->thread_id);
2399 native_thread_interrupt(th);
2403ubf_select(
void *ptr)
2406 RUBY_DEBUG_LOG(
"wakeup th:%u", rb_th_serial(th));
2407 ubf_wakeup_thread(th);
2408 register_ubf_list(th);
2412ubf_threads_empty(
void)
2414 return ccan_list_empty(&ubf_list_head) != 0;
2418ubf_wakeup_all_threads(
void)
2423 ccan_list_for_each(&ubf_list_head, th, sched.node.ubf) {
2424 ubf_wakeup_thread(th);
2431#define register_ubf_list(th) (void)(th)
2432#define unregister_ubf_list(th) (void)(th)
2434static void ubf_wakeup_all_threads(
void) {
return; }
2435static bool ubf_threads_empty(
void) {
return true; }
2436#define ubf_list_atfork() do {} while (0)
2440timer_thread_set_timeout(
rb_vm_t *vm)
2447 ractor_sched_lock(vm, NULL);
2449 if ( timeslice_scan(vm,
false)
2450 || !ubf_threads_empty()
2451 || vm->ractor.sched.grq_cnt > 0
2454 RUBY_DEBUG_LOG(
"ubf:%d grq:%d",
2455 !ubf_threads_empty(),
2456 (vm->ractor.sched.grq_cnt > 0));
2459 vm->ractor.sched.timeslice_wait_inf =
false;
2462 vm->ractor.sched.timeslice_wait_inf =
true;
2465 ractor_sched_unlock(vm, NULL);
2467 timeout = timer_wheel_timeout(timeout);
2469 RUBY_DEBUG_LOG(
"timeout:%d inf:%d", timeout, (
int)vm->ractor.sched.timeslice_wait_inf);
2477timer_thread_check_signal(
rb_vm_t *vm)
2481 int signum = rb_signal_buff_size();
2482 if (UNLIKELY(signum > 0) && vm->ractor.main_thread) {
2483 RUBY_DEBUG_LOG(
"signum:%d", signum);
2484 threadptr_trap_interrupt(vm->ractor.main_thread);
2491timeslice_scan(
rb_vm_t *vm,
bool interrupt)
2498 ccan_list_for_each_safe(&vm->ractor.sched.timeslice.scheds, sched, next, timeslice_node) {
2502 if (ccan_list_empty(&sched->readyq)) {
2503 ccan_list_del_init(&sched->timeslice_node);
2505 else if (sched->is_running) {
2506 VM_ASSERT(sched->running != NULL);
2509 RUBY_DEBUG_LOG(
"timeslice th:%u", rb_th_serial(sched->running));
2510 RUBY_VM_SET_TIMER_INTERRUPT(sched->running->ec);
2528timer_thread_check_timeslice(
rb_vm_t *vm)
2531 timeslice_scan(vm,
true);
2535timer_thread_func(
void *ptr)
2538#if defined(RUBY_NT_SERIAL)
2542 RUBY_DEBUG_LOG(
"started%s",
"");
2545 timer_thread_check_signal(vm);
2546 timer_thread_check_timeout(vm);
2547 ubf_wakeup_all_threads();
2550 timer_thread_polling(vm);
2553 RUBY_DEBUG_LOG(
"terminated");
2558timer_thread_wakeup_locked(
rb_vm_t *vm)
2561 ASSERT_ractor_sched_locked(vm, NULL);
2563 if (TIMER_THREAD_CREATED_P()) {
2564 if (vm->ractor.sched.timeslice_wait_inf) {
2565 RUBY_DEBUG_LOG(
"wakeup%s",
"");
2566 timer_thread_wakeup_force();
2569 RUBY_DEBUG_LOG(
"will be wakeup...");
2575timer_thread_wakeup(
void)
2579 ractor_sched_lock(vm, NULL);
2581 timer_thread_wakeup_locked(vm);
2583 ractor_sched_unlock(vm, NULL);
2591 RUBY_DEBUG_LOG(
"rel:%d", rel ? (
int)*rel : 0);
2593 if (rel && !th_has_dedicated_nt(th)) {
2595 thread_sched_wait_events(sched, th, -1, thread_sched_waiting_timeout, rel);
2606 const rb_hrtime_t max = (rb_hrtime_t)100000000 * RB_HRTIME_PER_SEC;
2607 if (*rel > max) *rel = max;
2609 rb_hrtime_t end = rb_hrtime_add(rb_hrtime_now(), *rel);
2610 thread_sched_to_waiting_until_wakeup(sched, th, &end);
2613 thread_sched_to_waiting_until_wakeup(sched, th, NULL);
2616 RUBY_DEBUG_LOG(
"wakeup");
2626 bool is_snt = th->nt->dedicated == 0;
2627 native_thread_dedicated_inc(th->vm, th->ractor, th->nt);
2638rb_ractor_sched_wait_terminate(
rb_vm_t *vm, rb_nativethread_cond_t *cond,
unsigned long msec)
2640 ASSERT_vm_locking();
2643 unsigned int lock_rec = vm->ractor.sync.lock_rec;
2644 rb_ractor_t *lock_owner = vm->ractor.sync.lock_owner;
2646 vm->ractor.sync.lock_rec = 0;
2647 vm->ractor.sync.lock_owner = NULL;
2649 native_thread_dedicated_inc(vm, th->ractor, th->nt);
2651 native_thread_dedicated_dec(vm, th->ractor, th->nt);
2653 vm->ractor.sync.lock_rec = lock_rec;
2654 vm->ractor.sync.lock_owner = lock_owner;
2658rb_thread_malloc_stack_set(
rb_thread_t *th,
void *stack,
size_t stack_size)
2660 th->sched.malloc_stack =
true;
2661 th->sched.context_stack = stack;
2662 th->sched.context_stack_size = stack_size;
2668thread_sched_init_vm(
rb_vm_t *vm)
2675 ccan_list_head_init(&vm->ractor.sched.grq);
2677 ccan_list_head_init(&vm->ractor.sched.ntlist.running_dnts);
2678 ccan_list_head_init(&vm->ractor.sched.ntlist.snts);
2680 ccan_list_head_init(&vm->ractor.sched.timeslice.scheds);
2682#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.