From 0dd5dcd0de7889cc7f49bb7e93b4f5c9b73ec3dc Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Mon, 23 Feb 2026 10:01:53 +0100 Subject: [PATCH 1/5] mono-threads-posix: use 8MB stack by default on s390x/ppc64le. Enable config through DOTNET_Thread_DefaultStackSize envvar. --- src/mono/CMakeLists.txt | 10 ++++++++ src/mono/cmake/config.h.in | 3 +++ src/mono/mono/utils/mono-threads-posix.c | 30 +++++++++++++++++++++--- 3 files changed, 40 insertions(+), 3 deletions(-) diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index 4a58182aa9eae6..2966e0206d02c6 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -496,6 +496,16 @@ else() message(FATAL_ERROR "TARGET_ARCH='${TARGET_ARCH}' not supported.") endif() +# Default stack size: 2MB for 64-bit, 1MB for 32-bit. +# Use large (8MB) stack size for s390x/ppc64le due to limited tail call optimization support. +if(TARGET_S390X OR TARGET_POWERPC64) + set(MONO_DEFAULT_STACKSIZE 8388608) +elseif(TARGET_SIZEOF_VOID_P EQUAL 4) + set(MONO_DEFAULT_STACKSIZE 1048576) +else() + set(MONO_DEFAULT_STACKSIZE 2097152) +endif() + # arm64 MacCatalyst runtime host or AOT target is more like Apple mobile targets than x64 if ((HOST_MACCAT AND HOST_ARM64) OR (TARGET_MACCAT AND TARGET_ARM64)) set(TARGET_APPLE_MOBILE 1) diff --git a/src/mono/cmake/config.h.in b/src/mono/cmake/config.h.in index 4fe413349369af..c887a617527d68 100644 --- a/src/mono/cmake/config.h.in +++ b/src/mono/cmake/config.h.in @@ -598,6 +598,9 @@ /* byte order of target */ #define TARGET_BYTE_ORDER @TARGET_BYTE_ORDER@ +/* default stack size for managed threads */ +#define MONO_DEFAULT_STACKSIZE @MONO_DEFAULT_STACKSIZE@ + /* wordsize of target */ #define TARGET_SIZEOF_VOID_P @TARGET_SIZEOF_VOID_P@ diff --git a/src/mono/mono/utils/mono-threads-posix.c b/src/mono/mono/utils/mono-threads-posix.c index fa18993907a032..af556b812a9bad 100644 --- a/src/mono/mono/utils/mono-threads-posix.c +++ b/src/mono/mono/utils/mono-threads-posix.c @@ -48,6 +48,30 @@ static pthread_mutex_t memory_barrier_process_wide_mutex = PTHREAD_MUTEX_INITIALIZER; static void *memory_barrier_process_wide_helper_page; +static gsize default_stacksize = 0; + +static gsize +get_default_stacksize (void) +{ + gsize stacksize = 0; + + if (default_stacksize != 0) + return default_stacksize; + + const char *value = getenv ("DOTNET_Thread_DefaultStackSize"); + if (value) { + errno = 0; + stacksize = (gsize)strtoull (value, NULL, 16); + if (errno != 0) + stacksize = 0; + } + + if (stacksize == 0) + stacksize = MONO_DEFAULT_STACKSIZE; + + default_stacksize = stacksize; + return default_stacksize; +} gboolean mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid) @@ -72,10 +96,10 @@ mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_d if (RUNNING_ON_VALGRIND) set_stack_size = 1 << 20; else - set_stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024; -#else - set_stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024; #endif + { + set_stack_size = get_default_stacksize (); + } } #ifdef PTHREAD_STACK_MIN From 92d557629892f07ca11fecfbd5d232b0875a31b4 Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Tue, 24 Feb 2026 06:27:45 +0100 Subject: [PATCH 2/5] Support DOTNET_Thread_DefaultStackSize also on Windows. --- src/mono/CMakeLists.txt | 5 +++- src/mono/mono/metadata/threads.c | 19 +++++++++++-- src/mono/mono/utils/mono-threads-posix.c | 26 +----------------- src/mono/mono/utils/mono-threads-wasm.c | 31 ---------------------- src/mono/mono/utils/mono-threads-windows.c | 5 +--- 5 files changed, 23 insertions(+), 63 deletions(-) diff --git a/src/mono/CMakeLists.txt b/src/mono/CMakeLists.txt index 2966e0206d02c6..4f9a1b1d896c58 100644 --- a/src/mono/CMakeLists.txt +++ b/src/mono/CMakeLists.txt @@ -498,7 +498,10 @@ endif() # Default stack size: 2MB for 64-bit, 1MB for 32-bit. # Use large (8MB) stack size for s390x/ppc64le due to limited tail call optimization support. -if(TARGET_S390X OR TARGET_POWERPC64) +# On Windows, use 0 to defer to the OS/PE header default. +if(TARGET_WIN32) + set(MONO_DEFAULT_STACKSIZE 0) +elseif(TARGET_S390X OR TARGET_POWERPC64) set(MONO_DEFAULT_STACKSIZE 8388608) elseif(TARGET_SIZEOF_VOID_P EQUAL 4) set(MONO_DEFAULT_STACKSIZE 1048576) diff --git a/src/mono/mono/metadata/threads.c b/src/mono/mono/metadata/threads.c index b0794d1a9f69f7..446c84fb007b6f 100644 --- a/src/mono/mono/metadata/threads.c +++ b/src/mono/mono/metadata/threads.c @@ -204,7 +204,7 @@ static MonoThreadAttachCB mono_thread_attach_cb = NULL; static MonoThreadCleanupFunc mono_thread_cleanup_fn = NULL; /* The default stack size for each thread */ -static guint32 default_stacksize = 0; +static guint32 default_stacksize = ~0; static void mono_free_static_data (gpointer* static_data); static void mono_init_static_data_info (StaticDataInfo *static_data); @@ -1371,7 +1371,7 @@ create_thread (MonoThread *thread, MonoInternalThread *internal, MonoThreadStart mono_coop_sem_init (&start_info->registered, 0); if (flags != MONO_THREAD_CREATE_FLAGS_SMALL_STACK) - stack_set_size = stack_size ? stack_size : default_stacksize; + stack_set_size = stack_size ? stack_size : mono_threads_get_default_stacksize(); else stack_set_size = 0; @@ -1445,6 +1445,21 @@ mono_threads_set_default_stacksize (guint32 stacksize) guint32 mono_threads_get_default_stacksize (void) { + if (default_stacksize == ~0) + { + unsigned long stacksize = 0; + + const char *value = getenv ("DOTNET_Thread_DefaultStackSize"); + if (value) { + errno = 0; + stacksize = strtoul (value, NULL, 16); + if (errno != 0 || stacksize >= UINT_MAX) + stacksize = 0; + } + + default_stacksize = (guint32)stacksize; + } + return default_stacksize; } diff --git a/src/mono/mono/utils/mono-threads-posix.c b/src/mono/mono/utils/mono-threads-posix.c index af556b812a9bad..711c2dd3b64553 100644 --- a/src/mono/mono/utils/mono-threads-posix.c +++ b/src/mono/mono/utils/mono-threads-posix.c @@ -48,30 +48,6 @@ static pthread_mutex_t memory_barrier_process_wide_mutex = PTHREAD_MUTEX_INITIALIZER; static void *memory_barrier_process_wide_helper_page; -static gsize default_stacksize = 0; - -static gsize -get_default_stacksize (void) -{ - gsize stacksize = 0; - - if (default_stacksize != 0) - return default_stacksize; - - const char *value = getenv ("DOTNET_Thread_DefaultStackSize"); - if (value) { - errno = 0; - stacksize = (gsize)strtoull (value, NULL, 16); - if (errno != 0) - stacksize = 0; - } - - if (stacksize == 0) - stacksize = MONO_DEFAULT_STACKSIZE; - - default_stacksize = stacksize; - return default_stacksize; -} gboolean mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid) @@ -98,7 +74,7 @@ mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_d else #endif { - set_stack_size = get_default_stacksize (); + set_stack_size = MONO_DEFAULT_STACKSIZE; } } diff --git a/src/mono/mono/utils/mono-threads-wasm.c b/src/mono/mono/utils/mono-threads-wasm.c index f43e46c4ffa20a..82ab43309700bd 100644 --- a/src/mono/mono/utils/mono-threads-wasm.c +++ b/src/mono/mono/utils/mono-threads-wasm.c @@ -224,37 +224,6 @@ mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_d if (res != 0) g_error ("%s: pthread_attr_init failed, error: \"%s\" (%d)", __func__, g_strerror (res), res); -#if 0 - gsize set_stack_size; - - if (stack_size) - set_stack_size = *stack_size; - else - set_stack_size = 0; - -#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE - if (set_stack_size == 0) { -#if HAVE_VALGRIND_MEMCHECK_H - if (RUNNING_ON_VALGRIND) - set_stack_size = 1 << 20; - else - set_stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024; -#else - set_stack_size = (SIZEOF_VOID_P / 4) * 1024 * 1024; -#endif - } - -#ifdef PTHREAD_STACK_MIN - if (set_stack_size < PTHREAD_STACK_MIN) - set_stack_size = PTHREAD_STACK_MIN; -#endif - - res = pthread_attr_setstacksize (&attr, set_stack_size); - if (res != 0) - g_error ("%s: pthread_attr_setstacksize failed, error: \"%s\" (%d)", __func__, g_strerror (res), res); -#endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */ -#endif - /* Actually start the thread */ res = pthread_create (&thread, &attr, (gpointer (*)(gpointer)) thread_fn, thread_data); if (res) { diff --git a/src/mono/mono/utils/mono-threads-windows.c b/src/mono/mono/utils/mono-threads-windows.c index 21997a97b624c1..f70021735ba761 100644 --- a/src/mono/mono/utils/mono-threads-windows.c +++ b/src/mono/mono/utils/mono-threads-windows.c @@ -395,15 +395,12 @@ mono_threads_suspend_get_abort_signal (void) #if defined (HOST_WIN32) -// Use default stack size on netcore. -#define MONO_WIN32_DEFAULT_NATIVE_STACK_SIZE 0 - gboolean mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_data, gsize* const stack_size, MonoNativeThreadId *tid) { HANDLE result; DWORD thread_id; - gsize set_stack_size = MONO_WIN32_DEFAULT_NATIVE_STACK_SIZE; + gsize set_stack_size = MONO_DEFAULT_STACKSIZE; if (stack_size && *stack_size) set_stack_size = *stack_size; From d4e8d7eccf5c81e8f73d32b78c50b05e4c44fc0c Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Tue, 24 Feb 2026 07:54:14 +0100 Subject: [PATCH 3/5] Fix build. --- src/mono/mono/utils/mono-threads-windows.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/utils/mono-threads-windows.c b/src/mono/mono/utils/mono-threads-windows.c index f70021735ba761..8aeb089b08340b 100644 --- a/src/mono/mono/utils/mono-threads-windows.c +++ b/src/mono/mono/utils/mono-threads-windows.c @@ -447,7 +447,7 @@ mono_native_thread_id_equals (MonoNativeThreadId id1, MonoNativeThreadId id2) gboolean mono_native_thread_create (MonoNativeThreadId *tid, gpointer func, gpointer arg) { - return CreateThread (NULL, MONO_WIN32_DEFAULT_NATIVE_STACK_SIZE, (LPTHREAD_START_ROUTINE)func, arg, 0, tid) != NULL; + return CreateThread (NULL, MONO_DEFAULT_STACKSIZE, (LPTHREAD_START_ROUTINE)func, arg, 0, tid) != NULL; } gboolean From 7c3fa342f1ac1087a5581e2e8d76be154913c9ab Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Tue, 24 Feb 2026 10:38:56 +0100 Subject: [PATCH 4/5] Update src/mono/mono/metadata/threads.c MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Alexander Köplinger --- src/mono/mono/metadata/threads.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mono/mono/metadata/threads.c b/src/mono/mono/metadata/threads.c index 446c84fb007b6f..8641175cfee2b7 100644 --- a/src/mono/mono/metadata/threads.c +++ b/src/mono/mono/metadata/threads.c @@ -1449,7 +1449,7 @@ mono_threads_get_default_stacksize (void) { unsigned long stacksize = 0; - const char *value = getenv ("DOTNET_Thread_DefaultStackSize"); + const char *value = g_getenv ("DOTNET_Thread_DefaultStackSize"); if (value) { errno = 0; stacksize = strtoul (value, NULL, 16); From 3867b51ca02f5b2c8cf3f4d5fb0984907b8d94fb Mon Sep 17 00:00:00 2001 From: Tom Deseyn Date: Tue, 24 Feb 2026 10:41:35 +0100 Subject: [PATCH 5/5] Sync mono-threads-wasm with mono-threads-posix. --- src/mono/mono/utils/mono-threads-wasm.c | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/src/mono/mono/utils/mono-threads-wasm.c b/src/mono/mono/utils/mono-threads-wasm.c index 82ab43309700bd..88ae9557381000 100644 --- a/src/mono/mono/utils/mono-threads-wasm.c +++ b/src/mono/mono/utils/mono-threads-wasm.c @@ -224,6 +224,35 @@ mono_thread_platform_create_thread (MonoThreadStart thread_fn, gpointer thread_d if (res != 0) g_error ("%s: pthread_attr_init failed, error: \"%s\" (%d)", __func__, g_strerror (res), res); +#if 0 + if (stack_size) + set_stack_size = *stack_size; + else + set_stack_size = 0; + +#ifdef HAVE_PTHREAD_ATTR_SETSTACKSIZE + if (set_stack_size == 0) { +#if HAVE_VALGRIND_MEMCHECK_H + if (RUNNING_ON_VALGRIND) + set_stack_size = 1 << 20; + else +#endif + { + set_stack_size = MONO_DEFAULT_STACKSIZE; + } + } + +#ifdef PTHREAD_STACK_MIN + if (set_stack_size < PTHREAD_STACK_MIN) + set_stack_size = PTHREAD_STACK_MIN; +#endif + + res = pthread_attr_setstacksize (&attr, set_stack_size); + if (res != 0) + g_error ("%s: pthread_attr_setstacksize failed, error: \"%s\" (%d)", __func__, g_strerror (res), res); +#endif /* HAVE_PTHREAD_ATTR_SETSTACKSIZE */ +#endif + /* Actually start the thread */ res = pthread_create (&thread, &attr, (gpointer (*)(gpointer)) thread_fn, thread_data); if (res) {