|
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.ServiceModel.Dispatcher
{
using System;
using System.Runtime;
using System.ServiceModel;
using System.ServiceModel.Diagnostics;
using System.ServiceModel.Diagnostics.Application;
interface ISessionThrottleNotification
{
void ThrottleAcquired();
}
public sealed class ServiceThrottle
{
internal const int DefaultMaxConcurrentCalls = 16;
internal const int DefaultMaxConcurrentSessions = 100;
internal static int DefaultMaxConcurrentCallsCpuCount = DefaultMaxConcurrentCalls * OSEnvironmentHelper.ProcessorCount;
internal static int DefaultMaxConcurrentSessionsCpuCount = DefaultMaxConcurrentSessions * OSEnvironmentHelper.ProcessorCount;
FlowThrottle calls;
FlowThrottle sessions;
QuotaThrottle dynamic;
FlowThrottle instanceContexts;
ServiceHostBase host;
ServicePerformanceCountersBase servicePerformanceCounters;
bool isActive;
object thisLock = new object();
internal ServiceThrottle(ServiceHostBase host)
{
if (!((host != null)))
{
Fx.Assert("ServiceThrottle.ServiceThrottle: (host != null)");
throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("host");
}
this.host = host;
this.MaxConcurrentCalls = ServiceThrottle.DefaultMaxConcurrentCallsCpuCount;
this.MaxConcurrentSessions = ServiceThrottle.DefaultMaxConcurrentSessionsCpuCount;
this.isActive = true;
}
internal FlowThrottle Calls
{
get
{
if (this.calls == null)
{
lock (this.ThisLock)
{
if (this.calls == null)
{
FlowThrottle callsFt = new FlowThrottle(this.GotCall, ServiceThrottle.DefaultMaxConcurrentCallsCpuCount,
ServiceThrottle.MaxConcurrentCallsPropertyName, ServiceThrottle.MaxConcurrentCallsConfigName);
callsFt.SetRatio(this.RatioCallsToken);
this.calls = callsFt;
}
}
}
return this.calls;
}
}
internal FlowThrottle Sessions
{
get
{
if (this.sessions == null)
{
lock (this.ThisLock)
{
if (this.sessions == null)
{
FlowThrottle sessionsFt = new FlowThrottle(this.GotSession, ServiceThrottle.DefaultMaxConcurrentSessionsCpuCount,
ServiceThrottle.MaxConcurrentSessionsPropertyName, ServiceThrottle.MaxConcurrentSessionsConfigName);
sessionsFt.SetRatio(this.RatioSessionsToken);
this.sessions = sessionsFt;
}
}
}
return this.sessions;
}
}
internal QuotaThrottle Dynamic
{
get
{
if (this.dynamic == null)
{
lock (this.ThisLock)
{
if (this.dynamic == null)
{
QuotaThrottle dynamicQt = new QuotaThrottle(this.GotDynamic, new object());
dynamicQt.Owner = "ServiceHost";
this.dynamic = dynamicQt;
}
}
}
this.UpdateIsActive();
return this.dynamic;
}
}
internal int ManualFlowControlLimit
{
get { return this.Dynamic.Limit; }
set { this.Dynamic.SetLimit(value); }
}
const string MaxConcurrentCallsPropertyName = "MaxConcurrentCalls";
const string MaxConcurrentCallsConfigName = "maxConcurrentCalls";
public int MaxConcurrentCalls
{
get { return this.Calls.Capacity; }
set
{
this.ThrowIfClosedOrOpened(MaxConcurrentCallsPropertyName);
this.Calls.Capacity = value;
this.UpdateIsActive();
if (null != this.servicePerformanceCounters)
{
this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.CallsPercentMaxCallsBase, this.Calls.Capacity);
}
}
}
const string MaxConcurrentSessionsPropertyName = "MaxConcurrentSessions";
const string MaxConcurrentSessionsConfigName = "maxConcurrentSessions";
public int MaxConcurrentSessions
{
get { return this.Sessions.Capacity; }
set
{
this.ThrowIfClosedOrOpened(MaxConcurrentSessionsPropertyName);
this.Sessions.Capacity = value;
this.UpdateIsActive();
if (null != this.servicePerformanceCounters)
{
this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.SessionsPercentMaxSessionsBase, this.Sessions.Capacity);
}
}
}
const string MaxConcurrentInstancesPropertyName = "MaxConcurrentInstances";
const string MaxConcurrentInstancesConfigName = "maxConcurrentInstances";
public int MaxConcurrentInstances
{
get { return this.InstanceContexts.Capacity; }
set
{
this.ThrowIfClosedOrOpened(MaxConcurrentInstancesPropertyName);
this.InstanceContexts.Capacity = value;
this.UpdateIsActive();
if (null != this.servicePerformanceCounters)
{
this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.InstancesPercentMaxInstancesBase, this.InstanceContexts.Capacity);
}
}
}
internal FlowThrottle InstanceContexts
{
get
{
if (this.instanceContexts == null)
{
lock (this.ThisLock)
{
if (this.instanceContexts == null)
{
FlowThrottle instanceContextsFt = new FlowThrottle(this.GotInstanceContext, Int32.MaxValue,
ServiceThrottle.MaxConcurrentInstancesPropertyName, ServiceThrottle.MaxConcurrentInstancesConfigName);
instanceContextsFt.SetRatio(this.RatioInstancesToken);
if (this.servicePerformanceCounters != null)
{
InitializeInstancePerfCounterSettings(instanceContextsFt);
}
this.instanceContexts = instanceContextsFt;
}
}
}
return this.instanceContexts;
}
}
internal bool IsActive
{
get { return this.isActive; }
}
internal object ThisLock
{
get { return this.thisLock; }
}
internal void SetServicePerformanceCounters(ServicePerformanceCountersBase counters)
{
this.servicePerformanceCounters = counters;
//instance throttle is created through the behavior, set the perf counter callbacks if initialized
if (this.instanceContexts != null)
{
InitializeInstancePerfCounterSettings(this.instanceContexts);
}
//this.calls and this.sessions throttles are created by the constructor. Set the perf counter callbacks
InitializeCallsPerfCounterSettings();
InitializeSessionsPerfCounterSettings();
}
void InitializeInstancePerfCounterSettings(FlowThrottle instanceContextsFt)
{
Fx.Assert(instanceContextsFt != null, "Expect instanceContext to be initialized");
Fx.Assert(this.servicePerformanceCounters != null, "expect servicePerformanceCounters to be set");
instanceContextsFt.SetAcquired(this.AcquiredInstancesToken);
instanceContextsFt.SetReleased(this.ReleasedInstancesToken);
instanceContextsFt.SetRatio(this.RatioInstancesToken);
this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.InstancesPercentMaxInstancesBase, instanceContextsFt.Capacity);
}
void InitializeCallsPerfCounterSettings()
{
Fx.Assert(this.calls != null, "Expect calls to be initialized");
Fx.Assert(this.servicePerformanceCounters != null, "expect servicePerformanceCounters to be set");
this.calls.SetAcquired(this.AcquiredCallsToken);
this.calls.SetReleased(this.ReleasedCallsToken);
this.calls.SetRatio(this.RatioCallsToken);
this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.CallsPercentMaxCallsBase, this.calls.Capacity);
}
void InitializeSessionsPerfCounterSettings()
{
Fx.Assert(this.sessions != null, "Expect sessions to be initialized");
Fx.Assert(this.servicePerformanceCounters != null, "expect servicePerformanceCounters to be set");
this.sessions.SetAcquired(this.AcquiredSessionsToken);
this.sessions.SetReleased(this.ReleasedSessionsToken);
this.sessions.SetRatio(this.RatioSessionsToken);
this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.SessionsPercentMaxSessionsBase, this.sessions.Capacity);
}
bool PrivateAcquireCall(ChannelHandler channel)
{
return (this.calls == null) || this.calls.Acquire(channel);
}
bool PrivateAcquireSessionListenerHandler(ListenerHandler listener)
{
if ((this.sessions != null) && (listener.Channel != null) && (listener.Channel.Throttle == null))
{
listener.Channel.Throttle = this;
return this.sessions.Acquire(listener);
}
else
{
return true;
}
}
bool PrivateAcquireSession(ISessionThrottleNotification source)
{
return (this.sessions == null || this.sessions.Acquire(source));
}
bool PrivateAcquireDynamic(ChannelHandler channel)
{
return (this.dynamic == null) || this.dynamic.Acquire(channel);
}
bool PrivateAcquireInstanceContext(ChannelHandler channel)
{
if ((this.instanceContexts != null) && (channel.InstanceContext == null))
{
channel.InstanceContextServiceThrottle = this;
return this.instanceContexts.Acquire(channel);
}
else
{
return true;
}
}
internal bool AcquireCall(ChannelHandler channel)
{
lock (this.ThisLock)
{
return (this.PrivateAcquireCall(channel));
}
}
internal bool AcquireInstanceContextAndDynamic(ChannelHandler channel, bool acquireInstanceContextThrottle)
{
lock (this.ThisLock)
{
if (!acquireInstanceContextThrottle)
{
return this.PrivateAcquireDynamic(channel);
}
else
{
return (this.PrivateAcquireInstanceContext(channel) &&
this.PrivateAcquireDynamic(channel));
}
}
}
internal bool AcquireSession(ISessionThrottleNotification source)
{
lock (this.ThisLock)
{
return this.PrivateAcquireSession(source);
}
}
internal bool AcquireSession(ListenerHandler listener)
{
lock (this.ThisLock)
{
return this.PrivateAcquireSessionListenerHandler(listener);
}
}
void GotCall(object state)
{
ChannelHandler channel = (ChannelHandler)state;
lock (this.ThisLock)
{
channel.ThrottleAcquiredForCall();
}
}
void GotDynamic(object state)
{
((ChannelHandler)state).ThrottleAcquired();
}
void GotInstanceContext(object state)
{
ChannelHandler channel = (ChannelHandler)state;
lock (this.ThisLock)
{
if (this.PrivateAcquireDynamic(channel))
channel.ThrottleAcquired();
}
}
void GotSession(object state)
{
((ISessionThrottleNotification)state).ThrottleAcquired();
}
internal void DeactivateChannel()
{
if (this.isActive)
{
if (this.sessions != null)
this.sessions.Release();
}
}
internal void DeactivateCall()
{
if (this.isActive)
{
if (this.calls != null)
this.calls.Release();
}
}
internal void DeactivateInstanceContext()
{
if (this.isActive)
{
if (this.instanceContexts != null)
{
this.instanceContexts.Release();
}
}
}
internal int IncrementManualFlowControlLimit(int incrementBy)
{
return this.Dynamic.IncrementLimit(incrementBy);
}
void ThrowIfClosedOrOpened(string memberName)
{
if (this.host.State == CommunicationState.Opened)
{
throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxImmutableThrottle1, memberName)));
}
else
{
this.host.ThrowIfClosedOrOpened();
}
}
void UpdateIsActive()
{
this.isActive = ((this.dynamic != null) ||
((this.calls != null) && (this.calls.Capacity != Int32.MaxValue)) ||
((this.sessions != null) && (this.sessions.Capacity != Int32.MaxValue)) ||
((this.instanceContexts != null) && (this.instanceContexts.Capacity != Int32.MaxValue)));
}
internal void AcquiredCallsToken()
{
this.servicePerformanceCounters.IncrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.CallsPercentMaxCalls);
}
internal void ReleasedCallsToken()
{
this.servicePerformanceCounters.DecrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.CallsPercentMaxCalls);
}
internal void RatioCallsToken(int count)
{
if (TD.ConcurrentCallsRatioIsEnabled())
{
TD.ConcurrentCallsRatio(count, this.MaxConcurrentCalls);
}
}
internal void AcquiredInstancesToken()
{
this.servicePerformanceCounters.IncrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.InstancesPercentMaxInstances);
}
internal void ReleasedInstancesToken()
{
this.servicePerformanceCounters.DecrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.InstancesPercentMaxInstances);
}
internal void RatioInstancesToken(int count)
{
if (TD.ConcurrentInstancesRatioIsEnabled())
{
TD.ConcurrentInstancesRatio(count, this.MaxConcurrentInstances);
}
}
internal void AcquiredSessionsToken()
{
this.servicePerformanceCounters.IncrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.SessionsPercentMaxSessions);
}
internal void ReleasedSessionsToken()
{
this.servicePerformanceCounters.DecrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.SessionsPercentMaxSessions);
}
internal void RatioSessionsToken(int count)
{
if (TD.ConcurrentSessionsRatioIsEnabled())
{
TD.ConcurrentSessionsRatio(count, this.MaxConcurrentSessions);
}
}
}
}
|