Skip to content

Commit 6e3762a

Browse files
authored
Configurable fiber stack sizes (#3773)
1 parent 83590eb commit 6e3762a

File tree

12 files changed

+88
-66
lines changed

12 files changed

+88
-66
lines changed

otherlibs/systhreads/st_stubs.c

Lines changed: 1 addition & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -393,28 +393,14 @@ static void caml_thread_leave_blocking_section(void)
393393
restore_runtime_state(th);
394394
}
395395

396-
static int get_pthreads_stack_size_in_bytes(void)
397-
{
398-
pthread_attr_t attr;
399-
size_t res =
400-
// default value, retrieved from a recent system (May 2024)
401-
8388608;
402-
if (pthread_attr_init(&attr) == 0) {
403-
pthread_attr_getstacksize(&attr, &res);
404-
}
405-
pthread_attr_destroy(&attr);
406-
return res;
407-
}
408-
409396
/* Create and setup a new thread info block.
410397
This block has no associated thread descriptor and
411398
is not inserted in the list of threads. */
412399
static caml_thread_t caml_thread_new_info(void)
413400
{
414401
caml_thread_t th = NULL;
415402
caml_domain_state *domain_state = Caml_state;
416-
uintnat stack_wsize =
417-
caml_get_init_stack_wsize(Wsize_bsize(get_pthreads_stack_size_in_bytes()));
403+
uintnat stack_wsize = caml_get_init_stack_wsize(STACK_SIZE_THREAD);
418404

419405
th = (caml_thread_t)caml_stat_alloc_noexc(sizeof(struct caml_thread_struct));
420406
if (th == NULL) return NULL;

runtime/amd64.S

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -920,7 +920,8 @@ LBL(caml_start_program):
920920
movq Caml_state(async_exn_handler), %r10
921921
movq %r10, Cstack_async_exn_handler(%rsp)
922922
movq %rsp, Caml_state(c_stack)
923-
/* Load the OCaml stack. */
923+
/* Load the OCaml stack.
924+
Stack space used here must be accounted for in caml_maybe_expand_stack */
924925
movq Caml_state(current_stack), %r11
925926
movq Stack_sp(%r11), %r10
926927
#if Stack_padding_word

runtime/backtrace_nat.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -134,16 +134,15 @@ void caml_stash_backtrace(value exn, uintnat pc, char * sp, char* trapsp)
134134

135135
void caml_stash_backtrace_wrapper(value exn, char* rsp, char* trapsp)
136136
{
137-
#if defined(NATIVE_CODE) && !defined(STACK_CHECKS_ENABLED)
137+
#ifdef STACK_GUARD_PAGES
138138
/* If we get an rsp that lies in the guard page, just do nothing - using rsp
139139
* would trigger another segfault, and we are probably in the process of
140140
* raising the exception from a segfault. In any case this behaviour seems
141141
* consistent with runtime4, where no backtrace appears to be available at
142142
* this point. */
143143
struct stack_info *block = Caml_state->current_stack;
144-
int page_size = getpagesize();
145-
char* protected_low = Protected_stack_page(block, page_size);
146-
char* protected_high = protected_low + page_size;
144+
char* protected_low = Protected_stack_page(block);
145+
char* protected_high = protected_low + caml_plat_pagesize;
147146
if ((rsp >= protected_low) && (rsp < protected_high)) {
148147
return;
149148
}

runtime/caml/config.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,11 @@ typedef uint64_t uintnat;
214214
#define Stack_ctx_words (10 + 2)
215215
#endif
216216

217+
/* Whether to use guard pages for fiber stacks */
218+
#if !defined(USE_MMAP_MAP_STACK) && defined(NATIVE_CODE) && !defined(STACK_CHECKS_ENABLED)
219+
#define STACK_GUARD_PAGES
220+
#endif
221+
217222
/* Whether to offset Stack_high to preserve alignment. */
218223
#if defined(TARGET_amd64) && !defined(WITH_FRAME_POINTERS)
219224
#define Stack_padding_word 1

runtime/caml/fiber.h

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "misc.h"
2525
#include "mlvalues.h"
2626
#include "roots.h"
27+
#include "platform.h"
2728

2829
struct stack_info;
2930

@@ -68,7 +69,15 @@ struct stack_info {
6869
intnat local_limit;
6970
};
7071

72+
#ifdef STACK_GUARD_PAGES
73+
// The OCaml stack starts after the stack_info and guard pages
74+
#define Stack_base(stk) ((value*)(((char*) (stk)) + 2 * caml_plat_pagesize))
75+
// We can assume that mmap returns page-aligned addresses.
76+
#define Protected_stack_page(block) (((char*) (block)) + caml_plat_pagesize)
77+
#else
7178
#define Stack_base(stk) ((value*)(stk + 1))
79+
#endif
80+
7281
#define Stack_threshold_ptr(stk) \
7382
(Stack_base(stk) + Stack_threshold / sizeof(value))
7483
#define Stack_high(stk) ((value*)stk->handler - Stack_padding_word)
@@ -271,8 +280,14 @@ CAMLextern int caml_try_realloc_stack (asize_t required_wsize);
271280
/* Parameters settable with OCAMLRUNPARAM */
272281
extern uintnat caml_init_main_stack_wsz; /* -Xmain_stack_size= */
273282
extern uintnat caml_init_thread_stack_wsz; /* -Xthread_stack_size= */
283+
extern uintnat caml_init_fiber_stack_wsz; /* -Xfiber_stack_size= */
284+
285+
#define STACK_SIZE_MAIN 0
286+
#define STACK_SIZE_THREAD 1
287+
#define STACK_SIZE_FIBER 2
288+
289+
CAMLextern uintnat caml_get_init_stack_wsize(int context);
274290

275-
CAMLextern uintnat caml_get_init_stack_wsize(int thread_stack_wsz);
276291
void caml_change_max_stack_size (uintnat new_max_wsize);
277292
void caml_maybe_expand_stack(void);
278293
CAMLextern void caml_free_stack(struct stack_info* stk);
@@ -303,12 +318,6 @@ CAMLnoret CAMLextern void caml_raise_unhandled_effect (value effect);
303318

304319
value caml_make_unhandled_effect_exn (value effect);
305320

306-
#if defined(NATIVE_CODE) && !defined(STACK_CHECKS_ENABLED)
307-
// We can assume that mmap returns page-aligned addresses.
308-
#define Protected_stack_page(block, page_size) \
309-
(((char*) (block)) + (page_size))
310-
#endif
311-
312321
#endif /* CAML_INTERNALS */
313322

314323
#endif /* CAML_FIBER_H */

runtime/domain.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -564,7 +564,7 @@ static void domain_create(uintnat initial_minor_heap_wsize,
564564
dom_internal* d = 0;
565565
caml_domain_state* domain_state;
566566
struct interruptor* s;
567-
uintnat stack_wsize = caml_get_init_stack_wsize(-1 /* main thread */);
567+
uintnat stack_wsize = caml_get_init_stack_wsize(STACK_SIZE_MAIN);
568568

569569
CAMLassert (domain_self == 0);
570570

runtime/fiber.c

Lines changed: 31 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -64,21 +64,29 @@ static _Atomic int64_t fiber_id = 0;
6464
/* Parameters settable with OCAMLRUNPARAM */
6565
uintnat caml_init_main_stack_wsz = 0; /* -Xmain_stack_size= */
6666
uintnat caml_init_thread_stack_wsz = 0; /* -Xthread_stack_size= */
67+
uintnat caml_init_fiber_stack_wsz = 0; /* -Xfiber_stack_size= */
6768

68-
uintnat caml_get_init_stack_wsize (int thread_stack_wsz)
69+
uintnat caml_get_init_stack_wsize (int context)
6970
{
70-
#if defined(NATIVE_CODE) && !defined(STACK_CHECKS_ENABLED)
71-
uintnat init_stack_wsize =
72-
thread_stack_wsz < 0
73-
? caml_init_main_stack_wsz
74-
: caml_init_thread_stack_wsz > 0
75-
? caml_init_thread_stack_wsz : thread_stack_wsz;
71+
uintnat init_stack_wsize = 0;
72+
73+
#ifdef STACK_GUARD_PAGES
74+
switch(context) {
75+
case STACK_SIZE_MAIN: init_stack_wsize = caml_init_main_stack_wsz; break;
76+
case STACK_SIZE_THREAD: init_stack_wsize = caml_init_thread_stack_wsz; break;
77+
case STACK_SIZE_FIBER: init_stack_wsize = caml_init_fiber_stack_wsz; break;
78+
default: caml_fatal_error("caml_get_init_stack_wsize: invalid context");
79+
}
7680
#else
77-
(void) thread_stack_wsz;
78-
uintnat init_stack_wsize = Wsize_bsize(Stack_init_bsize);
81+
switch(context) {
82+
case STACK_SIZE_MAIN:
83+
case STACK_SIZE_THREAD: init_stack_wsize = Wsize_bsize(Stack_init_bsize); break;
84+
case STACK_SIZE_FIBER: init_stack_wsize = Wsize_bsize(Stack_threshold * 2); break;
85+
default: caml_fatal_error("caml_get_init_stack_wsize: invalid context");
86+
}
7987
#endif
80-
uintnat stack_wsize;
8188

89+
uintnat stack_wsize = 0;
8290
if (init_stack_wsize < caml_max_stack_wsize)
8391
stack_wsize = init_stack_wsize;
8492
else
@@ -171,8 +179,7 @@ Caml_inline struct stack_info* alloc_for_stack (mlsize_t wosize)
171179
+ sizeof(value) * wosize, stack_alignment);
172180

173181
return si;
174-
#else
175-
#if defined(NATIVE_CODE) && !defined(STACK_CHECKS_ENABLED)
182+
#elif defined(STACK_GUARD_PAGES)
176183
/* (We use the following strategy only in native code, because bytecode
177184
* has its own way of dealing with stack checks.)
178185
*
@@ -225,14 +232,14 @@ Caml_inline struct stack_info* alloc_for_stack (mlsize_t wosize)
225232
// mmap is always expected to return a page-aligned value.
226233
CAMLassert((uintnat)stack % page_size == 0);
227234

228-
if (mprotect(Protected_stack_page(stack, page_size), page_size, PROT_NONE)) {
235+
if (mprotect(Protected_stack_page(stack), page_size, PROT_NONE)) {
229236
caml_mem_unmap(stack, len);
230237
return NULL;
231238
}
232239

233240
// Assert that the guard page does not impinge on the actual stack area.
234241
CAMLassert((char*) stack + len - (trailer_size + Bsize_wsize(wosize))
235-
>= Protected_stack_page(stack, page_size) + page_size);
242+
>= Protected_stack_page(stack) + page_size);
236243

237244
stack->size = len;
238245
stack->handler = (struct stack_handler*)((char*)stack + len - trailer_size);
@@ -251,8 +258,7 @@ Caml_inline struct stack_info* alloc_for_stack (mlsize_t wosize)
251258
round_up_p2((uintnat)stack + sizeof(struct stack_info) +
252259
sizeof(value) * wosize, stack_alignment);
253260
return stack;
254-
#endif /* NATIVE_CODE */
255-
#endif /* USE_MMAP_MAP_STACK */
261+
#endif /* USE_MMAP_MAP_STACK, STACK_GUARD_PAGES */
256262
}
257263

258264
/* Returns the index into the [Caml_state->stack_cache] array if this size is
@@ -614,12 +620,14 @@ void caml_maybe_expand_stack (void)
614620
(value*)stk->sp - Stack_base(stk);
615621
uintnat stack_needed =
616622
Stack_threshold / sizeof(value)
617-
+ 10 /* for words pushed by caml_start_program */;
618-
/* XXX does this "8" need updating? Provisionally changed to 10 */
623+
/* for words pushed by caml_start_program */
624+
+ 8 + Stack_padding_word;
619625

620-
if (stack_available < stack_needed)
621-
if (!caml_try_realloc_stack (stack_needed))
626+
if (stack_available < stack_needed) {
627+
if (!caml_try_realloc_stack (stack_needed)) {
622628
caml_raise_stack_overflow();
629+
}
630+
}
623631

624632
if (Caml_state->gc_regs_buckets == NULL) {
625633
/* Ensure there is at least one gc_regs bucket available before
@@ -793,9 +801,9 @@ void caml_rewrite_exception_stack(struct stack_info *old_stack,
793801

794802
int caml_try_realloc_stack(asize_t required_space)
795803
{
796-
#if defined(NATIVE_CODE) && !defined(STACK_CHECKS_ENABLED)
804+
#if defined(USE_MMAP_MAP_STACK) || defined(STACK_GUARD_PAGES)
797805
(void) required_space;
798-
abort();
806+
return 0;
799807
#else
800808
struct stack_info *old_stack, *new_stack;
801809
asize_t wsize;
@@ -944,12 +952,10 @@ void caml_free_stack (struct stack_info* stack)
944952
#endif
945953
#ifdef USE_MMAP_MAP_STACK
946954
munmap(stack, stack->size);
947-
#else
948-
#if defined(NATIVE_CODE) && !defined(STACK_CHECKS_ENABLED)
955+
#elif defined(STACK_GUARD_PAGES)
949956
caml_mem_unmap(stack, stack->size);
950957
#else
951958
caml_stat_free(stack);
952-
#endif
953959
#endif
954960
}
955961
}

runtime/gc_ctrl.c

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -373,13 +373,7 @@ void caml_init_gc (void)
373373
caml_norm_minor_heap_size(caml_params->init_minor_heap_wsz);
374374

375375
caml_max_stack_wsize = caml_params->init_max_stack_wsz;
376-
#if defined(NATIVE_CODE) && !defined(STACK_CHECKS_ENABLED)
377-
// CR mslater: initial fiber stack size should be configurable, and have a smaller
378-
// default than the main stack / thread stacks
379-
caml_fiber_wsz = caml_get_init_stack_wsize(-1);
380-
#else
381-
caml_fiber_wsz = (Stack_threshold * 2) / sizeof(value);
382-
#endif
376+
caml_fiber_wsz = caml_get_init_stack_wsize(STACK_SIZE_FIBER);
383377
caml_percent_free = norm_pfree (caml_params->init_percent_free);
384378
caml_max_percent_free = norm_pmax (caml_params->init_max_percent_free);
385379
CAML_GC_MESSAGE(STACKS, "Initial stack limit: %"
@@ -445,6 +439,7 @@ static struct gc_tweak gc_tweaks[] = {
445439
{ "pool_min_chunk_size", &caml_pool_min_chunk_bsz, 0 },
446440
{ "main_stack_size", &caml_init_main_stack_wsz, 0 },
447441
{ "thread_stack_size", &caml_init_thread_stack_wsz, 0 },
442+
{ "fiber_stack_size", &caml_init_fiber_stack_wsz, 0 },
448443
{ "percent_sweep_per_mark", &caml_percent_sweep_per_mark, 0 },
449444
{ "gc_pacing_policy", &caml_gc_pacing_policy, 0 },
450445
{ "gc_overhead_adjustment", &caml_gc_overhead_adjustment, 0 },

runtime/signals_nat.c

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ void caml_garbage_collection(void)
9090
}
9191
}
9292

93-
#if defined(NATIVE_CODE) && !defined(STACK_CHECKS_ENABLED)
93+
#ifdef STACK_GUARD_PAGES
9494

9595
#if !defined(POSIX_SIGNALS)
9696
#error "stack checks cannot be disabled if POSIX signals are not available"
@@ -110,9 +110,8 @@ DECLARE_SIGNAL_HANDLER(segv_handler)
110110
struct sigaction act;
111111
struct stack_info *block = Caml_state->current_stack;
112112
char* fault_addr = info->si_addr;
113-
int page_size = getpagesize();
114-
char* protected_low = Protected_stack_page(block, page_size);
115-
char* protected_high = protected_low + page_size;
113+
char* protected_low = Protected_stack_page(block);
114+
char* protected_high = protected_low + caml_plat_pagesize;
116115
if ((fault_addr >= protected_low) && (fault_addr < protected_high)) {
117116
context->uc_mcontext.gregs[REG_RIP]= (greg_t) &caml_raise_stack_overflow_nat;
118117
} else {

runtime/startup_aux.c

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@
3535
#include "caml/signals.h"
3636
#include "caml/gc_ctrl.h"
3737
#include "caml/fiber.h"
38+
#include "caml/platform.h"
3839

40+
#include <pthread.h>
3941
#include <sys/resource.h>
4042

4143
#ifdef _WIN32
@@ -47,12 +49,27 @@ extern void caml_win32_unregister_overflow_detection (void);
4749
static struct caml_params params;
4850
const struct caml_params* const caml_params = &params;
4951

52+
static size_t get_pthreads_stack_size_in_bytes(void)
53+
{
54+
pthread_attr_t attr;
55+
size_t res =
56+
// default value, retrieved from a recent system (May 2024)
57+
8 * 1024 * 1024;
58+
if (pthread_attr_init(&attr) == 0) {
59+
pthread_attr_getstacksize(&attr, &res);
60+
pthread_attr_destroy(&attr);
61+
}
62+
return res;
63+
}
64+
5065
static void init_startup_params(void)
5166
{
5267
#ifndef NATIVE_CODE
5368
char_os * cds_file;
5469
#endif
5570

71+
// Initial stack sizes only apply in native code with stack checks disabled.
72+
5673
struct rlimit rlimit;
5774
if (getrlimit(RLIMIT_STACK, &rlimit)) {
5875
// default value, retrieved from a recent system (May 2024)
@@ -68,6 +85,9 @@ static void init_startup_params(void)
6885
caml_init_main_stack_wsz = Max_stack_def;
6986
}
7087

88+
caml_init_thread_stack_wsz = Wsize_bsize(get_pthreads_stack_size_in_bytes());
89+
caml_init_fiber_stack_wsz = caml_init_thread_stack_wsz;
90+
7191
params.init_percent_free = Percent_free_def;
7292
params.init_max_percent_free = Max_percent_free_def;
7393
params.init_minor_heap_wsz = Minor_heap_def;

runtime/startup_byt.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -458,6 +458,8 @@ CAMLexport void caml_main(char_os **argv)
458458
char_os * shared_lib_path, * shared_libs;
459459
char_os * exe_name, * proc_self_exe;
460460

461+
caml_init_os_params();
462+
461463
/* Determine options */
462464
caml_parse_ocamlrunparam();
463465

@@ -476,7 +478,6 @@ CAMLexport void caml_main(char_os **argv)
476478
caml_install_invalid_parameter_handler();
477479
#endif
478480
caml_init_custom_operations();
479-
caml_init_os_params();
480481
caml_ext_table_init(&caml_shared_libs_path, 8);
481482

482483
/* Determine position of bytecode file */

runtime/startup_nat.c

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,8 @@ value caml_startup_common(char_os **argv, int pooling)
101101
char_os * exe_name, * proc_self_exe;
102102
value res;
103103

104+
caml_init_os_params();
105+
104106
/* Determine options */
105107
caml_parse_ocamlrunparam();
106108

@@ -120,7 +122,6 @@ value caml_startup_common(char_os **argv, int pooling)
120122
caml_install_invalid_parameter_handler();
121123
#endif
122124
caml_init_custom_operations();
123-
caml_init_os_params();
124125
caml_init_gc ();
125126

126127
/* runtime_events's init can cause a stop-the-world pause, so it must be done

0 commit comments

Comments
 (0)