910e62b5创建于 1月15日历史提交
// 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_