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