@@ -42,7 +42,11 @@ extern int madvise(caddr_t, size_t, int);
4242#include "id_table.h"
4343#include "ractor_core.h"
4444
45- static const int DEBUG = 0 ;
45+ enum {
46+ DEBUG = 0 ,
47+ DEBUG_EXPAND = 0 ,
48+ DEBUG_ACQUIRE = 0 ,
49+ };
4650
4751#define RB_PAGE_SIZE (pagesize)
4852#define RB_PAGE_MASK (~(RB_PAGE_SIZE - 1))
@@ -62,11 +66,11 @@ static VALUE rb_cFiberPool;
6266// Defined in `coroutine/$arch/Context.h`:
6367#ifdef COROUTINE_LIMITED_ADDRESS_SPACE
6468#define FIBER_POOL_ALLOCATION_FREE
65- #define FIBER_POOL_INITIAL_SIZE 8
66- #define FIBER_POOL_ALLOCATION_MAXIMUM_SIZE 32
69+ #define FIBER_POOL_MINIMUM_COUNT 8
70+ #define FIBER_POOL_MAXIMUM_ALLOCATIONS 32
6771#else
68- #define FIBER_POOL_INITIAL_SIZE 32
69- #define FIBER_POOL_ALLOCATION_MAXIMUM_SIZE 1024
72+ #define FIBER_POOL_MINIMUM_COUNT 32
73+ #define FIBER_POOL_MAXIMUM_ALLOCATIONS 1024
7074#endif
7175#ifdef RB_EXPERIMENTAL_FIBER_POOL
7276#define FIBER_POOL_ALLOCATION_FREE
@@ -189,7 +193,11 @@ struct fiber_pool {
189193 size_t count ;
190194
191195 // The initial number of stacks to allocate.
192- size_t initial_count ;
196+ size_t minimum_count ;
197+
198+ // If positive, total stacks in this pool cannot exceed this (shared pool only:
199+ // set via RUBY_SHARED_FIBER_POOL_MAXIMUM_COUNT). Expansion fails with errno EAGAIN.
200+ size_t maximum_count ;
193201
194202 // Whether to madvise(free) the stack or not.
195203 // If this value is set to 1, the stack will be madvise(free)ed
@@ -470,7 +478,7 @@ fiber_pool_allocate_memory(size_t * count, size_t stride)
470478 // the system would allow (e.g. overcommit * physical memory + swap), we
471479 // divide count by two and try again. This condition should only be
472480 // encountered in edge cases, but we handle it here gracefully.
473- while (* count > 1 ) {
481+ while (* count ) {
474482#if defined(_WIN32 )
475483 void * base = VirtualAlloc (0 , (* count )* stride , MEM_COMMIT , PAGE_READWRITE );
476484
@@ -518,11 +526,28 @@ fiber_pool_allocate_memory(size_t * count, size_t stride)
518526static struct fiber_pool_allocation *
519527fiber_pool_expand (struct fiber_pool * fiber_pool , size_t count )
520528{
529+ if (count == 0 ) {
530+ errno = EAGAIN ;
531+ return NULL ;
532+ }
533+
521534 STACK_GROW_DIR_DETECTION ;
522535
523536 size_t size = fiber_pool -> size ;
524537 size_t stride = size + RB_PAGE_SIZE ;
525538
539+ // If the maximum number of stacks is set, and we have reached it, return NULL.
540+ if (fiber_pool -> maximum_count > 0 ) {
541+ if (fiber_pool -> count >= fiber_pool -> maximum_count ) {
542+ errno = EAGAIN ;
543+ return NULL ;
544+ }
545+ size_t remaining = fiber_pool -> maximum_count - fiber_pool -> count ;
546+ if (count > remaining ) {
547+ count = remaining ;
548+ }
549+ }
550+
526551 // Allocate metadata before mmap: ruby_xmalloc (RB_ALLOC) raises on failure and
527552 // must not run after base is mapped, or the region would leak.
528553 struct fiber_pool_allocation * allocation = RB_ALLOC (struct fiber_pool_allocation );
@@ -548,7 +573,7 @@ fiber_pool_expand(struct fiber_pool * fiber_pool, size_t count)
548573#endif
549574 allocation -> pool = fiber_pool ;
550575
551- if (DEBUG ) {
576+ if (DEBUG_EXPAND ) {
552577 fprintf (stderr , "fiber_pool_expand(%" PRIuSIZE "): %p, %" PRIuSIZE "/%" PRIuSIZE " x [%" PRIuSIZE ":%" PRIuSIZE "]\n" ,
553578 count , (void * )fiber_pool , fiber_pool -> used , fiber_pool -> count , size , fiber_pool -> vm_stack_size );
554579 }
@@ -613,22 +638,24 @@ fiber_pool_expand(struct fiber_pool * fiber_pool, size_t count)
613638// Initialize the specified fiber pool with the given number of stacks.
614639// @param vm_stack_size The size of the vm stack to allocate.
615640static void
616- fiber_pool_initialize (struct fiber_pool * fiber_pool , size_t size , size_t count , size_t vm_stack_size )
641+ fiber_pool_initialize (struct fiber_pool * fiber_pool , size_t size , size_t minimum_count , size_t maximum_count , size_t vm_stack_size )
617642{
618643 VM_ASSERT (vm_stack_size < size );
619644
620645 fiber_pool -> allocations = NULL ;
621646 fiber_pool -> vacancies = NULL ;
622647 fiber_pool -> size = ((size / RB_PAGE_SIZE ) + 1 ) * RB_PAGE_SIZE ;
623648 fiber_pool -> count = 0 ;
624- fiber_pool -> initial_count = count ;
649+ fiber_pool -> minimum_count = minimum_count ;
650+ fiber_pool -> maximum_count = maximum_count ;
625651 fiber_pool -> free_stacks = 1 ;
626652 fiber_pool -> used = 0 ;
627-
628653 fiber_pool -> vm_stack_size = vm_stack_size ;
629654
630- if (RB_UNLIKELY (!fiber_pool_expand (fiber_pool , count ))) {
631- rb_raise (rb_eFiberError , "can't allocate initial fiber stacks (%" PRIuSIZE " x %" PRIuSIZE " bytes): %s" , count , fiber_pool -> size , strerror (errno ));
655+ if (fiber_pool -> minimum_count > 0 ) {
656+ if (RB_UNLIKELY (!fiber_pool_expand (fiber_pool , fiber_pool -> minimum_count ))) {
657+ rb_raise (rb_eFiberError , "can't allocate initial fiber stacks (%" PRIuSIZE " x %" PRIuSIZE " bytes): %s" , fiber_pool -> minimum_count , fiber_pool -> size , strerror (errno ));
658+ }
632659 }
633660}
634661
@@ -678,15 +705,30 @@ fiber_pool_allocation_free(struct fiber_pool_allocation * allocation)
678705#endif
679706
680707// Number of stacks to request when expanding the pool (clamped to min/max).
681- static inline size_t
708+ static size_t
682709fiber_pool_stack_expand_count (const struct fiber_pool * pool )
683710{
684- const size_t maximum = FIBER_POOL_ALLOCATION_MAXIMUM_SIZE ;
685- const size_t minimum = pool -> initial_count ;
711+ const size_t maximum_allocations = FIBER_POOL_MAXIMUM_ALLOCATIONS ;
712+ const size_t minimum_count = FIBER_POOL_MINIMUM_COUNT ;
686713
714+ // We are going try and double the number of stacks in the pool:
687715 size_t count = pool -> count ;
688- if (count > maximum ) count = maximum ;
689- if (count < minimum ) count = minimum ;
716+ if (count > maximum_allocations ) count = maximum_allocations ;
717+ if (count < minimum_count ) count = minimum_count ;
718+
719+ // If we have a maximum count, we need to clamp the number of stacks to the maximum:
720+ if (pool -> maximum_count > 0 ) {
721+ if (pool -> count >= pool -> maximum_count ) {
722+ // No expansion is possible:
723+ return 0 ;
724+ }
725+
726+ // Otherwise, compute the number of stacks we can allocate to bring us to the maximum:
727+ size_t remaining = pool -> maximum_count - pool -> count ;
728+ if (count > remaining ) {
729+ count = remaining ;
730+ }
731+ }
690732
691733 return count ;
692734}
@@ -698,15 +740,15 @@ fiber_pool_stack_acquire_expand(struct fiber_pool *fiber_pool)
698740{
699741 size_t count = fiber_pool_stack_expand_count (fiber_pool );
700742
701- if (DEBUG ) fprintf (stderr , "fiber_pool_stack_acquire: expanding fiber pool by %" PRIuSIZE " stacks\n" , count );
743+ if (DEBUG_ACQUIRE ) fprintf (stderr , "fiber_pool_stack_acquire: expanding fiber pool by %" PRIuSIZE " stacks\n" , count );
702744
703745 struct fiber_pool_vacancy * vacancy = NULL ;
704746
705747 if (RB_LIKELY (fiber_pool_expand (fiber_pool , count ))) {
706748 return fiber_pool_vacancy_pop (fiber_pool );
707749 }
708750 else {
709- if (DEBUG ) fprintf (stderr , "fiber_pool_stack_acquire: expand failed (%s), collecting garbage\n" , strerror (errno ));
751+ if (DEBUG_ACQUIRE ) fprintf (stderr , "fiber_pool_stack_acquire: expand failed (%s), collecting garbage\n" , strerror (errno ));
710752
711753 rb_gc ();
712754
@@ -716,6 +758,9 @@ fiber_pool_stack_acquire_expand(struct fiber_pool *fiber_pool)
716758 return vacancy ;
717759 }
718760
761+ // Recompute count as gc may have freed up some allocations:
762+ count = fiber_pool_stack_expand_count (fiber_pool );
763+
719764 // Try to expand the fiber pool again:
720765 if (RB_LIKELY (fiber_pool_expand (fiber_pool , count ))) {
721766 return fiber_pool_vacancy_pop (fiber_pool );
@@ -3526,7 +3571,7 @@ rb_fiber_pool_initialize(int argc, VALUE* argv, VALUE self)
35263571
35273572 TypedData_Get_Struct (self , struct fiber_pool , & FiberPoolDataType , fiber_pool );
35283573
3529- fiber_pool_initialize (fiber_pool , NUM2SIZET (size ), NUM2SIZET (count ), NUM2SIZET (vm_stack_size ));
3574+ fiber_pool_initialize (fiber_pool , NUM2SIZET (size ), NUM2SIZET (count ), 0 , NUM2SIZET (vm_stack_size ));
35303575
35313576 return self ;
35323577}
@@ -3545,6 +3590,46 @@ rb_fiber_pool_initialize(int argc, VALUE* argv, VALUE self)
35453590 * fiber.resume #=> FiberError: dead fiber called
35463591 */
35473592
3593+ static size_t
3594+ shared_fiber_pool_minimum_count ()
3595+ {
3596+ size_t minimum_count = FIBER_POOL_MINIMUM_COUNT ;
3597+
3598+ const char * minimum_count_env = getenv ("RUBY_SHARED_FIBER_POOL_MINIMUM_COUNT" );
3599+ if (minimum_count_env && minimum_count_env [0 ]) {
3600+ char * end ;
3601+ unsigned long value = strtoul (minimum_count_env , & end , 10 );
3602+ if (end != minimum_count_env && * end == '\0' ) {
3603+ minimum_count = (size_t )value ;
3604+ }
3605+ else {
3606+ rb_warn ("invalid RUBY_SHARED_FIBER_POOL_MINIMUM_COUNT=%s (expected a non-negative integer)" , minimum_count_env );
3607+ }
3608+ }
3609+
3610+ return minimum_count ;
3611+ }
3612+
3613+ static size_t
3614+ shared_fiber_pool_maximum_count ()
3615+ {
3616+ size_t maximum_count = 0 ;
3617+
3618+ const char * maximum_count_env = getenv ("RUBY_SHARED_FIBER_POOL_MAXIMUM_COUNT" );
3619+ if (maximum_count_env && maximum_count_env [0 ]) {
3620+ char * end ;
3621+ unsigned long value = strtoul (maximum_count_env , & end , 10 );
3622+ if (end != maximum_count_env && * end == '\0' ) {
3623+ maximum_count = (size_t )value ;
3624+ }
3625+ else {
3626+ rb_warn ("invalid RUBY_SHARED_FIBER_POOL_MAXIMUM_COUNT=%s (expected a non-negative integer)" , maximum_count_env );
3627+ }
3628+ }
3629+
3630+ return maximum_count ;
3631+ }
3632+
35483633void
35493634Init_Cont (void )
35503635{
@@ -3562,7 +3647,9 @@ Init_Cont(void)
35623647#endif
35633648 SET_MACHINE_STACK_END (& th -> ec -> machine .stack_end );
35643649
3565- fiber_pool_initialize (& shared_fiber_pool , stack_size , FIBER_POOL_INITIAL_SIZE , vm_stack_size );
3650+ size_t minimum_count = shared_fiber_pool_minimum_count ();
3651+ size_t maximum_count = shared_fiber_pool_maximum_count ();
3652+ fiber_pool_initialize (& shared_fiber_pool , stack_size , minimum_count , maximum_count , vm_stack_size );
35663653
35673654 fiber_initialize_keywords [0 ] = rb_intern_const ("blocking" );
35683655 fiber_initialize_keywords [1 ] = rb_intern_const ("pool" );
0 commit comments