Advertisement
Guest User

Untitled

a guest
Jun 27th, 2019
252
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 3.45 KB | None | 0 0
  1. public abstract class Disposable : IDisposable
  2. {
  3. private bool _disposed;
  4.  
  5. public void Dispose()
  6. {
  7. if (_disposed)
  8. {
  9. return;
  10. }
  11.  
  12. Dispose(_disposed = true);
  13. }
  14.  
  15. protected void ThrowIfDisposed(IDisposable disposable)
  16. {
  17. if (_disposed)
  18. {
  19. throw new ObjectDisposedException(disposable.GetType().Name);
  20. }
  21. }
  22.  
  23. protected abstract void Dispose(bool disposing);
  24. }
  25.  
  26. public interface IRateLimiter : IDisposable
  27. {
  28. TimeSpan Interval { get; set; }
  29.  
  30. int Limit { get; set; }
  31.  
  32. Task ThrottleAsync(int weight = 1, CancellationToken token = default);
  33. }
  34.  
  35. public class RateLimiter : Disposable, IRateLimiter
  36. {
  37. private readonly LinkedList<RateLimitRequest> _requests = new LinkedList<RateLimitRequest>();
  38.  
  39. private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
  40.  
  41. public RateLimiter(TimeSpan interval, int limit)
  42. {
  43. Interval = interval;
  44. Limit = limit;
  45. }
  46.  
  47. public TimeSpan Interval { get; set; }
  48.  
  49. public int Limit { get; set; }
  50.  
  51. public async Task ThrottleAsync(int weight = 1, CancellationToken token = default)
  52. {
  53. if (weight < 1)
  54. {
  55. return;
  56. }
  57.  
  58. ThrowIfDisposed(this);
  59.  
  60. var count = 0;
  61.  
  62. var current = DateTime.UtcNow;
  63. var last = DateTime.MinValue;
  64. var target = current - Interval;
  65.  
  66. await _semaphore.WaitAsync(token);
  67.  
  68. try
  69. {
  70. var node = _requests.First;
  71. while (node != null)
  72. {
  73. var timestamp = node.Value.Timestamp;
  74.  
  75. var next = node.Next;
  76.  
  77. if (timestamp > target)
  78. {
  79. if (count + weight <= Limit)
  80. {
  81. last = timestamp;
  82. count += node.Value.Weight;
  83. }
  84. }
  85. else
  86. {
  87. _requests.Remove(node);
  88. }
  89.  
  90. node = next;
  91. }
  92.  
  93. if (count + weight <= Limit)
  94. {
  95. _requests.AddFirst(new RateLimitRequest
  96. {
  97. Weight = weight,
  98. Timestamp = DateTime.UtcNow
  99. });
  100.  
  101. return;
  102. }
  103.  
  104. var delay = last.Add(Interval).Subtract(current);
  105.  
  106. await Task.Delay(delay, token);
  107.  
  108. _requests.AddFirst(new RateLimitRequest
  109. {
  110. Weight = weight,
  111. Timestamp = DateTime.UtcNow
  112. });
  113. }
  114. catch (Exception)
  115. {
  116. /* Ignore */
  117. }
  118. finally
  119. {
  120. _semaphore.Release();
  121. }
  122. }
  123.  
  124. protected override void Dispose(bool disposing)
  125. {
  126. if (!disposing)
  127. {
  128. return;
  129. }
  130.  
  131. _semaphore?.Dispose();
  132. }
  133.  
  134. private class RateLimitRequest
  135. {
  136. public DateTime Timestamp { get; set; }
  137. public int Weight { get; set; }
  138. }
  139. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement