5#if HAVE_SYS_EPOLL_H || HAVE_SYS_EVENT_H
6static void timer_thread_unregister_waiting(
rb_thread_t *th,
int fd,
enum thread_sched_waiting_flag flags);
10timer_thread_check_exceed(rb_hrtime_t abs, rb_hrtime_t now)
26#define TIMER_WHEEL_NO_EXPIRY RB_HRTIME_MAX
27#ifndef TIMER_WHEEL_TICK_MS
28#define TIMER_WHEEL_TICK_MS 1
44timer_wheel_tick(rb_hrtime_t hrt)
46 return hrt / (RB_HRTIME_PER_MSEC * TIMER_WHEEL_TICK_MS);
50timer_wheel_ctz64(uint64_t v)
52#if defined(__GNUC__) || defined(__clang__)
53 return __builtin_ctzll(v);
56 while (!(v & 1)) { v >>= 1; n++; }
62timer_wheel_level(uint64_t dist_ms)
64 if (dist_ms < ((uint64_t)1 << TIMER_WHEEL_SLOT_BITS))
return 0;
65 if (dist_ms < ((uint64_t)1 << 2 * TIMER_WHEEL_SLOT_BITS))
return 1;
66 if (dist_ms < ((uint64_t)1 << 3 * TIMER_WHEEL_SLOT_BITS))
return 2;
67 return TIMER_WHEEL_LEVELS - 1;
73 uint64_t dl_tick = timer_wheel_tick(w->data.timeout);
74 uint64_t cur = timer_th.wheel_cursor_tick;
78 uint64_t target = dl_tick > cur ? dl_tick : cur + 1;
79 int lvl = timer_wheel_level(target - cur);
80 int shift = lvl * TIMER_WHEEL_SLOT_BITS;
81 uint64_t slot_tick = target >> shift;
83 if (lvl == TIMER_WHEEL_LEVELS - 1) {
86 uint64_t far = (cur >> shift) + TIMER_WHEEL_SLOTS - 1;
87 if (slot_tick > far) slot_tick = far;
90 int slot = (int)(slot_tick & (TIMER_WHEEL_SLOTS - 1));
92 ccan_list_add_tail(&timer_th.wheel[lvl].slots[slot], &w->node);
93 timer_th.wheel[lvl].occupied |= UINT64_C(1) << slot;
94 w->wheel_lvl = (uint8_t)lvl;
95 w->wheel_slot = (uint8_t)slot;
97 if (w->data.timeout < timer_th.next_expiry) {
98 timer_th.next_expiry = w->data.timeout;
106 ccan_list_del_init(&w->node);
108 if (w->flags & thread_sched_waiting_timeout) {
109 struct timer_wheel_level *lv = &timer_th.wheel[w->wheel_lvl];
110 if (ccan_list_empty(&lv->slots[w->wheel_slot])) {
111 lv->occupied &= ~(UINT64_C(1) << w->wheel_slot);
120timer_wheel_next_expiry(
void)
122 rb_hrtime_t best = TIMER_WHEEL_NO_EXPIRY;
124 for (
int lvl = 0; lvl < TIMER_WHEEL_LEVELS; lvl++) {
125 uint64_t occ = timer_th.wheel[lvl].occupied;
128 int shift = lvl * TIMER_WHEEL_SLOT_BITS;
129 uint64_t cur_tick = timer_th.wheel_cursor_tick >> shift;
130 unsigned base = (unsigned)((cur_tick + 1) & (TIMER_WHEEL_SLOTS - 1));
132 uint64_t rot = (occ >> base) | (base ? (occ << (TIMER_WHEEL_SLOTS - base)) : 0);
133 uint64_t tick = cur_tick + 1 + timer_wheel_ctz64(rot);
134 rb_hrtime_t start = (rb_hrtime_t)(tick << shift) * RB_HRTIME_PER_MSEC * TIMER_WHEEL_TICK_MS;
136 if (start < best) best = start;
146timer_wheel_drain(rb_hrtime_t now, uint64_t now_tick,
struct ccan_list_head *expired)
148 uint64_t prev_tick = timer_th.wheel_cursor_tick;
149 timer_th.wheel_cursor_tick = now_tick;
153 rb_hrtime_t reinserted = TIMER_WHEEL_NO_EXPIRY;
155 for (
int lvl = 0; lvl < TIMER_WHEEL_LEVELS; lvl++) {
156 int shift = lvl * TIMER_WHEEL_SLOT_BITS;
157 uint64_t from = prev_tick >> shift;
158 uint64_t to = now_tick >> shift;
160 if (to == from)
break;
162 uint64_t steps = to - from;
163 if (steps > TIMER_WHEEL_SLOTS) steps = TIMER_WHEEL_SLOTS;
164 struct timer_wheel_level *lv = &timer_th.wheel[lvl];
166 for (uint64_t tick = to - steps + 1; tick <= to; tick++) {
167 int slot = (int)(tick & (TIMER_WHEEL_SLOTS - 1));
169 if (!(lv->occupied & (UINT64_C(1) << slot)))
continue;
170 lv->occupied &= ~(UINT64_C(1) << slot);
174 struct ccan_list_head pending;
175 ccan_list_head_init(&pending);
176 ccan_list_append_list(&pending, &lv->slots[slot]);
180 if (timer_thread_check_exceed(w->data.timeout, now)) {
183 RUBY_DEBUG_LOG(
"wakeup th:%u", rb_th_serial(th));
185#if HAVE_SYS_EPOLL_H || HAVE_SYS_EVENT_H
187 timer_thread_unregister_waiting(th, w->data.fd, w->flags);
195 ccan_list_add_tail(expired, &w->node);
200 if (w->data.timeout < reinserted) reinserted = w->data.timeout;
201 timer_wheel_insert(w);
207 timer_th.next_expiry = timer_wheel_next_expiry();
208 if (reinserted < timer_th.next_expiry) timer_th.next_expiry = reinserted;
214timer_wheel_timeout(
int timeout)
218 if (timer_th.next_expiry != TIMER_WHEEL_NO_EXPIRY) {
219 rb_hrtime_t now = rb_hrtime_now();
220 rb_hrtime_t hrrel = rb_hrtime_sub(timer_th.next_expiry, now);
222 RUBY_DEBUG_LOG(
"now:%lu rel:%lu", (
unsigned long)now, (
unsigned long)hrrel);
224 rb_hrtime_t msec = (hrrel + RB_HRTIME_PER_MSEC - 1) / RB_HRTIME_PER_MSEC;
227 int thread_timeout = msec > INT_MAX ? INT_MAX : (int)msec;
230 if (timeout < 0 || thread_timeout < timeout) {
231 timeout = thread_timeout;
243 if (sched->running != th && th->sched.event_serial == event_serial) {
244 thread_sched_to_ready_common(sched, th,
true,
false);
249timer_thread_wakeup_thread(
rb_thread_t *th, uint32_t event_serial)
251 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
254 thread_sched_lock(sched, th);
256 timer_thread_wakeup_thread_locked(sched, th, event_serial);
258 thread_sched_unlock(sched, th);
261#define TIMEOUT_WAKE_BATCH 16
264timer_thread_check_timeout(
rb_vm_t *vm)
266 rb_hrtime_t now = rb_hrtime_now();
267 uint64_t now_tick = timer_wheel_tick(now);
268 struct ccan_list_head expired;
270 ccan_list_head_init(&expired);
272 struct timeout_wake {
rb_thread_t *th; uint32_t serial; } batch[TIMEOUT_WAKE_BATCH];
281 if (now_tick > timer_th.wheel_cursor_tick) {
282 timer_wheel_drain(now, now_tick, &expired);
286 while (n < TIMEOUT_WAKE_BATCH &&
291 batch[n].th = thread_sched_waiting_thread(w);
292 batch[n].serial = w->data.event_serial;
293 w->flags = thread_sched_waiting_none;
296 more = !ccan_list_empty(&expired);
300 for (
int i = 0; i < n; i++) {
301 timer_thread_wakeup_thread(batch[i].th, batch[i].serial);
309 bool canceled =
false;
313 if (th->sched.waiting_reason.flags) {
315 timer_wheel_del(&th->sched.waiting_reason);
316 timer_thread_unregister_waiting(th, th->sched.waiting_reason.data.fd, th->sched.waiting_reason.flags);
317 th->sched.waiting_reason.flags = thread_sched_waiting_none;
326ubf_event_waiting(
void *ptr)
331 RUBY_DEBUG_LOG(
"th:%u", rb_th_serial(th));
333 VM_ASSERT(th->nt == NULL || !th_has_dedicated_nt(th));
336 th->unblock.func = NULL;
337 th->unblock.arg = NULL;
339 thread_sched_lock(sched, th);
341 bool canceled = timer_thread_cancel_waiting(th);
343 if (sched->running == th) {
344 RUBY_DEBUG_LOG(
"not waiting yet");
347 thread_sched_to_ready_common(sched, th,
true,
false);
350 RUBY_DEBUG_LOG(
"already not waiting");
353 thread_sched_unlock(sched, th);
358enum timer_thread_register_result {
359 timer_thread_registered,
360 timer_thread_already_ready,
361 timer_thread_unavailable,
364static enum timer_thread_register_result
365timer_thread_register_waiting(
rb_thread_t *th,
int fd,
enum thread_sched_waiting_flag flags, rb_hrtime_t *rel, uint32_t event_serial);
368static enum thread_sched_wait_result
369thread_sched_wait_events(
struct rb_thread_sched *sched,
rb_thread_t *th,
int fd,
enum thread_sched_waiting_flag events, rb_hrtime_t *rel)
371 VM_ASSERT(!th_has_dedicated_nt(th));
373 volatile bool timedout =
false, need_cancel =
false;
374 volatile enum timer_thread_register_result reg = timer_thread_unavailable;
376 uint32_t event_serial = ++th->sched.event_serial;
379 thread_sched_lock(sched, th);
382 if (ubf_set(th, ubf_event_waiting, (
void *)th, NULL)) {
385 thread_sched_unlock(sched, th);
386 return thread_sched_wait_event;
389 reg = timer_thread_register_waiting(th, fd, events, rel, event_serial);
391 if (reg == timer_thread_registered) {
392 RUBY_DEBUG_LOG(
"wait fd:%d", fd);
394 RB_VM_SAVE_MACHINE_CONTEXT(th);
398 if (th->sched.waiting_reason.flags == thread_sched_waiting_none) {
399 th->sched.event_serial++;
402 else if (RUBY_VM_INTERRUPTED(th->ec)) {
403 th->sched.event_serial++;
407 RUBY_DEBUG_LOG(
"sleep");
414 enum rb_thread_status prev_status = th->status;
415 if (prev_status == THREAD_RUNNABLE) th->status = THREAD_STOPPED_FOREVER;
416 thread_sched_wakeup_next_thread(sched, th,
true);
417 thread_sched_wait_running_turn(sched, th,
true);
418 if (prev_status == THREAD_RUNNABLE) th->status = THREAD_RUNNABLE;
420 RUBY_DEBUG_LOG(
"wakeup");
423 timedout = th->sched.waiting_reason.data.result == 0;
426 timer_thread_cancel_waiting(th);
432 RUBY_DEBUG_LOG(
"did not wait fd:%d reg:%d", fd, (
int)reg);
435 thread_sched_unlock(sched, th);
438 ubf_clear(th,
false);
440 VM_ASSERT(sched->running == th);
442 if (reg == timer_thread_unavailable)
return thread_sched_wait_unavailable;
444 if (reg == timer_thread_already_ready)
return thread_sched_wait_event;
445 return timedout ? thread_sched_wait_timeout : thread_sched_wait_event;
451get_sysconf_page_size(
void)
453 static long page_size = 0;
455 if (UNLIKELY(page_size == 0)) {
456 page_size = sysconf(_SC_PAGESIZE);
457 VM_ASSERT(page_size < INT_MAX);
459 return (
int)page_size;
462#define MSTACK_CHUNK_SIZE (512 * 1024 * 1024)
463#define MSTACK_PAGE_SIZE get_sysconf_page_size()
464#define MSTACK_CHUNK_PAGE_NUM (MSTACK_CHUNK_SIZE / MSTACK_PAGE_SIZE - 1)
477static struct nt_stack_chunk_header {
478 struct nt_stack_chunk_header *prev_chunk;
479 struct nt_stack_chunk_header *prev_free_chunk;
485 uint16_t stack_count;
486 uint16_t uninitialized_stack_count;
488 uint16_t free_stack_pos;
489 uint16_t free_stack[];
490} *nt_stack_chunks = NULL,
491 *nt_free_stack_chunks = NULL;
493struct nt_machine_stack_footer {
494 struct nt_stack_chunk_header *ch;
498static rb_nativethread_lock_t nt_machine_stack_lock = RB_NATIVETHREAD_LOCK_INIT;
502nt_machine_stack_atfork(
void)
514nt_vm_stack_area(
const rb_vm_t *vm)
516 return (
size_t)roomof(vm->default_params.thread_vm_stack_size, MSTACK_PAGE_SIZE) * MSTACK_PAGE_SIZE;
520nt_machine_stack_area(
const rb_vm_t *vm)
522 return (
size_t)roomof(vm->default_params.thread_machine_stack_size, MSTACK_PAGE_SIZE) * MSTACK_PAGE_SIZE;
527nt_thread_stack_size(
void)
530 if (LIKELY(msz > 0))
return msz;
533 msz = nt_vm_stack_area(vm) + MSTACK_PAGE_SIZE + nt_machine_stack_area(vm);
537static struct nt_stack_chunk_header *
538nt_alloc_thread_stack_chunk(
void)
540 const char *m = (
void *)mmap(NULL, MSTACK_CHUNK_SIZE, PROT_NONE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
541 if (m == MAP_FAILED) {
545 ruby_annotate_mmap(m, MSTACK_CHUNK_SIZE,
"Ruby:nt_alloc_thread_stack_chunk");
547 size_t msz = nt_thread_stack_size();
548 int header_page_cnt = 1;
549 int stack_count = ((MSTACK_CHUNK_PAGE_NUM - header_page_cnt) * MSTACK_PAGE_SIZE) / msz;
550 int ch_size =
sizeof(
struct nt_stack_chunk_header) + sizeof(uint16_t) * stack_count;
552 if (ch_size > MSTACK_PAGE_SIZE * header_page_cnt) {
553 header_page_cnt = (ch_size + MSTACK_PAGE_SIZE - 1) / MSTACK_PAGE_SIZE;
554 stack_count = ((MSTACK_CHUNK_PAGE_NUM - header_page_cnt) * MSTACK_PAGE_SIZE) / msz;
557 VM_ASSERT(stack_count <= UINT16_MAX);
560 if (mprotect((
void *)m, (
size_t)header_page_cnt * MSTACK_PAGE_SIZE, PROT_READ | PROT_WRITE) != 0) {
561 munmap((
void *)m, MSTACK_CHUNK_SIZE);
565 struct nt_stack_chunk_header *ch = (
struct nt_stack_chunk_header *)m;
567 ch->start_page = header_page_cnt;
568 ch->prev_chunk = nt_stack_chunks;
569 ch->prev_free_chunk = nt_free_stack_chunks;
570 ch->on_free_list =
true;
571 ch->uninitialized_stack_count = ch->stack_count = (uint16_t)stack_count;
572 ch->free_stack_pos = 0;
574 RUBY_DEBUG_LOG(
"ch:%p start_page:%d stack_cnt:%d stack_size:%d", ch, (
int)ch->start_page, (
int)ch->stack_count, (
int)msz);
580nt_stack_chunk_get_stack_start(
struct nt_stack_chunk_header *ch,
size_t idx)
582 const char *m = (
char *)ch;
583 return (
void *)(m + ch->start_page * MSTACK_PAGE_SIZE + idx * nt_thread_stack_size());
586static struct nt_machine_stack_footer *
587nt_stack_chunk_get_msf(
const rb_vm_t *vm,
const char *mstack)
590 const size_t msz = vm->default_params.thread_machine_stack_size;
591 return (
struct nt_machine_stack_footer *)&mstack[msz -
sizeof(
struct nt_machine_stack_footer)];
595nt_stack_chunk_get_stack(
const rb_vm_t *vm,
struct nt_stack_chunk_header *ch,
size_t idx,
void **vm_stack,
void **machine_stack)
600 const char *vstack, *mstack;
601 const char *guard_page;
602 vstack = nt_stack_chunk_get_stack_start(ch, idx);
603 guard_page = vstack + nt_vm_stack_area(vm);
604 mstack = guard_page + MSTACK_PAGE_SIZE;
606 struct nt_machine_stack_footer *msf = nt_stack_chunk_get_msf(vm, mstack);
611 RUBY_DEBUG_LOG(
"msf:%p vstack:%p-%p guard_page:%p-%p mstack:%p-%p", msf,
612 vstack, (
void *)(guard_page-1),
613 guard_page, (
void *)(mstack-1),
614 mstack, (
void *)(msf));
617 *vm_stack = (
void *)vstack;
618 *machine_stack = (
void *)mstack;
623nt_stack_chunk_dump(
void)
625 struct nt_stack_chunk_header *ch;
628 fprintf(stderr,
"** nt_stack_chunks\n");
629 ch = nt_stack_chunks;
630 for (i=0; ch; i++, ch = ch->prev_chunk) {
631 fprintf(stderr,
"%d %p free_pos:%d\n", i, (
void *)ch, (
int)ch->free_stack_pos);
634 fprintf(stderr,
"** nt_free_stack_chunks\n");
635 ch = nt_free_stack_chunks;
636 for (i=0; ch; i++, ch = ch->prev_free_chunk) {
637 fprintf(stderr,
"%d %p free_pos:%d\n", i, (
void *)ch, (
int)ch->free_stack_pos);
642nt_alloc_stack(
rb_vm_t *vm,
void **vm_stack,
void **machine_stack)
649 if (nt_free_stack_chunks) {
650 struct nt_stack_chunk_header *ch = nt_free_stack_chunks;
651 if (ch->free_stack_pos > 0) {
652 RUBY_DEBUG_LOG(
"free_stack_pos:%d", ch->free_stack_pos);
653 nt_stack_chunk_get_stack(vm, ch, ch->free_stack[--ch->free_stack_pos], vm_stack, machine_stack);
655 else if (ch->uninitialized_stack_count > 0) {
656 RUBY_DEBUG_LOG(
"uninitialized_stack_count:%d", ch->uninitialized_stack_count);
658 size_t idx = ch->stack_count - ch->uninitialized_stack_count--;
662 char *stack_start = nt_stack_chunk_get_stack_start(ch, idx);
663 size_t vm_stack_area = nt_vm_stack_area(vm);
664 size_t mstack_size = nt_thread_stack_size() - vm_stack_area - MSTACK_PAGE_SIZE;
665 char *mstack_start = stack_start + vm_stack_area + MSTACK_PAGE_SIZE;
667 int mstack_flags = MAP_FIXED | MAP_ANONYMOUS | MAP_PRIVATE;
668#if defined(MAP_STACK) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
669 mstack_flags |= MAP_STACK;
672 if (mprotect(stack_start, vm_stack_area, PROT_READ | PROT_WRITE) != 0 ||
673 mmap(mstack_start, mstack_size, PROT_READ | PROT_WRITE, mstack_flags, -1, 0) == MAP_FAILED) {
675 ch->uninitialized_stack_count++;
678 nt_stack_chunk_get_stack(vm, ch, idx, vm_stack, machine_stack);
682 nt_free_stack_chunks = ch->prev_free_chunk;
683 ch->prev_free_chunk = NULL;
684 ch->on_free_list =
false;
689 struct nt_stack_chunk_header *p = nt_alloc_thread_stack_chunk();
694 nt_free_stack_chunks = nt_stack_chunks = p;
705nt_madvise_free_or_dontneed(
void *addr,
size_t len)
718#if defined(MADV_FREE)
719 int r = madvise(addr,
len, MADV_FREE);
723#if defined(MADV_DONTNEED)
724 madvise(addr,
len, MADV_DONTNEED);
729nt_free_stack(
void *mstack)
735 struct nt_machine_stack_footer *msf = nt_stack_chunk_get_msf(GET_VM(), mstack);
736 struct nt_stack_chunk_header *ch = msf->ch;
737 int idx = (int)msf->index;
738 void *stack = nt_stack_chunk_get_stack_start(ch, idx);
740 RUBY_DEBUG_LOG(
"stack:%p mstack:%p ch:%p index:%d", stack, mstack, ch, idx);
742 if (!ch->on_free_list) {
743 ch->on_free_list =
true;
744 ch->prev_free_chunk = nt_free_stack_chunks;
745 nt_free_stack_chunks = ch;
747 ch->free_stack[ch->free_stack_pos++] = idx;
750 nt_madvise_free_or_dontneed(stack, nt_thread_stack_size());
757native_thread_check_and_create_shared(
rb_vm_t *vm)
759 bool need_to_make =
false;
761 ractor_sched_lock(vm, NULL);
763 unsigned int schedulable_ractor_cnt = vm->ractor.cnt;
766 if (!vm->ractor.main_ractor->threads.sched.enable_mn_threads)
767 schedulable_ractor_cnt--;
769 unsigned int snt_cnt = vm->ractor.sched.snt_cnt;
770 if (((
int)snt_cnt < MINIMUM_SNT) ||
771 (snt_cnt < schedulable_ractor_cnt &&
772 snt_cnt < vm->ractor.sched.max_cpu)) {
774 RUBY_DEBUG_LOG(
"added snt:%u dnt:%u ractor_cnt:%u grq_cnt:%u",
775 vm->ractor.sched.snt_cnt,
776 vm->ractor.sched.dnt_cnt,
778 vm->ractor.sched.grq_cnt);
780 vm->ractor.sched.snt_cnt++;
784 RUBY_DEBUG_LOG(
"snt:%d ractor_cnt:%d", (
int)vm->ractor.sched.snt_cnt, (
int)vm->ractor.cnt);
787 ractor_sched_unlock(vm, NULL);
792 int err = native_thread_create0(nt);
796 ractor_sched_lock(vm, NULL);
797 vm->ractor.sched.snt_cnt--;
798 ractor_sched_unlock(vm, NULL);
799 native_thread_destroy(nt);
814 struct rb_thread_context *tctx = (
struct rb_thread_context *)th->sched.context;
817 bool last = (th->invoke_type == thread_invoke_type_ractor_proc);
818 bool is_dnt = th_has_dedicated_nt(th);
842 VM_ASSERT(sched->running == th);
843 if (!last) rb_ractor_living_threads_remove(r, th);
845 thread_sched_lock(sched, th);
849 thread_sched_to_dead_common(sched, th);
858 wake_th = is_dnt ? NULL : sched->running;
861 native_thread_assign(NULL, th);
862 th->sched.context = NULL;
864 thread_sched_unlock(sched, th);
869 VM_ASSERT(sched->running == NULL);
870 VM_ASSERT(wake_th == NULL);
873 rb_ractor_living_threads_remove(r, th);
874 rb_current_ec_set(NULL);
877 rb_ractor_set_current_ec(r, NULL);
879 if (wake_th && wake_th->nt == NULL) {
882 thread_sched_lock(sched, NULL);
883 ractor_sched_enq(wake_th->vm, r);
884 thread_sched_unlock(sched, NULL);
890# define co_start ruby_coroutine_start
897#ifdef RUBY_ASAN_ENABLED
898 __sanitizer_finish_switch_fiber(self->fake_stack,
899 (
const void**)&from->stack_base, &from->stack_size);
904 VM_ASSERT(th->nt != NULL);
905 VM_ASSERT(th == sched->running);
906 VM_ASSERT(sched->lock_owner == NULL);
910 thread_sched_set_locked(sched, th);
911 thread_sched_add_running_thread(TH_SCHED(th), th);
912 thread_sched_unlock(sched, th);
915 call_thread_start_func_2(th);
922 struct rb_thread_context *tctx = (
struct rb_thread_context *)self;
926 tctx->nt->dead_co = &tctx->co;
927 coroutine_transfer0(&tctx->co, tctx->nt->nt_context,
true);
929 rb_bug(
"unreachable");
937 void *vm_stack = NULL, *machine_stack = NULL;
938 int err = nt_alloc_stack(vm, &vm_stack, &machine_stack);
941 VM_ASSERT(vm_stack < machine_stack);
944 size_t vm_stack_words = th->vm->default_params.thread_vm_stack_size/
sizeof(
VALUE);
945 rb_ec_initialize_vm_stack(th->ec, vm_stack, vm_stack_words);
948 size_t machine_stack_size = vm->default_params.thread_machine_stack_size -
sizeof(
struct nt_machine_stack_footer);
949 th->ec->machine.stack_start = (
void *)((uintptr_t)machine_stack + machine_stack_size);
950 th->ec->machine.stack_maxsize = machine_stack_size;
951 th->sched.context_stack = machine_stack;
952 th->sched.context_stack_size = machine_stack_size;
954 struct rb_thread_context *tctx = ruby_xmalloc(
sizeof(
struct rb_thread_context));
955 tctx->stack = machine_stack;
957 th->sched.context = &tctx->co;
958 coroutine_initialize(&tctx->co, co_start, machine_stack, machine_stack_size);
959 tctx->co.argument = th;
961 RUBY_DEBUG_LOG(
"th:%u vm_stack:%p machine_stack:%p", rb_th_serial(th), vm_stack, machine_stack);
966 int create_err = native_thread_check_and_create_shared(vm);
967 if (create_err)
return create_err;
969 thread_sched_to_ready(TH_SCHED(th), th);
974#if HAVE_SYS_EPOLL_H || HAVE_SYS_EVENT_H
980#define FD_WAIT_IO_MASK (thread_sched_waiting_io_read | thread_sched_waiting_io_write)
982#define FDMAP_CHUNK_BITS 10
983#define FDMAP_CHUNK_SIZE (1u << FDMAP_CHUNK_BITS)
984#define FDMAP_CHUNK_MASK (FDMAP_CHUNK_SIZE - 1)
988fd_waiters_lookup(
int fd,
bool create)
990 if (fd < 0)
return NULL;
992 unsigned int ci = (
unsigned int)fd >> FDMAP_CHUNK_BITS;
994 if (ci >= timer_th.fdmap_nchunks) {
995 if (!create)
return NULL;
997 unsigned int n = timer_th.fdmap_nchunks ? timer_th.fdmap_nchunks : 8;
998 while (n <= ci) n *= 2;
1002 struct rb_fd_waiters **chunks = realloc(timer_th.fdmap_chunks,
sizeof(*chunks) * n);
1003 if (chunks == NULL) rb_bug(
"fd_waiters_lookup: realloc failed");
1005 for (
unsigned int i = timer_th.fdmap_nchunks; i < n; i++) chunks[i] = NULL;
1006 timer_th.fdmap_chunks = chunks;
1007 timer_th.fdmap_nchunks = n;
1010 if (timer_th.fdmap_chunks[ci] == NULL) {
1011 if (!create)
return NULL;
1013 struct rb_fd_waiters *chunk = calloc(FDMAP_CHUNK_SIZE,
sizeof(*chunk));
1014 if (chunk == NULL) rb_bug(
"fd_waiters_lookup: calloc failed");
1016 for (
unsigned int i = 0; i < FDMAP_CHUNK_SIZE; i++) {
1017 ccan_list_head_init(&chunk[i].waiters);
1019 timer_th.fdmap_chunks[ci] = chunk;
1022 return &timer_th.fdmap_chunks[ci][(
unsigned int)fd & FDMAP_CHUNK_MASK];
1032 ccan_list_for_each(&e->waiters, w, fd_node) {
1033 want |= (uint32_t)(w->flags & FD_WAIT_IO_MASK);
1040static inline uint64_t
1041fd_event_tag(
int fd, uint32_t generation)
1043 return ((uint64_t)generation << 32) | (uint32_t)fd;
1046#define FD_EVENT_TAG_FD(tag) ((int)((tag) & 0xffffffffu))
1047#define FD_EVENT_TAG_GEN(tag) ((uint32_t)((tag) >> 32))
1053fd_waiters_arm(
int fd,
struct rb_fd_waiters *e, uint32_t want)
1055 if (want == e->armed_flags)
return true;
1058 struct kevent ke[2];
1060 uint32_t add = want & ~e->armed_flags;
1061 uint32_t del = e->armed_flags & ~want;
1062 void *tag = (
void *)(uintptr_t)fd_event_tag(fd, e->generation);
1064 if (del & thread_sched_waiting_io_read) { EV_SET(&ke[n], fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); n++; }
1065 if (del & thread_sched_waiting_io_write) { EV_SET(&ke[n], fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); n++; }
1067 if (n > 0 && kevent(timer_th.event_fd, ke, n, NULL, 0, NULL) == -1) {
1071 rb_bug(
"fd_waiters_arm/kevent delete failed (fd:%d errno:%d)", fd,
errno);
1076 if (add & thread_sched_waiting_io_read) { EV_SET(&ke[n], fd, EVFILT_READ, EV_ADD, 0, 0, tag); n++; }
1077 if (add & thread_sched_waiting_io_write) { EV_SET(&ke[n], fd, EVFILT_WRITE, EV_ADD, 0, 0, tag); n++; }
1079 if (n > 0 && kevent(timer_th.event_fd, ke, n, NULL, 0, NULL) == -1) {
1086 rb_bug(
"fd_waiters_arm/kevent add failed (fd:%d errno:%d)", fd,
errno);
1089#elif HAVE_SYS_EPOLL_H
1091 if (epoll_ctl(timer_th.event_fd, EPOLL_CTL_DEL, fd, NULL) == -1) {
1098 perror(
"epoll_ctl");
1099 rb_bug(
"fd_waiters_arm/epoll_ctl del failed (fd:%d errno:%d)", fd,
errno);
1108 uint32_t epoll_events = 0;
1109 if (want & thread_sched_waiting_io_read) epoll_events |= EPOLLIN;
1110 if (want & thread_sched_waiting_io_write) epoll_events |= EPOLLOUT;
1112 struct epoll_event event = {
1113 .events = epoll_events,
1114 .data = { .u64 = fd_event_tag(fd, e->generation) },
1117 int op = e->armed_flags ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
1119 if (epoll_ctl(timer_th.event_fd, op, fd, &event) == -1) {
1123 if (op == EPOLL_CTL_MOD &&
1124 epoll_ctl(timer_th.event_fd, EPOLL_CTL_ADD, fd, &event) == 0) {
1130 if (op == EPOLL_CTL_ADD &&
1131 epoll_ctl(timer_th.event_fd, EPOLL_CTL_MOD, fd, &event) == 0) {
1140 perror(
"epoll_ctl");
1141 rb_bug(
"fd_waiters_arm/epoll_ctl failed (fd:%d op:%d errno:%d)", fd, op,
errno);
1145# error "neither kqueue nor epoll"
1148 e->armed_flags = want;
1153fd_readable_nonblock(
int fd)
1155 struct pollfd pfd = {
1159 return poll(&pfd, 1, 0) != 0;
1163fd_writable_nonblock(
int fd)
1165 struct pollfd pfd = {
1169 return poll(&pfd, 1, 0) != 0;
1173verify_waiting_list(
void)
1175#if VM_CHECK_MODE > 0
1178 for (
int lvl = 0; lvl < TIMER_WHEEL_LEVELS; lvl++) {
1179 const struct timer_wheel_level *lv = &timer_th.wheel[lvl];
1181 for (
int slot = 0; slot < TIMER_WHEEL_SLOTS; slot++) {
1182 bool occupied = (lv->occupied >> slot) & 1;
1183 VM_ASSERT(occupied == !ccan_list_empty(&lv->slots[slot]));
1185 ccan_list_for_each(&lv->slots[slot], w, node) {
1186 VM_ASSERT(w->flags & thread_sched_waiting_timeout);
1187 VM_ASSERT(w->data.timeout != 0);
1188 VM_ASSERT(w->wheel_lvl == lvl);
1189 VM_ASSERT(w->wheel_slot == slot);
1194 ccan_list_for_each(&timer_th.waiting_untimed, w, node) {
1195 VM_ASSERT(!(w->flags & thread_sched_waiting_timeout));
1196 VM_ASSERT(w->data.timeout == 0);
1203static enum thread_sched_waiting_flag
1204kqueue_translate_filter_to_flags(int16_t filter)
1208 return thread_sched_waiting_io_read;
1210 return thread_sched_waiting_io_write;
1212 return thread_sched_waiting_timeout;
1214 rb_bug(
"kevent filter:%d not supported", filter);
1221 struct timespec calculated_timeout;
1223 int timeout_ms = timer_thread_set_timeout(vm);
1225 if (timeout_ms > 0) {
1226 calculated_timeout.tv_sec = timeout_ms / 1000;
1227 calculated_timeout.tv_nsec = (timeout_ms % 1000) * 1000000;
1228 timeout = &calculated_timeout;
1230 else if (timeout_ms == 0) {
1233 memset(&calculated_timeout, 0,
sizeof(
struct timespec));
1234 timeout = &calculated_timeout;
1237 return kevent(timer_th.event_fd, NULL, 0, timer_th.finished_events, KQUEUE_EVENTS_MAX, timeout);
1243 if ((timer_th.event_fd = kqueue()) == -1) rb_bug(
"kqueue creation failed (errno:%d)",
errno);
1244 int flags = fcntl(timer_th.event_fd, F_GETFD);
1246 rb_bug(
"kqueue GETFD failed (errno:%d)",
errno);
1249 flags |= FD_CLOEXEC;
1250 if (fcntl(timer_th.event_fd, F_SETFD, flags) == -1) {
1251 rb_bug(
"kqueue SETFD failed (errno:%d)",
errno);
1258static enum timer_thread_register_result
1259timer_thread_register_waiting(
rb_thread_t *th,
int fd,
enum thread_sched_waiting_flag flags, rb_hrtime_t *rel, uint32_t event_serial)
1261 RUBY_DEBUG_LOG(
"th:%u fd:%d flag:%d rel:%lu", rb_th_serial(th), fd, flags, rel ? (
unsigned long)*rel : 0);
1263 VM_ASSERT(th == NULL || TH_SCHED(th)->running == th);
1264 VM_ASSERT(flags != 0);
1266 rb_hrtime_t abs = 0;
1270 flags |= thread_sched_waiting_timeout;
1273 return timer_thread_already_ready;
1277 if (flags & thread_sched_waiting_timeout) {
1278 VM_ASSERT(rel != NULL);
1279 abs = rb_hrtime_add(rb_hrtime_now(), *rel);
1282 if (flags & thread_sched_waiting_io_read) {
1283 if (!(flags & thread_sched_waiting_io_force) && fd_readable_nonblock(fd)) {
1284 RUBY_DEBUG_LOG(
"fd_readable_nonblock");
1285 return timer_thread_already_ready;
1290 if (flags & thread_sched_waiting_io_write) {
1291 if (!(flags & thread_sched_waiting_io_force) && fd_writable_nonblock(fd)) {
1292 RUBY_DEBUG_LOG(
"fd_writable_nonblock");
1293 return timer_thread_already_ready;
1300 if (flags & FD_WAIT_IO_MASK) {
1301 VM_ASSERT(th != NULL);
1307 if (!fd_waiters_arm(fd, e, fd_waiters_union(e) | (uint32_t)(flags & FD_WAIT_IO_MASK))) {
1309 return timer_thread_unavailable;
1312 ccan_list_add_tail(&e->waiters, &th->sched.waiting_reason.fd_node);
1313 RUBY_DEBUG_LOG(
"armed fd:%d want:%u", fd, e->armed_flags);
1317 VM_ASSERT(th->sched.waiting_reason.flags == thread_sched_waiting_none);
1321 th->sched.waiting_reason.flags = flags;
1322 th->sched.waiting_reason.data.timeout = abs;
1323 th->sched.waiting_reason.data.fd = fd;
1324 th->sched.waiting_reason.data.result = 0;
1325 th->sched.waiting_reason.data.event_serial = event_serial;
1329 VM_ASSERT(!(flags & thread_sched_waiting_timeout));
1330 ccan_list_add_tail(&timer_th.waiting_untimed, &th->sched.waiting_reason.node);
1333 RUBY_DEBUG_LOG(
"abs:%lu", (
unsigned long)abs);
1334 VM_ASSERT(flags & thread_sched_waiting_timeout);
1336 rb_hrtime_t prev_expiry = timer_th.next_expiry;
1337 timer_wheel_insert(&th->sched.waiting_reason);
1339 verify_waiting_list();
1341 if (timer_th.next_expiry < prev_expiry) {
1343 timer_thread_wakeup_force();
1348 VM_ASSERT(abs == 0);
1353 return timer_thread_registered;
1359timer_thread_unregister_waiting(
rb_thread_t *th,
int fd,
enum thread_sched_waiting_flag flags)
1361 if (!(th->sched.waiting_reason.flags & FD_WAIT_IO_MASK)) {
1365 RUBY_DEBUG_LOG(
"th:%u fd:%d", rb_th_serial(th), fd);
1367 ccan_list_del_init(&th->sched.waiting_reason.fd_node);
1371 fd_waiters_arm(fd, e, fd_waiters_union(e));
1378timer_thread_arm_comm_pipe(
void)
1380 int fd = timer_th.comm_fds[0];
1384 EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, (
void *)(uintptr_t)fd_event_tag(fd, 0));
1385 if (kevent(timer_th.event_fd, &ke, 1, NULL, 0, NULL) == -1) {
1386 rb_bug(
"timer_thread_arm_comm_pipe/kevent failed (errno:%d)",
errno);
1388#elif HAVE_SYS_EPOLL_H
1389 struct epoll_event event = {
1391 .data = { .u64 = fd_event_tag(fd, 0) },
1393 if (epoll_ctl(timer_th.event_fd, EPOLL_CTL_ADD, fd, &event) == -1) {
1394 rb_bug(
"timer_thread_arm_comm_pipe/epoll_ctl failed (errno:%d)",
errno);
1397# error "neither kqueue nor epoll"
1402timer_thread_setup_mn(
void)
1406 RUBY_DEBUG_LOG(
"kqueue_fd:%d", timer_th.event_fd);
1408 if ((timer_th.event_fd = epoll_create1(EPOLL_CLOEXEC)) == -1) rb_bug(
"epoll_create (errno:%d)",
errno);
1409 RUBY_DEBUG_LOG(
"epoll_fd:%d", timer_th.event_fd);
1411 RUBY_DEBUG_LOG(
"comm_fds:%d/%d", timer_th.comm_fds[0], timer_th.comm_fds[1]);
1413 timer_thread_arm_comm_pipe();
1420 int r = kqueue_wait(vm);
1422 int r = epoll_wait(timer_th.event_fd, timer_th.finished_events, EPOLL_EVENTS_MAX, timer_thread_set_timeout(vm));
1428#define FD_WAKE_BATCH 16
1435timer_thread_wake_fd_waiters(
int fd, uint32_t generation, uint32_t wake_flags,
int result)
1437 struct {
rb_thread_t *th; uint32_t serial; } batch[FD_WAKE_BATCH];
1439 if (wake_flags == 0)
return;
1451 if (e != NULL && e->generation == generation) {
1454 ccan_list_for_each_safe(&e->waiters, w, nxt, fd_node) {
1455 if (!(w->flags & wake_flags))
continue;
1457 if (n == FD_WAKE_BATCH) {
1462 ccan_list_del_init(&w->fd_node);
1465 w->flags = thread_sched_waiting_none;
1467 w->data.result = result;
1469 batch[n].th = thread_sched_waiting_thread(w);
1470 batch[n].serial = w->data.event_serial;
1476 fd_waiters_arm(fd, e, fd_waiters_union(e));
1481 for (
int i = 0; i < n; i++) {
1482 timer_thread_wakeup_thread(batch[i].th, batch[i].serial);
1504timer_thread_polling(
rb_vm_t *vm)
1506 int r = event_wait(vm);
1508 RUBY_DEBUG_LOG(
"r:%d errno:%d", r,
errno);
1512 RUBY_DEBUG_LOG(
"timeout%s",
"");
1514 ractor_sched_lock(vm, NULL);
1517 timer_thread_check_timeslice(vm);
1520 if (vm->ractor.sched.grq_cnt > 0) {
1521 RUBY_DEBUG_LOG(
"GRQ cnt: %u", vm->ractor.sched.grq_cnt);
1525 ractor_sched_unlock(vm, NULL);
1528 native_thread_check_and_create_shared(vm);
1538 perror(
"event_wait");
1539 rb_bug(
"event_wait errno:%d",
errno);
1544 RUBY_DEBUG_LOG(
"%d event(s)", r);
1547 for (
int i=0; i<r; i++) {
1548 uint64_t tag = (uint64_t)(uintptr_t)timer_th.finished_events[i].udata;
1549 int fd = (int)timer_th.finished_events[i].ident;
1550 int16_t filter = timer_th.finished_events[i].filter;
1552 if (fd == timer_th.comm_fds[0]) {
1553 RUBY_DEBUG_LOG(
"comm from fd:%d", timer_th.comm_fds[1]);
1554 consume_communication_pipe(timer_th.comm_fds[0]);
1558 uint32_t wake_flags = kqueue_translate_filter_to_flags(filter) & FD_WAIT_IO_MASK;
1559 if (timer_th.finished_events[i].flags & (EV_EOF | EV_ERROR)) {
1560 wake_flags = FD_WAIT_IO_MASK;
1563 timer_thread_wake_fd_waiters(fd, FD_EVENT_TAG_GEN(tag), wake_flags, filter);
1565#elif HAVE_SYS_EPOLL_H
1566 for (
int i=0; i<r; i++) {
1567 uint64_t tag = timer_th.finished_events[i].data.u64;
1568 int fd = FD_EVENT_TAG_FD(tag);
1569 uint32_t events = timer_th.finished_events[i].events;
1571 if (fd == timer_th.comm_fds[0]) {
1572 RUBY_DEBUG_LOG(
"comm from fd:%d", timer_th.comm_fds[1]);
1573 consume_communication_pipe(timer_th.comm_fds[0]);
1577 RUBY_DEBUG_LOG(
"io event. fd:%d event:%s%s%s%s%s%s", fd,
1578 (events & EPOLLIN) ?
"in/" :
"",
1579 (events & EPOLLOUT) ?
"out/" :
"",
1580 (events & EPOLLRDHUP) ?
"RDHUP/" :
"",
1581 (events & EPOLLPRI) ?
"pri/" :
"",
1582 (events & EPOLLERR) ?
"err/" :
"",
1583 (events & EPOLLHUP) ?
"hup/" :
"");
1585 uint32_t wake_flags = 0;
1586 if (events & (EPOLLIN | EPOLLPRI | EPOLLRDHUP)) wake_flags |= thread_sched_waiting_io_read;
1587 if (events & EPOLLOUT) wake_flags |= thread_sched_waiting_io_write;
1589 if (events & (EPOLLERR | EPOLLHUP)) wake_flags |= FD_WAIT_IO_MASK;
1591 timer_thread_wake_fd_waiters(fd, FD_EVENT_TAG_GEN(tag), wake_flags, (
int)events);
1594# error "neither kqueue nor epoll"
1604timer_thread_setup_mn(
void)
1610timer_thread_polling(
rb_vm_t *vm)
1612 int timeout = timer_thread_set_timeout(vm);
1614 struct pollfd pfd = {
1615 .fd = timer_th.comm_fds[0],
1619 int r = poll(&pfd, 1, timeout);
1623 ractor_sched_lock(vm, NULL);
1626 timer_thread_check_timeslice(vm);
1628 ractor_sched_unlock(vm, NULL);
1638 rb_bug(
"poll errno:%d",
errno);
1643 consume_communication_pipe(timer_th.comm_fds[0]);
1647 rb_bug(
"unreachbale");
1654 rb_bug(
"unreachable");
1657static enum thread_sched_wait_result
1658thread_sched_wait_events(
struct rb_thread_sched *sched,
rb_thread_t *th,
int fd,
enum thread_sched_waiting_flag events, rb_hrtime_t *rel)
1660 rb_bug(
"unreachable");
1664timer_wheel_timeout(
int timeout)
1670timer_thread_check_timeout(
rb_vm_t *vm)
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
#define RUBY_ATOMIC_INC(var)
Atomically increments the value pointed by var.
int len
Length of the buffer.
#define RUBY_INTERNAL_THREAD_EVENT_RESUMED
Triggered when a thread successfully acquired the GVL.
#define RUBY_INTERNAL_THREAD_EVENT_SUSPENDED
Triggered when a thread released the GVL.
#define RBIMPL_ATTR_MAYBE_UNUSED()
Wraps (or simulates) [[maybe_unused]]
#define errno
Ractor-aware version of errno.
void rb_native_mutex_lock(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_lock.
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.
uintptr_t VALUE
Type that represents a Ruby object.