SHARE
TWEET

Untitled

a guest Jun 27th, 2019 126 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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.     }
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
Not a member of Pastebin yet?
Sign Up, it unlocks many cool features!
 
Top