| // 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_ |