12#include "ruby/internal/config.h"
21#ifdef NEED_MADVICE_PROTOTYPE_USING_CADDR_T
23extern int madvise(caddr_t,
size_t,
int);
28#include "eval_intern.h"
30#include "internal/cont.h"
31#include "internal/thread.h"
32#include "internal/error.h"
33#include "internal/eval.h"
34#include "internal/gc.h"
35#include "internal/proc.h"
36#include "internal/sanitizers.h"
37#include "internal/warnings.h"
43#include "ractor_core.h"
45static const int DEBUG = 0;
47#define RB_PAGE_SIZE (pagesize)
48#define RB_PAGE_MASK (~(RB_PAGE_SIZE - 1))
52static VALUE rb_cContinuation;
53static VALUE rb_cFiber;
54static VALUE rb_eFiberError;
55#ifdef RB_EXPERIMENTAL_FIBER_POOL
56static VALUE rb_cFiberPool;
59#define CAPTURE_JUST_VALID_VM_STACK 1
62#ifdef COROUTINE_LIMITED_ADDRESS_SPACE
63#define FIBER_POOL_ALLOCATION_FREE
64#define FIBER_POOL_INITIAL_SIZE 8
65#define FIBER_POOL_ALLOCATION_MAXIMUM_SIZE 32
67#define FIBER_POOL_INITIAL_SIZE 32
68#define FIBER_POOL_ALLOCATION_MAXIMUM_SIZE 1024
70#ifdef RB_EXPERIMENTAL_FIBER_POOL
71#define FIBER_POOL_ALLOCATION_FREE
75 CONTINUATION_CONTEXT = 0,
81#ifdef CAPTURE_JUST_VALID_VM_STACK
118#ifdef FIBER_POOL_ALLOCATION_FREE
161#ifdef FIBER_POOL_ALLOCATION_FREE
169#ifdef FIBER_POOL_ALLOCATION_FREE
190 size_t initial_count;
201 size_t vm_stack_size;
206 rb_execution_context_t *ec;
214 enum context_type type;
227 rb_execution_context_t saved_ec;
252#define FIBER_CREATED_P(fiber) ((fiber)->status == FIBER_CREATED)
253#define FIBER_RESUMED_P(fiber) ((fiber)->status == FIBER_RESUMED)
254#define FIBER_SUSPENDED_P(fiber) ((fiber)->status == FIBER_SUSPENDED)
255#define FIBER_TERMINATED_P(fiber) ((fiber)->status == FIBER_TERMINATED)
256#define FIBER_RUNNABLE_P(fiber) (FIBER_CREATED_P(fiber) || FIBER_SUSPENDED_P(fiber))
264 BITFIELD(
enum fiber_status, status, 2);
266 unsigned int yielding : 1;
267 unsigned int blocking : 1;
269 unsigned int killed : 1;
275static struct fiber_pool shared_fiber_pool = {NULL, NULL, 0, 0, 0, 0};
278rb_free_shared_fiber_pool(
void)
281 while (allocations) {
288static ID fiber_initialize_keywords[3] = {0};
295#if defined(MAP_STACK) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
296#define FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON | MAP_STACK)
298#define FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON)
301#define ERRNOMSG strerror(errno)
305fiber_pool_vacancy_pointer(
void * base,
size_t size)
307 STACK_GROW_DIR_DETECTION;
310 (
char*)base + STACK_DIR_UPPER(0, size - RB_PAGE_SIZE)
314#if defined(COROUTINE_SANITIZE_ADDRESS)
319 STACK_GROW_DIR_DETECTION;
321 return (
char*)stack->base + STACK_DIR_UPPER(RB_PAGE_SIZE, 0);
328 return stack->size - RB_PAGE_SIZE;
336 STACK_GROW_DIR_DETECTION;
338 stack->current = (
char*)stack->base + STACK_DIR_UPPER(0, stack->size);
339 stack->available = stack->size;
346 STACK_GROW_DIR_DETECTION;
348 VM_ASSERT(stack->current);
350 return STACK_DIR_UPPER(stack->current, (
char*)stack->current - stack->available);
358 STACK_GROW_DIR_DETECTION;
360 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_alloca(%p): %"PRIuSIZE
"/%"PRIuSIZE
"\n", (
void*)stack, offset, stack->available);
361 VM_ASSERT(stack->available >= offset);
364 void * pointer = STACK_DIR_UPPER(stack->current, (
char*)stack->current - offset);
367 stack->current = STACK_DIR_UPPER((
char*)stack->current + offset, (
char*)stack->current - offset);
368 stack->available -= offset;
377 fiber_pool_stack_reset(&vacancy->stack);
380 fiber_pool_stack_alloca(&vacancy->stack, RB_PAGE_SIZE);
386 vacancy->next = head;
388#ifdef FIBER_POOL_ALLOCATION_FREE
390 head->previous = vacancy;
391 vacancy->previous = NULL;
398#ifdef FIBER_POOL_ALLOCATION_FREE
403 vacancy->next->previous = vacancy->previous;
406 if (vacancy->previous) {
407 vacancy->previous->next = vacancy->next;
411 vacancy->stack.pool->vacancies = vacancy->next;
416fiber_pool_vacancy_pop(
struct fiber_pool * pool)
421 fiber_pool_vacancy_remove(vacancy);
428fiber_pool_vacancy_pop(
struct fiber_pool * pool)
433 pool->vacancies = vacancy->next;
448 vacancy->stack.base = base;
449 vacancy->stack.size = size;
451 fiber_pool_vacancy_reset(vacancy);
455 return fiber_pool_vacancy_push(vacancy, vacancies);
463fiber_pool_allocate_memory(
size_t * count,
size_t stride)
473 void * base = VirtualAlloc(0, (*count)*stride, MEM_COMMIT, PAGE_READWRITE);
476 errno = rb_w32_map_errno(GetLastError());
477 *count = (*count) >> 1;
484 size_t mmap_size = (*count)*stride;
485 void * base = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, FIBER_STACK_FLAGS, -1, 0);
487 if (base == MAP_FAILED) {
489 *count = (*count) >> 1;
492 ruby_annotate_mmap(base, mmap_size,
"Ruby:fiber_pool_allocate_memory");
493#if defined(MADV_FREE_REUSE)
497 while (madvise(base, mmap_size, MADV_FREE_REUSE) == -1 &&
errno == EAGAIN);
519 STACK_GROW_DIR_DETECTION;
522 size_t stride = size + RB_PAGE_SIZE;
529 void * base = fiber_pool_allocate_memory(&count, stride);
533 ruby_xfree(allocation);
540 allocation->base = base;
541 allocation->size = size;
542 allocation->stride = stride;
543 allocation->count = count;
544#ifdef FIBER_POOL_ALLOCATION_FREE
545 allocation->used = 0;
550 fprintf(stderr,
"fiber_pool_expand(%"PRIuSIZE
"): %p, %"PRIuSIZE
"/%"PRIuSIZE
" x [%"PRIuSIZE
":%"PRIuSIZE
"]\n",
555 for (
size_t i = 0; i < count; i += 1) {
556 void * base = (
char*)allocation->base + (stride * i);
557 void * page = (
char*)base + STACK_DIR_UPPER(size, 0);
561 if (!VirtualProtect(page, RB_PAGE_SIZE, PAGE_READWRITE | PAGE_GUARD, &old_protect)) {
562 int error = rb_w32_map_errno(GetLastError());
563 VirtualFree(allocation->base, 0, MEM_RELEASE);
564 ruby_xfree(allocation);
568#elif defined(__wasi__)
572 if (mprotect(page, RB_PAGE_SIZE, PROT_NONE) < 0) {
574 if (!error) error = ENOMEM;
575 munmap(allocation->base, count*stride);
576 ruby_xfree(allocation);
582 vacancies = fiber_pool_vacancy_initialize(
584 (
char*)base + STACK_DIR_UPPER(0, RB_PAGE_SIZE),
588#ifdef FIBER_POOL_ALLOCATION_FREE
589 vacancies->stack.allocation = allocation;
596#ifdef FIBER_POOL_ALLOCATION_FREE
597 if (allocation->next) {
598 allocation->next->previous = allocation;
601 allocation->previous = NULL;
614fiber_pool_initialize(
struct fiber_pool *
fiber_pool,
size_t size,
size_t count,
size_t vm_stack_size)
616 VM_ASSERT(vm_stack_size < size);
620 fiber_pool->size = ((size / RB_PAGE_SIZE) + 1) * RB_PAGE_SIZE;
628 if (RB_UNLIKELY(!fiber_pool_expand(
fiber_pool, count))) {
629 rb_raise(rb_eFiberError,
"can't allocate initial fiber stacks (%"PRIuSIZE
" x %"PRIuSIZE
" bytes): %s", count,
fiber_pool->size, strerror(
errno));
633#ifdef FIBER_POOL_ALLOCATION_FREE
638 STACK_GROW_DIR_DETECTION;
640 VM_ASSERT(allocation->used == 0);
642 if (DEBUG) fprintf(stderr,
"fiber_pool_allocation_free: %p base=%p count=%"PRIuSIZE
"\n", (
void*)allocation, allocation->base, allocation->count);
645 for (i = 0; i < allocation->count; i += 1) {
646 void * base = (
char*)allocation->base + (allocation->stride * i) + STACK_DIR_UPPER(0, RB_PAGE_SIZE);
648 struct fiber_pool_vacancy * vacancy = fiber_pool_vacancy_pointer(base, allocation->size);
651 fiber_pool_vacancy_remove(vacancy);
655 VirtualFree(allocation->base, 0, MEM_RELEASE);
657 munmap(allocation->base, allocation->stride * allocation->count);
660 if (allocation->previous) {
661 allocation->previous->next = allocation->next;
665 allocation->pool->allocations = allocation->next;
668 if (allocation->next) {
669 allocation->next->previous = allocation->previous;
672 allocation->pool->count -= allocation->count;
674 ruby_xfree(allocation);
680fiber_pool_stack_expand_count(
const struct fiber_pool *pool)
682 const size_t maximum = FIBER_POOL_ALLOCATION_MAXIMUM_SIZE;
683 const size_t minimum = pool->initial_count;
685 size_t count = pool->count;
686 if (count > maximum) count = maximum;
687 if (count < minimum) count = minimum;
697 size_t count = fiber_pool_stack_expand_count(
fiber_pool);
699 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_acquire: expanding fiber pool by %"PRIuSIZE
" stacks\n", count);
703 if (RB_LIKELY(fiber_pool_expand(
fiber_pool, count))) {
707 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_acquire: expand failed (%s), collecting garbage\n", strerror(
errno));
713 if (RB_LIKELY(vacancy)) {
718 if (RB_LIKELY(fiber_pool_expand(
fiber_pool, count))) {
735 RB_VM_LOCK_ENTER_LEV(&lev);
740 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_acquire: %p used=%"PRIuSIZE
"\n", (
void*)
fiber_pool->vacancies,
fiber_pool->used);
743 if (RB_UNLIKELY(!vacancy)) {
744 vacancy = fiber_pool_stack_acquire_expand(
fiber_pool);
747 if (RB_UNLIKELY(!vacancy)) {
748 RB_VM_LOCK_LEAVE_LEV(&lev);
749 rb_raise(rb_eFiberError,
"can't allocate fiber stack: %s", strerror(
errno));
754 VM_ASSERT(vacancy->stack.base);
756#if defined(COROUTINE_SANITIZE_ADDRESS)
757 __asan_unpoison_memory_region(fiber_pool_stack_poison_base(&vacancy->stack), fiber_pool_stack_poison_size(&vacancy->stack));
763#ifdef FIBER_POOL_ALLOCATION_FREE
764 vacancy->stack.allocation->used += 1;
767 fiber_pool_stack_reset(&vacancy->stack);
769 RB_VM_LOCK_LEAVE_LEV(&lev);
771 return vacancy->stack;
779 void * base = fiber_pool_stack_base(stack);
780 size_t size = stack->available;
783 VM_ASSERT(size <= (stack->size - RB_PAGE_SIZE));
785 int advice = stack->pool->free_stacks >> 1;
787 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_free: %p+%"PRIuSIZE
" [base=%p, size=%"PRIuSIZE
"] advice=%d\n", base, size, stack->base, stack->size, advice);
800#elif VM_CHECK_MODE > 0 && defined(MADV_DONTNEED)
801 if (!advice) advice = MADV_DONTNEED;
803 madvise(base, size, advice);
804#elif defined(MADV_FREE_REUSABLE)
805 if (!advice) advice = MADV_FREE_REUSABLE;
811 while (madvise(base, size, advice) == -1 &&
errno == EAGAIN);
812#elif defined(MADV_FREE)
813 if (!advice) advice = MADV_FREE;
815 madvise(base, size, advice);
816#elif defined(MADV_DONTNEED)
817 if (!advice) advice = MADV_DONTNEED;
819 madvise(base, size, advice);
820#elif defined(POSIX_MADV_DONTNEED)
821 if (!advice) advice = POSIX_MADV_DONTNEED;
823 posix_madvise(base, size, advice);
825 VirtualAlloc(base, size, MEM_RESET, PAGE_READWRITE);
830#if defined(COROUTINE_SANITIZE_ADDRESS)
831 __asan_poison_memory_region(fiber_pool_stack_poison_base(stack), fiber_pool_stack_poison_size(stack));
840 struct fiber_pool_vacancy * vacancy = fiber_pool_vacancy_pointer(stack->base, stack->size);
842 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_release: %p used=%"PRIuSIZE
"\n", stack->base, stack->pool->used);
845 vacancy->stack = *stack;
849 fiber_pool_vacancy_reset(vacancy);
852 pool->vacancies = fiber_pool_vacancy_push(vacancy, pool->vacancies);
855#ifdef FIBER_POOL_ALLOCATION_FREE
858 allocation->used -= 1;
861 if (allocation->used == 0) {
862 fiber_pool_allocation_free(allocation);
864 else if (stack->pool->free_stacks) {
865 fiber_pool_stack_free(&vacancy->stack);
870 if (stack->pool->free_stacks) {
871 fiber_pool_stack_free(&vacancy->stack);
877ec_switch(rb_thread_t *th, rb_fiber_t *fiber)
879 rb_execution_context_t *ec = &fiber->cont.saved_ec;
880#ifdef RUBY_ASAN_ENABLED
881 ec->machine.asan_fake_stack_handle = asan_get_thread_fake_stack_handle();
883 rb_ractor_set_current_ec(th->ractor, th->ec = ec);
890 if (th->vm->ractor.main_thread == th &&
891 rb_signal_buff_size() > 0) {
892 RUBY_VM_SET_TRAP_INTERRUPT(ec);
895 VM_ASSERT(ec->fiber_ptr->cont.self == 0 || ec->vm_stack != NULL);
899fiber_restore_thread(rb_thread_t *th, rb_fiber_t *fiber)
901 ec_switch(th, fiber);
902 VM_ASSERT(th->ec->fiber_ptr == fiber);
905#ifndef COROUTINE_DECL
906# define COROUTINE_DECL COROUTINE
912 rb_fiber_t *fiber = to->argument;
914#if defined(COROUTINE_SANITIZE_ADDRESS)
924 __sanitizer_finish_switch_fiber(to->fake_stack, (
const void**)&from->stack_base, &from->stack_size);
927 rb_thread_t *thread = fiber->cont.saved_ec.thread_ptr;
929#ifdef COROUTINE_PTHREAD_CONTEXT
930 ruby_thread_set_native(thread);
933 fiber_restore_thread(thread, fiber);
935 rb_fiber_start(fiber);
937#ifndef COROUTINE_PTHREAD_CONTEXT
938 VM_UNREACHABLE(fiber_entry);
944fiber_initialize_coroutine(rb_fiber_t *fiber,
size_t * vm_stack_size)
947 rb_execution_context_t *sec = &fiber->cont.saved_ec;
948 void * vm_stack = NULL;
952 fiber->stack = fiber_pool_stack_acquire(
fiber_pool);
953 vm_stack = fiber_pool_stack_alloca(&fiber->stack,
fiber_pool->vm_stack_size);
956 coroutine_initialize(&fiber->context, fiber_entry, fiber_pool_stack_base(&fiber->stack), fiber->stack.available);
959 sec->machine.stack_start = fiber->stack.current;
960 sec->machine.stack_maxsize = fiber->stack.available;
962 fiber->context.argument = (
void*)fiber;
970fiber_stack_release(rb_fiber_t * fiber)
972 rb_execution_context_t *ec = &fiber->cont.saved_ec;
974 if (DEBUG) fprintf(stderr,
"fiber_stack_release: %p, stack.base=%p\n", (
void*)fiber, fiber->stack.base);
977 if (fiber->stack.base) {
978 fiber_pool_stack_release(&fiber->stack);
979 fiber->stack.base = NULL;
983 rb_ec_clear_vm_stack(ec);
987fiber_stack_release_locked(rb_fiber_t *fiber)
989 if (!ruby_vm_during_cleanup) {
992 ASSERT_vm_locking_with_barrier();
994 fiber_stack_release(fiber);
998fiber_status_name(
enum fiber_status s)
1001 case FIBER_CREATED:
return "created";
1002 case FIBER_RESUMED:
return "resumed";
1003 case FIBER_SUSPENDED:
return "suspended";
1004 case FIBER_TERMINATED:
return "terminated";
1006 VM_UNREACHABLE(fiber_status_name);
1011fiber_verify(
const rb_fiber_t *fiber)
1013#if VM_CHECK_MODE > 0
1014 VM_ASSERT(fiber->cont.saved_ec.fiber_ptr == fiber);
1016 switch (fiber->status) {
1018 VM_ASSERT(fiber->cont.saved_ec.vm_stack != NULL);
1020 case FIBER_SUSPENDED:
1021 VM_ASSERT(fiber->cont.saved_ec.vm_stack != NULL);
1024 case FIBER_TERMINATED:
1028 VM_UNREACHABLE(fiber_verify);
1034fiber_status_set(rb_fiber_t *fiber,
enum fiber_status s)
1037 VM_ASSERT(!FIBER_TERMINATED_P(fiber));
1038 VM_ASSERT(fiber->status != s);
1039 fiber_verify(fiber);
1043static rb_context_t *
1059 if (!fiber) rb_raise(rb_eFiberError,
"uninitialized fiber");
1064NOINLINE(
static VALUE cont_capture(
volatile int *
volatile stat));
1066#define THREAD_MUST_BE_RUNNING(th) do { \
1067 if (!(th)->ec->tag) rb_raise(rb_eThreadError, "not running thread"); \
1071rb_fiber_threadptr(
const rb_fiber_t *fiber)
1073 return fiber->cont.saved_ec.thread_ptr;
1077cont_thread_value(
const rb_context_t *cont)
1079 return cont->saved_ec.thread_ptr->self;
1083cont_compact(
void *ptr)
1085 rb_context_t *cont = ptr;
1088 cont->self = rb_gc_location(cont->self);
1090 cont->value = rb_gc_location(cont->value);
1091 rb_execution_context_update(&cont->saved_ec);
1097 rb_context_t *cont = ptr;
1099 RUBY_MARK_ENTER(
"cont");
1101 rb_gc_mark_movable(cont->self);
1103 rb_gc_mark_movable(cont->value);
1105 rb_execution_context_mark(&cont->saved_ec);
1106 rb_gc_mark(cont_thread_value(cont));
1108 if (cont->saved_vm_stack.ptr) {
1109#ifdef CAPTURE_JUST_VALID_VM_STACK
1110 rb_gc_mark_locations(cont->saved_vm_stack.ptr,
1111 cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen + cont->saved_vm_stack.clen);
1113 rb_gc_mark_locations(cont->saved_vm_stack.ptr,
1114 cont->saved_vm_stack.ptr, cont->saved_ec.stack_size);
1118 if (cont->machine.stack) {
1119 if (cont->type == CONTINUATION_CONTEXT) {
1121 rb_gc_mark_locations(cont->machine.stack,
1122 cont->machine.stack + cont->machine.stack_size);
1130 RUBY_MARK_LEAVE(
"cont");
1135fiber_is_root_p(
const rb_fiber_t *fiber)
1137 return fiber == fiber->cont.saved_ec.thread_ptr->root_fiber;
1141static void jit_cont_free(
struct rb_jit_cont *cont);
1146 rb_context_t *cont = ptr;
1148 RUBY_FREE_ENTER(
"cont");
1150 if (cont->type == CONTINUATION_CONTEXT) {
1151 ruby_xfree(cont->saved_ec.vm_stack);
1152 RUBY_FREE_UNLESS_NULL(cont->machine.stack);
1155 rb_fiber_t *fiber = (rb_fiber_t*)cont;
1156 coroutine_destroy(&fiber->context);
1157 fiber_stack_release_locked(fiber);
1160 RUBY_FREE_UNLESS_NULL(cont->saved_vm_stack.ptr);
1162 VM_ASSERT(cont->jit_cont != NULL);
1163 jit_cont_free(cont->jit_cont);
1166 RUBY_FREE_LEAVE(
"cont");
1170cont_memsize(
const void *ptr)
1172 const rb_context_t *cont = ptr;
1175 size =
sizeof(*cont);
1176 if (cont->saved_vm_stack.ptr) {
1177#ifdef CAPTURE_JUST_VALID_VM_STACK
1178 size_t n = (cont->saved_vm_stack.slen + cont->saved_vm_stack.clen);
1180 size_t n = cont->saved_ec.vm_stack_size;
1182 size += n *
sizeof(*cont->saved_vm_stack.ptr);
1185 if (cont->machine.stack) {
1186 size += cont->machine.stack_size *
sizeof(*cont->machine.stack);
1193rb_fiber_update_self(rb_fiber_t *fiber)
1195 if (fiber->cont.self) {
1196 fiber->cont.self = rb_gc_location(fiber->cont.self);
1199 rb_execution_context_update(&fiber->cont.saved_ec);
1204rb_fiber_mark_self(
const rb_fiber_t *fiber)
1206 if (fiber->cont.self) {
1207 rb_gc_mark_movable(fiber->cont.self);
1210 rb_execution_context_mark(&fiber->cont.saved_ec);
1215fiber_compact(
void *ptr)
1217 rb_fiber_t *fiber = ptr;
1218 fiber->first_proc = rb_gc_location(fiber->first_proc);
1220 if (fiber->prev) rb_fiber_update_self(fiber->prev);
1222 cont_compact(&fiber->cont);
1223 fiber_verify(fiber);
1227fiber_mark(
void *ptr)
1229 rb_fiber_t *fiber = ptr;
1230 RUBY_MARK_ENTER(
"cont");
1231 fiber_verify(fiber);
1232 rb_gc_mark_movable(fiber->first_proc);
1233 if (fiber->prev) rb_fiber_mark_self(fiber->prev);
1234 cont_mark(&fiber->cont);
1235 RUBY_MARK_LEAVE(
"cont");
1239fiber_free(
void *ptr)
1241 rb_fiber_t *fiber = ptr;
1242 RUBY_FREE_ENTER(
"fiber");
1244 if (DEBUG) fprintf(stderr,
"fiber_free: %p[%p]\n", (
void *)fiber, fiber->stack.base);
1246 if (fiber->cont.saved_ec.local_storage) {
1247 rb_id_table_free(fiber->cont.saved_ec.local_storage);
1250 cont_free(&fiber->cont);
1251 RUBY_FREE_LEAVE(
"fiber");
1255fiber_memsize(
const void *ptr)
1257 const rb_fiber_t *fiber = ptr;
1258 size_t size =
sizeof(*fiber);
1259 const rb_execution_context_t *saved_ec = &fiber->cont.saved_ec;
1260 const rb_thread_t *th = rb_ec_thread_ptr(saved_ec);
1265 if (saved_ec->local_storage && fiber != th->root_fiber) {
1266 size += rb_id_table_memsize(saved_ec->local_storage);
1267 size += rb_obj_memsize_of(saved_ec->storage);
1270 size += cont_memsize(&fiber->cont);
1281cont_save_machine_stack(rb_thread_t *th, rb_context_t *cont)
1285 SET_MACHINE_STACK_END(&th->ec->machine.stack_end);
1287 if (th->ec->machine.stack_start > th->ec->machine.stack_end) {
1288 size = cont->machine.stack_size = th->ec->machine.stack_start - th->ec->machine.stack_end;
1289 cont->machine.stack_src = th->ec->machine.stack_end;
1292 size = cont->machine.stack_size = th->ec->machine.stack_end - th->ec->machine.stack_start;
1293 cont->machine.stack_src = th->ec->machine.stack_start;
1296 if (cont->machine.stack) {
1303 FLUSH_REGISTER_WINDOWS;
1304 asan_unpoison_memory_region(cont->machine.stack_src, size,
false);
1305 MEMCPY(cont->machine.stack, cont->machine.stack_src,
VALUE, size);
1310 {cont_mark, cont_free, cont_memsize, cont_compact},
1311 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
1315cont_save_thread(rb_context_t *cont, rb_thread_t *th)
1317 rb_execution_context_t *sec = &cont->saved_ec;
1319 VM_ASSERT(th->status == THREAD_RUNNABLE);
1326 sec->machine.stack_end = NULL;
1329static rb_nativethread_lock_t jit_cont_lock;
1334jit_cont_new(rb_execution_context_t *ec)
1347 if (first_jit_cont == NULL) {
1348 cont->next = cont->prev = NULL;
1352 cont->next = first_jit_cont;
1353 first_jit_cont->prev = cont;
1355 first_jit_cont = cont;
1368 if (cont == first_jit_cont) {
1369 first_jit_cont = cont->next;
1370 if (first_jit_cont != NULL)
1371 first_jit_cont->prev = NULL;
1374 cont->prev->next = cont->next;
1375 if (cont->next != NULL)
1376 cont->next->prev = cont->prev;
1385rb_jit_cont_each_iseq(rb_iseq_callback callback,
void *data)
1388 for (cont = first_jit_cont; cont != NULL; cont = cont->next) {
1389 if (cont->ec->vm_stack == NULL)
1392 const rb_control_frame_t *cfp = cont->ec->cfp;
1393 while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(cont->ec, cfp)) {
1394 if (cfp->pc && cfp->iseq && imemo_type((
VALUE)cfp->iseq) == imemo_iseq) {
1395 callback(cfp->iseq, data);
1397 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
1406rb_yjit_cancel_jit_return(
void *leave_exit,
void *leave_exception)
1409 for (cont = first_jit_cont; cont != NULL; cont = cont->next) {
1410 if (cont->ec->vm_stack == NULL)
1413 const rb_control_frame_t *cfp = cont->ec->cfp;
1414 while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(cont->ec, cfp)) {
1415 if (cfp->jit_return && cfp->jit_return != leave_exception) {
1416 ((rb_control_frame_t *)cfp)->jit_return = leave_exit;
1418 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
1426rb_jit_cont_finish(
void)
1429 for (cont = first_jit_cont; cont != NULL; cont = next) {
1437cont_init_jit_cont(rb_context_t *cont)
1439 VM_ASSERT(cont->jit_cont == NULL);
1441 cont->jit_cont = jit_cont_new(&(cont->saved_ec));
1447 return &fiber->cont.saved_ec;
1451cont_init(rb_context_t *cont, rb_thread_t *th)
1454 cont_save_thread(cont, th);
1455 cont->saved_ec.thread_ptr = th;
1456 cont->saved_ec.local_storage = NULL;
1457 cont->saved_ec.local_storage_recursive_hash =
Qnil;
1458 cont->saved_ec.local_storage_recursive_hash_for_trace =
Qnil;
1459 cont_init_jit_cont(cont);
1462static rb_context_t *
1463cont_new(
VALUE klass)
1466 volatile VALUE contval;
1467 rb_thread_t *th = GET_THREAD();
1469 THREAD_MUST_BE_RUNNING(th);
1471 cont->self = contval;
1472 cont_init(cont, th);
1479 return fiber->cont.self;
1485 return fiber->blocking;
1490rb_jit_cont_init(
void)
1497show_vm_stack(
const rb_execution_context_t *ec)
1499 VALUE *p = ec->vm_stack;
1500 while (p < ec->cfp->sp) {
1501 fprintf(stderr,
"%3d ", (
int)(p - ec->vm_stack));
1502 rb_obj_info_dump(*p);
1508show_vm_pcs(
const rb_control_frame_t *cfp,
1509 const rb_control_frame_t *end_of_cfp)
1512 while (cfp != end_of_cfp) {
1515 pc = cfp->pc - ISEQ_BODY(cfp->iseq)->iseq_encoded;
1517 fprintf(stderr,
"%2d pc: %d\n", i++, pc);
1518 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
1524cont_capture(
volatile int *
volatile stat)
1526 rb_context_t *
volatile cont;
1527 rb_thread_t *th = GET_THREAD();
1528 volatile VALUE contval;
1529 const rb_execution_context_t *ec = th->ec;
1531 THREAD_MUST_BE_RUNNING(th);
1532 rb_vm_stack_to_heap(th->ec);
1533 cont = cont_new(rb_cContinuation);
1534 contval = cont->self;
1536#ifdef CAPTURE_JUST_VALID_VM_STACK
1537 cont->saved_vm_stack.slen = ec->cfp->sp - ec->vm_stack;
1538 cont->saved_vm_stack.clen = ec->vm_stack + ec->vm_stack_size - (
VALUE*)ec->cfp;
1539 cont->saved_vm_stack.ptr =
ALLOC_N(
VALUE, cont->saved_vm_stack.slen + cont->saved_vm_stack.clen);
1540 MEMCPY(cont->saved_vm_stack.ptr,
1542 VALUE, cont->saved_vm_stack.slen);
1543 MEMCPY(cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen,
1546 cont->saved_vm_stack.clen);
1548 cont->saved_vm_stack.ptr =
ALLOC_N(
VALUE, ec->vm_stack_size);
1549 MEMCPY(cont->saved_vm_stack.ptr, ec->vm_stack,
VALUE, ec->vm_stack_size);
1552 rb_ec_set_vm_stack(&cont->saved_ec, NULL, 0);
1553 VM_ASSERT(cont->saved_ec.cfp != NULL);
1554 cont_save_machine_stack(th, cont);
1556 if (ruby_setjmp(cont->jmpbuf)) {
1559 VAR_INITIALIZED(cont);
1560 value = cont->value;
1573cont_restore_thread(rb_context_t *cont)
1575 rb_thread_t *th = GET_THREAD();
1578 if (cont->type == CONTINUATION_CONTEXT) {
1580 rb_execution_context_t *sec = &cont->saved_ec;
1581 rb_fiber_t *fiber = NULL;
1583 if (sec->fiber_ptr != NULL) {
1584 fiber = sec->fiber_ptr;
1586 else if (th->root_fiber) {
1587 fiber = th->root_fiber;
1590 if (fiber && th->ec != &fiber->cont.saved_ec) {
1591 ec_switch(th, fiber);
1594 if (th->ec->trace_arg != sec->trace_arg) {
1598#if defined(__wasm__) && !defined(__EMSCRIPTEN__)
1599 if (th->ec->tag != sec->tag) {
1602 struct rb_vm_tag *lowest_common_ancestor = NULL;
1603 size_t num_tags = 0;
1604 size_t num_saved_tags = 0;
1605 for (
struct rb_vm_tag *tag = th->ec->tag; tag != NULL; tag = tag->prev) {
1608 for (
struct rb_vm_tag *tag = sec->tag; tag != NULL; tag = tag->prev) {
1612 size_t min_tags = num_tags <= num_saved_tags ? num_tags : num_saved_tags;
1615 while (num_tags > min_tags) {
1621 while (num_saved_tags > min_tags) {
1622 saved_tag = saved_tag->prev;
1626 while (min_tags > 0) {
1627 if (tag == saved_tag) {
1628 lowest_common_ancestor = tag;
1632 saved_tag = saved_tag->prev;
1637 for (
struct rb_vm_tag *tag = th->ec->tag; tag != lowest_common_ancestor; tag = tag->prev) {
1638 rb_vm_tag_jmpbuf_deinit(&tag->buf);
1644#ifdef CAPTURE_JUST_VALID_VM_STACK
1646 cont->saved_vm_stack.ptr,
1647 VALUE, cont->saved_vm_stack.slen);
1648 MEMCPY(th->ec->vm_stack + th->ec->vm_stack_size - cont->saved_vm_stack.clen,
1649 cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen,
1650 VALUE, cont->saved_vm_stack.clen);
1652 MEMCPY(th->ec->vm_stack, cont->saved_vm_stack.ptr,
VALUE, sec->vm_stack_size);
1656 th->ec->cfp = sec->cfp;
1657 th->ec->raised_flag = sec->raised_flag;
1658 th->ec->tag = sec->tag;
1659 th->ec->root_lep = sec->root_lep;
1660 th->ec->root_svar = sec->root_svar;
1661 th->ec->errinfo = sec->errinfo;
1663 VM_ASSERT(th->ec->vm_stack != NULL);
1667 fiber_restore_thread(th, (rb_fiber_t*)cont);
1671NOINLINE(
static void fiber_setcontext(rb_fiber_t *new_fiber, rb_fiber_t *old_fiber));
1674fiber_setcontext(rb_fiber_t *new_fiber, rb_fiber_t *old_fiber)
1676 rb_thread_t *th = GET_THREAD();
1679 if (!FIBER_TERMINATED_P(old_fiber)) {
1680 STACK_GROW_DIR_DETECTION;
1681 SET_MACHINE_STACK_END(&th->ec->machine.stack_end);
1682 if (STACK_DIR_UPPER(0, 1)) {
1683 old_fiber->cont.machine.stack_size = th->ec->machine.stack_start - th->ec->machine.stack_end;
1684 old_fiber->cont.machine.stack = th->ec->machine.stack_end;
1687 old_fiber->cont.machine.stack_size = th->ec->machine.stack_end - th->ec->machine.stack_start;
1688 old_fiber->cont.machine.stack = th->ec->machine.stack_start;
1693 old_fiber->cont.saved_ec.machine.stack_start = th->ec->machine.stack_start;
1694 old_fiber->cont.saved_ec.machine.stack_end = FIBER_TERMINATED_P(old_fiber) ? NULL : th->ec->machine.stack_end;
1699#if defined(COROUTINE_SANITIZE_ADDRESS)
1700 __sanitizer_start_switch_fiber(FIBER_TERMINATED_P(old_fiber) ? NULL : &old_fiber->context.fake_stack, new_fiber->context.stack_base, new_fiber->context.stack_size);
1704 struct coroutine_context * from = coroutine_transfer(&old_fiber->context, &new_fiber->context);
1706#if defined(COROUTINE_SANITIZE_ADDRESS)
1707 __sanitizer_finish_switch_fiber(old_fiber->context.fake_stack, NULL, NULL);
1715 fiber_restore_thread(th, old_fiber);
1721NOINLINE(NORETURN(
static void cont_restore_1(rb_context_t *)));
1724cont_restore_1(rb_context_t *cont)
1726 cont_restore_thread(cont);
1729#if (defined(_M_AMD64) && !defined(__MINGW64__)) || defined(_M_ARM64)
1734 _JUMP_BUFFER *bp = (
void*)&cont->jmpbuf;
1735 bp->Frame = ((_JUMP_BUFFER*)((
void*)&buf))->Frame;
1738 if (cont->machine.stack_src) {
1739 FLUSH_REGISTER_WINDOWS;
1740 MEMCPY(cont->machine.stack_src, cont->machine.stack,
1741 VALUE, cont->machine.stack_size);
1744 ruby_longjmp(cont->jmpbuf, 1);
1747NORETURN(NOINLINE(
static void cont_restore_0(rb_context_t *,
VALUE *)));
1750cont_restore_0(rb_context_t *cont,
VALUE *addr_in_prev_frame)
1752 if (cont->machine.stack_src) {
1754#define STACK_PAD_SIZE 1
1756#define STACK_PAD_SIZE 1024
1758 VALUE space[STACK_PAD_SIZE];
1760#if !STACK_GROW_DIRECTION
1761 if (addr_in_prev_frame > &space[0]) {
1764#if STACK_GROW_DIRECTION <= 0
1765 volatile VALUE *
const end = cont->machine.stack_src;
1766 if (&space[0] > end) {
1775 cont_restore_0(cont, &space[0]);
1779#if !STACK_GROW_DIRECTION
1784#if STACK_GROW_DIRECTION >= 0
1785 volatile VALUE *
const end = cont->machine.stack_src + cont->machine.stack_size;
1786 if (&space[STACK_PAD_SIZE] < end) {
1791 cont_restore_0(cont, &space[STACK_PAD_SIZE-1]);
1795#if !STACK_GROW_DIRECTION
1799 cont_restore_1(cont);
1886rb_callcc(
VALUE self)
1888 volatile int called;
1889 volatile VALUE val = cont_capture(&called);
1898#ifdef RUBY_ASAN_ENABLED
1901MAYBE_UNUSED(
static void notusing_callcc(
void)) { rb_callcc(
Qnil); }
1902# define rb_callcc rb_f_notimplement
1907make_passing_arg(
int argc,
const VALUE *argv)
1923NORETURN(
static VALUE rb_cont_call(
int argc,
VALUE *argv,
VALUE contval));
1941rb_cont_call(
int argc,
VALUE *argv,
VALUE contval)
1943 rb_context_t *cont = cont_ptr(contval);
1944 rb_thread_t *th = GET_THREAD();
1946 if (cont_thread_value(cont) != th->self) {
1949 if (cont->saved_ec.fiber_ptr) {
1950 if (th->ec->fiber_ptr != cont->saved_ec.fiber_ptr) {
1956 cont->value = make_passing_arg(argc, argv);
1958 cont_restore_0(cont, &contval);
2051 {fiber_mark, fiber_free, fiber_memsize, fiber_compact,},
2052 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
2056fiber_alloc(
VALUE klass)
2062next_ec_serial(rb_ractor_t *cr)
2064 return cr->next_ec_serial++;
2068fiber_t_alloc(
VALUE fiber_value,
unsigned int blocking)
2071 rb_thread_t *th = GET_THREAD();
2077 THREAD_MUST_BE_RUNNING(th);
2078 fiber =
ZALLOC(rb_fiber_t);
2079 fiber->cont.self = fiber_value;
2080 fiber->cont.type = FIBER_CONTEXT;
2081 fiber->blocking = blocking;
2083 cont_init(&fiber->cont, th);
2085 fiber->cont.saved_ec.fiber_ptr = fiber;
2086 fiber->cont.saved_ec.serial = next_ec_serial(th->ractor);
2087 rb_ec_clear_vm_stack(&fiber->cont.saved_ec);
2093 VM_ASSERT(FIBER_CREATED_P(fiber));
2101root_fiber_alloc(rb_thread_t *th)
2103 VALUE fiber_value = fiber_alloc(rb_cFiber);
2104 rb_fiber_t *fiber = th->ec->fiber_ptr;
2106 VM_ASSERT(
DATA_PTR(fiber_value) == NULL);
2107 VM_ASSERT(fiber->cont.type == FIBER_CONTEXT);
2108 VM_ASSERT(FIBER_RESUMED_P(fiber));
2110 th->root_fiber = fiber;
2112 fiber->cont.self = fiber_value;
2114 coroutine_initialize_main(&fiber->context);
2119static inline rb_fiber_t*
2122 rb_execution_context_t *ec = GET_EC();
2123 if (ec->fiber_ptr->cont.self == 0) {
2124 root_fiber_alloc(rb_ec_thread_ptr(ec));
2126 return ec->fiber_ptr;
2130current_fiber_storage(
void)
2132 rb_execution_context_t *ec = GET_EC();
2137inherit_fiber_storage(
void)
2145 fiber->cont.saved_ec.storage = storage;
2149fiber_storage_get(rb_fiber_t *fiber,
int allocate)
2151 VALUE storage = fiber->cont.saved_ec.storage;
2152 if (storage ==
Qnil && allocate) {
2154 fiber_storage_set(fiber, storage);
2160storage_access_must_be_from_same_fiber(
VALUE self)
2162 rb_fiber_t *fiber = fiber_ptr(self);
2163 rb_fiber_t *current = fiber_current();
2164 if (fiber != current) {
2165 rb_raise(rb_eArgError,
"Fiber storage can only be accessed from the Fiber it belongs to");
2176rb_fiber_storage_get(
VALUE self)
2178 storage_access_must_be_from_same_fiber(self);
2180 VALUE storage = fiber_storage_get(fiber_ptr(self), FALSE);
2182 if (storage ==
Qnil) {
2199fiber_storage_validate(
VALUE value)
2202 if (value ==
Qnil)
return;
2242 "Fiber#storage= is experimental and may be removed in the future!");
2245 storage_access_must_be_from_same_fiber(self);
2246 fiber_storage_validate(value);
2248 fiber_ptr(self)->cont.saved_ec.storage =
rb_obj_dup(value);
2267 VALUE storage = fiber_storage_get(fiber_current(), FALSE);
2270 return rb_hash_aref(storage, key);
2288 VALUE storage = fiber_storage_get(fiber_current(), value !=
Qnil);
2291 if (value ==
Qnil) {
2292 return rb_hash_delete(storage, key);
2295 return rb_hash_aset(storage, key, value);
2304 storage = inherit_fiber_storage();
2307 fiber_storage_validate(storage);
2311 rb_fiber_t *fiber = fiber_t_alloc(self, blocking);
2313 fiber->cont.saved_ec.storage = storage;
2314 fiber->first_proc = proc;
2315 fiber->stack.base = NULL;
2322fiber_prepare_stack(rb_fiber_t *fiber)
2324 rb_context_t *cont = &fiber->cont;
2325 rb_execution_context_t *sec = &cont->saved_ec;
2327 size_t vm_stack_size = 0;
2328 VALUE *vm_stack = fiber_initialize_coroutine(fiber, &vm_stack_size);
2331 cont->saved_vm_stack.ptr = NULL;
2332 rb_ec_initialize_vm_stack(sec, vm_stack, vm_stack_size /
sizeof(
VALUE));
2335 sec->local_storage = NULL;
2336 sec->local_storage_recursive_hash =
Qnil;
2337 sec->local_storage_recursive_hash_for_trace =
Qnil;
2341rb_fiber_pool_default(
VALUE pool)
2343 return &shared_fiber_pool;
2349 fiber->cont.saved_ec.storage = storage;
2355rb_fiber_initialize_kw(
int argc,
VALUE* argv,
VALUE self,
int kw_splat)
2366 rb_get_kwargs(options, fiber_initialize_keywords, 0, 3, arguments);
2368 if (!UNDEF_P(arguments[0])) {
2369 blocking = arguments[0];
2372 if (!UNDEF_P(arguments[1])) {
2373 pool = arguments[1];
2376 storage = arguments[2];
2379 return fiber_initialize(self,
rb_block_proc(), rb_fiber_pool_default(pool),
RTEST(blocking), storage);
2432rb_fiber_initialize(
int argc,
VALUE* argv,
VALUE self)
2440 return fiber_initialize(fiber_alloc(rb_cFiber),
rb_proc_new(func, obj), rb_fiber_pool_default(
Qnil), 0, storage);
2446 return rb_fiber_new_storage(func, obj,
Qtrue);
2450rb_fiber_s_schedule_kw(
int argc,
VALUE* argv,
int kw_splat)
2452 rb_thread_t * th = GET_THREAD();
2453 VALUE scheduler = th->scheduler;
2456 if (scheduler !=
Qnil) {
2508rb_fiber_s_schedule(
int argc,
VALUE *argv,
VALUE obj)
2524rb_fiber_s_scheduler(
VALUE klass)
2538rb_fiber_current_scheduler(
VALUE klass)
2560rb_fiber_set_scheduler(
VALUE klass,
VALUE scheduler)
2565NORETURN(
static void rb_fiber_terminate(rb_fiber_t *fiber,
int need_interrupt,
VALUE err));
2568rb_fiber_start(rb_fiber_t *fiber)
2570 rb_thread_t *
volatile th = fiber->cont.saved_ec.thread_ptr;
2573 enum ruby_tag_type state;
2575 VM_ASSERT(th->ec == GET_EC());
2576 VM_ASSERT(FIBER_RESUMED_P(fiber));
2578 if (fiber->blocking) {
2582 EC_PUSH_TAG(th->ec);
2583 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
2584 rb_context_t *cont = &VAR_FROM_MEMORY(fiber)->cont;
2586 const VALUE *argv, args = cont->value;
2587 GetProcPtr(fiber->first_proc, proc);
2590 th->ec->errinfo =
Qnil;
2591 th->ec->root_lep = rb_vm_proc_local_ep(fiber->first_proc);
2592 th->ec->root_svar =
Qfalse;
2595 cont->value = rb_vm_invoke_proc(th->ec, proc, argc, argv, cont->kw_splat, VM_BLOCK_HANDLER_NONE);
2599 int need_interrupt = TRUE;
2602 err = th->ec->errinfo;
2603 VM_ASSERT(FIBER_RESUMED_P(fiber));
2605 if (state == TAG_RAISE) {
2608 else if (state == TAG_FATAL && err == RUBY_FATAL_FIBER_KILLED) {
2609 need_interrupt = FALSE;
2612 else if (state == TAG_FATAL) {
2613 rb_threadptr_pending_interrupt_enque(th, err);
2616 err = rb_vm_make_jump_tag_but_local_jump(state, err);
2620 rb_fiber_terminate(fiber, need_interrupt, err);
2625rb_threadptr_root_fiber_setup(rb_thread_t *th)
2627 rb_fiber_t *fiber = ruby_mimcalloc(1,
sizeof(rb_fiber_t));
2629 rb_bug(
"%s", strerror(
errno));
2631 fiber->cont.type = FIBER_CONTEXT;
2632 fiber->cont.saved_ec.fiber_ptr = fiber;
2633 fiber->cont.saved_ec.serial = next_ec_serial(th->ractor);
2634 fiber->cont.saved_ec.thread_ptr = th;
2635 fiber->blocking = 1;
2637 fiber_status_set(fiber, FIBER_RESUMED);
2638 th->ec = &fiber->cont.saved_ec;
2639 cont_init_jit_cont(&fiber->cont);
2643rb_threadptr_root_fiber_release(rb_thread_t *th)
2645 if (th->root_fiber) {
2649 rb_execution_context_t *ec = rb_current_execution_context(
false);
2651 VM_ASSERT(th->ec->fiber_ptr->cont.type == FIBER_CONTEXT);
2652 VM_ASSERT(th->ec->fiber_ptr->cont.self == 0);
2654 if (ec && th->ec == ec) {
2655 rb_ractor_set_current_ec(th->ractor, NULL);
2657 fiber_free(th->ec->fiber_ptr);
2663rb_threadptr_root_fiber_terminate(rb_thread_t *th)
2665 rb_fiber_t *fiber = th->ec->fiber_ptr;
2667 fiber->status = FIBER_TERMINATED;
2670 rb_ec_clear_vm_stack(th->ec);
2673static inline rb_fiber_t*
2674return_fiber(
bool terminate)
2676 rb_fiber_t *fiber = fiber_current();
2677 rb_fiber_t *prev = fiber->prev;
2681 prev->resuming_fiber = NULL;
2686 rb_raise(rb_eFiberError,
"attempt to yield on a not resumed fiber");
2689 rb_thread_t *th = GET_THREAD();
2690 rb_fiber_t *root_fiber = th->root_fiber;
2692 VM_ASSERT(root_fiber != NULL);
2695 for (fiber = root_fiber; fiber->resuming_fiber; fiber = fiber->resuming_fiber) {
2705 return fiber_current()->cont.self;
2710fiber_store(rb_fiber_t *next_fiber, rb_thread_t *th)
2714 if (th->ec->fiber_ptr != NULL) {
2715 fiber = th->ec->fiber_ptr;
2719 fiber = root_fiber_alloc(th);
2722 if (FIBER_CREATED_P(next_fiber)) {
2723 fiber_prepare_stack(next_fiber);
2726 VM_ASSERT(FIBER_RESUMED_P(fiber) || FIBER_TERMINATED_P(fiber));
2727 VM_ASSERT(FIBER_RUNNABLE_P(next_fiber));
2729 if (FIBER_RESUMED_P(fiber)) fiber_status_set(fiber, FIBER_SUSPENDED);
2731 fiber_status_set(next_fiber, FIBER_RESUMED);
2732 fiber_setcontext(next_fiber, fiber);
2736fiber_check_killed(rb_fiber_t *fiber)
2738 VM_ASSERT(fiber == fiber_current());
2740 if (fiber->killed) {
2741 rb_thread_t *thread = fiber->cont.saved_ec.thread_ptr;
2743 thread->ec->errinfo = RUBY_FATAL_FIBER_KILLED;
2744 EC_JUMP_TAG(thread->ec, RUBY_TAG_FATAL);
2749fiber_switch(rb_fiber_t *fiber,
int argc,
const VALUE *argv,
int kw_splat, rb_fiber_t *resuming_fiber,
bool yielding)
2752 rb_context_t *cont = &fiber->cont;
2753 rb_thread_t *th = GET_THREAD();
2756 if (th->root_fiber == NULL) root_fiber_alloc(th);
2758 if (th->ec->fiber_ptr == fiber) {
2762 return make_passing_arg(argc, argv);
2765 if (cont_thread_value(cont) != th->self) {
2766 rb_raise(rb_eFiberError,
"fiber called across threads");
2769 if (FIBER_TERMINATED_P(fiber)) {
2770 value =
rb_exc_new2(rb_eFiberError,
"dead fiber called");
2772 if (!FIBER_TERMINATED_P(th->ec->fiber_ptr)) {
2774 VM_UNREACHABLE(fiber_switch);
2780 VM_ASSERT(FIBER_SUSPENDED_P(th->root_fiber));
2782 cont = &th->root_fiber->cont;
2784 cont->value = value;
2786 fiber_setcontext(th->root_fiber, th->ec->fiber_ptr);
2788 VM_UNREACHABLE(fiber_switch);
2792 VM_ASSERT(FIBER_RUNNABLE_P(fiber));
2794 rb_fiber_t *current_fiber = fiber_current();
2796 VM_ASSERT(!current_fiber->resuming_fiber);
2798 if (resuming_fiber) {
2799 current_fiber->resuming_fiber = resuming_fiber;
2800 fiber->prev = fiber_current();
2801 fiber->yielding = 0;
2804 VM_ASSERT(!current_fiber->yielding);
2806 current_fiber->yielding = 1;
2809 if (current_fiber->blocking) {
2814 cont->kw_splat = kw_splat;
2815 cont->value = make_passing_arg(argc, argv);
2817 fiber_store(fiber, th);
2820#ifndef COROUTINE_PTHREAD_CONTEXT
2821 if (FIBER_TERMINATED_P(fiber)) {
2823 fiber_stack_release(fiber);
2828 if (fiber_current()->blocking) {
2832 RUBY_VM_CHECK_INTS(th->ec);
2836 current_fiber = th->ec->fiber_ptr;
2837 value = current_fiber->cont.value;
2839 fiber_check_killed(current_fiber);
2841 if (current_fiber->cont.argc == -1) {
2852 return fiber_switch(fiber_ptr(fiber_value), argc, argv,
RB_NO_KEYWORDS, NULL,
false);
2870rb_fiber_blocking_p(
VALUE fiber)
2872 return RBOOL(fiber_ptr(fiber)->blocking);
2876fiber_blocking_yield(
VALUE fiber_value)
2878 rb_fiber_t *fiber = fiber_ptr(fiber_value);
2879 rb_thread_t *
volatile th = fiber->cont.saved_ec.thread_ptr;
2881 VM_ASSERT(fiber->blocking == 0);
2884 fiber->blocking = 1;
2893fiber_blocking_ensure(
VALUE fiber_value)
2895 rb_fiber_t *fiber = fiber_ptr(fiber_value);
2896 rb_thread_t *
volatile th = fiber->cont.saved_ec.thread_ptr;
2899 fiber->blocking = 0;
2916rb_fiber_blocking(
VALUE class)
2919 rb_fiber_t *fiber = fiber_ptr(fiber_value);
2922 if (fiber->blocking) {
2926 return rb_ensure(fiber_blocking_yield, fiber_value, fiber_blocking_ensure, fiber_value);
2949rb_fiber_s_blocking_p(
VALUE klass)
2951 rb_thread_t *thread = GET_THREAD();
2952 unsigned blocking = thread->blocking;
2961rb_fiber_close(rb_fiber_t *fiber)
2963 fiber_status_set(fiber, FIBER_TERMINATED);
2964 rb_ec_close(&fiber->cont.saved_ec);
2968rb_fiber_terminate(rb_fiber_t *fiber,
int need_interrupt,
VALUE error)
2970 VALUE value = fiber->cont.value;
2972 VM_ASSERT(FIBER_RESUMED_P(fiber));
2973 rb_fiber_close(fiber);
2975 fiber->cont.machine.stack = NULL;
2976 fiber->cont.machine.stack_size = 0;
2978 rb_fiber_t *next_fiber = return_fiber(
true);
2980 if (need_interrupt) RUBY_VM_SET_INTERRUPT(&next_fiber->cont.saved_ec);
2983 fiber_switch(next_fiber, -1, &error,
RB_NO_KEYWORDS, NULL,
false);
2985 fiber_switch(next_fiber, 1, &value,
RB_NO_KEYWORDS, NULL,
false);
2990fiber_resume_kw(rb_fiber_t *fiber,
int argc,
const VALUE *argv,
int kw_splat)
2992 rb_fiber_t *current_fiber = fiber_current();
2994 if (argc == -1 && FIBER_CREATED_P(fiber)) {
2995 rb_raise(rb_eFiberError,
"cannot raise exception on unborn fiber");
2997 else if (FIBER_TERMINATED_P(fiber)) {
2998 rb_raise(rb_eFiberError,
"attempt to resume a terminated fiber");
3000 else if (fiber == current_fiber) {
3001 rb_raise(rb_eFiberError,
"attempt to resume the current fiber");
3003 else if (fiber->prev != NULL) {
3004 rb_raise(rb_eFiberError,
"attempt to resume a resumed fiber (double resume)");
3006 else if (fiber->resuming_fiber) {
3007 rb_raise(rb_eFiberError,
"attempt to resume a resuming fiber");
3009 else if (fiber->prev == NULL &&
3010 (!fiber->yielding && fiber->status != FIBER_CREATED)) {
3011 rb_raise(rb_eFiberError,
"attempt to resume a transferring fiber");
3014 return fiber_switch(fiber, argc, argv, kw_splat, fiber,
false);
3018rb_fiber_resume_kw(
VALUE self,
int argc,
const VALUE *argv,
int kw_splat)
3020 return fiber_resume_kw(fiber_ptr(self), argc, argv, kw_splat);
3026 return fiber_resume_kw(fiber_ptr(self), argc, argv,
RB_NO_KEYWORDS);
3030rb_fiber_yield_kw(
int argc,
const VALUE *argv,
int kw_splat)
3032 return fiber_switch(return_fiber(
false), argc, argv, kw_splat, NULL,
true);
3036rb_fiber_yield(
int argc,
const VALUE *argv)
3038 return fiber_switch(return_fiber(
false), argc, argv,
RB_NO_KEYWORDS, NULL,
true);
3042rb_fiber_reset_root_local_storage(rb_thread_t *th)
3044 if (th->root_fiber && th->root_fiber != th->ec->fiber_ptr) {
3045 th->ec->local_storage = th->root_fiber->cont.saved_ec.local_storage;
3060 return RBOOL(!FIBER_TERMINATED_P(fiber_ptr(fiber_value)));
3079rb_fiber_m_resume(
int argc,
VALUE *argv,
VALUE fiber)
3131rb_fiber_backtrace(
int argc,
VALUE *argv,
VALUE fiber)
3133 return rb_vm_backtrace(argc, argv, &fiber_ptr(fiber)->cont.saved_ec);
3156rb_fiber_backtrace_locations(
int argc,
VALUE *argv,
VALUE fiber)
3158 return rb_vm_backtrace_locations(argc, argv, &fiber_ptr(fiber)->cont.saved_ec);
3244rb_fiber_m_transfer(
int argc,
VALUE *argv,
VALUE self)
3250fiber_transfer_kw(rb_fiber_t *fiber,
int argc,
const VALUE *argv,
int kw_splat)
3252 if (fiber->resuming_fiber) {
3253 rb_raise(rb_eFiberError,
"attempt to transfer to a resuming fiber");
3256 if (fiber->yielding) {
3257 rb_raise(rb_eFiberError,
"attempt to transfer to a yielding fiber");
3260 return fiber_switch(fiber, argc, argv, kw_splat, NULL,
false);
3264rb_fiber_transfer_kw(
VALUE self,
int argc,
const VALUE *argv,
int kw_splat)
3266 return fiber_transfer_kw(fiber_ptr(self), argc, argv, kw_splat);
3280rb_fiber_s_yield(
int argc,
VALUE *argv,
VALUE klass)
3286fiber_raise(rb_fiber_t *fiber,
VALUE exception)
3288 if (fiber == fiber_current()) {
3291 else if (fiber->resuming_fiber) {
3292 return fiber_raise(fiber->resuming_fiber, exception);
3294 else if (FIBER_SUSPENDED_P(fiber) && !fiber->yielding) {
3305 VALUE exception = rb_exception_setup(argc, argv);
3307 return fiber_raise(fiber_ptr(fiber), exception);
3345rb_fiber_m_raise(
int argc,
VALUE *argv,
VALUE self)
3347 return rb_fiber_raise(self, argc, argv);
3368rb_fiber_m_kill(
VALUE self)
3370 rb_fiber_t *fiber = fiber_ptr(self);
3372 if (fiber->killed)
return Qfalse;
3375 if (fiber->status == FIBER_CREATED) {
3376 fiber->status = FIBER_TERMINATED;
3378 else if (fiber->status != FIBER_TERMINATED) {
3379 if (fiber_current() == fiber) {
3380 fiber_check_killed(fiber);
3383 fiber_raise(fiber_ptr(self),
Qnil);
3398rb_fiber_s_current(
VALUE klass)
3404fiber_to_s(
VALUE fiber_value)
3406 const rb_fiber_t *fiber = fiber_ptr(fiber_value);
3408 char status_info[0x20];
3410 if (fiber->resuming_fiber) {
3411 snprintf(status_info, 0x20,
" (%s by resuming)", fiber_status_name(fiber->status));
3414 snprintf(status_info, 0x20,
" (%s)", fiber_status_name(fiber->status));
3419 strlcat(status_info,
">",
sizeof(status_info));
3424 GetProcPtr(fiber->first_proc, proc);
3425 return rb_block_to_s(fiber_value, &proc->block, status_info);
3428#ifdef HAVE_WORKING_FORK
3430rb_fiber_atfork(rb_thread_t *th)
3432 if (th->root_fiber) {
3433 if (&th->root_fiber->cont.saved_ec != th->ec) {
3434 th->root_fiber = th->ec->fiber_ptr;
3436 th->root_fiber->prev = 0;
3437 th->root_fiber->blocking = 1;
3443#ifdef RB_EXPERIMENTAL_FIBER_POOL
3445fiber_pool_free(
void *ptr)
3448 RUBY_FREE_ENTER(
"fiber_pool");
3450 fiber_pool_allocation_free(
fiber_pool->allocations);
3453 RUBY_FREE_LEAVE(
"fiber_pool");
3457fiber_pool_memsize(
const void *ptr)
3460 size_t size =
sizeof(*fiber_pool);
3469 {NULL, fiber_pool_free, fiber_pool_memsize,},
3470 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
3474fiber_pool_alloc(
VALUE klass)
3482rb_fiber_pool_initialize(
int argc,
VALUE* argv,
VALUE self)
3484 rb_thread_t *th = GET_THREAD();
3489 rb_scan_args(argc, argv,
"03", &size, &count, &vm_stack_size);
3492 size =
SIZET2NUM(th->vm->default_params.fiber_machine_stack_size);
3499 if (
NIL_P(vm_stack_size)) {
3500 vm_stack_size =
SIZET2NUM(th->vm->default_params.fiber_vm_stack_size);
3527 rb_thread_t *th = GET_THREAD();
3528 size_t vm_stack_size = th->vm->default_params.fiber_vm_stack_size;
3529 size_t machine_stack_size = th->vm->default_params.fiber_machine_stack_size;
3530 size_t stack_size = machine_stack_size + vm_stack_size;
3534 GetSystemInfo(&info);
3535 pagesize = info.dwPageSize;
3537 pagesize = sysconf(_SC_PAGESIZE);
3539 SET_MACHINE_STACK_END(&th->ec->machine.stack_end);
3541 fiber_pool_initialize(&shared_fiber_pool, stack_size, FIBER_POOL_INITIAL_SIZE, vm_stack_size);
3547 const char *fiber_shared_fiber_pool_free_stacks = getenv(
"RUBY_SHARED_FIBER_POOL_FREE_STACKS");
3548 if (fiber_shared_fiber_pool_free_stacks) {
3549 shared_fiber_pool.free_stacks = atoi(fiber_shared_fiber_pool_free_stacks);
3551 if (shared_fiber_pool.free_stacks < 0) {
3552 rb_warn(
"Setting RUBY_SHARED_FIBER_POOL_FREE_STACKS to a negative value is not allowed.");
3553 shared_fiber_pool.free_stacks = 0;
3556 if (shared_fiber_pool.free_stacks > 1) {
3557 rb_warn(
"Setting RUBY_SHARED_FIBER_POOL_FREE_STACKS to a value greater than 1 is operating system specific, and may cause crashes.");
3578 rb_define_method(rb_cFiber,
"backtrace_locations", rb_fiber_backtrace_locations, -1);
3591#ifdef RB_EXPERIMENTAL_FIBER_POOL
3598 rb_define_method(rb_cFiberPool,
"initialize", rb_fiber_pool_initialize, -1);
3604RUBY_SYMBOL_EXPORT_BEGIN
3607ruby_Init_Continuation_body(
void)
3617RUBY_SYMBOL_EXPORT_END
#define rb_define_method(klass, mid, func, arity)
Defines klass#mid.
#define rb_define_singleton_method(klass, mid, func, arity)
Defines klass.mid.
#define rb_define_global_function(mid, func, arity)
Defines rb_mKernel #mid.
#define RUBY_EVENT_FIBER_SWITCH
Encountered a Fiber#yield.
static bool RB_OBJ_FROZEN(VALUE obj)
Checks if an object is frozen.
VALUE rb_define_class(const char *name, VALUE super)
Defines a top-level class.
VALUE rb_define_class_under(VALUE outer, const char *name, VALUE super)
Defines a class under the namespace of outer.
void rb_define_alias(VALUE klass, const char *name1, const char *name2)
Defines an alias of a method.
void rb_undef_method(VALUE klass, const char *name)
Defines an undef of a method.
int rb_scan_args_kw(int kw_flag, int argc, const VALUE *argv, const char *fmt,...)
Identical to rb_scan_args(), except it also accepts kw_splat.
int rb_scan_args(int argc, const VALUE *argv, const char *fmt,...)
Retrieves argument from argc and argv to given VALUE references according to the format string.
int rb_keyword_given_p(void)
Determines if the current method is given a keyword argument.
int rb_get_kwargs(VALUE keyword_hash, const ID *table, int required, int optional, VALUE *values)
Keyword argument deconstructor.
#define REALLOC_N
Old name of RB_REALLOC_N.
#define xfree
Old name of ruby_xfree.
#define Qundef
Old name of RUBY_Qundef.
#define UNREACHABLE_RETURN
Old name of RBIMPL_UNREACHABLE_RETURN.
#define ZALLOC
Old name of RB_ZALLOC.
#define CLASS_OF
Old name of rb_class_of.
#define rb_ary_new4
Old name of rb_ary_new_from_values.
#define SIZET2NUM
Old name of RB_SIZE2NUM.
#define rb_exc_new2
Old name of rb_exc_new_cstr.
#define T_HASH
Old name of RUBY_T_HASH.
#define ALLOC_N
Old name of RB_ALLOC_N.
#define Qtrue
Old name of RUBY_Qtrue.
#define INT2NUM
Old name of RB_INT2NUM.
#define Qnil
Old name of RUBY_Qnil.
#define Qfalse
Old name of RUBY_Qfalse.
#define NIL_P
Old name of RB_NIL_P.
#define T_SYMBOL
Old name of RUBY_T_SYMBOL.
#define NUM2SIZET
Old name of RB_NUM2SIZE.
void ruby_stop(int ex)
Calls ruby_cleanup() and exits the process.
void rb_category_warn(rb_warning_category_t category, const char *fmt,...)
Identical to rb_category_warning(), except it reports unless $VERBOSE is nil.
void rb_exc_raise(VALUE mesg)
Raises an exception in the current thread.
int rb_typeddata_is_kind_of(VALUE obj, const rb_data_type_t *data_type)
Checks if the given object is of given kind.
void rb_syserr_fail(int e, const char *mesg)
Raises appropriate exception that represents a C errno.
VALUE rb_eStandardError
StandardError exception.
VALUE rb_eFrozenError
FrozenError exception.
VALUE rb_eTypeError
TypeError exception.
VALUE rb_eRuntimeError
RuntimeError exception.
void rb_warn(const char *fmt,...)
Identical to rb_warning(), except it reports unless $VERBOSE is nil.
@ RB_WARN_CATEGORY_EXPERIMENTAL
Warning is for experimental features.
VALUE rb_any_to_s(VALUE obj)
Generates a textual representation of the given object.
VALUE rb_obj_dup(VALUE obj)
Duplicates the given object.
void rb_memerror(void)
Triggers out-of-memory error.
void rb_gc(void)
Triggers a GC process.
VALUE rb_fiber_current(void)
Queries the fiber which is calling this function.
VALUE rb_hash_new(void)
Creates a new, empty hash object.
void rb_provide(const char *feature)
Declares that the given feature is already provided by someone else.
VALUE rb_block_proc(void)
Constructs a Proc object from implicitly passed components.
VALUE rb_obj_is_proc(VALUE recv)
Queries if the given object is a proc.
void rb_str_set_len(VALUE str, long len)
Overwrites the length of the string.
#define rb_str_cat_cstr(buf, str)
Identical to rb_str_cat(), except it assumes the passed pointer is a pointer to a C string.
void rb_undef_alloc_func(VALUE klass)
Deletes the allocator function of a class.
void rb_define_alloc_func(VALUE klass, rb_alloc_func_t func)
Sets the allocator function of a class.
static ID rb_intern_const(const char *str)
This is a "tiny optimisation" over rb_intern().
VALUE rb_to_symbol(VALUE name)
Identical to rb_intern_str(), except it generates a dynamic symbol if necessary.
VALUE rb_yield(VALUE val)
Yields the block.
rb_block_call_func * rb_block_call_func_t
Shorthand type that represents an iterator-written-in-C function pointer.
#define MEMCPY(p1, p2, type, n)
Handy macro to call memcpy.
#define ALLOCA_N(type, n)
#define RB_ALLOC(type)
Shorthand of RB_ALLOC_N with n=1.
VALUE rb_proc_new(type *q, VALUE w)
Creates a rb_cProc instance.
void rb_hash_foreach(VALUE q, int_type *w, VALUE e)
Iteration over the given hash.
VALUE rb_ensure(type *q, VALUE w, type *e, VALUE r)
An equivalent of ensure clause.
#define RARRAY_CONST_PTR
Just another name of rb_array_const_ptr.
#define DATA_PTR(obj)
Convenient getter macro.
#define TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
#define TypedData_Wrap_Struct(klass, data_type, sval)
Converts sval, a pointer to your struct, into a Ruby object.
struct rb_data_type_struct rb_data_type_t
This is the struct that holds necessary info for a struct.
#define TypedData_Make_Struct(klass, type, data_type, sval)
Identical to TypedData_Wrap_Struct, except it allocates a new data region internally instead of takin...
#define errno
Ractor-aware version of errno.
#define RB_NO_KEYWORDS
Do not pass keywords.
VALUE rb_fiber_scheduler_current(void)
Identical to rb_fiber_scheduler_get(), except it also returns RUBY_Qnil in case of a blocking fiber.
VALUE rb_fiber_scheduler_set(VALUE scheduler)
Destructively assigns the passed scheduler to that of the current thread that is calling this functio...
VALUE rb_fiber_scheduler_get(void)
Queries the current scheduler of the current thread that is calling this function.
VALUE rb_fiber_scheduler_fiber(VALUE scheduler, int argc, VALUE *argv, int kw_splat)
Create and schedule a non-blocking fiber.
#define RTEST
This is an old name of RB_TEST.
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_mutex_destroy(rb_nativethread_lock_t *lock)
Just another name of rb_nativethread_lock_destroy.
uintptr_t ID
Type that represents a Ruby identifier such as a variable name.
uintptr_t VALUE
Type that represents a Ruby object.
static void Check_Type(VALUE v, enum ruby_value_type t)
Identical to RB_TYPE_P(), except it raises exceptions on predication failure.
static bool RB_TYPE_P(VALUE obj, enum ruby_value_type t)
Queries if the given object is of given type.