diff --git a/src/System.Management.Automation/engine/PSConfiguration.cs b/src/System.Management.Automation/engine/PSConfiguration.cs
index 8c60d6735d8..03aa7ecf8d5 100644
--- a/src/System.Management.Automation/engine/PSConfiguration.cs
+++ b/src/System.Management.Automation/engine/PSConfiguration.cs
@@ -558,6 +558,7 @@ internal sealed class PowerShellPolicies
public ProtectedEventLogging ProtectedEventLogging { get; set; }
public Transcription Transcription { get; set; }
public UpdatableHelp UpdatableHelp { get; set; }
+ public PipelineMaxStackSize PipelineMaxStackSizeMB { get; set; }
public ConsoleSessionConfiguration ConsoleSessionConfiguration { get; set; }
}
@@ -618,6 +619,14 @@ internal sealed class ConsoleSessionConfiguration : PolicyBase
public string ConsoleSessionConfigurationName { get; set; }
}
+ ///
+ /// Setting about PipelineMaxStackSize
+ ///
+ internal sealed class PipelineMaxStackSize : PolicyBase
+ {
+ public int PipelineMaxStackSizeMB { get; set; }
+ }
+
///
/// Setting about ProtectedEventLogging
///
diff --git a/src/System.Management.Automation/engine/Utils.cs b/src/System.Management.Automation/engine/Utils.cs
index 3659d0c4cdd..9bc948e5cb7 100644
--- a/src/System.Management.Automation/engine/Utils.cs
+++ b/src/System.Management.Automation/engine/Utils.cs
@@ -507,7 +507,7 @@ internal static bool IsValidPSEditionValue(string editionValue)
T policy = null;
#if !UNIX
// On Windows, group policy settings from registry take precedence.
- // If the requested policy is not defined in registry, we query the configuration file.
+ // If the requested policy is not defined in registry, we query the configuration file.
policy = GetPolicySettingFromGPO(preferenceOrder);
if (policy != null) { return policy; }
#endif
@@ -547,6 +547,7 @@ internal static bool IsValidPSEditionValue(string editionValue)
case nameof(ScriptExecution): result = policies.ScriptExecution; break;
case nameof(ScriptBlockLogging): result = policies.ScriptBlockLogging; break;
case nameof(ModuleLogging): result = policies.ModuleLogging; break;
+ case nameof(PipelineMaxStackSize): result = policies.PipelineMaxStackSizeMB; break;
case nameof(ProtectedEventLogging): result = policies.ProtectedEventLogging; break;
case nameof(Transcription): result = policies.Transcription; break;
case nameof(UpdatableHelp): result = policies.UpdatableHelp; break;
@@ -566,6 +567,7 @@ internal static bool IsValidPSEditionValue(string editionValue)
{nameof(ScriptExecution), @"Software\Policies\Microsoft\PowerShellCore"},
{nameof(ScriptBlockLogging), @"Software\Policies\Microsoft\PowerShellCore\ScriptBlockLogging"},
{nameof(ModuleLogging), @"Software\Policies\Microsoft\PowerShellCore\ModuleLogging"},
+ {nameof(PipelineMaxStackSize), @"Software\Policies\Microsoft\PowerShellCore"},
{nameof(ProtectedEventLogging), @"Software\Policies\Microsoft\Windows\EventLog\ProtectedEventLogging"},
{nameof(Transcription), @"Software\Policies\Microsoft\PowerShellCore\Transcription"},
{nameof(UpdatableHelp), @"Software\Policies\Microsoft\PowerShellCore\UpdatableHelp"},
@@ -586,8 +588,11 @@ internal static bool IsValidPSEditionValue(string editionValue)
GroupPolicyKeys.TryGetValue(tType.Name, out string gpoKeyPath);
Diagnostics.Assert(gpoKeyPath != null, StringUtil.Format("The GPO registry key path should be pre-defined for {0}", tType.Name));
- using (RegistryKey gpoKey = rootKey.OpenSubKey(gpoKeyPath))
+ RegistryKey gpoKey = null;
+ try
{
+ gpoKey = rootKey.OpenSubKey(gpoKeyPath, writable: false);
+
// If the corresponding GPO key doesn't exist, return null
if (gpoKey == null) { return null; }
@@ -614,7 +619,7 @@ internal static bool IsValidPSEditionValue(string editionValue)
}
else if (subKeyNameSet != null && subKeyNameSet.Contains(settingName))
{
- using (RegistryKey subKey = gpoKey.OpenSubKey(settingName))
+ using (RegistryKey subKey = gpoKey.OpenSubKey(settingName, writable: false))
{
if (subKey != null) { rawRegistryValue = subKey.GetValueNames(); }
}
@@ -629,11 +634,17 @@ internal static bool IsValidPSEditionValue(string editionValue)
switch (propertyType)
{
- case var _ when propertyType == typeof(bool?):
+ case var _ when propertyType == typeof(int):
if (rawRegistryValue is int rawIntValue)
{
- if (rawIntValue == 1) { propertyValue = true; }
- else if (rawIntValue == 0) { propertyValue = false; }
+ propertyValue = rawIntValue;
+ }
+ break;
+ case var _ when propertyType == typeof(bool?):
+ if (rawRegistryValue is int rawBoolValue)
+ {
+ if (rawBoolValue == 1) { propertyValue = true; }
+ else if (rawBoolValue == 0) { propertyValue = false; }
}
break;
case var _ when propertyType == typeof(string):
@@ -669,6 +680,20 @@ internal static bool IsValidPSEditionValue(string editionValue)
// If no property is set, then we consider this policy as undefined
return isAnyPropertySet ? (T) tInstance : null;
}
+ catch
+ {
+ // In any case, if the registry key or value does not exist or we do not have permissions to read it,
+ // we continue silently to use a default value.
+ }
+ finally
+ {
+ if (gpoKey != null)
+ {
+ gpoKey.Dispose();
+ }
+ }
+
+ return null;
}
///
diff --git a/src/System.Management.Automation/engine/hostifaces/LocalPipeline.cs b/src/System.Management.Automation/engine/hostifaces/LocalPipeline.cs
index 2cac96bb84f..c5af6b2e2a4 100644
--- a/src/System.Management.Automation/engine/hostifaces/LocalPipeline.cs
+++ b/src/System.Management.Automation/engine/hostifaces/LocalPipeline.cs
@@ -7,6 +7,7 @@
using System.Globalization;
using System.Threading;
using Microsoft.Win32;
+using System.Management.Automation.Configuration;
using System.Management.Automation.Internal;
using System.Management.Automation.Internal.Host;
using System.Management.Automation.Tracing;
@@ -247,54 +248,36 @@ private void SetupInvokeThread(Thread invokeThread, bool changeName)
}
}
-#if !CORECLR
///
- /// Stack Reserve setting for pipeline threads
+ /// Set stack size for local pipeline threads.
+ /// Default is 10MB.
+ /// Default 10MB may be too large if we use many local pipelines for paralleling.PipelineMaxStackSizeMB
///
- internal static int MaxStack
- {
- get
- {
- int i = ReadRegistryInt("PipelineMaxStackSizeMB", 10);
- if (i < 10)
- i = 10; // minimum 10MB
- else if (i > 100)
- i = 100; // maximum 100MB
- return i * 1000000;
- }
- }
+ private const int DefaultPipelineStackSize = 10;
+ private const int MaxPipelineStackSize = 100;
- internal static int ReadRegistryInt(string policyValueName, int defaultValue)
+ internal static int GetPipelineStackSizeOption()
{
- RegistryKey key;
- try
- {
- key = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\PowerShell\\1\\ShellIds");
- }
- catch (System.Security.SecurityException)
- {
- return defaultValue;
- }
- if (null == key)
- return defaultValue;
+ int stackSize = DefaultPipelineStackSize;
- object temp;
- try
+ var pipelineMaxStackSizeOption = Utils.GetPolicySetting(Utils.SystemWideThenCurrentUserConfig);
+
+ if (pipelineMaxStackSizeOption != null)
{
- temp = key.GetValue(policyValueName);
+ stackSize = pipelineMaxStackSizeOption.PipelineMaxStackSizeMB;
}
- catch (System.Security.SecurityException)
+
+ if (stackSize < DefaultPipelineStackSize)
{
- return defaultValue;
+ stackSize = DefaultPipelineStackSize;
}
- if (!(temp is int))
+ else if (stackSize > MaxPipelineStackSize)
{
- return defaultValue;
+ stackSize = MaxPipelineStackSize;
}
- int i = (int)temp;
- return i;
+
+ return stackSize * 1_000_000;
}
-#endif
///
/// Helper method for asynchronous invoke
@@ -1225,17 +1208,17 @@ protected override
}
///
- /// Helper class that holds the thread used to execute pipelines when CreateThreadOptions.ReuseThread is used
+ /// Helper class that holds the thread used to execute pipelines when CreateThreadOptions.ReuseThread is used.
///
internal class PipelineThread : IDisposable
{
///
- /// Creates the worker thread and waits for it to be ready
+ /// Creates the worker thread and waits for it to be ready.
///
#if CORECLR
internal PipelineThread()
{
- _worker = new Thread(WorkerProc);
+ _worker = new Thread(WorkerProc, LocalPipeline.GetPipelineStackSizeOption());
_workItem = null;
_workItemReady = new AutoResetEvent(false);
_closed = false;
@@ -1243,7 +1226,7 @@ internal PipelineThread()
#else
internal PipelineThread(ApartmentState apartmentState)
{
- _worker = new Thread(WorkerProc, LocalPipeline.MaxStack);
+ _worker = new Thread(WorkerProc, LocalPipeline.GetPipelineStackSizeOption());
_workItem = null;
_workItemReady = new AutoResetEvent(false);
_closed = false;