Task <IRateLimit> RunAsync2(Func <Task <IRateLimit> > action, object operationDescription)
        {
            var githubTask = new GitHubTask(action, operationDescription);

            RunGitHubTaskThrottled(githubTask);
            return(githubTask.CompletionSource.Task);
        }
        //  Returns true if the request succeeded or failed, false if it hit the rate limit
        //  (and thus should be retried later)
        async Task <bool> RunGitHubTask(GitHubTask task)
        {
            try
            {
                _logger.Information("Running {@Operation} ({RemainingRequests}/{RequestLimit})", task.OperationDescription, _remainingRequests, _limit);

                IRateLimit result = await task.Action();

                _logger.Information("Finished {@Operation} ({RemainingRequests}/{RequestLimit})", task.OperationDescription, _remainingRequests, _limit);

                if (result != null)
                {
                    lock (_lockObject)
                    {
                        _remainingRequests = result.RateLimit.Remaining;
                        _limit             = result.RateLimit.Limit;
                        _resetTime         = result.RateLimit.Reset;
                    }
                }

                task.CompletionSource.SetResult(result);
                _requestCompleted.Set();
            }
            catch (RateLimitExceededException ex)
            {
                _logger
                .ForContext("ResetTime", ex.Reset)
                .Warning("Rate limit exceeded for {@Operation}, resets in {ResetDelay}. {RemainingRequests}/{RequestLimit}",
                         task.OperationDescription, ex.Reset.Subtract(DateTimeOffset.UtcNow), ex.Remaining, ex.Limit);

                lock (_lockObject)
                {
                    _remainingRequests = ex.Remaining;
                    _limit             = ex.Limit;
                    _resetTime         = ex.Reset;
                }

                return(false);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "Failed {@Operation}", task.OperationDescription);
                task.CompletionSource.SetException(ex);
            }
            finally
            {
                lock (_lockObject)
                {
                    _executingOperations--;
                }
            }
            return(true);
        }
        async void RunGitHubTaskThrottled(GitHubTask task)
        {
            try
            {
                while (true)
                {
                    bool           canRunNow = false;
                    DateTimeOffset resetTime = DateTimeOffset.MinValue;
                    while (!canRunNow)
                    {
                        lock (_lockObject)
                        {
                            if (_executingOperations < _remainingRequests)
                            {
                                _executingOperations++;
                                canRunNow = true;
                            }
                            else
                            {
                                resetTime = _resetTime;
                            }
                        }

                        if (!canRunNow)
                        {
                            //  Wait until the reset time is reached or until another request has completed
                            //  (which will update the remaining requests and reset time, possibly allowing
                            //  this request to be executed)

                            TimeSpan delayTime = resetTime.Subtract(DateTimeOffset.UtcNow);
                            if (delayTime.TotalMilliseconds < 10)
                            {
                                delayTime = TimeSpan.FromMilliseconds(10);
                            }

                            _logger.ForContext("ResetTime", resetTime)
                            .Information("{@Operation} waiting for rate limit reset (in {ResetDelay} or finished request",
                                         task.OperationDescription, delayTime);

                            Task delayTask            = Task.Delay(delayTime);
                            Task requestCompletedTask = _requestCompleted.WaitOneAsync();

                            if (await Task.WhenAny(delayTask, requestCompletedTask) == requestCompletedTask)
                            {
                                //  Reset "request completed" WaitHandle
                                //  If multiple requests are waiting, then they should all get signalled and
                                //  then they'll all Reset the WaitHandle.  It only needs to be reset once but
                                //  doing it multiple times shouldn't hurt (I think)
                                _requestCompleted.Reset();
                            }
                            else
                            {
                                lock (_lockObject)
                                {
                                    if (_remainingRequests == 0)
                                    {
                                        _remainingRequests = _limit;
                                    }
                                }
                            }
                        }
                    }

                    if (await RunGitHubTask(task))
                    {
                        break;
                    }
                }
            }
            catch (Exception ex)
            {
                task.CompletionSource.SetException(ex);
            }
        }