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"
31#include "internal/cont.h"
32#include "internal/error.h"
33#include "internal/proc.h"
34#include "internal/sanitizers.h"
35#include "internal/warnings.h"
42#include "ractor_core.h"
44static const int DEBUG = 0;
46#define RB_PAGE_SIZE (pagesize)
47#define RB_PAGE_MASK (~(RB_PAGE_SIZE - 1))
51static VALUE rb_cContinuation;
52static VALUE rb_cFiber;
53static VALUE rb_eFiberError;
54#ifdef RB_EXPERIMENTAL_FIBER_POOL
55static VALUE rb_cFiberPool;
58#define CAPTURE_JUST_VALID_VM_STACK 1
61#ifdef COROUTINE_LIMITED_ADDRESS_SPACE
62#define FIBER_POOL_ALLOCATION_FREE
63#define FIBER_POOL_INITIAL_SIZE 8
64#define FIBER_POOL_ALLOCATION_MAXIMUM_SIZE 32
66#define FIBER_POOL_INITIAL_SIZE 32
67#define FIBER_POOL_ALLOCATION_MAXIMUM_SIZE 1024
69#ifdef RB_EXPERIMENTAL_FIBER_POOL
70#define FIBER_POOL_ALLOCATION_FREE
73#define jit_cont_enabled (mjit_enabled || rb_yjit_enabled_p())
76 CONTINUATION_CONTEXT = 0,
82#ifdef CAPTURE_JUST_VALID_VM_STACK
119#ifdef FIBER_POOL_ALLOCATION_FREE
162#ifdef FIBER_POOL_ALLOCATION_FREE
170#ifdef FIBER_POOL_ALLOCATION_FREE
191 size_t initial_count;
200 size_t vm_stack_size;
213 enum context_type type;
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;
273static struct fiber_pool shared_fiber_pool = {NULL, NULL, 0, 0, 0, 0};
275static ID fiber_initialize_keywords[3] = {0};
282#if defined(MAP_STACK) && !defined(__FreeBSD__) && !defined(__FreeBSD_kernel__)
283#define FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON | MAP_STACK)
285#define FIBER_STACK_FLAGS (MAP_PRIVATE | MAP_ANON)
288#define ERRNOMSG strerror(errno)
292fiber_pool_vacancy_pointer(
void * base,
size_t size)
294 STACK_GROW_DIR_DETECTION;
297 (
char*)base + STACK_DIR_UPPER(0, size - RB_PAGE_SIZE)
301#if defined(COROUTINE_SANITIZE_ADDRESS)
306 STACK_GROW_DIR_DETECTION;
308 return (
char*)stack->base + STACK_DIR_UPPER(RB_PAGE_SIZE, 0);
315 return stack->size - RB_PAGE_SIZE;
323 STACK_GROW_DIR_DETECTION;
325 stack->current = (
char*)stack->base + STACK_DIR_UPPER(0, stack->size);
326 stack->available = stack->size;
333 STACK_GROW_DIR_DETECTION;
335 VM_ASSERT(stack->current);
337 return STACK_DIR_UPPER(stack->current, (
char*)stack->current - stack->available);
345 STACK_GROW_DIR_DETECTION;
347 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_alloca(%p): %"PRIuSIZE
"/%"PRIuSIZE
"\n", (
void*)stack, offset, stack->available);
348 VM_ASSERT(stack->available >= offset);
351 void * pointer = STACK_DIR_UPPER(stack->current, (
char*)stack->current - offset);
354 stack->current = STACK_DIR_UPPER((
char*)stack->current + offset, (
char*)stack->current - offset);
355 stack->available -= offset;
364 fiber_pool_stack_reset(&vacancy->stack);
367 fiber_pool_stack_alloca(&vacancy->stack, RB_PAGE_SIZE);
373 vacancy->next = head;
375#ifdef FIBER_POOL_ALLOCATION_FREE
377 head->previous = vacancy;
378 vacancy->previous = NULL;
385#ifdef FIBER_POOL_ALLOCATION_FREE
390 vacancy->next->previous = vacancy->previous;
393 if (vacancy->previous) {
394 vacancy->previous->next = vacancy->next;
398 vacancy->stack.pool->vacancies = vacancy->next;
403fiber_pool_vacancy_pop(
struct fiber_pool * pool)
408 fiber_pool_vacancy_remove(vacancy);
415fiber_pool_vacancy_pop(
struct fiber_pool * pool)
420 pool->vacancies = vacancy->next;
435 vacancy->stack.base = base;
436 vacancy->stack.size = size;
438 fiber_pool_vacancy_reset(vacancy);
442 return fiber_pool_vacancy_push(vacancy, vacancies);
450fiber_pool_allocate_memory(
size_t * count,
size_t stride)
460 void * base = VirtualAlloc(0, (*count)*stride, MEM_COMMIT, PAGE_READWRITE);
463 *count = (*count) >> 1;
470 void * base = mmap(NULL, (*count)*stride, PROT_READ | PROT_WRITE, FIBER_STACK_FLAGS, -1, 0);
472 if (base == MAP_FAILED) {
474 *count = (*count) >> 1;
477#if defined(MADV_FREE_REUSE)
481 while (madvise(base, (*count)*stride, MADV_FREE_REUSE) == -1 && errno == EAGAIN);
498 STACK_GROW_DIR_DETECTION;
501 size_t stride = size + RB_PAGE_SIZE;
504 void * base = fiber_pool_allocate_memory(&count, stride);
507 rb_raise(rb_eFiberError,
"can't alloc machine stack to fiber (%"PRIuSIZE
" x %"PRIuSIZE
" bytes): %s", count, size, ERRNOMSG);
514 allocation->base = base;
515 allocation->size = size;
516 allocation->stride = stride;
517 allocation->count = count;
518#ifdef FIBER_POOL_ALLOCATION_FREE
519 allocation->used = 0;
524 fprintf(stderr,
"fiber_pool_expand(%"PRIuSIZE
"): %p, %"PRIuSIZE
"/%"PRIuSIZE
" x [%"PRIuSIZE
":%"PRIuSIZE
"]\n",
529 for (
size_t i = 0; i < count; i += 1) {
530 void * base = (
char*)allocation->base + (stride * i);
531 void * page = (
char*)base + STACK_DIR_UPPER(size, 0);
536 if (!VirtualProtect(page, RB_PAGE_SIZE, PAGE_READWRITE | PAGE_GUARD, &old_protect)) {
537 VirtualFree(allocation->base, 0, MEM_RELEASE);
538 rb_raise(rb_eFiberError,
"can't set a guard page: %s", ERRNOMSG);
541 if (mprotect(page, RB_PAGE_SIZE, PROT_NONE) < 0) {
542 munmap(allocation->base, count*stride);
543 rb_raise(rb_eFiberError,
"can't set a guard page: %s", ERRNOMSG);
547 vacancies = fiber_pool_vacancy_initialize(
549 (
char*)base + STACK_DIR_UPPER(0, RB_PAGE_SIZE),
553#ifdef FIBER_POOL_ALLOCATION_FREE
554 vacancies->stack.allocation = allocation;
561#ifdef FIBER_POOL_ALLOCATION_FREE
562 if (allocation->next) {
563 allocation->next->previous = allocation;
566 allocation->previous = NULL;
579fiber_pool_initialize(
struct fiber_pool *
fiber_pool,
size_t size,
size_t count,
size_t vm_stack_size)
581 VM_ASSERT(vm_stack_size < size);
585 fiber_pool->size = ((size / RB_PAGE_SIZE) + 1) * RB_PAGE_SIZE;
596#ifdef FIBER_POOL_ALLOCATION_FREE
601 STACK_GROW_DIR_DETECTION;
603 VM_ASSERT(allocation->used == 0);
605 if (DEBUG) fprintf(stderr,
"fiber_pool_allocation_free: %p base=%p count=%"PRIuSIZE
"\n", (
void*)allocation, allocation->base, allocation->count);
608 for (i = 0; i < allocation->count; i += 1) {
609 void * base = (
char*)allocation->base + (allocation->stride * i) + STACK_DIR_UPPER(0, RB_PAGE_SIZE);
611 struct fiber_pool_vacancy * vacancy = fiber_pool_vacancy_pointer(base, allocation->size);
614 fiber_pool_vacancy_remove(vacancy);
618 VirtualFree(allocation->base, 0, MEM_RELEASE);
620 munmap(allocation->base, allocation->stride * allocation->count);
623 if (allocation->previous) {
624 allocation->previous->next = allocation->next;
628 allocation->pool->allocations = allocation->next;
631 if (allocation->next) {
632 allocation->next->previous = allocation->previous;
635 allocation->pool->count -= allocation->count;
637 ruby_xfree(allocation);
647 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_acquire: %p used=%"PRIuSIZE
"\n", (
void*)
fiber_pool->vacancies,
fiber_pool->used);
650 const size_t maximum = FIBER_POOL_ALLOCATION_MAXIMUM_SIZE;
651 const size_t minimum =
fiber_pool->initial_count;
654 if (count > maximum) count = maximum;
655 if (count < minimum) count = minimum;
666 VM_ASSERT(vacancy->stack.base);
668#if defined(COROUTINE_SANITIZE_ADDRESS)
669 __asan_unpoison_memory_region(fiber_pool_stack_poison_base(&vacancy->stack), fiber_pool_stack_poison_size(&vacancy->stack));
675#ifdef FIBER_POOL_ALLOCATION_FREE
676 vacancy->stack.allocation->used += 1;
679 fiber_pool_stack_reset(&vacancy->stack);
681 return vacancy->stack;
689 void * base = fiber_pool_stack_base(stack);
690 size_t size = stack->available;
693 VM_ASSERT(size <= (stack->size - RB_PAGE_SIZE));
695 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_free: %p+%"PRIuSIZE
" [base=%p, size=%"PRIuSIZE
"]\n", base, size, stack->base, stack->size);
708#elif VM_CHECK_MODE > 0 && defined(MADV_DONTNEED)
710 madvise(base, size, MADV_DONTNEED);
711#elif defined(MADV_FREE_REUSABLE)
717 while (madvise(base, size, MADV_FREE_REUSABLE) == -1 && errno == EAGAIN);
718#elif defined(MADV_FREE)
720 madvise(base, size, MADV_FREE);
721#elif defined(MADV_DONTNEED)
723 madvise(base, size, MADV_DONTNEED);
724#elif defined(POSIX_MADV_DONTNEED)
726 posix_madvise(base, size, POSIX_MADV_DONTNEED);
728 VirtualAlloc(base, size, MEM_RESET, PAGE_READWRITE);
733#if defined(COROUTINE_SANITIZE_ADDRESS)
734 __asan_poison_memory_region(fiber_pool_stack_poison_base(stack), fiber_pool_stack_poison_size(stack));
743 struct fiber_pool_vacancy * vacancy = fiber_pool_vacancy_pointer(stack->base, stack->size);
745 if (DEBUG) fprintf(stderr,
"fiber_pool_stack_release: %p used=%"PRIuSIZE
"\n", stack->base, stack->pool->used);
748 vacancy->stack = *stack;
752 fiber_pool_vacancy_reset(vacancy);
755 pool->vacancies = fiber_pool_vacancy_push(vacancy, pool->vacancies);
758#ifdef FIBER_POOL_ALLOCATION_FREE
761 allocation->used -= 1;
764 if (allocation->used == 0) {
765 fiber_pool_allocation_free(allocation);
767 else if (stack->pool->free_stacks) {
768 fiber_pool_stack_free(&vacancy->stack);
773 if (stack->pool->free_stacks) {
774 fiber_pool_stack_free(&vacancy->stack);
783 rb_ractor_set_current_ec(th->ractor, th->ec = ec);
790 if (th->vm->ractor.main_thread == th &&
791 rb_signal_buff_size() > 0) {
792 RUBY_VM_SET_TRAP_INTERRUPT(ec);
795 VM_ASSERT(ec->fiber_ptr->cont.self == 0 || ec->vm_stack != NULL);
801 ec_switch(th, fiber);
802 VM_ASSERT(th->ec->fiber_ptr == fiber);
810#if defined(COROUTINE_SANITIZE_ADDRESS)
820 __sanitizer_finish_switch_fiber(to->fake_stack, (
const void**)&from->stack_base, &from->stack_size);
823 rb_thread_t *thread = fiber->cont.saved_ec.thread_ptr;
825#ifdef COROUTINE_PTHREAD_CONTEXT
826 ruby_thread_set_native(thread);
829 fiber_restore_thread(thread, fiber);
831 rb_fiber_start(fiber);
833#ifndef COROUTINE_PTHREAD_CONTEXT
834 VM_UNREACHABLE(fiber_entry);
840fiber_initialize_coroutine(
rb_fiber_t *fiber,
size_t * vm_stack_size)
844 void * vm_stack = NULL;
848 fiber->stack = fiber_pool_stack_acquire(
fiber_pool);
849 vm_stack = fiber_pool_stack_alloca(&fiber->stack,
fiber_pool->vm_stack_size);
852 coroutine_initialize(&fiber->context, fiber_entry, fiber_pool_stack_base(&fiber->stack), fiber->stack.available);
855 sec->machine.stack_start = fiber->stack.current;
856 sec->machine.stack_maxsize = fiber->stack.available;
858 fiber->context.argument = (
void*)fiber;
870 if (DEBUG) fprintf(stderr,
"fiber_stack_release: %p, stack.base=%p\n", (
void*)fiber, fiber->stack.base);
873 if (fiber->stack.base) {
874 fiber_pool_stack_release(&fiber->stack);
875 fiber->stack.base = NULL;
879 rb_ec_clear_vm_stack(ec);
883fiber_status_name(
enum fiber_status s)
886 case FIBER_CREATED:
return "created";
887 case FIBER_RESUMED:
return "resumed";
888 case FIBER_SUSPENDED:
return "suspended";
889 case FIBER_TERMINATED:
return "terminated";
891 VM_UNREACHABLE(fiber_status_name);
899 VM_ASSERT(fiber->cont.saved_ec.fiber_ptr == fiber);
901 switch (fiber->status) {
903 VM_ASSERT(fiber->cont.saved_ec.vm_stack != NULL);
905 case FIBER_SUSPENDED:
906 VM_ASSERT(fiber->cont.saved_ec.vm_stack != NULL);
909 case FIBER_TERMINATED:
913 VM_UNREACHABLE(fiber_verify);
919fiber_status_set(
rb_fiber_t *fiber,
enum fiber_status s)
922 VM_ASSERT(!FIBER_TERMINATED_P(fiber));
923 VM_ASSERT(fiber->status != s);
944 if (!fiber)
rb_raise(rb_eFiberError,
"uninitialized fiber");
949NOINLINE(
static VALUE cont_capture(
volatile int *
volatile stat));
951#define THREAD_MUST_BE_RUNNING(th) do { \
952 if (!(th)->ec->tag) rb_raise(rb_eThreadError, "not running thread"); \
958 return fiber->cont.saved_ec.thread_ptr;
964 return cont->saved_ec.thread_ptr->self;
968cont_compact(
void *ptr)
973 cont->self = rb_gc_location(cont->self);
975 cont->value = rb_gc_location(cont->value);
976 rb_execution_context_update(&cont->saved_ec);
984 RUBY_MARK_ENTER(
"cont");
986 rb_gc_mark_movable(cont->self);
988 rb_gc_mark_movable(cont->value);
990 rb_execution_context_mark(&cont->saved_ec);
991 rb_gc_mark(cont_thread_value(cont));
993 if (cont->saved_vm_stack.ptr) {
994#ifdef CAPTURE_JUST_VALID_VM_STACK
995 rb_gc_mark_locations(cont->saved_vm_stack.ptr,
996 cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen + cont->saved_vm_stack.clen);
998 rb_gc_mark_locations(cont->saved_vm_stack.ptr,
999 cont->saved_vm_stack.ptr, cont->saved_ec.stack_size);
1003 if (cont->machine.stack) {
1004 if (cont->type == CONTINUATION_CONTEXT) {
1006 rb_gc_mark_locations(cont->machine.stack,
1007 cont->machine.stack + cont->machine.stack_size);
1013 if (!FIBER_TERMINATED_P(fiber)) {
1014 rb_gc_mark_locations(cont->machine.stack,
1015 cont->machine.stack + cont->machine.stack_size);
1020 RUBY_MARK_LEAVE(
"cont");
1027 return fiber == fiber->cont.saved_ec.thread_ptr->root_fiber;
1031static void jit_cont_free(
struct rb_jit_cont *cont);
1038 RUBY_FREE_ENTER(
"cont");
1040 if (cont->type == CONTINUATION_CONTEXT) {
1041 ruby_xfree(cont->saved_ec.vm_stack);
1042 ruby_xfree(cont->ensure_array);
1043 RUBY_FREE_UNLESS_NULL(cont->machine.stack);
1047 coroutine_destroy(&fiber->context);
1048 fiber_stack_release(fiber);
1051 RUBY_FREE_UNLESS_NULL(cont->saved_vm_stack.ptr);
1053 if (jit_cont_enabled) {
1054 VM_ASSERT(cont->jit_cont != NULL);
1055 jit_cont_free(cont->jit_cont);
1059 RUBY_FREE_LEAVE(
"cont");
1063cont_memsize(
const void *ptr)
1068 size =
sizeof(*cont);
1069 if (cont->saved_vm_stack.ptr) {
1070#ifdef CAPTURE_JUST_VALID_VM_STACK
1071 size_t n = (cont->saved_vm_stack.slen + cont->saved_vm_stack.clen);
1073 size_t n = cont->saved_ec.vm_stack_size;
1075 size += n *
sizeof(*cont->saved_vm_stack.ptr);
1078 if (cont->machine.stack) {
1079 size += cont->machine.stack_size *
sizeof(*cont->machine.stack);
1088 if (fiber->cont.self) {
1089 fiber->cont.self = rb_gc_location(fiber->cont.self);
1092 rb_execution_context_update(&fiber->cont.saved_ec);
1099 if (fiber->cont.self) {
1100 rb_gc_mark_movable(fiber->cont.self);
1103 rb_execution_context_mark(&fiber->cont.saved_ec);
1108fiber_compact(
void *ptr)
1111 fiber->first_proc = rb_gc_location(fiber->first_proc);
1113 if (fiber->prev) rb_fiber_update_self(fiber->prev);
1115 cont_compact(&fiber->cont);
1116 fiber_verify(fiber);
1120fiber_mark(
void *ptr)
1123 RUBY_MARK_ENTER(
"cont");
1124 fiber_verify(fiber);
1125 rb_gc_mark_movable(fiber->first_proc);
1126 if (fiber->prev) rb_fiber_mark_self(fiber->prev);
1127 cont_mark(&fiber->cont);
1128 RUBY_MARK_LEAVE(
"cont");
1132fiber_free(
void *ptr)
1135 RUBY_FREE_ENTER(
"fiber");
1137 if (DEBUG) fprintf(stderr,
"fiber_free: %p[%p]\n", (
void *)fiber, fiber->stack.base);
1139 if (fiber->cont.saved_ec.local_storage) {
1140 rb_id_table_free(fiber->cont.saved_ec.local_storage);
1143 cont_free(&fiber->cont);
1144 RUBY_FREE_LEAVE(
"fiber");
1148fiber_memsize(
const void *ptr)
1151 size_t size =
sizeof(*fiber);
1153 const rb_thread_t *th = rb_ec_thread_ptr(saved_ec);
1158 if (saved_ec->local_storage && fiber != th->root_fiber) {
1159 size += rb_id_table_memsize(saved_ec->local_storage);
1160 size += rb_obj_memsize_of(saved_ec->storage);
1163 size += cont_memsize(&fiber->cont);
1178 SET_MACHINE_STACK_END(&th->ec->machine.stack_end);
1180 if (th->ec->machine.stack_start > th->ec->machine.stack_end) {
1181 size = cont->machine.stack_size = th->ec->machine.stack_start - th->ec->machine.stack_end;
1182 cont->machine.stack_src = th->ec->machine.stack_end;
1185 size = cont->machine.stack_size = th->ec->machine.stack_end - th->ec->machine.stack_start;
1186 cont->machine.stack_src = th->ec->machine.stack_start;
1189 if (cont->machine.stack) {
1196 FLUSH_REGISTER_WINDOWS;
1197 asan_unpoison_memory_region(cont->machine.stack_src, size,
false);
1198 MEMCPY(cont->machine.stack, cont->machine.stack_src,
VALUE, size);
1203 {cont_mark, cont_free, cont_memsize, cont_compact},
1204 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
1212 VM_ASSERT(th->status == THREAD_RUNNABLE);
1219 sec->machine.stack_end = NULL;
1222static rb_nativethread_lock_t jit_cont_lock;
1240 if (first_jit_cont == NULL) {
1241 cont->next = cont->prev = NULL;
1245 cont->next = first_jit_cont;
1246 first_jit_cont->prev = cont;
1248 first_jit_cont = cont;
1261 if (cont == first_jit_cont) {
1262 first_jit_cont = cont->next;
1263 if (first_jit_cont != NULL)
1264 first_jit_cont->prev = NULL;
1267 cont->prev->next = cont->next;
1268 if (cont->next != NULL)
1269 cont->next->prev = cont->prev;
1278rb_jit_cont_each_iseq(rb_iseq_callback callback,
void *data)
1281 for (cont = first_jit_cont; cont != NULL; cont = cont->next) {
1282 if (cont->ec->vm_stack == NULL)
1286 for (cfp = RUBY_VM_END_CONTROL_FRAME(cont->ec) - 1; ; cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp)) {
1288 if (cfp->pc && (iseq = cfp->iseq) != NULL && imemo_type((
VALUE)iseq) == imemo_iseq) {
1289 callback(iseq, data);
1292 if (cfp == cont->ec->cfp)
1300rb_jit_cont_finish(
void)
1302 if (!jit_cont_enabled)
1306 for (cont = first_jit_cont; cont != NULL; cont = next) {
1316 VM_ASSERT(cont->jit_cont == NULL);
1317 if (jit_cont_enabled) {
1318 cont->jit_cont = jit_cont_new(&(cont->saved_ec));
1325 return &fiber->cont.saved_ec;
1332 cont_save_thread(cont, th);
1333 cont->saved_ec.thread_ptr = th;
1334 cont->saved_ec.local_storage = NULL;
1335 cont->saved_ec.local_storage_recursive_hash =
Qnil;
1336 cont->saved_ec.local_storage_recursive_hash_for_trace =
Qnil;
1337 cont_init_jit_cont(cont);
1341cont_new(
VALUE klass)
1344 volatile VALUE contval;
1347 THREAD_MUST_BE_RUNNING(th);
1349 cont->self = contval;
1350 cont_init(cont, th);
1357 return fiber->cont.self;
1363 return fiber->blocking;
1368rb_jit_cont_init(
void)
1370 if (!jit_cont_enabled)
1374 cont_init_jit_cont(&GET_EC()->fiber_ptr->cont);
1381 VALUE *p = ec->vm_stack;
1382 while (p < ec->cfp->sp) {
1383 fprintf(stderr,
"%3d ", (
int)(p - ec->vm_stack));
1384 rb_obj_info_dump(*p);
1394 while (cfp != end_of_cfp) {
1397 pc = cfp->pc - ISEQ_BODY(cfp->iseq)->iseq_encoded;
1399 fprintf(stderr,
"%2d pc: %d\n", i++, pc);
1400 cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
1406cont_capture(
volatile int *
volatile stat)
1410 volatile VALUE contval;
1413 THREAD_MUST_BE_RUNNING(th);
1414 rb_vm_stack_to_heap(th->ec);
1415 cont = cont_new(rb_cContinuation);
1416 contval = cont->self;
1418#ifdef CAPTURE_JUST_VALID_VM_STACK
1419 cont->saved_vm_stack.slen = ec->cfp->sp - ec->vm_stack;
1420 cont->saved_vm_stack.clen = ec->vm_stack + ec->vm_stack_size - (
VALUE*)ec->cfp;
1421 cont->saved_vm_stack.ptr =
ALLOC_N(
VALUE, cont->saved_vm_stack.slen + cont->saved_vm_stack.clen);
1422 MEMCPY(cont->saved_vm_stack.ptr,
1424 VALUE, cont->saved_vm_stack.slen);
1425 MEMCPY(cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen,
1428 cont->saved_vm_stack.clen);
1430 cont->saved_vm_stack.ptr =
ALLOC_N(
VALUE, ec->vm_stack_size);
1431 MEMCPY(cont->saved_vm_stack.ptr, ec->vm_stack,
VALUE, ec->vm_stack_size);
1434 rb_ec_set_vm_stack(&cont->saved_ec, NULL, 0);
1435 VM_ASSERT(cont->saved_ec.cfp != NULL);
1436 cont_save_machine_stack(th, cont);
1443 for (p=th->ec->ensure_list; p; p=p->next)
1446 for (p=th->ec->ensure_list; p; p=p->next) {
1447 if (!p->entry.marker)
1448 p->entry.marker = rb_ary_hidden_new(0);
1449 *entry++ = p->entry;
1454 if (ruby_setjmp(cont->jmpbuf)) {
1457 VAR_INITIALIZED(cont);
1458 value = cont->value;
1476 if (cont->type == CONTINUATION_CONTEXT) {
1481 if (sec->fiber_ptr != NULL) {
1482 fiber = sec->fiber_ptr;
1484 else if (th->root_fiber) {
1485 fiber = th->root_fiber;
1488 if (fiber && th->ec != &fiber->cont.saved_ec) {
1489 ec_switch(th, fiber);
1492 if (th->ec->trace_arg != sec->trace_arg) {
1497#ifdef CAPTURE_JUST_VALID_VM_STACK
1499 cont->saved_vm_stack.ptr,
1500 VALUE, cont->saved_vm_stack.slen);
1501 MEMCPY(th->ec->vm_stack + th->ec->vm_stack_size - cont->saved_vm_stack.clen,
1502 cont->saved_vm_stack.ptr + cont->saved_vm_stack.slen,
1503 VALUE, cont->saved_vm_stack.clen);
1505 MEMCPY(th->ec->vm_stack, cont->saved_vm_stack.ptr,
VALUE, sec->vm_stack_size);
1509 th->ec->cfp = sec->cfp;
1510 th->ec->raised_flag = sec->raised_flag;
1511 th->ec->tag = sec->tag;
1512 th->ec->root_lep = sec->root_lep;
1513 th->ec->root_svar = sec->root_svar;
1514 th->ec->ensure_list = sec->ensure_list;
1515 th->ec->errinfo = sec->errinfo;
1517 VM_ASSERT(th->ec->vm_stack != NULL);
1533 if (!FIBER_TERMINATED_P(old_fiber)) {
1534 STACK_GROW_DIR_DETECTION;
1535 SET_MACHINE_STACK_END(&th->ec->machine.stack_end);
1536 if (STACK_DIR_UPPER(0, 1)) {
1537 old_fiber->cont.machine.stack_size = th->ec->machine.stack_start - th->ec->machine.stack_end;
1538 old_fiber->cont.machine.stack = th->ec->machine.stack_end;
1541 old_fiber->cont.machine.stack_size = th->ec->machine.stack_end - th->ec->machine.stack_start;
1542 old_fiber->cont.machine.stack = th->ec->machine.stack_start;
1547 old_fiber->cont.saved_ec.machine.stack_start = th->ec->machine.stack_start;
1550 old_fiber->cont.saved_ec.machine.stack_end = NULL;
1554#if defined(COROUTINE_SANITIZE_ADDRESS)
1555 __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);
1559 struct coroutine_context * from = coroutine_transfer(&old_fiber->context, &new_fiber->context);
1561#if defined(COROUTINE_SANITIZE_ADDRESS)
1562 __sanitizer_finish_switch_fiber(old_fiber->context.fake_stack, NULL, NULL);
1570 fiber_restore_thread(th, old_fiber);
1576NOINLINE(NORETURN(
static void cont_restore_1(
rb_context_t *)));
1581 cont_restore_thread(cont);
1584#if defined(_M_AMD64) && !defined(__MINGW64__)
1589 _JUMP_BUFFER *bp = (
void*)&cont->jmpbuf;
1590 bp->Frame = ((_JUMP_BUFFER*)((
void*)&buf))->Frame;
1593 if (cont->machine.stack_src) {
1594 FLUSH_REGISTER_WINDOWS;
1595 MEMCPY(cont->machine.stack_src, cont->machine.stack,
1596 VALUE, cont->machine.stack_size);
1599 ruby_longjmp(cont->jmpbuf, 1);
1607 if (cont->machine.stack_src) {
1609#define STACK_PAD_SIZE 1
1611#define STACK_PAD_SIZE 1024
1613 VALUE space[STACK_PAD_SIZE];
1615#if !STACK_GROW_DIRECTION
1616 if (addr_in_prev_frame > &space[0]) {
1619#if STACK_GROW_DIRECTION <= 0
1620 volatile VALUE *
const end = cont->machine.stack_src;
1621 if (&space[0] > end) {
1630 cont_restore_0(cont, &space[0]);
1634#if !STACK_GROW_DIRECTION
1639#if STACK_GROW_DIRECTION >= 0
1640 volatile VALUE *
const end = cont->machine.stack_src + cont->machine.stack_size;
1641 if (&space[STACK_PAD_SIZE] < end) {
1646 cont_restore_0(cont, &space[STACK_PAD_SIZE-1]);
1650#if !STACK_GROW_DIRECTION
1654 cont_restore_1(cont);
1741rb_callcc(
VALUE self)
1743 volatile int called;
1744 volatile VALUE val = cont_capture(&called);
1755make_passing_arg(
int argc,
const VALUE *argv)
1774ruby_register_rollback_func_for_ensure(e_proc *ensure_func, e_proc *rollback_func)
1776 st_table **table_p = &GET_VM()->ensure_rollback_table;
1777 if (UNLIKELY(*table_p == NULL)) {
1778 *table_p = st_init_numtable();
1780 st_insert(*table_p, (st_data_t)ensure_func, (st_data_t)rollback_func);
1783static inline e_proc *
1784lookup_rollback_func(e_proc *ensure_func)
1786 st_table *table = GET_VM()->ensure_rollback_table;
1788 if (table && st_lookup(table, (st_data_t)ensure_func, &val))
1789 return (e_proc *) val;
1790 return (e_proc *)
Qundef;
1806 for (p=current; p; p=p->next)
1809 for (entry=target; entry->marker; entry++)
1814 base_point = cur_size;
1815 while (base_point) {
1816 if (target_size >= base_point &&
1817 p->entry.marker == target[target_size - base_point].marker)
1824 for (i=0; i < target_size - base_point; i++) {
1825 if (!lookup_rollback_func(target[i].e_proc)) {
1830 while (cur_size > base_point) {
1832 (*current->entry.e_proc)(current->entry.data2);
1833 current = current->next;
1837 for (j = 0; j < i; j++) {
1838 func = lookup_rollback_func(target[i - j - 1].e_proc);
1839 if (!UNDEF_P((
VALUE)func)) {
1840 (*func)(target[i - j - 1].data2);
1845NORETURN(
static VALUE rb_cont_call(
int argc,
VALUE *argv,
VALUE contval));
1863rb_cont_call(
int argc,
VALUE *argv,
VALUE contval)
1868 if (cont_thread_value(cont) != th->self) {
1871 if (cont->saved_ec.fiber_ptr) {
1872 if (th->ec->fiber_ptr != cont->saved_ec.fiber_ptr) {
1876 rollback_ensure_stack(contval, th->ec->ensure_list, cont->ensure_array);
1879 cont->value = make_passing_arg(argc, argv);
1881 cont_restore_0(cont, &contval);
1974 {fiber_mark, fiber_free, fiber_memsize, fiber_compact,},
1975 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
1979fiber_alloc(
VALUE klass)
1985fiber_t_alloc(
VALUE fiber_value,
unsigned int blocking)
1994 THREAD_MUST_BE_RUNNING(th);
1996 fiber->cont.self = fiber_value;
1997 fiber->cont.type = FIBER_CONTEXT;
1998 fiber->blocking = blocking;
1999 cont_init(&fiber->cont, th);
2001 fiber->cont.saved_ec.fiber_ptr = fiber;
2002 rb_ec_clear_vm_stack(&fiber->cont.saved_ec);
2008 VM_ASSERT(FIBER_CREATED_P(fiber));
2018 VALUE fiber_value = fiber_alloc(rb_cFiber);
2021 VM_ASSERT(
DATA_PTR(fiber_value) == NULL);
2022 VM_ASSERT(fiber->cont.type == FIBER_CONTEXT);
2023 VM_ASSERT(FIBER_RESUMED_P(fiber));
2025 th->root_fiber = fiber;
2027 fiber->cont.self = fiber_value;
2029 coroutine_initialize_main(&fiber->context);
2038 if (ec->fiber_ptr->cont.self == 0) {
2039 root_fiber_alloc(rb_ec_thread_ptr(ec));
2041 return ec->fiber_ptr;
2045current_fiber_storage(
void)
2052inherit_fiber_storage(
void)
2060 fiber->cont.saved_ec.storage = storage;
2066 VALUE storage = fiber->cont.saved_ec.storage;
2067 if (storage ==
Qnil) {
2068 storage = rb_hash_new();
2069 fiber_storage_set(fiber, storage);
2075storage_access_must_be_from_same_fiber(
VALUE self)
2079 if (fiber != current) {
2091rb_fiber_storage_get(
VALUE self)
2093 storage_access_must_be_from_same_fiber(self);
2094 return rb_obj_dup(fiber_storage_get(fiber_ptr(self)));
2106fiber_storage_validate(
VALUE value)
2109 if (value ==
Qnil)
return;
2149 "Fiber#storage= is experimental and may be removed in the future!");
2152 storage_access_must_be_from_same_fiber(self);
2153 fiber_storage_validate(value);
2155 fiber_ptr(self)->cont.saved_ec.storage =
rb_obj_dup(value);
2174 VALUE storage = fiber_storage_get(fiber_current());
2178 return rb_hash_aref(storage, key);
2196 VALUE storage = fiber_storage_get(fiber_current());
2198 return rb_hash_aset(storage, key, value);
2206 storage = inherit_fiber_storage();
2209 fiber_storage_validate(storage);
2213 rb_fiber_t *fiber = fiber_t_alloc(self, blocking);
2215 fiber->cont.saved_ec.storage = storage;
2216 fiber->first_proc = proc;
2217 fiber->stack.base = NULL;
2229 size_t vm_stack_size = 0;
2230 VALUE *vm_stack = fiber_initialize_coroutine(fiber, &vm_stack_size);
2233 cont->saved_vm_stack.ptr = NULL;
2234 rb_ec_initialize_vm_stack(sec, vm_stack, vm_stack_size /
sizeof(
VALUE));
2237 sec->local_storage = NULL;
2238 sec->local_storage_recursive_hash =
Qnil;
2239 sec->local_storage_recursive_hash_for_trace =
Qnil;
2243rb_fiber_pool_default(
VALUE pool)
2245 return &shared_fiber_pool;
2251 fiber->cont.saved_ec.storage = storage;
2257rb_fiber_initialize_kw(
int argc,
VALUE* argv,
VALUE self,
int kw_splat)
2268 rb_get_kwargs(options, fiber_initialize_keywords, 0, 3, arguments);
2270 if (!UNDEF_P(arguments[0])) {
2271 blocking = arguments[0];
2274 if (!UNDEF_P(arguments[1])) {
2275 pool = arguments[1];
2278 storage = arguments[2];
2281 return fiber_initialize(self,
rb_block_proc(), rb_fiber_pool_default(pool),
RTEST(blocking), storage);
2334rb_fiber_initialize(
int argc,
VALUE* argv,
VALUE self)
2342 return fiber_initialize(fiber_alloc(rb_cFiber),
rb_proc_new(func, obj), rb_fiber_pool_default(
Qnil), 1, storage);
2348 return rb_fiber_new_storage(func, obj,
Qtrue);
2352rb_fiber_s_schedule_kw(
int argc,
VALUE* argv,
int kw_splat)
2355 VALUE scheduler = th->scheduler;
2358 if (scheduler !=
Qnil) {
2410rb_fiber_s_schedule(
int argc,
VALUE *argv,
VALUE obj)
2426rb_fiber_s_scheduler(
VALUE klass)
2440rb_fiber_current_scheduler(
VALUE klass)
2462rb_fiber_set_scheduler(
VALUE klass,
VALUE scheduler)
2467NORETURN(
static void rb_fiber_terminate(
rb_fiber_t *fiber,
int need_interrupt,
VALUE err));
2472 rb_thread_t *
volatile th = fiber->cont.saved_ec.thread_ptr;
2475 enum ruby_tag_type state;
2476 int need_interrupt = TRUE;
2478 VM_ASSERT(th->ec == GET_EC());
2479 VM_ASSERT(FIBER_RESUMED_P(fiber));
2481 if (fiber->blocking) {
2485 EC_PUSH_TAG(th->ec);
2486 if ((state = EC_EXEC_TAG()) == TAG_NONE) {
2489 const VALUE *argv, args = cont->value;
2490 GetProcPtr(fiber->first_proc, proc);
2493 th->ec->errinfo =
Qnil;
2494 th->ec->root_lep = rb_vm_proc_local_ep(fiber->first_proc);
2495 th->ec->root_svar =
Qfalse;
2498 cont->value = rb_vm_invoke_proc(th->ec, proc, argc, argv, cont->kw_splat, VM_BLOCK_HANDLER_NONE);
2504 err = th->ec->errinfo;
2505 VM_ASSERT(FIBER_RESUMED_P(fiber));
2507 if (state == TAG_RAISE) {
2510 else if (state == TAG_FATAL) {
2511 rb_threadptr_pending_interrupt_enque(th, err);
2514 err = rb_vm_make_jump_tag_but_local_jump(state, err);
2516 need_interrupt = TRUE;
2519 rb_fiber_terminate(fiber, need_interrupt, err);
2528 rb_bug(
"%s", strerror(errno));
2531 fiber->cont.type = FIBER_CONTEXT;
2532 fiber->cont.saved_ec.fiber_ptr = fiber;
2533 fiber->cont.saved_ec.thread_ptr = th;
2534 fiber->blocking = 1;
2535 fiber_status_set(fiber, FIBER_RESUMED);
2536 th->ec = &fiber->cont.saved_ec;
2541 cont_init_jit_cont(&fiber->cont);
2547 if (th->root_fiber) {
2553 VM_ASSERT(th->ec->fiber_ptr->cont.type == FIBER_CONTEXT);
2554 VM_ASSERT(th->ec->fiber_ptr->cont.self == 0);
2557 rb_ractor_set_current_ec(th->ractor, NULL);
2559 fiber_free(th->ec->fiber_ptr);
2569 fiber->status = FIBER_TERMINATED;
2572 rb_ec_clear_vm_stack(th->ec);
2576return_fiber(
bool terminate)
2583 prev->resuming_fiber = NULL;
2588 rb_raise(rb_eFiberError,
"attempt to yield on a not resumed fiber");
2594 VM_ASSERT(root_fiber != NULL);
2597 for (fiber = root_fiber; fiber->resuming_fiber; fiber = fiber->resuming_fiber) {
2605rb_fiber_current(
void)
2607 return fiber_current()->cont.self;
2616 if (th->ec->fiber_ptr != NULL) {
2617 fiber = th->ec->fiber_ptr;
2621 fiber = root_fiber_alloc(th);
2624 if (FIBER_CREATED_P(next_fiber)) {
2625 fiber_prepare_stack(next_fiber);
2628 VM_ASSERT(FIBER_RESUMED_P(fiber) || FIBER_TERMINATED_P(fiber));
2629 VM_ASSERT(FIBER_RUNNABLE_P(next_fiber));
2631 if (FIBER_RESUMED_P(fiber)) fiber_status_set(fiber, FIBER_SUSPENDED);
2633 fiber_status_set(next_fiber, FIBER_RESUMED);
2634 fiber_setcontext(next_fiber, fiber);
2645 if (th->root_fiber == NULL) root_fiber_alloc(th);
2647 if (th->ec->fiber_ptr == fiber) {
2651 return make_passing_arg(argc, argv);
2654 if (cont_thread_value(cont) != th->self) {
2655 rb_raise(rb_eFiberError,
"fiber called across threads");
2658 if (FIBER_TERMINATED_P(fiber)) {
2659 value =
rb_exc_new2(rb_eFiberError,
"dead fiber called");
2661 if (!FIBER_TERMINATED_P(th->ec->fiber_ptr)) {
2663 VM_UNREACHABLE(fiber_switch);
2669 VM_ASSERT(FIBER_SUSPENDED_P(th->root_fiber));
2671 cont = &th->root_fiber->cont;
2673 cont->value = value;
2675 fiber_setcontext(th->root_fiber, th->ec->fiber_ptr);
2677 VM_UNREACHABLE(fiber_switch);
2681 VM_ASSERT(FIBER_RUNNABLE_P(fiber));
2685 VM_ASSERT(!current_fiber->resuming_fiber);
2687 if (resuming_fiber) {
2688 current_fiber->resuming_fiber = resuming_fiber;
2689 fiber->prev = fiber_current();
2690 fiber->yielding = 0;
2693 VM_ASSERT(!current_fiber->yielding);
2695 current_fiber->yielding = 1;
2698 if (current_fiber->blocking) {
2703 cont->kw_splat = kw_splat;
2704 cont->value = make_passing_arg(argc, argv);
2706 fiber_store(fiber, th);
2709#ifndef COROUTINE_PTHREAD_CONTEXT
2710 if (resuming_fiber && FIBER_TERMINATED_P(fiber)) {
2711 fiber_stack_release(fiber);
2715 if (fiber_current()->blocking) {
2719 RUBY_VM_CHECK_INTS(th->ec);
2723 current_fiber = th->ec->fiber_ptr;
2724 value = current_fiber->cont.value;
2725 if (current_fiber->cont.argc == -1)
rb_exc_raise(value);
2732 return fiber_switch(fiber_ptr(fiber_value), argc, argv,
RB_NO_KEYWORDS, NULL,
false);
2750rb_fiber_blocking_p(
VALUE fiber)
2752 return RBOOL(fiber_ptr(fiber)->blocking);
2756fiber_blocking_yield(
VALUE fiber_value)
2759 rb_thread_t *
volatile th = fiber->cont.saved_ec.thread_ptr;
2762 fiber->blocking = 1;
2771fiber_blocking_ensure(
VALUE fiber_value)
2774 rb_thread_t *
volatile th = fiber->cont.saved_ec.thread_ptr;
2777 fiber->blocking = 0;
2794rb_fiber_blocking(
VALUE class)
2796 VALUE fiber_value = rb_fiber_current();
2800 if (fiber->blocking) {
2803 return rb_ensure(fiber_blocking_yield, fiber_value, fiber_blocking_ensure, fiber_value);
2826rb_fiber_s_blocking_p(
VALUE klass)
2829 unsigned blocking = thread->blocking;
2840 fiber_status_set(fiber, FIBER_TERMINATED);
2846 VALUE value = fiber->cont.value;
2848 VM_ASSERT(FIBER_RESUMED_P(fiber));
2849 rb_fiber_close(fiber);
2851 fiber->cont.machine.stack = NULL;
2852 fiber->cont.machine.stack_size = 0;
2856 if (need_interrupt) RUBY_VM_SET_INTERRUPT(&next_fiber->cont.saved_ec);
2859 fiber_switch(next_fiber, -1, &error,
RB_NO_KEYWORDS, NULL,
false);
2861 fiber_switch(next_fiber, 1, &value,
RB_NO_KEYWORDS, NULL,
false);
2866fiber_resume_kw(
rb_fiber_t *fiber,
int argc,
const VALUE *argv,
int kw_splat)
2870 if (argc == -1 && FIBER_CREATED_P(fiber)) {
2871 rb_raise(rb_eFiberError,
"cannot raise exception on unborn fiber");
2873 else if (FIBER_TERMINATED_P(fiber)) {
2874 rb_raise(rb_eFiberError,
"attempt to resume a terminated fiber");
2876 else if (fiber == current_fiber) {
2877 rb_raise(rb_eFiberError,
"attempt to resume the current fiber");
2879 else if (fiber->prev != NULL) {
2880 rb_raise(rb_eFiberError,
"attempt to resume a resumed fiber (double resume)");
2882 else if (fiber->resuming_fiber) {
2883 rb_raise(rb_eFiberError,
"attempt to resume a resuming fiber");
2885 else if (fiber->prev == NULL &&
2886 (!fiber->yielding && fiber->status != FIBER_CREATED)) {
2887 rb_raise(rb_eFiberError,
"attempt to resume a transferring fiber");
2890 return fiber_switch(fiber, argc, argv, kw_splat, fiber,
false);
2894rb_fiber_resume_kw(
VALUE self,
int argc,
const VALUE *argv,
int kw_splat)
2896 return fiber_resume_kw(fiber_ptr(self), argc, argv, kw_splat);
2902 return fiber_resume_kw(fiber_ptr(self), argc, argv,
RB_NO_KEYWORDS);
2906rb_fiber_yield_kw(
int argc,
const VALUE *argv,
int kw_splat)
2908 return fiber_switch(return_fiber(
false), argc, argv, kw_splat, NULL,
true);
2912rb_fiber_yield(
int argc,
const VALUE *argv)
2914 return fiber_switch(return_fiber(
false), argc, argv,
RB_NO_KEYWORDS, NULL,
true);
2920 if (th->root_fiber && th->root_fiber != th->ec->fiber_ptr) {
2921 th->ec->local_storage = th->root_fiber->cont.saved_ec.local_storage;
2936 return RBOOL(!FIBER_TERMINATED_P(fiber_ptr(fiber_value)));
2955rb_fiber_m_resume(
int argc,
VALUE *argv,
VALUE fiber)
3007rb_fiber_backtrace(
int argc,
VALUE *argv,
VALUE fiber)
3009 return rb_vm_backtrace(argc, argv, &fiber_ptr(fiber)->cont.saved_ec);
3032rb_fiber_backtrace_locations(
int argc,
VALUE *argv,
VALUE fiber)
3034 return rb_vm_backtrace_locations(argc, argv, &fiber_ptr(fiber)->cont.saved_ec);
3120rb_fiber_m_transfer(
int argc,
VALUE *argv,
VALUE self)
3126fiber_transfer_kw(
rb_fiber_t *fiber,
int argc,
const VALUE *argv,
int kw_splat)
3128 if (fiber->resuming_fiber) {
3129 rb_raise(rb_eFiberError,
"attempt to transfer to a resuming fiber");
3132 if (fiber->yielding) {
3133 rb_raise(rb_eFiberError,
"attempt to transfer to a yielding fiber");
3136 return fiber_switch(fiber, argc, argv, kw_splat, NULL,
false);
3140rb_fiber_transfer_kw(
VALUE self,
int argc,
const VALUE *argv,
int kw_splat)
3142 return fiber_transfer_kw(fiber_ptr(self), argc, argv, kw_splat);
3156rb_fiber_s_yield(
int argc,
VALUE *argv,
VALUE klass)
3164 VALUE exception = rb_make_exception(argc, argv);
3166 if (fiber->resuming_fiber) {
3167 rb_raise(rb_eFiberError,
"attempt to raise a resuming fiber");
3169 else if (FIBER_SUSPENDED_P(fiber) && !fiber->yielding) {
3180 return fiber_raise(fiber_ptr(fiber), argc, argv);
3205rb_fiber_m_raise(
int argc,
VALUE *argv,
VALUE self)
3207 return rb_fiber_raise(self, argc, argv);
3218rb_fiber_s_current(
VALUE klass)
3220 return rb_fiber_current();
3224fiber_to_s(
VALUE fiber_value)
3226 const rb_fiber_t *fiber = fiber_ptr(fiber_value);
3228 char status_info[0x20];
3230 if (fiber->resuming_fiber) {
3231 snprintf(status_info, 0x20,
" (%s by resuming)", fiber_status_name(fiber->status));
3234 snprintf(status_info, 0x20,
" (%s)", fiber_status_name(fiber->status));
3239 strlcat(status_info,
">",
sizeof(status_info));
3244 GetProcPtr(fiber->first_proc, proc);
3245 return rb_block_to_s(fiber_value, &proc->block, status_info);
3248#ifdef HAVE_WORKING_FORK
3252 if (th->root_fiber) {
3253 if (&th->root_fiber->cont.saved_ec != th->ec) {
3254 th->root_fiber = th->ec->fiber_ptr;
3256 th->root_fiber->prev = 0;
3261#ifdef RB_EXPERIMENTAL_FIBER_POOL
3263fiber_pool_free(
void *ptr)
3266 RUBY_FREE_ENTER(
"fiber_pool");
3268 fiber_pool_allocation_free(
fiber_pool->allocations);
3271 RUBY_FREE_LEAVE(
"fiber_pool");
3275fiber_pool_memsize(
const void *ptr)
3278 size_t size =
sizeof(*fiber_pool);
3287 {NULL, fiber_pool_free, fiber_pool_memsize,},
3288 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
3292fiber_pool_alloc(
VALUE klass)
3300rb_fiber_pool_initialize(
int argc,
VALUE* argv,
VALUE self)
3307 rb_scan_args(argc, argv,
"03", &size, &count, &vm_stack_size);
3310 size =
SIZET2NUM(th->vm->default_params.fiber_machine_stack_size);
3317 if (
NIL_P(vm_stack_size)) {
3318 vm_stack_size =
SIZET2NUM(th->vm->default_params.fiber_vm_stack_size);
3346 size_t vm_stack_size = th->vm->default_params.fiber_vm_stack_size;
3347 size_t machine_stack_size = th->vm->default_params.fiber_machine_stack_size;
3348 size_t stack_size = machine_stack_size + vm_stack_size;
3352 GetSystemInfo(&info);
3353 pagesize = info.dwPageSize;
3355 pagesize = sysconf(_SC_PAGESIZE);
3357 SET_MACHINE_STACK_END(&th->ec->machine.stack_end);
3359 fiber_pool_initialize(&shared_fiber_pool, stack_size, FIBER_POOL_INITIAL_SIZE, vm_stack_size);
3365 const char *fiber_shared_fiber_pool_free_stacks = getenv(
"RUBY_SHARED_FIBER_POOL_FREE_STACKS");
3366 if (fiber_shared_fiber_pool_free_stacks) {
3367 shared_fiber_pool.free_stacks = atoi(fiber_shared_fiber_pool_free_stacks);
3386 rb_define_method(rb_cFiber,
"backtrace_locations", rb_fiber_backtrace_locations, -1);
3399#ifdef RB_EXPERIMENTAL_FIBER_POOL
3402 rb_define_method(rb_cFiberPool,
"initialize", rb_fiber_pool_initialize, -1);
3408RUBY_SYMBOL_EXPORT_BEGIN
3411ruby_Init_Continuation_body(
void)
3421RUBY_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 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 always regardless of runtime -W flag.
void rb_raise(VALUE exc, const char *fmt,...)
Exception entry point.
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.
void rb_bug(const char *fmt,...)
Interpreter panic switch.
VALUE rb_eStandardError
StandardError exception.
VALUE rb_eFrozenError
FrozenError exception.
VALUE rb_eTypeError
TypeError exception.
VALUE rb_eRuntimeError
RuntimeError exception.
VALUE rb_eArgError
ArgumentError exception.
@ 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_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_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.
#define MEMZERO(p, type, n)
Handy macro to erase a region of memory.
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.
static long RSTRING_LEN(VALUE str)
Queries the length of the string.
#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.
#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 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.
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.