@@ -33,7 +33,7 @@ namespace Discord.Net.Queue
_lock = new object();
_lock = new object();
if (request.Options.IsClientBucket)
if (request.Options.IsClientBucket)
WindowCount = ClientBucket.Get(request.Options.Bucket Id).WindowCount;
WindowCount = ClientBucket.Get(Id).WindowCount;
else
else
WindowCount = 1; //Only allow one request until we get a header back
WindowCount = 1; //Only allow one request until we get a header back
_semaphore = WindowCount;
_semaphore = WindowCount;
@@ -181,7 +181,8 @@ namespace Discord.Net.Queue
}
}
DateTimeOffset? timeoutAt = request.TimeoutAt;
DateTimeOffset? timeoutAt = request.TimeoutAt;
if (windowCount > 0 && Interlocked.Decrement(ref _semaphore) < 0)
int semaphore = 0;
if (windowCount > 0 && (semaphore = Interlocked.Decrement(ref _semaphore)) < 0)
{
{
if (!isRateLimited)
if (!isRateLimited)
{
{
@@ -216,7 +217,7 @@ namespace Discord.Net.Queue
}
}
#if DEBUG_LIMITS
#if DEBUG_LIMITS
else
else
Debug.WriteLine($"[{id}] Entered Semaphore ({_ semaphore}/{WindowCount} remaining)");
Debug.WriteLine($"[{id}] Entered Semaphore ({semaphore}/{WindowCount} remaining)");
#endif
#endif
break;
break;
}
}
@@ -230,31 +231,47 @@ namespace Discord.Net.Queue
lock (_lock)
lock (_lock)
{
{
if (redirected)
if (redirected)
Interlocked.Decrement(ref _semaphore); //we might still hit a real ratelimit if all tickets were already taken, can't do much about it since we didn't know they were the same
bool hasQueuedReset = _resetTick != null;
if (info.Limit.HasValue && WindowCount != info.Limit.Value)
{
{
WindowCount = info.Limit.Value;
_semaphore = info.Remaining.Value;
Interlocked.Decrement(ref _semaphore); //we might still hit a real ratelimit if all tickets were already taken, can't do much about it since we didn't know they were the same
#if DEBUG_LIMITS
#if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Upgraded Semaphore to {info.Remaining.Value}/{WindowCount} ");
Debug.WriteLine($"[{id}] Decrease Semaphore ");
#endif
#endif
}
}
bool hasQueuedReset = _resetTick != null;
if (info.Bucket != null && !redirected)
if (info.Bucket != null && !redirected)
{
{
(RequestBucket, BucketId) hashBucket = _queue.UpdateBucketHash(request.Options.BucketId, info.Bucket);
if (hashBucket.Item1 is null || hashBucket.Item2 is null)
return;
if (hashBucket.Item1 == this) //this bucket got promoted to a hash queue
Id = hashBucket.Item2;
else
(RequestBucket, BucketId) hashBucket = _queue.UpdateBucketHash(Id, info.Bucket);
if (!(hashBucket.Item1 is null) && !(hashBucket.Item2 is null))
{
{
_redirectBucket = hashBucket.Item1; //this request should be part of another bucket, this bucket will be disabled, redirect everything
_redirectBucket.UpdateRateLimit(id, request, info, is429, redirected: true); //update the hash bucket ratelimit
if (hashBucket.Item1 == this) //this bucket got promoted to a hash queue
{
Id = hashBucket.Item2;
#if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Promoted to Hash Bucket ({hashBucket.Item2})");
#endif
}
else
{
_redirectBucket = hashBucket.Item1; //this request should be part of another bucket, this bucket will be disabled, redirect everything
_redirectBucket.UpdateRateLimit(id, request, info, is429, redirected: true); //update the hash bucket ratelimit
#if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Redirected to {_redirectBucket.Id}");
#endif
return;
}
}
}
}
}
if (info.Limit.HasValue && WindowCount != info.Limit.Value)
{
WindowCount = info.Limit.Value;
_semaphore = info.Remaining.Value;
#if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Upgraded Semaphore to {info.Remaining.Value}/{WindowCount}");
#endif
}
DateTimeOffset? resetTick = null;
DateTimeOffset? resetTick = null;
//Using X-RateLimit-Remaining causes a race condition
//Using X-RateLimit-Remaining causes a race condition
@@ -289,11 +306,11 @@ namespace Discord.Net.Queue
Debug.WriteLine($"[{id}] X-RateLimit-Reset: {info.Reset.Value.ToUnixTimeSeconds()} ({diff} ms, {info.Lag?.TotalMilliseconds} ms lag)");
Debug.WriteLine($"[{id}] X-RateLimit-Reset: {info.Reset.Value.ToUnixTimeSeconds()} ({diff} ms, {info.Lag?.TotalMilliseconds} ms lag)");
#endif
#endif
}
}
else if (request.Options.IsClientBucket && request.Options.Bucket Id != null)
else if (request.Options.IsClientBucket && Id != null)
{
{
resetTick = DateTimeOffset.UtcNow.AddSeconds(ClientBucket.Get(request.Options.Bucket Id).WindowSeconds);
resetTick = DateTimeOffset.UtcNow.AddSeconds(ClientBucket.Get(Id).WindowSeconds);
#if DEBUG_LIMITS
#if DEBUG_LIMITS
Debug.WriteLine($"[{id}] Client Bucket ({ClientBucket.Get(request.Options.Bucket Id).WindowSeconds * 1000} ms)");
Debug.WriteLine($"[{id}] Client Bucket ({ClientBucket.Get(Id).WindowSeconds * 1000} ms)");
#endif
#endif
}
}