| | | 1 | | // Licensed to the .NET Foundation under one or more agreements. |
| | | 2 | | // The .NET Foundation licenses this file to you under the MIT license. |
| | | 3 | | |
| | | 4 | | using System; |
| | | 5 | | using System.Collections.Generic; |
| | | 6 | | using System.Threading.Tasks; |
| | | 7 | | |
| | | 8 | | namespace CoreWCF.Dispatcher |
| | | 9 | | { |
| | | 10 | | internal sealed class FlowThrottle |
| | | 11 | | { |
| | | 12 | | private int _capacity; |
| | | 13 | | private int _count; |
| | | 14 | | //private bool _warningIssued; |
| | | 15 | | private readonly int _warningRestoreLimit; |
| | | 16 | | private readonly object _mutex; |
| | | 17 | | // TODO: See if there's a way to pool resettable awaitables to remove allocation. Same in QuotaThrottle |
| | | 18 | | private readonly Queue<TaskCompletionSource<object>> _waiters; |
| | | 19 | | #pragma warning disable IDE0052 // Remove unread private members - Will be used once events are restored |
| | | 20 | | private readonly string _propertyName; |
| | | 21 | | private readonly string _configName; |
| | | 22 | | #pragma warning restore IDE0052 // Remove unread private members |
| | | 23 | | private Action _acquired; |
| | | 24 | | private Action _released; |
| | | 25 | | private Action<int> _ratio; |
| | | 26 | | |
| | 0 | 27 | | internal FlowThrottle(int capacity, string propertyName, string configName) |
| | | 28 | | { |
| | 0 | 29 | | if (capacity <= 0) |
| | | 30 | | { |
| | 0 | 31 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxThrottleLi |
| | | 32 | | } |
| | | 33 | | |
| | 0 | 34 | | _count = 0; |
| | 0 | 35 | | _capacity = capacity; |
| | 0 | 36 | | _mutex = new object(); |
| | 0 | 37 | | _waiters = new Queue<TaskCompletionSource<object>>(); |
| | 0 | 38 | | _propertyName = propertyName; |
| | 0 | 39 | | _configName = configName; |
| | 0 | 40 | | _warningRestoreLimit = (int)Math.Floor(0.7 * (double)capacity); |
| | 0 | 41 | | } |
| | | 42 | | |
| | | 43 | | internal int Capacity |
| | | 44 | | { |
| | 0 | 45 | | get { return _capacity; } |
| | | 46 | | set |
| | | 47 | | { |
| | 0 | 48 | | if (value <= 0) |
| | | 49 | | { |
| | 0 | 50 | | throw DiagnosticUtility.ExceptionUtility.ThrowHelperError(new InvalidOperationException(SR.SFxThrott |
| | | 51 | | } |
| | | 52 | | |
| | 0 | 53 | | _capacity = value; |
| | 0 | 54 | | } |
| | | 55 | | } |
| | | 56 | | |
| | | 57 | | internal async ValueTask AcquireAsync() |
| | | 58 | | { |
| | 0 | 59 | | TaskCompletionSource<object> tcs = null; |
| | 0 | 60 | | bool acquiredThrottle = true; |
| | | 61 | | |
| | 0 | 62 | | lock (_mutex) |
| | | 63 | | { |
| | 0 | 64 | | if (_count < _capacity) |
| | | 65 | | { |
| | 0 | 66 | | _count++; |
| | | 67 | | } |
| | | 68 | | else |
| | | 69 | | { |
| | 0 | 70 | | if (_waiters.Count == 0) |
| | | 71 | | { |
| | | 72 | | //if (TD.MessageThrottleExceededIsEnabled()) |
| | | 73 | | //{ |
| | | 74 | | // if (!this.warningIssued) |
| | | 75 | | // { |
| | | 76 | | // TD.MessageThrottleExceeded(this.propertyName, this.capacity); |
| | | 77 | | // this.warningIssued = true; |
| | | 78 | | // } |
| | | 79 | | //} |
| | | 80 | | //if (DiagnosticUtility.ShouldTraceWarning) |
| | | 81 | | //{ |
| | | 82 | | // string traceMessage; |
| | | 83 | | // if (this.propertyName != null) |
| | | 84 | | // { |
| | | 85 | | // traceMessage = SR.GetString(SR.TraceCodeServiceThrottleLimitReached, |
| | | 86 | | // this.propertyName, this.capacity, this.configName); |
| | | 87 | | // } |
| | | 88 | | // else |
| | | 89 | | // { |
| | | 90 | | // traceMessage = SR.GetString(SR.TraceCodeServiceThrottleLimitReachedInternal, |
| | | 91 | | // this.capacity); |
| | | 92 | | // } |
| | | 93 | | |
| | | 94 | | // TraceUtility.TraceEvent( |
| | | 95 | | // TraceEventType.Warning, TraceCode.ServiceThrottleLimitReached, traceMessage); |
| | | 96 | | |
| | | 97 | | //} |
| | | 98 | | } |
| | | 99 | | |
| | | 100 | | // To prevent the thread that's releasing a throttle being hijacked to run the continuations, |
| | | 101 | | // set the TaskCreationOptions to make the waiting method run on a new thread. |
| | 0 | 102 | | tcs = new TaskCompletionSource<object>(TaskCreationOptions.RunContinuationsAsynchronously); |
| | 0 | 103 | | _waiters.Enqueue(tcs); |
| | 0 | 104 | | acquiredThrottle = false; |
| | | 105 | | } |
| | | 106 | | |
| | 0 | 107 | | _acquired?.Invoke(); |
| | 0 | 108 | | _ratio?.Invoke(_count); |
| | 0 | 109 | | } |
| | | 110 | | |
| | 0 | 111 | | if (!acquiredThrottle && tcs != null) |
| | | 112 | | { |
| | 0 | 113 | | _ = await tcs.Task; |
| | | 114 | | } |
| | 0 | 115 | | } |
| | | 116 | | |
| | | 117 | | internal void Release() |
| | | 118 | | { |
| | 0 | 119 | | TaskCompletionSource<object> next = null; |
| | | 120 | | |
| | 0 | 121 | | lock (_mutex) |
| | | 122 | | { |
| | 0 | 123 | | if (_waiters.Count > 0) |
| | | 124 | | { |
| | 0 | 125 | | next = _waiters.Dequeue(); |
| | 0 | 126 | | if (_waiters.Count == 0) |
| | | 127 | | { |
| | 0 | 128 | | _waiters.TrimExcess(); |
| | | 129 | | } |
| | | 130 | | } |
| | | 131 | | else |
| | | 132 | | { |
| | 0 | 133 | | _count--; |
| | 0 | 134 | | if (_count < _warningRestoreLimit) |
| | | 135 | | { |
| | | 136 | | //if (TD.MessageThrottleAtSeventyPercentIsEnabled() && this.warningIssued) |
| | | 137 | | //{ |
| | | 138 | | // TD.MessageThrottleAtSeventyPercent(this.propertyName, this.capacity); |
| | | 139 | | //} |
| | | 140 | | //_warningIssued = false; |
| | | 141 | | } |
| | | 142 | | } |
| | 0 | 143 | | } |
| | | 144 | | |
| | 0 | 145 | | if (next != null) |
| | | 146 | | { |
| | 0 | 147 | | next.TrySetResult(null); |
| | | 148 | | } |
| | | 149 | | |
| | 0 | 150 | | _released?.Invoke(); |
| | 0 | 151 | | _ratio?.Invoke(_count); |
| | 0 | 152 | | } |
| | | 153 | | |
| | | 154 | | internal void SetReleased(Action action) |
| | | 155 | | { |
| | 0 | 156 | | _released = action; |
| | 0 | 157 | | } |
| | | 158 | | |
| | | 159 | | internal void SetAcquired(Action action) |
| | | 160 | | { |
| | 0 | 161 | | _acquired = action; |
| | 0 | 162 | | } |
| | | 163 | | |
| | | 164 | | internal void SetRatio(Action<int> action) |
| | | 165 | | { |
| | 0 | 166 | | _ratio = action; |
| | 0 | 167 | | } |
| | | 168 | | } |
| | | 169 | | } |