namespace Sitecore.Support.Pipelines.Loader
{
using System.Text;
using System.Threading;
using System.Web.Hosting;
using System.Xml;
using Sitecore.Abstractions;
using Sitecore.Diagnostics;
using Sitecore.Pipelines;
using Sitecore.Reflection;
using Sitecore.Xml;
///
/// Configures thread limits for .
/// Thread pool limits should be configured in machine config, however an access to machine config is not provided in cloud, thereby API-based approach must be taken.
///
[UsedImplicitly]
public class ConfigureThreadPool : IInitializable
{
#region fields
///
/// The log to output running thread pool configuration in 'audit' level.
///
[NotNull]
private readonly BaseLog Log;
///
/// Gets or sets the maximum worker threads.
///
///
/// The maximum worker threads.
///
protected int MaxWorkerThreadsPerCore;
///
/// Gets or sets the maximum completion port threads.
///
///
/// The maximum completion port threads.
///
protected int MaxCompletionPortThreadsPerCore;
///
/// Gets or sets the minimum worker threads.
///
///
/// The minimum worker threads.
///
protected int MinWorkerThreadsPerCore;
///
/// Gets or sets the minimum completion port threads.
///
///
/// The minimum completion port threads.
///
protected int MinCompletionPortThreadsPerCore;
#endregion
///
/// Initializes a new instance of the class.
///
/// The log to output running thread pool configuration in 'audit' level.
public ConfigureThreadPool([NotNull]BaseLog log)
{
Assert.ArgumentNotNull(log, nameof(log));
this.Log = log;
}
#region Properties
///
/// Gets a value indicating whether the needs calling code to assign properties even after calls.
///
///
/// true if the assigns the properties; otherwise, false.
///
public virtual bool AssignProperties => false;
///
/// Gets the processor count.
///
///
/// The processor count.
///
public virtual int ProcessorCount => System.Environment.ProcessorCount;
///
/// Gets or sets the maximum concurrent requests per CPU.
///
///
/// The maximum concurrent requests allowed to be processed by one CPU at a time.
///
public int MaxConcurrentRequestsPerCPU { get; protected set; }
///
/// Gets the maximum worker threads.
///
///
/// The maximum worker threads.
///
public int MaxWorkerThreads => this.MaxWorkerThreadsPerCore * this.ProcessorCount;
///
/// Gets the maximum completion port threads.
///
///
/// The maximum completion port threads.
///
public int MaxCompletionPortThreads => this.MaxCompletionPortThreadsPerCore * this.ProcessorCount;
///
/// Gets the minimum worker threads.
///
///
/// The minimum worker threads.
///
public int MinWorkerThreads => this.MinWorkerThreadsPerCore * this.ProcessorCount;
///
/// Gets the minimum completion port threads.
///
///
/// The minimum completion port threads.
///
public int MinCompletionPortThreads => this.MinCompletionPortThreadsPerCore * this.ProcessorCount;
///
/// Gets a value indicating whether this code is executed in hosted environment (IIS).
///
///
/// true if this instance is executed in hosting environment (f.e. IIS); otherwise, false (f.e. in tests).
///
protected virtual bool IsHostedEnvironment => HostingEnvironment.IsHosted;
#endregion
///
/// Configures thread limits for based on configuration values.
///
/// The arguments.
public virtual void Process(PipelineArgs args)
{
if (!this.IsHostedEnvironment)
{
this.Log.Debug("Running outside IIS scope, skipping processor execution");
return;
}
this.LogThreadPoolStatistics(args, "Default thread pool size:");
if (this.MaxWorkerThreads != 0 && this.MaxCompletionPortThreads != 0)
{
this.SetMaxThreadPoolLimit(this.MaxWorkerThreads, this.MaxCompletionPortThreads, args);
}
if (this.MinWorkerThreads != 0 && this.MinCompletionPortThreads != 0)
{
this.SetMinThreadPoolLimit(this.MinWorkerThreads, this.MinCompletionPortThreads, args);
}
if (this.MaxConcurrentRequestsPerCPU != 0)
{
HostingEnvironment.MaxConcurrentRequestsPerCPU = this.MaxConcurrentRequestsPerCPU;
}
this.LogThreadPoolStatistics(args, "New thread pool size:");
}
///
/// Logs the statistics.
///
/// The arguments.
/// The header for a message to begin with.
public virtual void LogThreadPoolStatistics(PipelineArgs args, [NotNull] string header)
{
Assert.ArgumentNotNull(header, "header");
int minWorkerThreads;
int minCompletionPortThreads;
int maxWorkerThreads;
int maxCompletionPortThreads;
this.GetThreadStatistics(out minWorkerThreads, out minCompletionPortThreads, out maxWorkerThreads, out maxCompletionPortThreads);
var message = new StringBuilder()
.AppendLine(header)
.AppendLine("Processor count:" + this.ProcessorCount)
.AppendLine("minWorkerThreadsPerCore count:" + minWorkerThreads)
.AppendLine("minCompletionPortThreadsPerCore count:" + minCompletionPortThreads)
.AppendLine("maxWorkerThreadsPerCore count:" + maxWorkerThreads)
.AppendLine("maxCompletionPortThreadsPerCore count:" + maxCompletionPortThreads)
.AppendLine("MaxConcurrentRequestsPerCPU count:" + this.MaxConcurrentRequestsPerCPU)
.ToString();
this.Log.Audit(message, this);
}
///
/// Gets the the statistics.
///
/// The minimum worker threads.
/// The minimum completion port threads.
/// The maximum worker threads.
/// The maximum completion port threads.
public virtual void GetThreadStatistics(out int minWorkerThreads, out int minCompletionPortThreads, out int maxWorkerThreads, out int maxCompletionPortThreads)
{
ThreadPool.GetMaxThreads(out maxWorkerThreads, out maxCompletionPortThreads);
ThreadPool.GetMinThreads(out minWorkerThreads, out minCompletionPortThreads);
}
///
/// Sets the minimum thread pool limit.
///
/// The minimum worker threads.
/// The minimum completion port threads.
/// The arguments.
public virtual void SetMinThreadPoolLimit(int minWorkerThreads, int minCompletionPortThreads, PipelineArgs args)
{
ThreadPool.SetMinThreads(minWorkerThreads, minCompletionPortThreads);
}
///
/// Sets the maximum thread pool limit.
///
/// The maximum worker threads.
/// The maximum completion port threads.
/// The arguments.
public virtual void SetMaxThreadPoolLimit(int maxWorkerThreads, int maxCompletionPortThreads, PipelineArgs args)
{
ThreadPool.SetMaxThreads(maxWorkerThreads, maxCompletionPortThreads);
}
///
/// Initializes the object from the specified config node.
///
/// The config node.
public virtual void Initialize([CanBeNull]XmlNode configNode)
{
if (configNode == null)
{
return;
}
var attribute = XmlUtil.FindChildNode("MaxWorkerThreadsPerCore", configNode, false);
if (attribute != null)
{
int.TryParse(attribute.InnerText, out this.MaxWorkerThreadsPerCore);
}
attribute = XmlUtil.FindChildNode("MaxCompletionPortThreadsPerCore", configNode, false);
if (attribute != null)
{
int.TryParse(attribute.InnerText, out this.MaxCompletionPortThreadsPerCore);
}
attribute = XmlUtil.FindChildNode("MinWorkerThreadsPerCore", configNode, false);
if (attribute != null)
{
int.TryParse(attribute.InnerText, out this.MinWorkerThreadsPerCore);
}
attribute = XmlUtil.FindChildNode("MinCompletionPortThreadsPerCore", configNode, false);
if (attribute != null)
{
int.TryParse(attribute.InnerText, out this.MinCompletionPortThreadsPerCore);
}
attribute = XmlUtil.FindChildNode("MaxConcurrentRequestsPerCPU", configNode, false);
if (attribute != null)
{
int maxConcurrentRequestsPerCPU;
int.TryParse(attribute.InnerText, out maxConcurrentRequestsPerCPU);
this.MaxConcurrentRequestsPerCPU = maxConcurrentRequestsPerCPU;
}
}
}
}