1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef EXTENSIONS_BROWSER_UPDATER_REQUEST_QUEUE_H_
#define EXTENSIONS_BROWSER_UPDATER_REQUEST_QUEUE_H_
#include <stddef.h>
#include <memory>
#include <optional>
#include <utility>
#include "base/containers/circular_deque.h"
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/time/time.h"
#include "base/timer/timer.h"
#include "net/base/backoff_entry.h"
namespace extensions {
// This class keeps track of a queue of requests, and contains the logic to
// retry requests with some backoff policy. Each request has a
// net::BackoffEntry instance associated with it.
//
// The general flow when using this class would be something like this:
// - requests are queued up by calling ScheduleRequest.
// - when a request is ready to be executed, RequestQueue removes the
// request from the queue, assigns it as active request, and calls
// the callback that was passed to the constructor.
// - (optionally) when a request has completed unsuccessfully call
// RetryRequest to put the request back in the queue, using the
// backoff policy and minimum backoff delay to determine when to
// next schedule this request.
// - call reset_active_request() to indicate that the active request has
// been dealt with.
// - call StartNextRequest to schedule the next pending request (if any).
template <typename T>
class RequestQueue {
public:
struct Request {
Request(std::unique_ptr<net::BackoffEntry> backoff_entry,
std::unique_ptr<T> fetch)
: backoff_entry(std::move(backoff_entry)), fetch(std::move(fetch)) {}
int failure_count() { return backoff_entry->failure_count(); }
std::unique_ptr<net::BackoffEntry> backoff_entry;
std::unique_ptr<T> fetch;
};
class iterator;
RequestQueue(net::BackoffEntry::Policy backoff_policy,
const base::RepeatingClosure& start_request_callback);
~RequestQueue();
// Returns the request that is currently being processed.
T* active_request();
// Returns the number of times the current request has been retried already.
int active_request_failure_count();
// Signals RequestQueue that processing of the current request has completed.
Request reset_active_request();
// Add the given request to the queue, and starts the next request if no
// request is currently being processed.
void ScheduleRequest(std::unique_ptr<T> request);
// Add the request which already was in the queue, but we've decided to retry
// it. The queue will take care of the retry backoff.
void ScheduleRetriedRequest(Request request,
const base::TimeDelta& min_backoff_delay);
bool empty() const;
size_t size() const;
// Returns the earliest release time of all requests currently in the queue.
base::TimeTicks NextReleaseTime() const;
// Starts the next request, if no request is currently active. This will
// synchronously call the start_request_callback if the release time of the
// earliest available request is in the past, otherwise it will call that
// callback asynchronously after enough time has passed.
void StartNextRequest();
// Tell RequestQueue to put the current request back in the queue, after
// applying the backoff policy to determine when to next try this request.
// If the policy results in a backoff delay smaller than |min_backoff_delay|,
// that delay is used instead.
void RetryRequest(const base::TimeDelta& min_backoff_delay);
iterator begin();
iterator end();
// Checks all pending requests in the queue for the given condition, removes
// from the queue and returns the ones for which the condition returned true.
std::vector<std::unique_ptr<T>> erase_if(
const base::RepeatingCallback<bool(const T&)> condition);
// Change the backoff policy used by the queue.
void set_backoff_policy(net::BackoffEntry::Policy backoff_policy);
private:
// Compares the release time of two pending requests.
static bool CompareRequests(const Request& a, const Request& b);
// Pushes a request with a given backoff entry onto the queue.
void PushImpl(Request request);
// The backoff policy used to determine backoff delays.
net::BackoffEntry::Policy backoff_policy_;
// Callback to call when a new request has become the active request.
base::RepeatingClosure start_request_callback_;
// Priority queue of pending requests. Not using std::priority_queue since
// the code needs to be able to iterate over all pending requests.
base::circular_deque<Request> pending_requests_;
// Active entry with its associated backoff.
std::optional<Request> active_request_;
// Timer to schedule calls to StartNextRequest, if the first pending request
// hasn't passed its release time yet.
base::OneShotTimer timer_;
};
// Iterator class that wraps a base::circular_deque<> iterator, only giving
// access to the actual request part of each item.
template <typename T>
class RequestQueue<T>::iterator {
public:
iterator() = default;
T* operator*() { return it_->fetch.get(); }
T* operator->() { return it_->fetch.get(); }
iterator& operator++() {
++it_;
return *this;
}
bool operator!=(const iterator& b) const { return it_ != b.it_; }
private:
friend class RequestQueue<T>;
using Container = base::circular_deque<typename RequestQueue<T>::Request>;
explicit iterator(const typename Container::iterator& it) : it_(it) {}
typename Container::iterator it_;
};
} // namespace extensions
#endif // EXTENSIONS_BROWSER_UPDATER_REQUEST_QUEUE_H_
|