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);
108#include THREAD_IMPL_SRC
112#ifndef RB_NATIVE_MUTEX_TRYLOCK_DETECTS_SELF
116#define RB_NATIVE_MUTEX_TRYLOCK_DETECTS_SELF 1
127 return th->nt->dedicated > 0;
132thread_sched_dump_(const
char *file,
int line, struct
rb_thread_sched *sched)
134 fprintf(stderr,
"@%s:%d running:%d\n", file, line, sched->running ? (
int)sched->running->serial : -1);
137 ccan_list_for_each(&sched->readyq, th, sched.node.readyq) {
138 i++;
if (i>10) rb_bug(
"too many");
139 fprintf(stderr,
" ready:%d (%sNT:%d)\n", th->serial,
140 th->nt ? (th->nt->dedicated ?
"D" :
"S") :
"x",
141 th->nt ? (int)th->nt->serial : -1);
148ractor_sched_dump_(const
char *file,
int line,
rb_vm_t *vm)
152 fprintf(stderr,
"ractor_sched_dump %s:%d\n", file, line);
155 ccan_list_for_each(&vm->ractor.sched.grq, r, threads.sched.grq_node) {
157 if (i>10) rb_bug(
"!!");
158 fprintf(stderr,
" %d ready:%d\n", i, rb_ractor_id(r));
167 VM_ASSERT(sched->lock_owner == NULL);
169 sched->lock_owner = th;
177 VM_ASSERT(sched->lock_owner == th);
179 sched->lock_owner = NULL;
189 RUBY_DEBUG_LOG2(file, line,
"r:%d th:%u", th ? (
int)rb_ractor_id(th->ractor) : -1, rb_th_serial(th));
191 RUBY_DEBUG_LOG2(file, line,
"th:%u", rb_th_serial(th));
194 thread_sched_set_locked(sched, th);
200 RUBY_DEBUG_LOG2(file, line,
"th:%u", rb_th_serial(th));
202 thread_sched_set_unlocked(sched, th);
214 thread_sched_set_unlocked(sched, th);
222#if RB_NATIVE_MUTEX_TRYLOCK_DETECTS_SELF
228 VM_ASSERT(sched->lock_owner == th);
231 VM_ASSERT(sched->lock_owner != NULL);
242 return rb_ractor_id(r);
253 VM_ASSERT(vm->ractor.sched.lock_owner == NULL);
254 VM_ASSERT(vm->ractor.sched.locked ==
false);
256 vm->ractor.sched.lock_owner = cr;
257 vm->ractor.sched.locked =
true;
265 VM_ASSERT(vm->ractor.sched.locked);
266 VM_ASSERT(vm->ractor.sched.lock_owner == cr);
268 vm->ractor.sched.locked =
false;
269 vm->ractor.sched.lock_owner = NULL;
280 RUBY_DEBUG_LOG2(file, line,
"cr:%u prev_owner:%u", rb_ractor_serial(cr), rb_ractor_serial(vm->ractor.sched.lock_owner));
282 RUBY_DEBUG_LOG2(file, line,
"cr:%u", rb_ractor_serial(cr));
285 ractor_sched_set_locked(vm, cr);
291 RUBY_DEBUG_LOG2(file, line,
"cr:%u", rb_ractor_serial(cr));
293 ractor_sched_set_unlocked(vm, cr);
300#if RB_NATIVE_MUTEX_TRYLOCK_DETECTS_SELF
303 VM_ASSERT(vm->ractor.sched.locked);
304 VM_ASSERT(cr == NULL || vm->ractor.sched.lock_owner == cr);
307static void ractor_sched_barrier_join_signal_locked(
rb_vm_t *vm);
319 if (nt != NULL && nt->dedicated == 0) {
322 VM_ASSERT(nt->running_th == NULL);
332 nt->running_thread = th;
333 ccan_list_add(&vm->ractor.sched.ntlist.running_dnts, &nt->running_dnts_node);
355 in_running_th = (nt->running_th == th);
357 nt->running_th = NULL;
358 serial = vm->ractor.sched.barrier_serial;
359 counted = (nt->barrier_counted_serial == serial);
360 nt->barrier_counted_serial = serial - 1;
365 if (!in_running_th) {
368 ccan_list_del_init(&nt->running_dnts_node);
369 serial = vm->ractor.sched.barrier_serial;
370 counted = (nt->barrier_counted_serial == serial);
371 nt->barrier_counted_serial = serial - 1;
386 uint32_t serial = vm->ractor.sched.barrier_serial;
392 in_running_th = (nt->running_th == th);
394 stamped = (nt->barrier_counted_serial != serial);
395 nt->barrier_counted_serial = serial;
400 if (!in_running_th) {
403 stamped = (nt->barrier_counted_serial != serial);
404 nt->barrier_counted_serial = serial;
422 if (sched->timeslice_node.next == &sched->timeslice_node) {
423 ccan_list_add_tail(&vm->ractor.sched.timeslice.scheds, &sched->timeslice_node);
433 RUBY_DEBUG_LOG(
"+:%u -:%u", rb_th_serial(add_th), rb_th_serial(del_th));
436 bool counted = ntlist_del_running(vm, del_th);
437 sched->is_running =
false;
442 ractor_sched_lock(vm, cr);
446 VM_ASSERT(vm->ractor.sched.barrier_running_cnt > 0);
447 vm->ractor.sched.barrier_running_cnt--;
449 ractor_sched_barrier_join_signal_locked(vm);
452 ractor_sched_unlock(vm, cr);
457 ntlist_add_running(vm, add_th);
465 ractor_sched_lock(vm, cr);
468 ntlist_stamp_if_uncounted(vm, add_th)) {
470 RUBY_DEBUG_LOG(
"barrier_is_waiting");
471 vm->ractor.sched.barrier_running_cnt++;
472 RUBY_VM_SET_VM_BARRIER_INTERRUPT(add_th->ec);
475 ractor_sched_unlock(vm, cr);
478 sched->is_running =
true;
481 if (!ccan_list_empty(&sched->readyq)) {
482 timeslice_sched_link(vm, sched);
483 ractor_sched_lock(vm, cr);
485 if (vm->ractor.sched.timeslice_wait_inf) {
486 timer_thread_wakeup_locked(vm);
489 ractor_sched_unlock(vm, cr);
497 ASSERT_thread_sched_locked(sched, th);
498 VM_ASSERT(sched->running == th);
501 thread_sched_setup_running_threads(sched, th->ractor, vm, th, NULL);
507 ASSERT_thread_sched_locked(sched, th);
510 thread_sched_setup_running_threads(sched, th->ractor, vm, NULL, th);
518 thread_sched_lock(sched, th);
520 thread_sched_add_running_thread(sched, th);
522 thread_sched_unlock(sched, th);
530 thread_sched_lock(sched, th);
532 thread_sched_del_running_thread(sched, th);
534 thread_sched_unlock(sched, th);
544 RUBY_DEBUG_LOG(
"th:%u->th:%u", rb_th_serial(sched->running), rb_th_serial(th));
545 VM_ASSERT(sched->running != th);
547 if (RUBY_DTRACE_RTS_SET_RUNNING_ENABLED()) {
548 RUBY_DTRACE_RTS_SET_RUNNING(sched, sched->running, th);
559 ccan_list_for_each(&sched->readyq, rth, sched.node.readyq) {
561 VM_ASSERT(th->sched.node.is_ready);
565 VM_ASSERT(!th->sched.node.is_ready);
576 ASSERT_thread_sched_locked(sched, NULL);
579 VM_ASSERT(sched->running != NULL);
581 if (ccan_list_empty(&sched->readyq)) {
585 next_th = ccan_list_pop(&sched->readyq,
rb_thread_t, sched.node.readyq);
586 VM_ASSERT(next_th->sched.node.is_ready);
587 next_th->sched.node.is_ready =
false;
589 VM_ASSERT(sched->readyq_cnt > 0);
591 ccan_list_node_init(&next_th->sched.node.readyq);
594 RUBY_DEBUG_LOG(
"next_th:%u readyq_cnt:%d", rb_th_serial(next_th), sched->readyq_cnt);
603 ASSERT_thread_sched_locked(sched, NULL);
604 RUBY_DEBUG_LOG(
"ready_th:%u readyq_cnt:%d", rb_th_serial(ready_th), sched->readyq_cnt);
606 VM_ASSERT(sched->running != NULL);
607 VM_ASSERT(!thread_sched_readyq_contain_p(sched, ready_th));
609 bool timeslice_onset = sched->is_running && ccan_list_empty(&sched->readyq);
611 ccan_list_add_tail(&sched->readyq, &ready_th->sched.node.readyq);
612 ready_th->sched.node.is_ready =
true;
615 if (timeslice_onset) {
620 timeslice_sched_link(vm, sched);
621 ractor_sched_lock(vm, NULL);
623 if (vm->ractor.sched.timeslice_wait_inf) {
624 timer_thread_wakeup_locked(vm);
627 ractor_sched_unlock(vm, NULL);
636 ASSERT_thread_sched_locked(sched, NULL);
637 VM_ASSERT(sched->running == next_th);
641 if (th_has_dedicated_nt(next_th)) {
642 RUBY_DEBUG_LOG(
"pinning th:%u", next_th->serial);
647 RUBY_DEBUG_LOG(
"th:%u is already running.", next_th->serial);
652 RUBY_DEBUG_LOG(
"th:%u (do nothing)", rb_th_serial(next_th));
655 RUBY_DEBUG_LOG(
"th:%u (enq)", rb_th_serial(next_th));
656 ractor_sched_enq(next_th->vm, next_th->ractor);
661 RUBY_DEBUG_LOG(
"no waiting threads%s",
"");
669 RUBY_DEBUG_LOG(
"th:%u running:%u redyq_cnt:%d", rb_th_serial(th), rb_th_serial(sched->running), sched->readyq_cnt);
671 VM_ASSERT(sched->running != th);
672 VM_ASSERT(!thread_sched_readyq_contain_p(sched, th));
675 if (sched->running == NULL) {
676 thread_sched_set_running(sched, th);
677 if (wakeup) thread_sched_wakeup_running_thread(sched, th, will_switch);
680 thread_sched_enq(sched, th);
692 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
694 thread_sched_lock(sched, th);
696 thread_sched_to_ready_common(sched, th,
true,
false);
698 thread_sched_unlock(sched, th);
703thread_sched_wait_running_turn(
struct rb_thread_sched *sched,
rb_thread_t *th,
bool can_direct_transfer,
const rb_hrtime_t *end)
705 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
707 ASSERT_thread_sched_locked(sched, th);
708 VM_ASSERT(th == rb_ec_thread_ptr(rb_current_ec_noinline()));
710 bool timedout =
false;
712 if (th != sched->running) {
714 if (th->has_dedicated_nt && th == sched->runnable_hot_th && (sched->running == NULL || sched->running->has_dedicated_nt)) {
715 RUBY_DEBUG_LOG(
"(nt) stealing: hot-th:%u. running:%u", rb_th_serial(th), rb_th_serial(sched->running));
721 ractor_sched_cancel_enq(th->vm, sched);
724 if (sched->running != NULL) {
726 VM_ASSERT(!thread_sched_readyq_contain_p(sched, running));
727 running->sched.node.is_ready =
true;
728 ccan_list_add(&sched->readyq, &running->sched.node.readyq);
733 if (th->sched.node.is_ready) {
734 VM_ASSERT(thread_sched_readyq_contain_p(sched, th));
735 ccan_list_del_init(&th->sched.node.readyq);
736 th->sched.node.is_ready =
false;
739 thread_sched_set_running(sched, th);
740 rb_ractor_thread_switch(th->ractor, th,
false);
742 else if (th == sched->runnable_hot_th) {
746 sched->runnable_hot_th = NULL;
747 sched->runnable_hot_th_waiting = 0;
755 while((next_th = sched->running) != th) {
756 if (th_has_dedicated_nt(th)) {
757 RUBY_DEBUG_LOG(
"(nt) sleep th:%u running:%u", rb_th_serial(th), rb_th_serial(sched->running));
759 thread_sched_set_unlocked(sched, th);
761 RUBY_DEBUG_LOG(
"nt:%d cond:%p", th->nt->serial, &th->nt->readyq);
762 rb_nativethread_cond_t *cond = &th->nt->readyq;
770 if (end && !th->sched.node.is_ready) {
771 rb_hrtime_t abs = *end;
773 if (!RB_NATIVE_COND_HRTIME_DEADLINE_P()) {
775 rb_hrtime_t now = rb_hrtime_now();
776 abs = native_cond_timeout(cond, *end > now ? *end - now : 0);
778 timedout = native_cond_timedwait(cond, &sched->lock_, &abs) == ETIMEDOUT;
784 thread_sched_set_locked(sched, th);
787 sched->running != th && !th->sched.node.is_ready) {
790 thread_sched_to_ready_common(sched, th,
false,
false);
794 if (sched->runnable_hot_th != NULL && sched->runnable_hot_th_waiting) {
795 VM_ASSERT(sched->runnable_hot_th != th);
799 thread_sched_unlock(sched, th);
800 thread_sched_lock(sched, th);
803 RUBY_DEBUG_LOG(
"(nt) wakeup %s", sched->running == th ?
"success" :
"failed");
804 if (th == sched->running) {
805 rb_ractor_thread_switch(th->ractor, th,
false);
810 if (can_direct_transfer &&
811 (next_th = sched->running) != NULL &&
815 RUBY_DEBUG_LOG(
"th:%u->%u (direct)", rb_th_serial(th), rb_th_serial(next_th));
817 thread_sched_set_unlocked(sched, th);
819 rb_ractor_set_current_ec(th->ractor, NULL);
820 thread_sched_switch(th, next_th);
822 thread_sched_set_locked(sched, th);
827 native_thread_assign(NULL, th);
829 RUBY_DEBUG_LOG(
"th:%u->%u (ractor scheduling)", rb_th_serial(th), rb_th_serial(next_th));
831 thread_sched_set_unlocked(sched, th);
833 rb_ractor_set_current_ec(th->ractor, NULL);
834 coroutine_transfer0(th->sched.context, nt->nt_context,
false);
836 thread_sched_set_locked(sched, th);
839 VM_ASSERT(rb_current_ec_noinline() == th->ec);
843 VM_ASSERT(th->nt != NULL);
844 VM_ASSERT(rb_current_ec_noinline() == th->ec);
845 VM_ASSERT(th->sched.waiting_reason.flags == thread_sched_waiting_none);
848 thread_sched_add_running_thread(sched, th);
853 sched->runnable_hot_th = NULL;
854 sched->runnable_hot_th_waiting = 0;
864 RUBY_DEBUG_LOG(
"th:%u dedicated:%d", rb_th_serial(th), th_has_dedicated_nt(th));
866 VM_ASSERT(sched->running != th);
867 VM_ASSERT(th_has_dedicated_nt(th));
868 VM_ASSERT(GET_THREAD() == th);
870 native_thread_dedicated_dec(th->vm, th->ractor, th->nt);
873 thread_sched_to_ready_common(sched, th,
false,
false);
875 if (sched->running == th) {
876 thread_sched_add_running_thread(sched, th);
880 thread_sched_wait_running_turn(sched, th,
false, NULL);
896 if (sched->runnable_hot_th == th) {
897 sched->runnable_hot_th_waiting = 1;
899 thread_sched_lock(sched, th);
901 thread_sched_to_running_common(sched, th);
903 thread_sched_unlock(sched, th);
916 ASSERT_thread_sched_locked(sched, th);
918 VM_ASSERT(sched->running == th);
919 VM_ASSERT(sched->running->nt != NULL);
923 RUBY_DEBUG_LOG(
"next_th:%u", rb_th_serial(next_th));
924 VM_ASSERT(th != next_th);
926 thread_sched_set_running(sched, next_th);
927 VM_ASSERT(next_th == sched->running);
928 thread_sched_wakeup_running_thread(sched, next_th, will_switch);
931 thread_sched_del_running_thread(sched, th);
939 RUBY_DEBUG_LOG(
"th:%u DNT:%d", rb_th_serial(th), th->nt->dedicated);
947 thread_sched_wakeup_next_thread(sched, th, !th_has_dedicated_nt(th));
957 timer_thread_wake_fence(th);
959 thread_sched_lock(sched, th);
961 thread_sched_to_dead_common(sched, th);
963 thread_sched_unlock(sched, th);
972 RUBY_DEBUG_LOG(
"th:%u DNT:%d", rb_th_serial(th), th->nt->dedicated);
976 native_thread_dedicated_inc(th->vm, th->ractor, th->nt);
977 if (!yield_immediately) {
978 sched->runnable_hot_th = th;
979 sched->runnable_hot_th_waiting = 0;
981 thread_sched_wakeup_next_thread(sched, th,
false);
990 thread_sched_lock(sched, th);
992 thread_sched_to_waiting_common(sched, th, yield_immediately);
994 thread_sched_unlock(sched, th);
1002 VM_ASSERT(func != NULL);
1005 if (RUBY_VM_INTERRUPTED(th->ec)) {
1006 RUBY_DEBUG_LOG(
"interrupted:0x%x", th->ec->interrupt_flag);
1012 if (!th->ec->raised_flag && RUBY_VM_INTERRUPTED(th->ec)) {
1017 VM_ASSERT(th->unblock.func == NULL);
1018 th->unblock.func = func;
1019 th->unblock.arg = arg;
1022 *event_serial = prev_serial+1;
1035 th->unblock.func = NULL;
1036 th->unblock.arg = NULL;
1045ubf_waiting(
void *ptr)
1051 th->unblock.func = NULL;
1052 th->unblock.arg = NULL;
1054 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
1056 thread_sched_lock(sched, th);
1058 if (sched->running == th || th->sched.node.is_ready) {
1062 thread_sched_to_ready_common(sched, th,
true,
false);
1069 if (sched->running != th && th->sched.waiting_timed &&
1070 th->nt != NULL && th_has_dedicated_nt(th)) {
1075 thread_sched_unlock(sched, th);
1086 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
1088 VM_ASSERT(end == NULL || th_has_dedicated_nt(th));
1090 RB_VM_SAVE_MACHINE_CONTEXT(th);
1095 thread_sched_lock(sched, th);
1098 if (ubf_set(th, ubf_waiting, (
void *)th, NULL)) {
1099 RUBY_DEBUG_LOG(
"th:%u interrupted", rb_th_serial(th));
1102 bool can_direct_transfer = !th_has_dedicated_nt(th);
1103 th->sched.waiting_timed = (end != NULL);
1105 thread_sched_wakeup_next_thread(sched, th, can_direct_transfer);
1106 thread_sched_wait_running_turn(sched, th, can_direct_transfer, end);
1107 th->sched.waiting_timed =
false;
1110 thread_sched_unlock(sched, th);
1112 ubf_clear(th,
false);
1120 RUBY_DEBUG_LOG(
"th:%d sched->readyq_cnt:%d", (
int)th->serial, sched->readyq_cnt);
1122 thread_sched_lock(sched, th);
1124 if (!ccan_list_empty(&sched->readyq)) {
1126 thread_sched_wakeup_next_thread(sched, th, !th_has_dedicated_nt(th));
1127 bool can_direct_transfer = !th_has_dedicated_nt(th);
1128 thread_sched_to_ready_common(sched, th,
false, can_direct_transfer);
1129 thread_sched_wait_running_turn(sched, th, can_direct_transfer, NULL);
1130 th->status = THREAD_RUNNABLE;
1133 VM_ASSERT(sched->readyq_cnt == 0);
1136 thread_sched_unlock(sched, th);
1145 sched->lock_owner = NULL;
1148 ccan_list_head_init(&sched->readyq);
1149 sched->readyq_cnt = 0;
1150 ccan_list_node_init(&sched->grq_node);
1151 ccan_list_node_init(&sched->timeslice_node);
1154 if (!atfork) sched->enable_mn_threads =
true;
1161#ifdef RUBY_ASAN_ENABLED
1162 void **fake_stack = to_dead ? NULL : &transfer_from->fake_stack;
1163 __sanitizer_start_switch_fiber(fake_stack, transfer_to->stack_base, transfer_to->stack_size);
1166#if defined(COROUTINE_SANITIZE_THREAD)
1170 __tsan_switch_to_fiber(transfer_to->tsan_fiber, 0);
1174 struct
coroutine_context *returning_from = coroutine_transfer(transfer_from, transfer_to);
1178 VM_ASSERT(!to_dead);
1179#ifdef RUBY_ASAN_ENABLED
1180 __sanitizer_finish_switch_fiber(transfer_from->fake_stack,
1181 (
const void**)&returning_from->stack_base, &returning_from->stack_size);
1188 VM_ASSERT(!nt->dedicated);
1189 VM_ASSERT(next_th->nt == NULL);
1191 RUBY_DEBUG_LOG(
"next_th:%u", rb_th_serial(next_th));
1195 ractor_sched_cancel_enq(next_th->vm, TH_SCHED(next_th));
1197 ruby_thread_set_native(next_th);
1198 native_thread_assign(nt, next_th);
1200 coroutine_transfer0(current_cont, next_th->sched.context, to_dead);
1207 native_thread_assign(NULL, cth);
1208 RUBY_DEBUG_LOG(
"th:%u->%u on nt:%d", rb_th_serial(cth), rb_th_serial(next_th), nt->serial);
1209 thread_sched_switch0(cth->sched.context, next_th, nt, cth->status == THREAD_KILLED);
1212#if VM_CHECK_MODE > 0
1217 ASSERT_ractor_sched_locked(vm, cr);
1222 ccan_list_for_each(&vm->ractor.sched.grq, r, threads.sched.grq_node) {
1225 VM_ASSERT(r != prev_r);
1237rb_thread_sched_winding_begin(
rb_vm_t *vm)
1243rb_thread_sched_winding_end(
rb_vm_t *vm)
1254rb_thread_sched_wait_winding(
rb_vm_t *vm)
1257 native_thread_yield();
1271 if (sched->grq_node.next != &sched->grq_node) {
1272 ractor_sched_lock(vm, NULL);
1274 if (sched->grq_node.next != &sched->grq_node) {
1275 ccan_list_del_init(&sched->grq_node);
1276 VM_ASSERT(vm->ractor.sched.grq_cnt > 0);
1277 vm->ractor.sched.grq_cnt--;
1280 ractor_sched_unlock(vm, NULL);
1290 VM_ASSERT(sched->running != NULL);
1291 VM_ASSERT(sched->running->nt == NULL);
1293 ractor_sched_lock(vm, cr);
1302 if (sched->grq_node.next != &sched->grq_node) {
1303 rb_bug(
"ractor_sched_enq: already enqueued");
1305 ccan_list_add_tail(&vm->ractor.sched.grq, &sched->grq_node);
1306 vm->ractor.sched.grq_cnt++;
1307 VM_ASSERT(grq_size(vm, cr) == vm->ractor.sched.grq_cnt);
1309 RUBY_DEBUG_LOG(
"r:%u th:%u grq_cnt:%u", rb_ractor_id(r), rb_th_serial(sched->running), vm->ractor.sched.grq_cnt);
1319 timer_thread_wakeup_locked(vm);
1324 ractor_sched_unlock(vm, cr);
1330#define MINIMUM_SNT 0
1337#ifndef SNT_IDLE_RETIRE
1338#define SNT_IDLE_RETIRE 3
1344#define SNT_KEEP_MINIMUM (MINIMUM_SNT > 1 ? MINIMUM_SNT : 1)
1350 int idle_streak = 0;
1352 ractor_sched_lock(vm, cr);
1354 RUBY_DEBUG_LOG(
"empty? %d", ccan_list_empty(&vm->ractor.sched.grq));
1357 VM_ASSERT(rb_current_execution_context(
false) == NULL);
1358 VM_ASSERT(grq_size(vm, cr) == vm->ractor.sched.grq_cnt);
1360 while ((r = ccan_list_pop(&vm->ractor.sched.grq,
rb_ractor_t, threads.sched.grq_node)) == NULL) {
1361 RUBY_DEBUG_LOG(
"wait grq_cnt:%d", (
int)vm->ractor.sched.grq_cnt);
1363 if (SNT_IDLE_RETIRE >= 0 && ++idle_streak > SNT_IDLE_RETIRE &&
1366 RUBY_DEBUG_LOG(
"retire, snt_cnt:%d", (
int)vm->ractor.sched.snt_cnt);
1370 ractor_sched_set_unlocked(vm, cr);
1372 ractor_sched_set_locked(vm, cr);
1374 RUBY_DEBUG_LOG(
"wakeup grq_cnt:%d", (
int)vm->ractor.sched.grq_cnt);
1377 VM_ASSERT(rb_current_execution_context(
false) == NULL);
1380 ccan_list_node_init(&r->threads.sched.grq_node);
1381 VM_ASSERT(vm->ractor.sched.grq_cnt > 0);
1382 vm->ractor.sched.grq_cnt--;
1383 RUBY_DEBUG_LOG(
"r:%d grq_cnt:%u", (
int)rb_ractor_id(r), vm->ractor.sched.grq_cnt);
1387 VM_ASSERT(idle_streak > SNT_IDLE_RETIRE);
1390 ractor_sched_unlock(vm, cr);
1405 RUBY_DEBUG_LOG(
"start%s",
"");
1411 if (ubf_set(th, ubf, ubf_arg, &waiter->event_serial)) {
1416 thread_sched_lock(sched, th);
1417 rb_ractor_unlock_self(cr);
1422 bool dedicated = th_has_dedicated_nt(th);
1423 const rb_hrtime_t *end_p = NULL;
1424 bool armed =
false, expired =
false;
1428 end_p = waiter->end;
1432 rb_hrtime_t now = rb_hrtime_now();
1433 rb_hrtime_t rel = *waiter->end > now ? *waiter->end - now : 0;
1435 armed = ractor_sched_timeout_arm(th, &rel);
1441 RUBY_DEBUG_LOG(
"expired before sleep%s",
"");
1443 else if (armed && th->sched.waiting_reason.flags == thread_sched_waiting_none) {
1446 th->sched.event_serial++;
1450 bool can_direct_transfer = !dedicated;
1451 RB_VM_SAVE_MACHINE_CONTEXT(th);
1452 th->status = THREAD_STOPPED_FOREVER;
1453 th->sched.waiting_timed = (end_p != NULL);
1455 thread_sched_wakeup_next_thread(sched, th, can_direct_transfer);
1457 thread_sched_wait_running_turn(sched, th, can_direct_transfer, end_p);
1458 th->sched.waiting_timed =
false;
1459 th->status = THREAD_RUNNABLE;
1462 VM_ASSERT(th->sched.waiting_reason.flags == thread_sched_waiting_none);
1465 thread_sched_unlock(sched, th);
1466 rb_ractor_lock_self(cr);
1468 ubf_clear(th,
true);
1470 RUBY_DEBUG_LOG(
"end%s",
"");
1479 RUBY_DEBUG_LOG(
"r:%u th:%d", (
unsigned int)rb_ractor_id(r), r_th->serial);
1481 thread_sched_lock(sched, r_th);
1483 if (r_th->status == THREAD_STOPPED_FOREVER) {
1489 if (r_th->sched.waiting_reason.flags != thread_sched_waiting_none) {
1490 ractor_sched_timeout_disarm(r_th);
1495 if (sched->running != r_th && !r_th->sched.node.is_ready) {
1496 r_th->sched.event_serial++;
1497 thread_sched_to_ready_common(sched, r_th,
true,
false);
1501 thread_sched_unlock(sched, r_th);
1505ractor_sched_barrier_completed_p(
rb_vm_t *vm)
1509 RUBY_DEBUG_LOG(
"run:%u wait:%u", vm->ractor.sched.barrier_running_cnt, vm->ractor.sched.barrier_joined_cnt);
1510 VM_ASSERT(vm->ractor.sched.barrier_running_cnt - 1 >= vm->ractor.sched.barrier_joined_cnt);
1512 return (vm->ractor.sched.barrier_running_cnt - vm->ractor.sched.barrier_joined_cnt) == 1;
1518 VM_ASSERT(cr == GET_RACTOR());
1519 VM_ASSERT(vm->ractor.sync.lock_owner == cr);
1520 VM_ASSERT(!vm->ractor.sched.barrier_is_waiting);
1521 VM_ASSERT(vm->ractor.sched.barrier_joined_cnt == 0);
1522 VM_ASSERT(vm->ractor.sched.barrier_ractor == NULL);
1523 VM_ASSERT(vm->ractor.sched.barrier_lock_rec == 0);
1525 RUBY_DEBUG_LOG(
"start serial:%u", vm->ractor.sched.barrier_serial);
1527 unsigned int lock_rec;
1529 ractor_sched_lock(vm, cr);
1532 vm->ractor.sched.barrier_ractor = cr;
1533 vm->ractor.sched.barrier_lock_rec = vm->ractor.sync.lock_rec;
1536 lock_rec = vm->ractor.sync.lock_rec;
1537 vm->ractor.sync.lock_rec = 0;
1538 vm->ractor.sync.lock_owner = NULL;
1547 unsigned int running_cnt = 0;
1548 uint32_t serial = vm->ractor.sched.barrier_serial;
1553 ccan_list_for_each(&vm->ractor.sched.ntlist.running_dnts, dnt, running_dnts_node) {
1554 ith = dnt->running_thread;
1555 dnt->barrier_counted_serial = serial;
1557 if (ith->ractor != cr) {
1558 RUBY_DEBUG_LOG(
"barrier request to th:%u", rb_th_serial(ith));
1559 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ith->ec);
1564 ccan_list_for_each(&vm->ractor.sched.ntlist.snts, nt, snts_node) {
1567 ith = nt->running_th;
1569 nt->barrier_counted_serial = serial;
1571 if (ith->ractor != cr) {
1572 RUBY_DEBUG_LOG(
"barrier request to th:%u", rb_th_serial(ith));
1573 RUBY_VM_SET_VM_BARRIER_INTERRUPT(ith->ec);
1582 vm->ractor.sched.barrier_running_cnt = running_cnt;
1585 while (!ractor_sched_barrier_completed_p(vm)) {
1586 ractor_sched_set_unlocked(vm, cr);
1588 ractor_sched_set_locked(vm, cr);
1591 RUBY_DEBUG_LOG(
"completed seirial:%u", vm->ractor.sched.barrier_serial);
1594 vm->ractor.sched.barrier_serial++;
1595 vm->ractor.sched.barrier_joined_cnt = 0;
1600 vm->ractor.sync.lock_rec = lock_rec;
1601 vm->ractor.sync.lock_owner = cr;
1612 RUBY_DEBUG_LOG(
"serial:%u", (
unsigned int)vm->ractor.sched.barrier_serial - 1);
1613 VM_ASSERT(vm->ractor.sched.barrier_is_waiting);
1614 VM_ASSERT(vm->ractor.sched.barrier_ractor);
1615 VM_ASSERT(vm->ractor.sched.barrier_lock_rec > 0);
1618 vm->ractor.sched.barrier_ractor = NULL;
1619 vm->ractor.sched.barrier_lock_rec = 0;
1620 ractor_sched_unlock(vm, cr);
1624ractor_sched_barrier_join_signal_locked(
rb_vm_t *vm)
1626 if (ractor_sched_barrier_completed_p(vm)) {
1634 VM_ASSERT(vm->ractor.sched.barrier_is_waiting);
1636 unsigned int barrier_serial = vm->ractor.sched.barrier_serial;
1638 while (vm->ractor.sched.barrier_serial == barrier_serial) {
1639 RUBY_DEBUG_LOG(
"sleep serial:%u", barrier_serial);
1640 RB_VM_SAVE_MACHINE_CONTEXT(th);
1643 ractor_sched_set_unlocked(vm, cr);
1645 ractor_sched_set_locked(vm, cr);
1647 RUBY_DEBUG_LOG(
"wakeup serial:%u", barrier_serial);
1654 VM_ASSERT(cr->threads.sched.running != NULL);
1655 VM_ASSERT(cr == GET_RACTOR());
1656 VM_ASSERT(vm->ractor.sync.lock_owner == NULL);
1657 VM_ASSERT(vm->ractor.sched.barrier_is_waiting);
1659#if USE_RUBY_DEBUG_LOG || VM_CHECK_MODE > 0
1660 unsigned int barrier_serial = vm->ractor.sched.barrier_serial;
1663 RUBY_DEBUG_LOG(
"join");
1667 VM_ASSERT(vm->ractor.sched.barrier_is_waiting);
1668 VM_ASSERT(vm->ractor.sched.barrier_serial == barrier_serial);
1670 ractor_sched_lock(vm, cr);
1675 vm->ractor.sched.barrier_joined_cnt++;
1676 RUBY_DEBUG_LOG(
"waiting_cnt:%u serial:%u", vm->ractor.sched.barrier_joined_cnt, barrier_serial);
1678 ractor_sched_barrier_join_signal_locked(vm);
1679 ractor_sched_barrier_join_wait_locked(vm, cr->threads.sched.running);
1681 ractor_sched_unlock(vm, cr);
1698 ccan_list_del_init(&sched->timeslice_node);
1703#if defined(HAVE_WORKING_FORK)
1704static void rb_internal_thread_event_hooks_rw_lock_atfork(
void);
1710 rb_thread_sched_init(sched,
true);
1714 if (th_has_dedicated_nt(th)) {
1715 vm->ractor.sched.snt_cnt = 0;
1716#if USE_RUBY_DEBUG_LOG
1717 vm->ractor.sched.dnt_cnt = 1;
1721 vm->ractor.sched.snt_cnt = 1;
1722#if USE_RUBY_DEBUG_LOG
1723 vm->ractor.sched.dnt_cnt = 0;
1728#if VM_CHECK_MODE > 0
1729 vm->ractor.sched.lock_owner = NULL;
1730 vm->ractor.sched.locked =
false;
1738 ccan_list_head_init(&vm->ractor.sched.grq);
1739 vm->ractor.sched.grq_cnt = 0;
1742 vm->ractor.sched.barrier_is_waiting = 0;
1743 vm->ractor.sched.barrier_joined_cnt = 0;
1744 vm->ractor.sched.barrier_ractor = NULL;
1745 vm->ractor.sched.barrier_lock_rec = 0;
1749 vm->ractor.sched.winding_cnt = 0;
1751 ccan_list_head_init(&vm->ractor.sched.ntlist.running_dnts);
1752 ccan_list_head_init(&vm->ractor.sched.ntlist.snts);
1754 ccan_list_head_init(&vm->ractor.sched.timeslice.scheds);
1759 ccan_list_for_each(&vm->ractor.set, r, vmlr_node) {
1760 ccan_list_node_init(&r->threads.sched.timeslice_node);
1762 ccan_list_for_each(&vm->ractor.terminated_set, r, vmlr_node) {
1763 ccan_list_node_init(&r->threads.sched.timeslice_node);
1766 if (th->nt && th->nt->dedicated == 0) {
1769 ccan_list_add(&vm->ractor.sched.ntlist.snts, &th->nt->snts_node);
1773 nt_machine_stack_atfork();
1775 rb_internal_thread_event_hooks_rw_lock_atfork();
1777 VM_ASSERT(sched->is_running);
1779 if (sched->running != th) {
1780 thread_sched_to_running(sched, th);
1783 thread_sched_setup_running_threads(sched, th->ractor, vm, th, NULL);
1786#ifdef RB_THREAD_T_HAS_NATIVE_ID
1788 th->nt->tid = get_native_thread_id();
1795extern int ruby_mn_threads_enabled;
1798ruby_mn_threads_params(
void)
1803 const char *mn_threads_cstr = getenv(
"RUBY_MN_THREADS");
1804 bool enable_mn_threads =
false;
1806 if (USE_MN_THREADS && mn_threads_cstr && (enable_mn_threads = atoi(mn_threads_cstr) > 0)) {
1808 ruby_mn_threads_enabled = 1;
1810 main_ractor->threads.sched.enable_mn_threads = enable_mn_threads;
1812 const char *max_cpu_cstr = getenv(
"RUBY_MAX_CPU");
1813 int max_cpu = native_thread_default_max_cpu();
1815 if (USE_MN_THREADS && max_cpu_cstr) {
1816 int given_max_cpu = atoi(max_cpu_cstr);
1817 if (given_max_cpu > 0) {
1818 max_cpu = given_max_cpu;
1822 vm->ractor.sched.max_cpu = max_cpu;
1828 RUBY_DEBUG_LOG(
"nt:%d %d->%d", nt->serial, nt->dedicated, nt->dedicated + 1);
1830 if (nt->dedicated == 0) {
1835 ractor_sched_lock(vm, cr);
1837 if (vm->ractor.sched.grq_cnt > 0) {
1838 timer_thread_wakeup_locked(vm);
1841 ractor_sched_unlock(vm, cr);
1843#if USE_RUBY_DEBUG_LOG
1844 vm->ractor.sched.dnt_cnt++;
1854 RUBY_DEBUG_LOG(
"nt:%d %d->%d", nt->serial, nt->dedicated, nt->dedicated - 1);
1855 VM_ASSERT(nt->dedicated > 0);
1858 if (nt->dedicated == 0) {
1863 if (snt < vm->ractor.sched.max_cpu || (
int)snt <= MINIMUM_SNT) {
1864 if (
RUBY_ATOMIC_CAS(vm->ractor.sched.snt_cnt, snt, snt + 1) == snt)
break;
1867 nt->retiring =
true;
1871#if USE_RUBY_DEBUG_LOG
1872 vm->ractor.sched.dnt_cnt--;
1880#if USE_RUBY_DEBUG_LOG
1883 RUBY_DEBUG_LOG(
"th:%d nt:%d->%d", (
int)th->serial, (
int)th->nt->serial, (
int)nt->serial);
1886 RUBY_DEBUG_LOG(
"th:%d nt:NULL->%d", (
int)th->serial, (
int)nt->serial);
1891 RUBY_DEBUG_LOG(
"th:%d nt:%d->NULL", (
int)th->serial, (
int)th->nt->serial);
1894 RUBY_DEBUG_LOG(
"th:%d nt:NULL->NULL", (
int)th->serial);
1905 th->nt = native_thread_alloc();
1906 th->nt->vm = th->vm;
1907 th->nt->running_thread = th;
1908 th->nt->dedicated = 1;
1911 size_t vm_stack_word_size = th->vm->default_params.thread_vm_stack_size /
sizeof(
VALUE);
1912 void *vm_stack = ruby_xmalloc(vm_stack_word_size *
sizeof(
VALUE));
1913 th->sched.malloc_stack =
true;
1914 rb_ec_initialize_vm_stack(th->ec, vm_stack, vm_stack_word_size);
1915 th->sched.context_stack = vm_stack;
1916 th->sched.context_stack_size = vm_stack_word_size;
1918 int err = native_thread_create0(th->nt);
1921 thread_sched_to_ready(TH_SCHED(th), th);
1935 VALUE stack_start = 0;
1936 VALUE *stack_start_addr = asan_get_real_stack_addr(&stack_start);
1938 native_thread_init_stack(th, stack_start_addr);
1939 thread_start_func_2(th, th->ec->machine.stack_start);
1948 native_thread_setup_on_thread(nt);
1951#ifdef RB_THREAD_T_HAS_NATIVE_ID
1952 nt->tid = get_native_thread_id();
1955#if USE_RUBY_DEBUG_LOG && defined(RUBY_NT_SERIAL)
1956 ruby_nt_serial = nt->serial;
1959 RUBY_DEBUG_LOG(
"nt:%u", nt->serial);
1961 bool in_snts =
false;
1963 if (!nt->dedicated) {
1964 coroutine_initialize_main(nt->nt_context);
1969 ccan_list_add(&vm->ractor.sched.ntlist.snts, &nt->snts_node);
1975 bool retired =
false;
1978 if (nt->dedicated) {
1983 RUBY_DEBUG_LOG(
"on dedicated th:%u", rb_th_serial(th));
1984 ruby_thread_set_native(th);
1986 thread_sched_lock(sched, th);
1988 if (sched->running == th) {
1989 thread_sched_add_running_thread(sched, th);
1991 thread_sched_wait_running_turn(sched, th,
false, NULL);
1993 thread_sched_unlock(sched, th);
1996 call_thread_start_func_2(th);
2000 RUBY_DEBUG_LOG(
"check next");
2013 thread_sched_lock(sched, NULL);
2017 if (next_th && next_th->nt == NULL) {
2018 RUBY_DEBUG_LOG(
"nt:%d next_th:%d", (
int)nt->serial, (
int)next_th->serial);
2020 thread_sched_switch0(nt->nt_context, next_th, nt,
false);
2027 if (thread_sched_reclaim(dead_co)) {
2033 thread_sched_switch0(nt->nt_context, next_th, nt,
false);
2037 RUBY_DEBUG_LOG(
"no schedulable threads -- next_th:%p", next_th);
2041 thread_sched_unlock(sched, NULL);
2050 if (nt->dedicated) {
2061 VM_ASSERT(nt->running_th == NULL);
2064 ccan_list_del_init(&nt->snts_node);
2071 RUBY_DEBUG_LOG(
"retired nt:%u", nt->serial);
2072 native_thread_destroy_self(nt);
2078static int native_thread_create_shared(
rb_thread_t *th);
2081static void nt_free_stack(
void *mstack);
2092 struct rb_thread_context *tctx = (
struct rb_thread_context *)dead_co;
2094 if (tctx != NULL && tctx->dead) {
2095 nt_free_stack(tctx->stack);
2110 timer_thread_wake_fence(th);
2116 timer_thread_wake_fence(th);
2118 if (th->sched.malloc_stack) {
2120 SIZED_FREE_N((
VALUE *)th->sched.context_stack, th->sched.context_stack_size);
2121 native_thread_destroy(th->nt);
2123 else if (th->sched.context != NULL) {
2127 struct rb_thread_context *tctx = (
struct rb_thread_context *)th->sched.context;
2128 nt_free_stack(tctx->stack);
2130 th->sched.context = NULL;
2134 SIZED_FREE_N((
VALUE *)th->sched.context_stack, th->sched.context_stack_size);
2135 native_thread_destroy(th->nt);
2145 VM_ASSERT(th->nt == 0);
2146 RUBY_DEBUG_LOG(
"th:%d has_dnt:%d", th->serial, th->has_dedicated_nt);
2149 if (!th->ractor->threads.sched.enable_mn_threads) {
2150 th->has_dedicated_nt = 1;
2153 if (th->has_dedicated_nt) {
2154 return native_thread_create_dedicated(th);
2157 return native_thread_create_shared(th);
2162static CCAN_LIST_HEAD(ubf_list_head);
2163#ifdef RB_NATIVETHREAD_LOCK_INIT
2164static rb_nativethread_lock_t ubf_list_lock = RB_NATIVETHREAD_LOCK_INIT;
2167static rb_nativethread_lock_t ubf_list_lock;
2171ubf_list_atfork(
void)
2173 ccan_list_head_init(&ubf_list_head);
2182 ccan_list_for_each(&ubf_list_head, list_th, sched.node.ubf) {
2183 if (list_th == th)
return true;
2192 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
2193 struct ccan_list_node *node = &th->sched.node.ubf;
2195 VM_ASSERT(th->unblock.func != NULL);
2200 if (ccan_list_empty((
struct ccan_list_head*)node)) {
2201 VM_ASSERT(!ubf_list_contain_p(th));
2202 ccan_list_add(&ubf_list_head, node);
2207 timer_thread_wakeup();
2214 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
2215 struct ccan_list_node *node = &th->sched.node.ubf;
2218 VM_ASSERT(th->unblock.func == NULL);
2220 if (!ccan_list_empty((
struct ccan_list_head*)node)) {
2223 VM_ASSERT(ubf_list_contain_p(th));
2224 ccan_list_del_init(node);
2237 RUBY_DEBUG_LOG(
"th:%u thread_id:%p", rb_th_serial(th), (
void *)th->nt->thread_id);
2239 native_thread_interrupt(th);
2243ubf_select(
void *ptr)
2246 RUBY_DEBUG_LOG(
"wakeup th:%u", rb_th_serial(th));
2247 ubf_wakeup_thread(th);
2248 register_ubf_list(th);
2252ubf_threads_empty(
void)
2254 return ccan_list_empty(&ubf_list_head) != 0;
2258ubf_wakeup_all_threads(
void)
2263 ccan_list_for_each(&ubf_list_head, th, sched.node.ubf) {
2264 ubf_wakeup_thread(th);
2271#define register_ubf_list(th) (void)(th)
2272#define unregister_ubf_list(th) (void)(th)
2274static void ubf_wakeup_all_threads(
void) {
return; }
2275static bool ubf_threads_empty(
void) {
return true; }
2276#define ubf_list_atfork() do {} while (0)
2280timer_thread_set_timeout(
rb_vm_t *vm)
2287 ractor_sched_lock(vm, NULL);
2289 if ( timeslice_scan(vm,
false)
2290 || !ubf_threads_empty()
2291 || vm->ractor.sched.grq_cnt > 0
2294 RUBY_DEBUG_LOG(
"ubf:%d grq:%d",
2295 !ubf_threads_empty(),
2296 (vm->ractor.sched.grq_cnt > 0));
2299 vm->ractor.sched.timeslice_wait_inf =
false;
2302 vm->ractor.sched.timeslice_wait_inf =
true;
2305 ractor_sched_unlock(vm, NULL);
2307 timeout = timer_wheel_timeout(timeout);
2309 RUBY_DEBUG_LOG(
"timeout:%d inf:%d", timeout, (
int)vm->ractor.sched.timeslice_wait_inf);
2317timer_thread_check_signal(
rb_vm_t *vm)
2321 int signum = rb_signal_buff_size();
2322 if (UNLIKELY(signum > 0) && vm->ractor.main_thread) {
2323 RUBY_DEBUG_LOG(
"signum:%d", signum);
2324 threadptr_trap_interrupt(vm->ractor.main_thread);
2331timeslice_scan(
rb_vm_t *vm,
bool interrupt)
2338 ccan_list_for_each_safe(&vm->ractor.sched.timeslice.scheds, sched, next, timeslice_node) {
2342 if (ccan_list_empty(&sched->readyq)) {
2343 ccan_list_del_init(&sched->timeslice_node);
2345 else if (sched->is_running) {
2346 VM_ASSERT(sched->running != NULL);
2349 RUBY_DEBUG_LOG(
"timeslice th:%u", rb_th_serial(sched->running));
2350 RUBY_VM_SET_TIMER_INTERRUPT(sched->running->ec);
2368timer_thread_check_timeslice(
rb_vm_t *vm)
2371 timeslice_scan(vm,
true);
2375timer_thread_func(
void *ptr)
2378#if defined(RUBY_NT_SERIAL)
2382 RUBY_DEBUG_LOG(
"started%s",
"");
2385 timer_thread_check_signal(vm);
2386 timer_thread_check_timeout(vm);
2387 ubf_wakeup_all_threads();
2390 timer_thread_polling(vm);
2393 RUBY_DEBUG_LOG(
"terminated");
2398timer_thread_wakeup_locked(
rb_vm_t *vm)
2401 ASSERT_ractor_sched_locked(vm, NULL);
2403 if (TIMER_THREAD_CREATED_P()) {
2404 if (vm->ractor.sched.timeslice_wait_inf) {
2405 RUBY_DEBUG_LOG(
"wakeup%s",
"");
2406 timer_thread_wakeup_force();
2409 RUBY_DEBUG_LOG(
"will be wakeup...");
2415timer_thread_wakeup(
void)
2419 ractor_sched_lock(vm, NULL);
2421 timer_thread_wakeup_locked(vm);
2423 ractor_sched_unlock(vm, NULL);
2431 RUBY_DEBUG_LOG(
"rel:%d", rel ? (
int)*rel : 0);
2433 if (rel && !th_has_dedicated_nt(th)) {
2435 thread_sched_wait_events(sched, th, -1, thread_sched_waiting_timeout, rel);
2446 const rb_hrtime_t max = (rb_hrtime_t)100000000 * RB_HRTIME_PER_SEC;
2447 if (*rel > max) *rel = max;
2449 rb_hrtime_t end = rb_hrtime_add(rb_hrtime_now(), *rel);
2450 thread_sched_to_waiting_until_wakeup(sched, th, &end);
2453 thread_sched_to_waiting_until_wakeup(sched, th, NULL);
2456 RUBY_DEBUG_LOG(
"wakeup");
2466 bool is_snt = th->nt->dedicated == 0;
2467 native_thread_dedicated_inc(th->vm, th->ractor, th->nt);
2473rb_thread_malloc_stack_set(
rb_thread_t *th,
void *stack,
size_t stack_size)
2475 th->sched.malloc_stack =
true;
2476 th->sched.context_stack = stack;
2477 th->sched.context_stack_size = stack_size;
2483thread_sched_init_vm(
rb_vm_t *vm)
2490 ccan_list_head_init(&vm->ractor.sched.grq);
2492 ccan_list_head_init(&vm->ractor.sched.ntlist.running_dnts);
2493 ccan_list_head_init(&vm->ractor.sched.ntlist.snts);
2495 ccan_list_head_init(&vm->ractor.sched.timeslice.scheds);
2497#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_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.
uintptr_t VALUE
Type that represents a Ruby object.