17 using System.Threading;
18 using System.Collections.Generic;
44 private readonly SemaphoreSlim _semaphore;
47 private readonly Queue<int> _exitTimes;
50 private readonly Timer _exitTimer;
53 private bool _isDisposed;
88 public RateGate(
int occurrences, TimeSpan timeUnit)
92 throw new ArgumentOutOfRangeException(nameof(occurrences),
"Number of occurrences must be a positive integer");
93 if (timeUnit != timeUnit.Duration())
94 throw new ArgumentOutOfRangeException(nameof(timeUnit),
"Time unit must be a positive span of time");
95 if (timeUnit >= TimeSpan.FromMilliseconds(UInt32.MaxValue))
96 throw new ArgumentOutOfRangeException(nameof(timeUnit),
"Time unit must be less than 2^32 milliseconds");
116 private void ExitTimerCallback(
object state)
123 var exitTimeValid =
false;
124 var tickCount = Environment.TickCount;
127 exitTimeValid = _exitTimes.TryPeek(out exitTime);
128 while (exitTimeValid)
130 if (unchecked(exitTime - tickCount) > 0)
134 _semaphore.Release();
135 _exitTimes.Dequeue();
136 exitTimeValid = _exitTimes.TryPeek(out exitTime);
141 var timeUntilNextCheck = exitTimeValid
145 _exitTimer.Change(timeUntilNextCheck, -1);
162 if (millisecondsTimeout < -1)
163 throw new ArgumentOutOfRangeException(nameof(millisecondsTimeout));
168 var entered = _semaphore.Wait(millisecondsTimeout);
177 _exitTimes.Enqueue(timeToExit);
204 private void CheckDisposed()
207 throw new ObjectDisposedException(
"RateGate is already disposed");
216 GC.SuppressFinalize(
this);
223 protected virtual void Dispose(
bool isDisposing)
231 _semaphore.Dispose();
232 _exitTimer.Dispose();