b7fa1512628af63281d6b87f4c9003b0f014405f
[mono.git] / mcs / class / referencesource / System.ServiceModel / System / ServiceModel / Dispatcher / ServiceThrottle.cs
1 //-----------------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation.  All rights reserved.
3 //-----------------------------------------------------------------------------
4
5 namespace System.ServiceModel.Dispatcher
6 {
7     using System;
8     using System.Runtime;
9     using System.ServiceModel;
10     using System.ServiceModel.Diagnostics;
11     using System.ServiceModel.Diagnostics.Application;
12
13     interface ISessionThrottleNotification
14     {
15         void ThrottleAcquired();
16     }
17
18     public sealed class ServiceThrottle
19     {
20         internal const int DefaultMaxConcurrentCalls = 16;
21         internal const int DefaultMaxConcurrentSessions = 100;
22         internal static int DefaultMaxConcurrentCallsCpuCount = DefaultMaxConcurrentCalls * OSEnvironmentHelper.ProcessorCount;
23         internal static int DefaultMaxConcurrentSessionsCpuCount = DefaultMaxConcurrentSessions * OSEnvironmentHelper.ProcessorCount;
24
25         FlowThrottle calls;
26         FlowThrottle sessions;
27         QuotaThrottle dynamic;
28         FlowThrottle instanceContexts;
29
30         ServiceHostBase host;
31         ServicePerformanceCountersBase servicePerformanceCounters;
32         bool isActive;
33         object thisLock = new object();
34
35         internal ServiceThrottle(ServiceHostBase host)
36         {
37             if (!((host != null)))
38             {
39                 Fx.Assert("ServiceThrottle.ServiceThrottle: (host != null)");
40                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperArgumentNull("host");
41             }
42             this.host = host;
43             this.MaxConcurrentCalls = ServiceThrottle.DefaultMaxConcurrentCallsCpuCount;
44             this.MaxConcurrentSessions = ServiceThrottle.DefaultMaxConcurrentSessionsCpuCount;
45
46             this.isActive = true;
47         }
48
49         FlowThrottle Calls
50         {
51             get
52             {
53                 lock (this.ThisLock)
54                 {
55                     if (this.calls == null)
56                     {
57                         this.calls = new FlowThrottle(this.GotCall, ServiceThrottle.DefaultMaxConcurrentCallsCpuCount,
58                                                       ServiceThrottle.MaxConcurrentCallsPropertyName, ServiceThrottle.MaxConcurrentCallsConfigName);
59                         this.calls.SetRatio(this.RatioCallsToken);
60                     }
61                     return this.calls;
62                 }
63             }
64         }
65
66         FlowThrottle Sessions
67         {
68             get
69             {
70                 lock (this.ThisLock)
71                 {
72                     if (this.sessions == null)
73                     {
74                         this.sessions = new FlowThrottle(this.GotSession, ServiceThrottle.DefaultMaxConcurrentSessionsCpuCount,
75                                                          ServiceThrottle.MaxConcurrentSessionsPropertyName, ServiceThrottle.MaxConcurrentSessionsConfigName);
76                         this.sessions.SetRatio(this.RatioSessionsToken);
77                     }
78                     return this.sessions;
79                 }
80             }
81         }
82
83         QuotaThrottle Dynamic
84         {
85             get
86             {
87                 lock (this.ThisLock)
88                 {
89                     if (this.dynamic == null)
90                     {
91                         this.dynamic = new QuotaThrottle(this.GotDynamic, new object());
92                         this.dynamic.Owner = "ServiceHost";
93                     }
94                     this.UpdateIsActive();
95                     return this.dynamic;
96                 }
97             }
98         }
99
100         internal int ManualFlowControlLimit
101         {
102             get { return this.Dynamic.Limit; }
103             set { this.Dynamic.SetLimit(value); }
104         }
105
106         const string MaxConcurrentCallsPropertyName = "MaxConcurrentCalls";
107         const string MaxConcurrentCallsConfigName = "maxConcurrentCalls";
108         public int MaxConcurrentCalls
109         {
110             get { return this.Calls.Capacity; }
111             set
112             {
113                 this.ThrowIfClosedOrOpened(MaxConcurrentCallsPropertyName);
114                 this.Calls.Capacity = value;
115                 this.UpdateIsActive();
116                 if (null != this.servicePerformanceCounters)
117                 {
118                     this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.CallsPercentMaxCallsBase, this.Calls.Capacity);
119                 }
120             }
121         }
122
123         const string MaxConcurrentSessionsPropertyName = "MaxConcurrentSessions";
124         const string MaxConcurrentSessionsConfigName = "maxConcurrentSessions";
125         public int MaxConcurrentSessions
126         {
127             get { return this.Sessions.Capacity; }
128             set
129             {
130                 this.ThrowIfClosedOrOpened(MaxConcurrentSessionsPropertyName);
131                 this.Sessions.Capacity = value;
132                 this.UpdateIsActive();
133                 if (null != this.servicePerformanceCounters)
134                 {
135                     this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.SessionsPercentMaxSessionsBase, this.Sessions.Capacity);
136                 }
137             }
138         }
139
140         const string MaxConcurrentInstancesPropertyName = "MaxConcurrentInstances";
141         const string MaxConcurrentInstancesConfigName = "maxConcurrentInstances";
142         public int MaxConcurrentInstances
143         {
144             get { return this.InstanceContexts.Capacity; }
145             set
146             {
147                 this.ThrowIfClosedOrOpened(MaxConcurrentInstancesPropertyName);
148                 this.InstanceContexts.Capacity = value;
149                 this.UpdateIsActive();
150                 if (null != this.servicePerformanceCounters)
151                 {
152                     this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.InstancesPercentMaxInstancesBase, this.InstanceContexts.Capacity);
153                 }
154             }
155         }
156
157         FlowThrottle InstanceContexts
158         {
159             get
160             {
161                 lock (this.ThisLock)
162                 {
163                     if (this.instanceContexts == null)
164                     {
165                         this.instanceContexts = new FlowThrottle(this.GotInstanceContext, Int32.MaxValue,
166                                                                  ServiceThrottle.MaxConcurrentInstancesPropertyName, ServiceThrottle.MaxConcurrentInstancesConfigName);
167                         this.instanceContexts.SetRatio(this.RatioInstancesToken);
168                         if (this.servicePerformanceCounters != null)
169                         {
170                             InitializeInstancePerfCounterSettings();
171                         }
172                     }
173                     return this.instanceContexts;
174                 }
175             }
176         }
177
178         internal bool IsActive
179         {
180             get { return this.isActive; }
181         }
182
183         internal object ThisLock
184         {
185             get { return this.thisLock; }
186         }
187
188         internal void SetServicePerformanceCounters(ServicePerformanceCountersBase counters)
189         {
190             this.servicePerformanceCounters = counters;
191             //instance throttle is created through the behavior, set the perf counter callbacks if initialized
192             if (this.instanceContexts != null)
193             {
194                 InitializeInstancePerfCounterSettings();
195             }
196
197             //this.calls and this.sessions throttles are created by the constructor. Set the perf counter callbacks
198             InitializeCallsPerfCounterSettings();
199             InitializeSessionsPerfCounterSettings();
200         }
201
202         void InitializeInstancePerfCounterSettings()
203         {
204             Fx.Assert(this.instanceContexts != null, "Expect instanceContext to be initialized");
205             Fx.Assert(this.servicePerformanceCounters != null, "expect servicePerformanceCounters to be set");
206             this.instanceContexts.SetAcquired(this.AcquiredInstancesToken);
207             this.instanceContexts.SetReleased(this.ReleasedInstancesToken);
208             this.instanceContexts.SetRatio(this.RatioInstancesToken);
209             this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.InstancesPercentMaxInstancesBase, this.instanceContexts.Capacity);
210         }
211
212         void InitializeCallsPerfCounterSettings()
213         {
214             Fx.Assert(this.calls != null, "Expect calls to be initialized");
215             Fx.Assert(this.servicePerformanceCounters != null, "expect servicePerformanceCounters to be set");
216             this.calls.SetAcquired(this.AcquiredCallsToken);
217             this.calls.SetReleased(this.ReleasedCallsToken);
218             this.calls.SetRatio(this.RatioCallsToken);
219             this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.CallsPercentMaxCallsBase, this.calls.Capacity);
220         }
221
222         void InitializeSessionsPerfCounterSettings()
223         {
224             Fx.Assert(this.sessions != null, "Expect sessions to be initialized");
225             Fx.Assert(this.servicePerformanceCounters != null, "expect servicePerformanceCounters to be set");
226             this.sessions.SetAcquired(this.AcquiredSessionsToken);
227             this.sessions.SetReleased(this.ReleasedSessionsToken);
228             this.sessions.SetRatio(this.RatioSessionsToken);
229             this.servicePerformanceCounters.SetThrottleBase((int)ServicePerformanceCounters.PerfCounters.SessionsPercentMaxSessionsBase, this.sessions.Capacity);
230         }
231
232         bool PrivateAcquireCall(ChannelHandler channel)
233         {
234             return (this.calls == null) || this.calls.Acquire(channel);
235         }
236
237         bool PrivateAcquireSessionListenerHandler(ListenerHandler listener)
238         {
239             if ((this.sessions != null) && (listener.Channel != null) && (listener.Channel.Throttle == null))
240             {
241                 listener.Channel.Throttle = this;
242                 return this.sessions.Acquire(listener);
243             }
244             else
245             {
246                 return true;
247             }
248         }
249
250         bool PrivateAcquireSession(ISessionThrottleNotification source)
251         {
252             return (this.sessions == null || this.sessions.Acquire(source));
253         }
254
255         bool PrivateAcquireDynamic(ChannelHandler channel)
256         {
257             return (this.dynamic == null) || this.dynamic.Acquire(channel);
258         }
259
260         bool PrivateAcquireInstanceContext(ChannelHandler channel)
261         {
262             if ((this.instanceContexts != null) && (channel.InstanceContext == null))
263             {
264                 channel.InstanceContextServiceThrottle = this;
265                 return this.instanceContexts.Acquire(channel);
266             }
267             else
268             {
269                 return true;
270             }
271         }
272
273         internal bool AcquireCall(ChannelHandler channel)
274         {
275             lock (this.ThisLock)
276             {
277                 return (this.PrivateAcquireCall(channel));
278             }
279         }
280
281         internal bool AcquireInstanceContextAndDynamic(ChannelHandler channel, bool acquireInstanceContextThrottle)
282         {
283             lock (this.ThisLock)
284             {
285                 if (!acquireInstanceContextThrottle)
286                 {
287                     return this.PrivateAcquireDynamic(channel);
288                 }
289                 else
290                 {
291                     return (this.PrivateAcquireInstanceContext(channel) &&
292                             this.PrivateAcquireDynamic(channel));
293                 }
294             }
295         }
296
297         internal bool AcquireSession(ISessionThrottleNotification source)
298         {
299             lock (this.ThisLock)
300             {
301                 return this.PrivateAcquireSession(source);
302             }
303         }
304
305         internal bool AcquireSession(ListenerHandler listener)
306         {
307             lock (this.ThisLock)
308             {
309                 return this.PrivateAcquireSessionListenerHandler(listener);
310             }
311         }
312
313         void GotCall(object state)
314         {
315             ChannelHandler channel = (ChannelHandler)state;
316
317             lock (this.ThisLock)
318             {
319                 channel.ThrottleAcquiredForCall();
320             }
321         }
322
323         void GotDynamic(object state)
324         {
325             ((ChannelHandler)state).ThrottleAcquired();
326         }
327
328         void GotInstanceContext(object state)
329         {
330             ChannelHandler channel = (ChannelHandler)state;
331
332             lock (this.ThisLock)
333             {
334                 if (this.PrivateAcquireDynamic(channel))
335                     channel.ThrottleAcquired();
336             }
337         }
338
339         void GotSession(object state)
340         {
341             ((ISessionThrottleNotification)state).ThrottleAcquired();
342         }
343
344         internal void DeactivateChannel()
345         {
346             if (this.isActive)
347             {
348                 if (this.sessions != null)
349                     this.sessions.Release();
350             }
351         }
352
353         internal void DeactivateCall()
354         {
355             if (this.isActive)
356             {
357                 if (this.calls != null)
358                     this.calls.Release();
359             }
360         }
361
362         internal void DeactivateInstanceContext()
363         {
364             if (this.isActive)
365             {
366                 if (this.instanceContexts != null)
367                 {
368                     this.instanceContexts.Release();
369                 }
370             }
371         }
372
373         internal int IncrementManualFlowControlLimit(int incrementBy)
374         {
375             return this.Dynamic.IncrementLimit(incrementBy);
376         }
377
378         void ThrowIfClosedOrOpened(string memberName)
379         {
380             if (this.host.State == CommunicationState.Opened)
381             {
382                 throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.GetString(SR.SFxImmutableThrottle1, memberName)));
383             }
384             else
385             {
386                 this.host.ThrowIfClosedOrOpened();
387             }
388         }
389
390         void UpdateIsActive()
391         {
392             this.isActive = ((this.dynamic != null) ||
393                              ((this.calls != null) && (this.calls.Capacity != Int32.MaxValue)) ||
394                              ((this.sessions != null) && (this.sessions.Capacity != Int32.MaxValue)) ||
395                              ((this.instanceContexts != null) && (this.instanceContexts.Capacity != Int32.MaxValue)));
396         }
397
398         internal void AcquiredCallsToken()
399         {
400             this.servicePerformanceCounters.IncrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.CallsPercentMaxCalls);
401         }
402
403         internal void ReleasedCallsToken()
404         {
405             this.servicePerformanceCounters.DecrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.CallsPercentMaxCalls);
406         }
407
408         internal void RatioCallsToken(int count)
409         {
410             if (TD.ConcurrentCallsRatioIsEnabled())
411             {
412                 TD.ConcurrentCallsRatio(count, this.MaxConcurrentCalls);
413             }
414         }
415
416         internal void AcquiredInstancesToken()
417         {
418             this.servicePerformanceCounters.IncrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.InstancesPercentMaxInstances);
419         }
420
421         internal void ReleasedInstancesToken()
422         {
423             this.servicePerformanceCounters.DecrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.InstancesPercentMaxInstances);
424         }
425
426         internal void RatioInstancesToken(int count)
427         {
428             if (TD.ConcurrentInstancesRatioIsEnabled())
429             {
430                 TD.ConcurrentInstancesRatio(count, this.MaxConcurrentInstances);
431             }
432         }
433
434         internal void AcquiredSessionsToken()
435         {
436             this.servicePerformanceCounters.IncrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.SessionsPercentMaxSessions);
437         }
438
439         internal void ReleasedSessionsToken()
440         {
441             this.servicePerformanceCounters.DecrementThrottlePercent((int)ServicePerformanceCounters.PerfCounters.SessionsPercentMaxSessions);
442         }
443
444         internal void RatioSessionsToken(int count)
445         {
446             if (TD.ConcurrentSessionsRatioIsEnabled())
447             {
448                 TD.ConcurrentSessionsRatio(count, this.MaxConcurrentSessions);
449             }
450         }
451     }
452 }