File: System\ServiceModel\Dispatcher\ServiceThrottle.cs
Project: ndp\cdf\src\WCF\ServiceModel\System.ServiceModel.csproj (System.ServiceModel)
//-----------------------------------------------------------------------------
// 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);
            }
        }
    }
}