Name | Description | Type | Class | Default Value | Flags
-----|-------------|------|-------|---------------|-------
+`DefaultStackSize` | Stack size to use for new VM threads when thread is created with default stack size (dwStackSize == 0). | `DWORD` | `INTERNAL` | `0` |
`Thread_DeadThreadCountThresholdForGCTrigger` | In the heuristics to clean up dead threads, this threshold must be reached before triggering a GC will be considered. Set to 0 to disable triggering a GC based on dead threads. | `DWORD` | `INTERNAL` | `75` |
`Thread_DeadThreadGCTriggerPeriodMilliseconds` | In the heuristics to clean up dead threads, this much time must have elapsed since the previous max-generation GC before triggering another GC will be considered | `DWORD` | `INTERNAL` | `1000 * 60 * 30` |
///
/// Thread (miscellaneous)
///
+RETAIL_CONFIG_DWORD_INFO(INTERNAL_DefaultStackSize, W("DefaultStackSize"), 0, "Stack size to use for new VM threads when thread is created with default stack size (dwStackSize == 0).")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_Thread_DeadThreadCountThresholdForGCTrigger, W("Thread_DeadThreadCountThresholdForGCTrigger"), 75, "In the heuristics to clean up dead threads, this threshold must be reached before triggering a GC will be considered. Set to 0 to disable triggering a GC based on dead threads.")
RETAIL_CONFIG_DWORD_INFO(INTERNAL_Thread_DeadThreadGCTriggerPeriodMilliseconds, W("Thread_DeadThreadGCTriggerPeriodMilliseconds"), 1000 * 60 * 30, "In the heuristics to clean up dead threads, this much time must have elapsed since the previous max-generation GC before triggering another GC will be considered")
pwzAppNiPaths = pPropertyValues[i];
}
else
+ if (wcscmp(pPropertyNames[i], W("DEFAULT_STACK_SIZE")) == 0)
+ {
+ extern void ParseDefaultStackSize(LPCWSTR value);
+ ParseDefaultStackSize(pPropertyValues[i]);
+ }
+ else
if (wcscmp(pPropertyNames[i], W("USE_ENTRYPOINT_FILTER")) == 0)
{
extern void ParseUseEntryPointFilter(LPCWSTR value);
}
+// Represent the value of DEFAULT_STACK_SIZE as passed in the property bag to the host during construction
+static unsigned long s_defaultStackSizeProperty = 0;
+
+void ParseDefaultStackSize(LPCWSTR valueStr)
+{
+ if (valueStr)
+ {
+ LPWSTR end;
+ errno = 0;
+ unsigned long value = wcstoul(valueStr, &end, 16); // Base 16 without a prefix
+
+ if ((errno == ERANGE) // Parsed value doesn't fit in an unsigned long
+ || (valueStr == end) // No characters parsed
+ || (end == nullptr) // Unexpected condition (should never happen)
+ || (end[0] != 0)) // Unprocessed terminal characters
+ {
+ ThrowHR(E_INVALIDARG);
+ }
+ else
+ {
+ s_defaultStackSizeProperty = value;
+ }
+ }
+}
+
+SIZE_T GetDefaultStackSizeSetting()
+{
+ static DWORD s_defaultStackSizeEnv = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_DefaultStackSize);
+
+ uint64_t value = s_defaultStackSizeEnv ? s_defaultStackSizeEnv : s_defaultStackSizeProperty;
+
+ SIZE_T minStack = 0x10000; // 64K - Somewhat arbitrary minimum thread stack size
+ SIZE_T maxStack = 0x80000000; // 2G - Somewhat arbitrary maximum thread stack size
+
+ if ((value >= maxStack) || ((value != 0) && (value < minStack)))
+ {
+ ThrowHR(E_INVALIDARG);
+ }
+
+ return (SIZE_T) value;
+}
+
BOOL Thread::GetProcessDefaultStackSize(SIZE_T* reserveSize, SIZE_T* commitSize)
{
CONTRACTL
static BOOL fSizesGot = FALSE;
+ if (!fSizesGot)
+ {
+ SIZE_T defaultStackSizeSetting = GetDefaultStackSizeSetting();
+
+ if (defaultStackSizeSetting != 0)
+ {
+ ExeSizeOfStackReserve = defaultStackSizeSetting;
+ ExeSizeOfStackCommit = defaultStackSizeSetting;
+ fSizesGot = TRUE;
+ }
+ }
+
#ifndef FEATURE_PAL
if (!fSizesGot)
{
dwCreationFlags |= STACK_SIZE_PARAM_IS_A_RESERVATION;
+ if (sizeToCommitOrReserve == 0)
+ {
+ sizeToCommitOrReserve = GetDefaultStackSizeSetting();
+ }
+
#ifndef FEATURE_PAL // the PAL does its own adjustments as necessary
if (sizeToCommitOrReserve != 0 && sizeToCommitOrReserve <= GetOsPageSize())
{