12#include "ruby/internal/config.h"
21#include "eval_intern.h"
23#include "internal/cont.h"
24#include "internal/jit.h"
25#include "internal/thread.h"
26#include "internal/error.h"
27#include "internal/eval.h"
28#include "internal/gc.h"
29#include "internal/proc.h"
30#include "internal/sanitizers.h"
31#include "internal/vm_map.h"
32#include "internal/warnings.h"
39#include "ractor_core.h"
48#define RB_PAGE_SIZE (pagesize)
49#define RB_PAGE_MASK (~(RB_PAGE_SIZE - 1))
54static VALUE rb_cContinuation;
55static VALUE rb_cFiber;
56static VALUE rb_eFiberError;
57#ifdef RB_EXPERIMENTAL_FIBER_POOL
58static VALUE rb_cFiberPool;
61#define CAPTURE_JUST_VALID_VM_STACK 1
64#ifdef COROUTINE_LIMITED_ADDRESS_SPACE
65#define FIBER_POOL_ALLOCATION_FREE
66#define FIBER_POOL_MINIMUM_COUNT 8
67#define FIBER_POOL_MAXIMUM_ALLOCATIONS 32
69#define FIBER_POOL_MINIMUM_COUNT 32
70#define FIBER_POOL_MAXIMUM_ALLOCATIONS 1024
72#ifdef RB_EXPERIMENTAL_FIBER_POOL
73#define FIBER_POOL_ALLOCATION_FREE
77 CONTINUATION_CONTEXT = 0,
84#ifdef CAPTURE_JUST_VALID_VM_STACK
121#ifdef FIBER_POOL_ALLOCATION_FREE
164#ifdef FIBER_POOL_ALLOCATION_FREE
172#ifdef FIBER_POOL_ALLOCATION_FREE
178#if VM_CHECK_MODE > 0 && defined(HAVE_PTHREAD_H)
179#define ASSERT_FIBER_POOL_LOCK_OWNER_P (true)
181#define ASSERT_FIBER_POOL_LOCK_OWNER_P (false)
199 size_t minimum_count;
203 size_t maximum_count;
214 size_t vm_stack_size;
217 rb_nativethread_lock_t lock;
219#if ASSERT_FIBER_POOL_LOCK_OWNER_P
220 pthread_t lock_owner;
224 struct ccan_list_node list_node;
237 enum context_type type;
275#define FIBER_CREATED_P(fiber) ((fiber)->status == FIBER_CREATED)
276#define FIBER_RESUMED_P(fiber) ((fiber)->status == FIBER_RESUMED)
277#define FIBER_SUSPENDED_P(fiber) ((fiber)->status == FIBER_SUSPENDED)
278#define FIBER_TERMINATED_P(fiber) ((fiber)->status == FIBER_TERMINATED)
279#define FIBER_RUNNABLE_P(fiber) (FIBER_CREATED_P(fiber) || FIBER_SUSPENDED_P(fiber))
287 BITFIELD(
enum fiber_status, status, 2);
289 unsigned int yielding : 1;
290 unsigned int blocking : 1;
292 unsigned int killed : 1;
298static struct fiber_pool shared_fiber_pool = {NULL, NULL, 0, 0, 0, 0};
301rb_free_shared_fiber_pool(
void)
304 while (allocations) {
306 ruby_mimfree(allocations);
311static ID fiber_initialize_keywords[3] = {0};
313static CCAN_LIST_HEAD(fiber_pool_list);
314static rb_nativethread_lock_t fiber_pool_list_lock;
316#if ASSERT_FIBER_POOL_LOCK_OWNER_P
320 return pthread_equal(pthread_self(),
fiber_pool->lock_owner);
326#if VM_CHECK_MODE == 0
327 if (!rb_multi_ractor_p()) {
337#if VM_CHECK_MODE == 0
338 if (!rb_multi_ractor_p()) {
345#define ASSERT_fiber_pool_locked(fiber_pool) (void)0
346#define ASSERT_fiber_pool_unlocked(fiber_pool) (void)0
352#if VM_CHECK_MODE == 0
354 if (!rb_multi_ractor_p())
return;
358#if ASSERT_FIBER_POOL_LOCK_OWNER_P
366#if VM_CHECK_MODE == 0
367 if (!rb_multi_ractor_p())
return;
370#if ASSERT_FIBER_POOL_LOCK_OWNER_P
380#if ASSERT_FIBER_POOL_LOCK_OWNER_P
390 ccan_list_add(&fiber_pool_list, &
fiber_pool->list_node);
395#ifdef RB_EXPERIMENTAL_FIBER_POOL
408rb_fiber_pool_lock_atfork(
void)
413 ccan_list_for_each(&fiber_pool_list,
fiber_pool, list_node) {
423#if defined(MAP_STACK) && !defined(__FreeBSD__)
424#define FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON | MAP_STACK)
425#define FIBER_PROT_FLAGS (PROT_READ | PROT_WRITE)
427#define FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON)
429#define FIBER_BASE_PROT_FLAGS PROT_READ | PROT_WRITE
430#define FIBER_PROT_FLAGS (FIBER_BASE_PROT_FLAGS | PROT_MAX(FIBER_BASE_PROT_FLAGS))
432#define FIBER_PROT_FLAGS (PROT_READ | PROT_WRITE)
436#define ERRNOMSG strerror(errno)
440fiber_pool_vacancy_pointer(
void * base,
size_t size)
442 STACK_GROW_DIR_DETECTION;
445 (
char*)base + STACK_DIR_UPPER(0, size - RB_PAGE_SIZE)
449#if defined(COROUTINE_SANITIZE_ADDRESS)
454 STACK_GROW_DIR_DETECTION;
456 return (
char*)stack->base + STACK_DIR_UPPER(RB_PAGE_SIZE, 0);
463 return stack->size - RB_PAGE_SIZE;
471 STACK_GROW_DIR_DETECTION;
473 stack->current = (
char*)stack->base + STACK_DIR_UPPER(0, stack->size);
474 stack->available = stack->size;
481 STACK_GROW_DIR_DETECTION;
483 VM_ASSERT(stack->current);
485 return STACK_DIR_UPPER(stack->current, (
char*)stack->current - stack->available);
493 STACK_GROW_DIR_DETECTION;
495 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_alloca(%p): %"PRIuSIZE
"/%"PRIuSIZE
"\n", (
void*)stack, offset, stack->available);
496 VM_ASSERT(stack->available >= offset);
499 void * pointer = STACK_DIR_UPPER(stack->current, (
char*)stack->current - offset);
502 stack->current = STACK_DIR_UPPER((
char*)stack->current + offset, (
char*)stack->current - offset);
503 stack->available -= offset;
512 fiber_pool_stack_reset(&vacancy->stack);
515 fiber_pool_stack_alloca(&vacancy->stack, RB_PAGE_SIZE);
521 ASSERT_fiber_pool_locked(vacancy->stack.pool);
522 vacancy->next = head;
524#ifdef FIBER_POOL_ALLOCATION_FREE
526 head->previous = vacancy;
527 vacancy->previous = NULL;
534#ifdef FIBER_POOL_ALLOCATION_FREE
539 vacancy->next->previous = vacancy->previous;
542 if (vacancy->previous) {
543 vacancy->previous->next = vacancy->next;
547 vacancy->stack.pool->vacancies = vacancy->next;
552fiber_pool_vacancy_pop(
struct fiber_pool * pool)
554 ASSERT_fiber_pool_locked(pool);
558 fiber_pool_vacancy_remove(vacancy);
565fiber_pool_vacancy_pop(
struct fiber_pool * pool)
567 ASSERT_fiber_pool_locked(pool);
571 pool->vacancies = vacancy->next;
586 vacancy->stack.base = base;
587 vacancy->stack.size = size;
589 fiber_pool_vacancy_reset(vacancy);
593 return fiber_pool_vacancy_push(vacancy, vacancies);
601fiber_pool_allocate_memory(
size_t * count,
size_t stride)
611 void * base = VirtualAlloc(0, (*count)*stride, MEM_COMMIT, PAGE_READWRITE);
614 errno = rb_w32_map_errno(GetLastError());
615 *count = (*count) >> 1;
622 size_t mmap_size = (*count)*stride;
623 void * base = mmap(NULL, mmap_size, FIBER_PROT_FLAGS, FIBER_STACK_FLAGS, -1, 0);
625 if (base == MAP_FAILED) {
627 *count = (*count) >> 1;
630 ruby_annotate_mmap(base, mmap_size,
"Ruby:fiber_pool_allocate_memory");
631 rb_vm_map_reuse(base, mmap_size);
657 if (RB_UNLIKELY(!allocation)) {
663 STACK_GROW_DIR_DETECTION;
666 size_t stride = size + RB_PAGE_SIZE;
671 if (needs_lock) fiber_pool_unlock(
fiber_pool);
672 ruby_mimfree(allocation);
677 if (count > remaining) {
683 void * base = fiber_pool_allocate_memory(&count, stride);
686 int saved_errno =
errno;
687 if (!saved_errno) saved_errno = ENOMEM;
688 if (needs_lock) fiber_pool_unlock(
fiber_pool);
689 ruby_mimfree(allocation);
697 allocation->base = base;
698 allocation->size = size;
699 allocation->stride = stride;
700 allocation->count = count;
701#ifdef FIBER_POOL_ALLOCATION_FREE
702 allocation->used = 0;
707 fprintf(stderr,
"fiber_pool_expand(%"PRIuSIZE
"): %p, %"PRIuSIZE
"/%"PRIuSIZE
" x [%"PRIuSIZE
":%"PRIuSIZE
"]\n",
712 for (
size_t i = 0; i < count; i += 1) {
713 void * base = (
char*)allocation->base + (stride * i);
714 void * page = (
char*)base + STACK_DIR_UPPER(size, 0);
718 if (!VirtualProtect(page, RB_PAGE_SIZE, PAGE_READWRITE | PAGE_GUARD, &old_protect)) {
719 int error = rb_w32_map_errno(GetLastError());
720 if (needs_lock) fiber_pool_unlock(
fiber_pool);
721 VirtualFree(allocation->base, 0, MEM_RELEASE);
722 ruby_mimfree(allocation);
726#elif defined(__wasi__)
730 if (mprotect(page, RB_PAGE_SIZE, PROT_NONE) < 0) {
732 if (needs_lock) fiber_pool_unlock(
fiber_pool);
733 if (!error) error = ENOMEM;
734 munmap(allocation->base, count*stride);
735 ruby_mimfree(allocation);
741 vacancies = fiber_pool_vacancy_initialize(
743 (
char*)base + STACK_DIR_UPPER(0, RB_PAGE_SIZE),
747#ifdef FIBER_POOL_ALLOCATION_FREE
748 vacancies->stack.allocation = allocation;
755#ifdef FIBER_POOL_ALLOCATION_FREE
756 if (allocation->next) {
757 allocation->next->previous = allocation;
760 allocation->previous = NULL;
768 *vacancy_out = fiber_pool_vacancy_pop(
fiber_pool);
770 if (needs_lock) fiber_pool_unlock(
fiber_pool);
782 VM_ASSERT(vacancy_out);
793fiber_pool_initialize(
struct fiber_pool *
fiber_pool,
size_t size,
size_t minimum_count,
size_t maximum_count,
size_t vm_stack_size)
795 VM_ASSERT(vm_stack_size < size);
799 fiber_pool->size = ((size / RB_PAGE_SIZE) + 1) * RB_PAGE_SIZE;
812 rb_raise(rb_eFiberError,
"can't allocate initial fiber stacks (%"PRIuSIZE
" x %"PRIuSIZE
" bytes): %s",
fiber_pool->minimum_count,
fiber_pool->size, strerror(
errno));
817#ifdef FIBER_POOL_ALLOCATION_FREE
822 STACK_GROW_DIR_DETECTION;
824 VM_ASSERT(allocation->used == 0);
826 if (DEBUG) fprintf(stderr,
"fiber_pool_allocation_free: %p base=%p count=%"PRIuSIZE
"\n", (
void*)allocation, allocation->base, allocation->count);
829 for (i = 0; i < allocation->count; i += 1) {
830 void * base = (
char*)allocation->base + (allocation->stride * i) + STACK_DIR_UPPER(0, RB_PAGE_SIZE);
832 struct fiber_pool_vacancy * vacancy = fiber_pool_vacancy_pointer(base, allocation->size);
835 fiber_pool_vacancy_remove(vacancy);
839 VirtualFree(allocation->base, 0, MEM_RELEASE);
841 munmap(allocation->base, allocation->stride * allocation->count);
844 if (allocation->previous) {
845 allocation->previous->next = allocation->next;
849 allocation->pool->allocations = allocation->next;
852 if (allocation->next) {
853 allocation->next->previous = allocation->previous;
856 allocation->pool->count -= allocation->count;
858 ruby_mimfree(allocation);
864fiber_pool_stack_expand_count(
const struct fiber_pool *pool)
866 ASSERT_fiber_pool_locked(pool);
867 const size_t maximum_allocations = FIBER_POOL_MAXIMUM_ALLOCATIONS;
868 const size_t minimum_count = FIBER_POOL_MINIMUM_COUNT;
871 size_t count = pool->count;
872 if (count > maximum_allocations) count = maximum_allocations;
873 if (count < minimum_count) count = minimum_count;
876 if (pool->maximum_count > 0) {
877 if (pool->count >= pool->maximum_count) {
883 size_t remaining = pool->maximum_count - pool->count;
884 if (count > remaining) {
898 size_t count = fiber_pool_stack_expand_count(
fiber_pool);
900 if (DEBUG_ACQUIRE) fprintf(stderr,
"fiber_pool_stack_acquire: expanding fiber pool by %"PRIuSIZE
" stacks\n", count);
904 if (RB_LIKELY((vacancy = fiber_pool_expand_and_pop(
fiber_pool, count)))) {
908 if (DEBUG_ACQUIRE) fprintf(stderr,
"fiber_pool_stack_acquire: expand failed (%s), collecting garbage\n", strerror(
errno));
918 if (RB_LIKELY(vacancy)) {
923 count = fiber_pool_stack_expand_count(
fiber_pool);
926 if (RB_LIKELY((vacancy = fiber_pool_expand_and_pop(
fiber_pool, count)))) {
947 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_acquire: %p used=%"PRIuSIZE
"\n", (
void*)
fiber_pool->vacancies,
fiber_pool->used);
950 if (RB_UNLIKELY(!vacancy)) {
951 vacancy = fiber_pool_stack_acquire_expand(
fiber_pool);
954 if (RB_UNLIKELY(!vacancy)) {
956 rb_raise(rb_eFiberError,
"can't allocate fiber stack: %s", strerror(
errno));
961 VM_ASSERT(vacancy->stack.base);
963#if defined(COROUTINE_SANITIZE_ADDRESS)
964 __asan_unpoison_memory_region(fiber_pool_stack_poison_base(&vacancy->stack), fiber_pool_stack_poison_size(&vacancy->stack));
970#ifdef FIBER_POOL_ALLOCATION_FREE
971 vacancy->stack.allocation->used += 1;
974 fiber_pool_stack_reset(&vacancy->stack);
978 return vacancy->stack;
986 void * base = fiber_pool_stack_base(stack);
987 size_t size = stack->available;
990 VM_ASSERT(size <= (stack->size - RB_PAGE_SIZE));
992 int advice = stack->pool->free_stacks >> 1;
994 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_free: %p+%"PRIuSIZE
" [base=%p, size=%"PRIuSIZE
"] advice=%d\n", base, size, stack->base, stack->size, advice);
1005 rb_vm_map_reusable_lazy(base, size, advice);
1007#if defined(COROUTINE_SANITIZE_ADDRESS)
1008 __asan_poison_memory_region(fiber_pool_stack_poison_base(stack), fiber_pool_stack_poison_size(stack));
1016 ASSERT_fiber_pool_locked(stack->pool);
1018 struct fiber_pool_vacancy * vacancy = fiber_pool_vacancy_pointer(stack->base, stack->size);
1020 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_release: %p used=%"PRIuSIZE
"\n", stack->base, stack->pool->used);
1023 vacancy->stack = *stack;
1027 fiber_pool_vacancy_reset(vacancy);
1030 pool->vacancies = fiber_pool_vacancy_push(vacancy, pool->vacancies);
1033#ifdef FIBER_POOL_ALLOCATION_FREE
1036 allocation->used -= 1;
1039 if (allocation->used == 0) {
1040 fiber_pool_allocation_free(allocation);
1042 else if (stack->pool->free_stacks) {
1043 fiber_pool_stack_free(&vacancy->stack);
1048 if (stack->pool->free_stacks) {
1049 fiber_pool_stack_free(&vacancy->stack);
1058#ifdef RUBY_ASAN_ENABLED
1059 ec->machine.asan_fake_stack_handle = asan_get_thread_fake_stack_handle();
1061 rb_ractor_set_current_ec(th->ractor, th->ec = ec);
1068 if (th->vm->ractor.main_thread == th &&
1069 rb_signal_buff_size() > 0) {
1070 RUBY_VM_SET_TRAP_INTERRUPT(ec);
1073 VM_ASSERT(ec->fiber_ptr->cont.self == 0 || ec->vm_stack != NULL);
1079 ec_switch(th, fiber);
1080 VM_ASSERT(th->ec->fiber_ptr == fiber);
1083#ifndef COROUTINE_DECL
1084# define COROUTINE_DECL COROUTINE
1092#if defined(COROUTINE_SANITIZE_ADDRESS)
1102 __sanitizer_finish_switch_fiber(to->fake_stack, (
const void**)&from->stack_base, &from->stack_size);
1105 rb_thread_t *thread = fiber->cont.saved_ec.thread_ptr;
1107#ifdef COROUTINE_PTHREAD_CONTEXT
1108 ruby_thread_set_native(thread);
1111 fiber_restore_thread(thread, fiber);
1113 rb_fiber_start(fiber);
1115#ifndef COROUTINE_PTHREAD_CONTEXT
1116 VM_UNREACHABLE(fiber_entry);
1122fiber_initialize_coroutine(
rb_fiber_t *fiber,
size_t * vm_stack_size)
1126 void * vm_stack = NULL;
1130 fiber->stack = fiber_pool_stack_acquire(
fiber_pool);
1131 vm_stack = fiber_pool_stack_alloca(&fiber->stack,
fiber_pool->vm_stack_size);
1134 coroutine_initialize(&fiber->context, fiber_entry, fiber_pool_stack_base(&fiber->stack), fiber->stack.available);
1137 sec->machine.stack_start = fiber->stack.current;
1138 sec->machine.stack_maxsize = fiber->stack.available;
1140 fiber->context.argument = (
void*)fiber;
1152 if (DEBUG) fprintf(stderr,
"fiber_stack_release: %p, stack.base=%p\n", (
void*)fiber, fiber->stack.base);
1155 if (fiber->stack.base) {
1160 fiber_pool_stack_release(&fiber->stack);
1164 fiber->stack.base = NULL;
1168 rb_ec_clear_vm_stack(ec);
1172fiber_status_name(
enum fiber_status s)
1175 case FIBER_CREATED:
return "created";
1176 case FIBER_RESUMED:
return "resumed";
1177 case FIBER_SUSPENDED:
return "suspended";
1178 case FIBER_TERMINATED:
return "terminated";
1180 VM_UNREACHABLE(fiber_status_name);
1187#if VM_CHECK_MODE > 0
1188 VM_ASSERT(fiber->cont.saved_ec.fiber_ptr == fiber);
1190 switch (fiber->status) {
1192 if (fiber->cont.saved_ec.thread_ptr->self == 0) {
1193 VM_ASSERT(fiber->cont.saved_ec.vm_stack != NULL);
1196 case FIBER_SUSPENDED:
1197 VM_ASSERT(fiber->cont.saved_ec.vm_stack != NULL);
1200 case FIBER_TERMINATED:
1204 VM_UNREACHABLE(fiber_verify);
1210fiber_status_set(
rb_fiber_t *fiber,
enum fiber_status s)
1213 VM_ASSERT(!FIBER_TERMINATED_P(fiber));
1214 VM_ASSERT(fiber->status != s);
1215 fiber_verify(fiber);
1235 if (!fiber) rb_raise(rb_eFiberError,
"uninitialized fiber");
1240NOINLINE(
static VALUE cont_capture(
volatile int *
volatile stat));
1242#define THREAD_MUST_BE_RUNNING(th) do { \
1243 if (!(th)->ec->tag) rb_raise(rb_eThreadError, "not running thread"); \
1249 return fiber->cont.saved_ec.thread_ptr;
1255 return cont->saved_ec.thread_ptr->self;
1259cont_compact(
void *ptr)
1264 rb_gc_update_moved(&cont->self);
1266 rb_gc_update_moved(&cont->value);
1267 rb_execution_context_update(&cont->saved_ec);
1273 RUBY_MARK_ENTER(
"cont");
1275 rb_gc_mark_movable_ctx(ctx, cont->self);
1277 rb_gc_mark_movable_ctx(ctx, cont->value);
1279 rb_execution_context_mark_ctx(ctx, &cont->saved_ec);
1282 if (cont->saved_vm_stack.ptr) {
1283#ifdef CAPTURE_JUST_VALID_VM_STACK
1284 rb_gc_mark_locations_ctx(ctx, cont->saved_vm_stack.ptr,
1285 cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen + cont->saved_vm_stack.clen);
1287 rb_gc_mark_locations_ctx(ctx, cont->saved_vm_stack.ptr,
1288 cont->saved_vm_stack.ptr, cont->saved_ec.stack_size);
1292 if (cont->machine.stack) {
1293 if (cont->type == CONTINUATION_CONTEXT) {
1295 rb_gc_mark_locations_ctx(ctx, cont->machine.stack,
1296 cont->machine.stack + cont->machine.stack_size);
1304 RUBY_MARK_LEAVE(
"cont");
1319 return fiber == fiber->cont.saved_ec.thread_ptr->root_fiber;
1323static void jit_cont_free(
struct rb_jit_cont *cont);
1330 RUBY_FREE_ENTER(
"cont");
1332 if (cont->type == CONTINUATION_CONTEXT) {
1333 SIZED_FREE_N(cont->saved_ec.vm_stack, cont->saved_ec.vm_stack_size);
1334 SIZED_FREE_N(cont->machine.stack, cont->machine.stack_size);
1338 coroutine_destroy(&fiber->context);
1339 fiber_stack_release(fiber);
1342 SIZED_FREE_N(cont->saved_vm_stack.ptr, cont->saved_vm_stack.size);
1344 VM_ASSERT(cont->jit_cont != NULL);
1345 jit_cont_free(cont->jit_cont);
1347 if (cont->type == CONTINUATION_CONTEXT) {
1353 RUBY_FREE_LEAVE(
"cont");
1357cont_memsize(
const void *ptr)
1362 size =
sizeof(*cont);
1363 if (cont->saved_vm_stack.ptr) {
1364#ifdef CAPTURE_JUST_VALID_VM_STACK
1365 size_t n = (cont->saved_vm_stack.slen + cont->saved_vm_stack.clen);
1367 size_t n = cont->saved_ec.vm_stack_size;
1369 size += n *
sizeof(*cont->saved_vm_stack.ptr);
1372 if (cont->machine.stack) {
1373 size += cont->machine.stack_size *
sizeof(*cont->machine.stack);
1382 if (fiber->cont.self) {
1383 rb_gc_update_moved(&fiber->cont.self);
1386 rb_execution_context_update(&fiber->cont.saved_ec);
1393 rb_gc_mark_movable_ctx(ctx, fiber->cont.self);
1397fiber_compact(
void *ptr)
1400 rb_gc_update_moved(&fiber->first_proc);
1402 if (fiber->prev) rb_fiber_update_self(fiber->prev);
1404 cont_compact(&fiber->cont);
1405 fiber_verify(fiber);
1409fiber_mark(
void *ptr)
1414 RUBY_MARK_ENTER(
"cont");
1415 fiber_verify(fiber);
1416 rb_gc_mark_movable_ctx(&ctx, fiber->first_proc);
1417 if (fiber->prev) rb_fiber_mark_self(&ctx, fiber->prev);
1418 cont_mark_ctx(&ctx, &fiber->cont);
1419 RUBY_MARK_LEAVE(
"cont");
1423fiber_free(
void *ptr)
1431 if (&fiber->cont.saved_ec == rb_current_execution_context(
false)) {
1432 fiber->cont.self = 0;
1435 rb_fiber_free_body(ptr);
1439rb_fiber_free_body(
void *ptr)
1442 RUBY_FREE_ENTER(
"fiber");
1444 if (DEBUG) fprintf(stderr,
"fiber_free: %p[%p]\n", (
void *)fiber, fiber->stack.base);
1446 if (fiber->cont.saved_ec.local_storage) {
1447 rb_id_table_free(fiber->cont.saved_ec.local_storage);
1450 cont_free(&fiber->cont);
1451 RUBY_FREE_LEAVE(
"fiber");
1455fiber_memsize(
const void *ptr)
1458 size_t size =
sizeof(*fiber);
1464 if (saved_ec->local_storage && fiber->first_proc != 0) {
1465 size += rb_id_table_memsize(saved_ec->local_storage);
1466 size += rb_obj_memsize_of(saved_ec->storage);
1469 size += cont_memsize(&fiber->cont);
1476 return RBOOL(rb_typeddata_is_kind_of(obj, &rb_fiber_data_type));
1482 const size_t old_stack_size = cont->machine.stack_size;
1485 SET_MACHINE_STACK_END(&th->ec->machine.stack_end);
1487 if (th->ec->machine.stack_start > th->ec->machine.stack_end) {
1488 size = cont->machine.stack_size = th->ec->machine.stack_start - th->ec->machine.stack_end;
1489 cont->machine.stack_src = th->ec->machine.stack_end;
1492 size = cont->machine.stack_size = th->ec->machine.stack_end - th->ec->machine.stack_start;
1493 cont->machine.stack_src = th->ec->machine.stack_start;
1496 if (cont->machine.stack) {
1497 SIZED_REALLOC_N(cont->machine.stack,
VALUE, cont->machine.stack_size, old_stack_size);
1500 cont->machine.stack =
ALLOC_N(
VALUE, cont->machine.stack_size);
1503 FLUSH_REGISTER_WINDOWS;
1504 asan_unpoison_memory_region(cont->machine.stack_src, size,
false);
1505 MEMCPY(cont->machine.stack, cont->machine.stack_src,
VALUE, size);
1509cont_handle_weak_references(
void *ptr)
1515 if (!rb_gc_handle_weak_references_alive_p(cont->saved_ec.gen_fields_cache.obj) ||
1516 !rb_gc_handle_weak_references_alive_p(cont->saved_ec.gen_fields_cache.fields_obj)) {
1517 cont->saved_ec.gen_fields_cache.obj =
Qundef;
1518 cont->saved_ec.gen_fields_cache.fields_obj =
Qundef;
1524 {cont_mark, cont_free, cont_memsize, cont_compact, cont_handle_weak_references},
1525 0, 0, RUBY_TYPED_THREAD_SAFE_FREE
1533 VM_ASSERT(th->status == THREAD_RUNNABLE);
1540 sec->machine.stack_end = NULL;
1543static rb_nativethread_lock_t jit_cont_lock;
1555 cont = ruby_mimcalloc(1,
sizeof(
struct rb_jit_cont));
1561 if (first_jit_cont == NULL) {
1562 cont->next = cont->prev = NULL;
1566 cont->next = first_jit_cont;
1567 first_jit_cont->prev = cont;
1569 first_jit_cont = cont;
1582 if (cont == first_jit_cont) {
1583 first_jit_cont = cont->next;
1584 if (first_jit_cont != NULL)
1585 first_jit_cont->prev = NULL;
1588 cont->prev->next = cont->next;
1589 if (cont->next != NULL)
1590 cont->next->prev = cont->prev;
1599rb_jit_cont_each_iseq(rb_iseq_callback callback,
void *data)
1602 for (cont = first_jit_cont; cont != NULL; cont = cont->next) {
1603 if (cont->ec->vm_stack == NULL)
1607 while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(cont->ec, cfp)) {
1608 if (CFP_PC(cfp) && CFP_ISEQ(cfp)) {
1610 if (iseq && imemo_type((
VALUE)iseq) == imemo_iseq) {
1611 callback(iseq, data);
1614 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
1623rb_yjit_cancel_jit_return(
void *leave_exit,
void *leave_exception)
1626 for (cont = first_jit_cont; cont != NULL; cont = cont->next) {
1627 if (cont->ec->vm_stack == NULL)
1631 while (!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(cont->ec, cfp)) {
1632 if (cfp->jit_return && cfp->jit_return != leave_exception) {
1635 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
1643rb_jit_cont_finish(
void)
1646 for (cont = first_jit_cont; cont != NULL; cont = next) {
1656 VM_ASSERT(cont->jit_cont == NULL);
1658 cont->jit_cont = jit_cont_new(&(cont->saved_ec));
1664 return &fiber->cont.saved_ec;
1671 cont_save_thread(cont, th);
1672 cont->saved_ec.thread_ptr = th;
1673 cont->saved_ec.local_storage = NULL;
1674 cont->saved_ec.local_storage_recursive_hash =
Qnil;
1675 cont->saved_ec.local_storage_recursive_hash_for_trace =
Qnil;
1676 cont_init_jit_cont(cont);
1680cont_new(
VALUE klass)
1683 volatile VALUE contval;
1686 THREAD_MUST_BE_RUNNING(th);
1688 rb_gc_declare_weak_references(contval);
1689 cont->self = contval;
1690 cont_init(cont, th);
1697 return fiber->cont.self;
1703 return fiber->blocking;
1708rb_jit_cont_init(
void)
1717 VALUE *p = ec->vm_stack;
1718 while (p < ec->cfp->sp) {
1719 fprintf(stderr,
"%3d ", (
int)(p - ec->vm_stack));
1720 rb_obj_info_dump(*p);
1730 while (cfp != end_of_cfp) {
1732 if (CFP_ISEQ(cfp)) {
1733 pc = cfp->pc - ISEQ_BODY(CFP_ISEQ(cfp))->iseq_encoded;
1735 fprintf(stderr,
"%2d pc: %d\n", i++, pc);
1736 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
1742cont_capture(
volatile int *
volatile stat)
1746 volatile VALUE contval;
1749 THREAD_MUST_BE_RUNNING(th);
1750 rb_vm_stack_to_heap(th->ec);
1751 cont = cont_new(rb_cContinuation);
1752 contval = cont->self;
1754#ifdef CAPTURE_JUST_VALID_VM_STACK
1755 cont->saved_vm_stack.slen = ec->cfp->sp - ec->vm_stack;
1756 cont->saved_vm_stack.clen = ec->vm_stack + ec->vm_stack_size - (
VALUE*)ec->cfp;
1757 cont->saved_vm_stack.size = cont->saved_vm_stack.slen + cont->saved_vm_stack.clen;
1758 cont->saved_vm_stack.ptr =
ALLOC_N(
VALUE, cont->saved_vm_stack.slen + cont->saved_vm_stack.clen);
1759 MEMCPY(cont->saved_vm_stack.ptr,
1761 VALUE, cont->saved_vm_stack.slen);
1762 MEMCPY(cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen,
1765 cont->saved_vm_stack.clen);
1767 cont->saved_vm_stack.size = ec->vm_stack_size;
1768 cont->saved_vm_stack.ptr =
ALLOC_N(
VALUE, ec->vm_stack_size);
1769 MEMCPY(cont->saved_vm_stack.ptr, ec->vm_stack,
VALUE, ec->vm_stack_size);
1772 rb_ec_set_vm_stack(&cont->saved_ec, NULL, 0);
1773 VM_ASSERT(cont->saved_ec.cfp != NULL);
1774 cont_save_machine_stack(th, cont);
1776 if (ruby_setjmp(cont->jmpbuf)) {
1779 VAR_INITIALIZED(cont);
1780 value = cont->value;
1798 if (cont->type == CONTINUATION_CONTEXT) {
1803 if (sec->fiber_ptr != NULL) {
1804 fiber = sec->fiber_ptr;
1806 else if (th->root_fiber) {
1807 fiber = th->root_fiber;
1810 if (fiber && th->ec != &fiber->cont.saved_ec) {
1811 ec_switch(th, fiber);
1814 if (th->ec->trace_arg != sec->trace_arg) {
1818#if defined(__wasm__) && !defined(__EMSCRIPTEN__)
1819 if (th->ec->tag != sec->tag) {
1822 struct rb_vm_tag *lowest_common_ancestor = NULL;
1823 size_t num_tags = 0;
1824 size_t num_saved_tags = 0;
1825 for (
struct rb_vm_tag *tag = th->ec->tag; tag != NULL; tag = tag->prev) {
1828 for (
struct rb_vm_tag *tag = sec->tag; tag != NULL; tag = tag->prev) {
1832 size_t min_tags = num_tags <= num_saved_tags ? num_tags : num_saved_tags;
1835 while (num_tags > min_tags) {
1841 while (num_saved_tags > min_tags) {
1842 saved_tag = saved_tag->prev;
1846 while (min_tags > 0) {
1847 if (tag == saved_tag) {
1848 lowest_common_ancestor = tag;
1852 saved_tag = saved_tag->prev;
1857 for (
struct rb_vm_tag *tag = th->ec->tag; tag != lowest_common_ancestor; tag = tag->prev) {
1858 rb_vm_tag_jmpbuf_deinit(&tag->buf);
1864#ifdef CAPTURE_JUST_VALID_VM_STACK
1866 cont->saved_vm_stack.ptr,
1867 VALUE, cont->saved_vm_stack.slen);
1868 MEMCPY(th->ec->vm_stack + th->ec->vm_stack_size - cont->saved_vm_stack.clen,
1869 cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen,
1870 VALUE, cont->saved_vm_stack.clen);
1872 MEMCPY(th->ec->vm_stack, cont->saved_vm_stack.ptr,
VALUE, sec->vm_stack_size);
1876 th->ec->cfp = sec->cfp;
1877 th->ec->raised_flag = sec->raised_flag;
1878 th->ec->tag = sec->tag;
1879 th->ec->root_lep = sec->root_lep;
1880 th->ec->root_svar = sec->root_svar;
1881 th->ec->errinfo = sec->errinfo;
1883 VM_ASSERT(th->ec->vm_stack != NULL);
1899 if (!FIBER_TERMINATED_P(old_fiber)) {
1900 STACK_GROW_DIR_DETECTION;
1901 SET_MACHINE_STACK_END(&th->ec->machine.stack_end);
1902 if (STACK_DIR_UPPER(0, 1)) {
1903 old_fiber->cont.machine.stack_size = th->ec->machine.stack_start - th->ec->machine.stack_end;
1904 old_fiber->cont.machine.stack = th->ec->machine.stack_end;
1907 old_fiber->cont.machine.stack_size = th->ec->machine.stack_end - th->ec->machine.stack_start;
1908 old_fiber->cont.machine.stack = th->ec->machine.stack_start;
1913 old_fiber->cont.saved_ec.machine.stack_start = th->ec->machine.stack_start;
1914 old_fiber->cont.saved_ec.machine.stack_end = FIBER_TERMINATED_P(old_fiber) ? NULL : th->ec->machine.stack_end;
1919#if defined(COROUTINE_SANITIZE_ADDRESS)
1920 __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);
1924 struct coroutine_context * from = coroutine_transfer(&old_fiber->context, &new_fiber->context);
1926#if defined(COROUTINE_SANITIZE_ADDRESS)
1927 __sanitizer_finish_switch_fiber(old_fiber->context.fake_stack, NULL, NULL);
1935 fiber_restore_thread(th, old_fiber);
1941NOINLINE(NORETURN(
static void cont_restore_1(
rb_context_t *)));
1946 cont_restore_thread(cont);
1949#if (defined(_M_AMD64) && !defined(__MINGW64__)) || defined(_M_ARM64)
1954 _JUMP_BUFFER *bp = (
void*)&cont->jmpbuf;
1955 bp->Frame = ((_JUMP_BUFFER*)((
void*)&buf))->Frame;
1958 if (cont->machine.stack_src) {
1959 FLUSH_REGISTER_WINDOWS;
1960 MEMCPY(cont->machine.stack_src, cont->machine.stack,
1961 VALUE, cont->machine.stack_size);
1964 ruby_longjmp(cont->jmpbuf, 1);
1972 if (cont->machine.stack_src) {
1974#define STACK_PAD_SIZE 1
1976#define STACK_PAD_SIZE 1024
1978 VALUE space[STACK_PAD_SIZE];
1980#if !STACK_GROW_DIRECTION
1981 if (addr_in_prev_frame > &space[0]) {
1984#if STACK_GROW_DIRECTION <= 0
1985 volatile VALUE *
const end = cont->machine.stack_src;
1986 if (&space[0] > end) {
1995 cont_restore_0(cont, &space[0]);
1999#if !STACK_GROW_DIRECTION
2004#if STACK_GROW_DIRECTION >= 0
2005 volatile VALUE *
const end = cont->machine.stack_src + cont->machine.stack_size;
2006 if (&space[STACK_PAD_SIZE] < end) {
2011 cont_restore_0(cont, &space[STACK_PAD_SIZE-1]);
2015#if !STACK_GROW_DIRECTION
2019 cont_restore_1(cont);
2106rb_callcc(
VALUE self)
2108 volatile int called;
2109 volatile VALUE val = cont_capture(&called);
2118#ifdef RUBY_ASAN_ENABLED
2121MAYBE_UNUSED(
static void notusing_callcc(
void)) { rb_callcc(
Qnil); }
2122# define rb_callcc rb_f_notimplement
2127make_passing_arg(
int argc,
const VALUE *argv)
2143NORETURN(
static VALUE rb_cont_call(
int argc,
VALUE *argv,
VALUE contval));
2161rb_cont_call(
int argc,
VALUE *argv,
VALUE contval)
2166 if (cont_thread_value(cont) != th->self) {
2169 if (cont->saved_ec.fiber_ptr) {
2170 if (th->ec->fiber_ptr != cont->saved_ec.fiber_ptr) {
2176 cont->value = make_passing_arg(argc, argv);
2178 cont_restore_0(cont, &contval);
2270fiber_handle_weak_references(
void *ptr)
2276 if (!rb_gc_handle_weak_references_alive_p(fiber->cont.saved_ec.gen_fields_cache.obj) ||
2277 !rb_gc_handle_weak_references_alive_p(fiber->cont.saved_ec.gen_fields_cache.fields_obj)) {
2278 fiber->cont.saved_ec.gen_fields_cache.obj =
Qundef;
2279 fiber->cont.saved_ec.gen_fields_cache.fields_obj =
Qundef;
2285 {fiber_mark, fiber_free, fiber_memsize, fiber_compact, fiber_handle_weak_references},
2286 0, 0, RUBY_TYPED_THREAD_SAFE_FREE
2292fiber_alloc(
VALUE klass)
2294 return fiber_alloc_in(klass, GET_RACTOR()->
objspace);
2300 VALUE obj = rb_data_typed_object_wrap_in_objspace(
objspace, klass, 0, &rb_fiber_data_type);
2301 rb_gc_declare_weak_references(obj);
2308 return cr->next_ec_serial++;
2312fiber_t_alloc(
VALUE fiber_value,
unsigned int blocking)
2321 THREAD_MUST_BE_RUNNING(th);
2323 fiber->cont.self = fiber_value;
2324 fiber->cont.type = FIBER_CONTEXT;
2325 fiber->blocking = blocking;
2327 cont_init(&fiber->cont, th);
2329 fiber->cont.saved_ec.fiber_ptr = fiber;
2330 fiber->cont.saved_ec.serial = next_ec_serial(th->ractor);
2331 rb_ec_clear_vm_stack(&fiber->cont.saved_ec);
2337 VM_ASSERT(FIBER_CREATED_P(fiber));
2350 return ec->fiber_ptr;
2354current_fiber_storage(
void)
2361inherit_fiber_storage(
void)
2369 fiber->cont.saved_ec.storage = storage;
2373fiber_storage_get(
rb_fiber_t *fiber,
int allocate)
2375 VALUE storage = fiber->cont.saved_ec.storage;
2376 if (storage ==
Qnil && allocate) {
2377 storage = rb_hash_new();
2378 fiber_storage_set(fiber, storage);
2384storage_access_must_be_from_same_fiber(
VALUE self)
2388 if (fiber != current) {
2389 rb_raise(rb_eArgError,
"Fiber storage can only be accessed from the Fiber it belongs to");
2400rb_fiber_storage_get(
VALUE self)
2402 storage_access_must_be_from_same_fiber(self);
2404 VALUE storage = fiber_storage_get(fiber_ptr(self), FALSE);
2406 if (storage ==
Qnil) {
2423fiber_storage_validate(
VALUE value)
2426 if (value ==
Qnil)
return;
2466 "Fiber#storage= is experimental and may be removed in the future!");
2469 storage_access_must_be_from_same_fiber(self);
2470 fiber_storage_validate(value);
2472 fiber_ptr(self)->cont.saved_ec.storage =
rb_obj_dup(value);
2491 VALUE storage = fiber_storage_get(fiber_current(), FALSE);
2494 return rb_hash_aref(storage, key);
2513 VALUE storage = fiber_storage_get(fiber_current(), value !=
Qnil);
2516 if (value ==
Qnil) {
2517 return rb_hash_delete(storage, key);
2520 return rb_hash_aset(storage, key, value);
2529 storage = inherit_fiber_storage();
2532 fiber_storage_validate(storage);
2536 rb_fiber_t *fiber = fiber_t_alloc(self, blocking);
2538 fiber->cont.saved_ec.storage = storage;
2539 fiber->first_proc = proc;
2540 fiber->stack.base = NULL;
2552 size_t vm_stack_size = 0;
2553 VALUE *vm_stack = fiber_initialize_coroutine(fiber, &vm_stack_size);
2556 cont->saved_vm_stack.ptr = NULL;
2557 rb_ec_initialize_vm_stack(sec, vm_stack, vm_stack_size /
sizeof(
VALUE));
2560 sec->local_storage = NULL;
2561 sec->local_storage_recursive_hash =
Qnil;
2562 sec->local_storage_recursive_hash_for_trace =
Qnil;
2566rb_fiber_pool_default(
VALUE pool)
2568 return &shared_fiber_pool;
2574 fiber->cont.saved_ec.storage = storage;
2580rb_fiber_initialize_kw(
int argc,
VALUE* argv,
VALUE self,
int kw_splat)
2591 rb_get_kwargs(options, fiber_initialize_keywords, 0, 3, arguments);
2593 if (!UNDEF_P(arguments[0])) {
2594 blocking = arguments[0];
2597 if (!UNDEF_P(arguments[1])) {
2598 pool = arguments[1];
2601 storage = arguments[2];
2604 return fiber_initialize(self,
rb_block_proc(), rb_fiber_pool_default(pool),
RTEST(blocking), storage);
2657rb_fiber_initialize(
int argc,
VALUE* argv,
VALUE self)
2665 return fiber_initialize(fiber_alloc(rb_cFiber),
rb_proc_new(func, obj), rb_fiber_pool_default(
Qnil), 0, storage);
2671 return rb_fiber_new_storage(func, obj,
Qtrue);
2675rb_fiber_s_schedule_kw(
int argc,
VALUE* argv,
int kw_splat)
2678 VALUE scheduler = th->scheduler;
2681 if (scheduler !=
Qnil) {
2733rb_fiber_s_schedule(
int argc,
VALUE *argv,
VALUE obj)
2749rb_fiber_s_scheduler(
VALUE klass)
2763rb_fiber_current_scheduler(
VALUE klass)
2785rb_fiber_set_scheduler(
VALUE klass,
VALUE scheduler)
2790NORETURN(
static void rb_fiber_terminate(
rb_fiber_t *fiber,
int need_interrupt,
VALUE err));
2796 rb_thread_t *
volatile th = fiber->cont.saved_ec.thread_ptr;
2798 enum ruby_tag_type state;
2800 VM_ASSERT(th->ec == GET_EC());
2801 VM_ASSERT(FIBER_RESUMED_P(fiber));
2803 if (fiber->blocking) {
2807 EC_PUSH_TAG(th->ec);
2808 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
2812 const VALUE *argv, args = cont->value;
2813 GetProcPtr(fiber->first_proc, proc);
2816 th->ec->errinfo =
Qnil;
2817 th->ec->root_lep = rb_vm_proc_local_ep(fiber->first_proc);
2818 th->ec->root_svar =
Qfalse;
2821 const rb_cref_t *cref = rb_proc_refinements_cref_for_call(fiber->first_proc);
2822 cont->value = rb_vm_invoke_proc(th->ec, proc, argc, argv, cont->kw_splat, VM_BLOCK_HANDLER_NONE, cref);
2826 int need_interrupt = TRUE;
2829 err = th->ec->errinfo;
2830 VM_ASSERT(FIBER_RESUMED_P(fiber));
2832 if (state == TAG_RAISE) {
2835 else if (state == TAG_FATAL && err == RUBY_FATAL_FIBER_KILLED) {
2836 need_interrupt = FALSE;
2839 else if (state == TAG_FATAL) {
2840 rb_threadptr_pending_interrupt_enque(th, err);
2843 err = rb_vm_make_jump_tag_but_local_jump(state, err);
2847 rb_fiber_terminate(fiber, need_interrupt, err);
2856 rb_bug(
"%s", strerror(
errno));
2859 fiber->cont.type = FIBER_CONTEXT;
2860 fiber->cont.saved_ec.fiber_ptr = fiber;
2861 fiber->cont.saved_ec.serial = next_ec_serial(th->ractor);
2862 fiber->cont.saved_ec.thread_ptr = th;
2863 fiber->blocking = 1;
2865 fiber_status_set(fiber, FIBER_RESUMED);
2867 coroutine_initialize_main(&fiber->context);
2869 th->ec = &fiber->cont.saved_ec;
2871 cont_init_jit_cont(&fiber->cont);
2880 fiber->cont.self = fiber_value;
2886 if (th->root_fiber) {
2892 VM_ASSERT(th->ec->fiber_ptr->cont.type == FIBER_CONTEXT);
2893 VM_ASSERT(th->ec->fiber_ptr->cont.self == 0);
2895 if (ec && th->ec == ec) {
2896 rb_ractor_set_current_ec(th->ractor, NULL);
2898 fiber_free(th->ec->fiber_ptr);
2908 fiber->status = FIBER_TERMINATED;
2911 rb_ec_clear_vm_stack(th->ec);
2915return_fiber(
bool terminate)
2922 prev->resuming_fiber = NULL;
2927 rb_raise(rb_eFiberError,
"attempt to yield on a not resumed fiber");
2933 VM_ASSERT(root_fiber != NULL);
2936 for (fiber = root_fiber; fiber->resuming_fiber; fiber = fiber->resuming_fiber) {
2944rb_fiber_current(
void)
2946 return fiber_current()->cont.self;
2955 if (FIBER_CREATED_P(next_fiber)) {
2956 fiber_prepare_stack(next_fiber);
2959 VM_ASSERT(FIBER_RESUMED_P(fiber) || FIBER_TERMINATED_P(fiber));
2960 VM_ASSERT(FIBER_RUNNABLE_P(next_fiber));
2962 if (FIBER_RESUMED_P(fiber)) fiber_status_set(fiber, FIBER_SUSPENDED);
2964 fiber_status_set(next_fiber, FIBER_RESUMED);
2965 fiber_setcontext(next_fiber, fiber);
2971 VM_ASSERT(fiber == fiber_current());
2973 if (fiber->killed) {
2974 rb_thread_t *thread = fiber->cont.saved_ec.thread_ptr;
2976 thread->ec->errinfo = RUBY_FATAL_FIBER_KILLED;
2977 EC_JUMP_TAG(thread->ec, RUBY_TAG_FATAL);
2989 if (th->root_fiber == NULL) {
2990 th->root_fiber = th->ec->fiber_ptr;
2993 if (th->ec->fiber_ptr == fiber) {
2997 return make_passing_arg(argc, argv);
3000 if (cont_thread_value(cont) != th->self) {
3001 rb_raise(rb_eFiberError,
"fiber called across threads");
3004 if (FIBER_TERMINATED_P(fiber)) {
3005 value =
rb_exc_new2(rb_eFiberError,
"dead fiber called");
3007 if (!FIBER_TERMINATED_P(th->ec->fiber_ptr)) {
3009 VM_UNREACHABLE(fiber_switch);
3015 VM_ASSERT(FIBER_SUSPENDED_P(th->root_fiber));
3017 cont = &th->root_fiber->cont;
3019 cont->value = value;
3021 fiber_setcontext(th->root_fiber, th->ec->fiber_ptr);
3023 VM_UNREACHABLE(fiber_switch);
3027 VM_ASSERT(FIBER_RUNNABLE_P(fiber));
3034 VALUE fiber_value = fiber->cont.self;
3038 VM_ASSERT(!current_fiber->resuming_fiber);
3040 if (resuming_fiber) {
3041 current_fiber->resuming_fiber = resuming_fiber;
3042 fiber->prev = fiber_current();
3043 fiber->yielding = 0;
3046 VM_ASSERT(!current_fiber->yielding);
3048 current_fiber->yielding = 1;
3051 if (current_fiber->blocking) {
3056 cont->kw_splat = kw_splat;
3057 cont->value = make_passing_arg(argc, argv);
3059 fiber_store(fiber, th);
3062#ifndef COROUTINE_PTHREAD_CONTEXT
3063 if (FIBER_TERMINATED_P(fiber)) {
3064 fiber_stack_release(fiber);
3069 if (fiber_current()->blocking) {
3073 RUBY_VM_CHECK_INTS(th->ec);
3077 current_fiber = th->ec->fiber_ptr;
3078 value = current_fiber->cont.value;
3080 fiber_check_killed(current_fiber);
3082 if (current_fiber->cont.argc == -1) {
3093 return fiber_switch(fiber_ptr(fiber_value), argc, argv,
RB_NO_KEYWORDS, NULL,
false);
3111rb_fiber_blocking_p(
VALUE fiber)
3113 return RBOOL(fiber_ptr(fiber)->blocking);
3117fiber_blocking_yield(
VALUE fiber_value)
3120 rb_thread_t *
volatile th = fiber->cont.saved_ec.thread_ptr;
3122 VM_ASSERT(fiber->blocking == 0);
3125 fiber->blocking = 1;
3134fiber_blocking_ensure(
VALUE fiber_value)
3137 rb_thread_t *
volatile th = fiber->cont.saved_ec.thread_ptr;
3140 fiber->blocking = 0;
3157rb_fiber_blocking(
VALUE class)
3159 VALUE fiber_value = rb_fiber_current();
3163 if (fiber->blocking) {
3167 return rb_ensure(fiber_blocking_yield, fiber_value, fiber_blocking_ensure, fiber_value);
3190rb_fiber_s_blocking_p(
VALUE klass)
3193 unsigned blocking = thread->blocking;
3204 fiber_status_set(fiber, FIBER_TERMINATED);
3205 rb_ec_close(&fiber->cont.saved_ec);
3211 VALUE value = fiber->cont.value;
3213 VM_ASSERT(FIBER_RESUMED_P(fiber));
3214 rb_fiber_close(fiber);
3216 fiber->cont.machine.stack = NULL;
3217 fiber->cont.machine.stack_size = 0;
3221 if (need_interrupt) RUBY_VM_SET_INTERRUPT(&next_fiber->cont.saved_ec);
3224 fiber_switch(next_fiber, -1, &error,
RB_NO_KEYWORDS, NULL,
false);
3226 fiber_switch(next_fiber, 1, &value,
RB_NO_KEYWORDS, NULL,
false);
3231fiber_resume_kw(
rb_fiber_t *fiber,
int argc,
const VALUE *argv,
int kw_splat)
3235 if (argc == -1 && FIBER_CREATED_P(fiber)) {
3236 rb_raise(rb_eFiberError,
"cannot raise exception on unborn fiber");
3238 else if (FIBER_TERMINATED_P(fiber)) {
3239 rb_raise(rb_eFiberError,
"attempt to resume a terminated fiber");
3241 else if (fiber == current_fiber) {
3242 rb_raise(rb_eFiberError,
"attempt to resume the current fiber");
3244 else if (fiber->prev != NULL) {
3245 rb_raise(rb_eFiberError,
"attempt to resume a resumed fiber (double resume)");
3247 else if (fiber->resuming_fiber) {
3248 rb_raise(rb_eFiberError,
"attempt to resume a resuming fiber");
3250 else if (fiber->prev == NULL &&
3251 (!fiber->yielding && fiber->status != FIBER_CREATED)) {
3252 rb_raise(rb_eFiberError,
"attempt to resume a transferring fiber");
3255 return fiber_switch(fiber, argc, argv, kw_splat, fiber,
false);
3259rb_fiber_resume_kw(
VALUE self,
int argc,
const VALUE *argv,
int kw_splat)
3261 return fiber_resume_kw(fiber_ptr(self), argc, argv, kw_splat);
3267 return fiber_resume_kw(fiber_ptr(self), argc, argv,
RB_NO_KEYWORDS);
3271rb_fiber_yield_kw(
int argc,
const VALUE *argv,
int kw_splat)
3273 return fiber_switch(return_fiber(
false), argc, argv, kw_splat, NULL,
true);
3277rb_fiber_yield(
int argc,
const VALUE *argv)
3279 return fiber_switch(return_fiber(
false), argc, argv,
RB_NO_KEYWORDS, NULL,
true);
3285 if (th->root_fiber && th->root_fiber != th->ec->fiber_ptr) {
3286 th->ec->local_storage = th->root_fiber->cont.saved_ec.local_storage;
3301 return RBOOL(!FIBER_TERMINATED_P(fiber_ptr(fiber_value)));
3320rb_fiber_m_resume(
int argc,
VALUE *argv,
VALUE fiber)
3372rb_fiber_backtrace(
int argc,
VALUE *argv,
VALUE fiber)
3374 return rb_vm_backtrace(argc, argv, &fiber_ptr(fiber)->cont.saved_ec);
3397rb_fiber_backtrace_locations(
int argc,
VALUE *argv,
VALUE fiber)
3399 return rb_vm_backtrace_locations(argc, argv, &fiber_ptr(fiber)->cont.saved_ec);
3485rb_fiber_m_transfer(
int argc,
VALUE *argv,
VALUE self)
3491fiber_transfer_kw(
rb_fiber_t *fiber,
int argc,
const VALUE *argv,
int kw_splat)
3493 if (fiber->resuming_fiber) {
3494 rb_raise(rb_eFiberError,
"attempt to transfer to a resuming fiber");
3497 if (fiber->yielding) {
3498 rb_raise(rb_eFiberError,
"attempt to transfer to a yielding fiber");
3501 return fiber_switch(fiber, argc, argv, kw_splat, NULL,
false);
3505rb_fiber_transfer_kw(
VALUE self,
int argc,
const VALUE *argv,
int kw_splat)
3507 return fiber_transfer_kw(fiber_ptr(self), argc, argv, kw_splat);
3521rb_fiber_s_yield(
int argc,
VALUE *argv,
VALUE klass)
3529 if (fiber == fiber_current()) {
3532 else if (fiber->resuming_fiber) {
3533 return fiber_raise(fiber->resuming_fiber, exception);
3535 else if (FIBER_SUSPENDED_P(fiber) && !fiber->yielding) {
3546 VALUE exception = rb_exception_setup(argc, argv);
3548 return fiber_raise(fiber_ptr(fiber), exception);
3586rb_fiber_m_raise(
int argc,
VALUE *argv,
VALUE self)
3588 return rb_fiber_raise(self, argc, argv);
3609rb_fiber_m_kill(
VALUE self)
3613 if (fiber->killed)
return Qfalse;
3616 if (fiber->status == FIBER_CREATED) {
3617 fiber->status = FIBER_TERMINATED;
3619 else if (fiber->status != FIBER_TERMINATED) {
3620 if (fiber_current() == fiber) {
3621 fiber_check_killed(fiber);
3624 fiber_raise(fiber_ptr(self),
Qnil);
3639rb_fiber_s_current(
VALUE klass)
3641 return rb_fiber_current();
3645fiber_to_s(
VALUE fiber_value)
3647 const rb_fiber_t *fiber = fiber_ptr(fiber_value);
3649 char status_info[0x20];
3651 if (fiber->resuming_fiber) {
3652 snprintf(status_info, 0x20,
" (%s by resuming)", fiber_status_name(fiber->status));
3655 snprintf(status_info, 0x20,
" (%s)", fiber_status_name(fiber->status));
3660 strlcat(status_info,
">",
sizeof(status_info));
3665 GetProcPtr(fiber->first_proc, proc);
3666 return rb_block_to_s(fiber_value, &proc->block, status_info);
3669#ifdef HAVE_WORKING_FORK
3673 if (th->root_fiber) {
3674 if (&th->root_fiber->cont.saved_ec != th->ec) {
3675 th->root_fiber = th->ec->fiber_ptr;
3677 th->root_fiber->prev = 0;
3678 th->root_fiber->blocking = 1;
3684#ifdef RB_EXPERIMENTAL_FIBER_POOL
3686fiber_pool_free(
void *ptr)
3689 RUBY_FREE_ENTER(
"fiber_pool");
3692 fiber_pool_allocation_free(
fiber_pool->allocations);
3696 RUBY_FREE_LEAVE(
"fiber_pool");
3700fiber_pool_memsize(
const void *ptr)
3703 size_t size =
sizeof(*fiber_pool);
3712 {NULL, fiber_pool_free, fiber_pool_memsize,},
3713 0, 0, RUBY_TYPED_THREAD_SAFE_FREE
3717fiber_pool_alloc(
VALUE klass)
3725rb_fiber_pool_initialize(
int argc,
VALUE* argv,
VALUE self)
3732 rb_scan_args(argc, argv,
"03", &size, &count, &vm_stack_size);
3735 size =
SIZET2NUM(th->vm->default_params.fiber_machine_stack_size);
3742 if (
NIL_P(vm_stack_size)) {
3743 vm_stack_size =
SIZET2NUM(th->vm->default_params.fiber_vm_stack_size);
3768shared_fiber_pool_minimum_count(
void)
3770 size_t minimum_count = FIBER_POOL_MINIMUM_COUNT;
3772 const char *minimum_count_env = getenv(
"RUBY_SHARED_FIBER_POOL_MINIMUM_COUNT");
3773 if (minimum_count_env && minimum_count_env[0]) {
3775 unsigned long value = strtoul(minimum_count_env, &end, 10);
3776 if (end != minimum_count_env && *end ==
'\0') {
3777 minimum_count = (size_t)value;
3780 rb_warn(
"invalid RUBY_SHARED_FIBER_POOL_MINIMUM_COUNT=%s (expected a non-negative integer)", minimum_count_env);
3784 return minimum_count;
3788shared_fiber_pool_maximum_count(
void)
3790 size_t maximum_count = 0;
3792 const char *maximum_count_env = getenv(
"RUBY_SHARED_FIBER_POOL_MAXIMUM_COUNT");
3793 if (maximum_count_env && maximum_count_env[0]) {
3795 unsigned long value = strtoul(maximum_count_env, &end, 10);
3796 if (end != maximum_count_env && *end ==
'\0') {
3797 maximum_count = (size_t)value;
3800 rb_warn(
"invalid RUBY_SHARED_FIBER_POOL_MAXIMUM_COUNT=%s (expected a non-negative integer)", maximum_count_env);
3804 return maximum_count;
3811 size_t vm_stack_size = th->vm->default_params.fiber_vm_stack_size;
3812 size_t machine_stack_size = th->vm->default_params.fiber_machine_stack_size;
3813 size_t stack_size = machine_stack_size + vm_stack_size;
3817 GetSystemInfo(&info);
3818 pagesize = info.dwPageSize;
3820 pagesize = sysconf(_SC_PAGESIZE);
3822 SET_MACHINE_STACK_END(&th->ec->machine.stack_end);
3828 size_t minimum_count = shared_fiber_pool_minimum_count();
3829 size_t maximum_count = shared_fiber_pool_maximum_count();
3830 fiber_pool_initialize(&shared_fiber_pool, stack_size, minimum_count, maximum_count, vm_stack_size);
3836 const char *fiber_shared_fiber_pool_free_stacks = getenv(
"RUBY_SHARED_FIBER_POOL_FREE_STACKS");
3837 if (fiber_shared_fiber_pool_free_stacks) {
3838 shared_fiber_pool.free_stacks = atoi(fiber_shared_fiber_pool_free_stacks);
3840 if (shared_fiber_pool.free_stacks < 0) {
3841 rb_warn(
"Setting RUBY_SHARED_FIBER_POOL_FREE_STACKS to a negative value is not allowed.");
3842 shared_fiber_pool.free_stacks = 0;
3845 if (shared_fiber_pool.free_stacks > 1) {
3846 rb_warn(
"Setting RUBY_SHARED_FIBER_POOL_FREE_STACKS to a value greater than 1 is operating system specific, and may cause crashes.");
3850 rb_cFiber = rb_define_class(
"Fiber",
rb_cObject);
3866 rb_define_method(rb_cFiber,
"backtrace_locations", rb_fiber_backtrace_locations, -1);
3879 rb_thread_t *current_thread = rb_current_thread();
3881 *(
VALUE *)&((
struct RBasic *)current_thread->ec->fiber_ptr->cont.self)->klass = rb_cFiber;
3883#ifdef RB_EXPERIMENTAL_FIBER_POOL
3888 rb_cFiberPool = rb_define_class_under(rb_cFiber,
"Pool",
rb_cObject);
3890 rb_define_method(rb_cFiberPool,
"initialize", rb_fiber_pool_initialize, -1);
3896RUBY_SYMBOL_EXPORT_BEGIN
3899ruby_Init_Continuation_body(
void)
3901 rb_cContinuation = rb_define_class(
"Continuation",
rb_cObject);
3906#ifdef COROUTINE_SHADOW_STACK
3907 if (coroutine_shadow_stack_enabled()) {
3916RUBY_SYMBOL_EXPORT_END
#define RUBY_ASSERT(...)
Asserts that the given expression is truthy if and only if RUBY_DEBUG is truthy.
#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.
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 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.
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_cObject
Object class.
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.
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.
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, rb_len_t 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.
VALUE rb_f_notimplement(int argc, const VALUE *argv, VALUE obj, VALUE marker)
Raises rb_eNotImpError.
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_GC_GUARD(v)
Prevents premature destruction of local objects.
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 TypedData_Get_Struct(obj, type, data_type, sval)
Obtains a C struct from inside of a wrapper Ruby object.
#define DATA_PTR(obj)
Convenient casting macro for backward compatibility.
#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.
#define RTEST
This is an old name of RB_TEST.
Ruby object's base components.
This is the struct that holds necessary info for a struct.
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.