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 CHROME_BROWSER_EXTENSIONS_UPDATER_REQUEST_QUEUE_H_
6#define CHROME_BROWSER_EXTENSIONS_UPDATER_REQUEST_QUEUE_H_
7
8#include <deque>
9#include <utility>
10
11#include "base/callback.h"
12#include "base/memory/linked_ptr.h"
13#include "base/memory/scoped_ptr.h"
14#include "base/time/time.h"
15#include "base/timer/timer.h"
16#include "net/base/backoff_entry.h"
17
18namespace extensions {
19
20// This class keeps track of a queue of requests, and contains the logic to
21// retry requests with some backoff policy. Each request has a
22// net::BackoffEntry instance associated with it.
23//
24// The general flow when using this class would be something like this:
25//   - requests are queued up by calling ScheduleRequest.
26//   - when a request is ready to be executed, RequestQueue removes the
27//     request from the queue, assigns it as active request, and calls
28//     the callback that was passed to the constructor.
29//   - (optionally) when a request has completed unsuccessfully call
30//     RetryRequest to put the request back in the queue, using the
31//     backoff policy and minimum backoff delay to determine when to
32//     next schedule this request.
33//   - call reset_active_request() to indicate that the active request has
34//     been dealt with.
35//   - call StartNextRequest to schedule the next pending request (if any).
36template<typename T>
37class RequestQueue {
38 public:
39  class iterator;
40
41  RequestQueue(const net::BackoffEntry::Policy* backoff_policy,
42               const base::Closure& start_request_callback);
43  ~RequestQueue();
44
45  // Returns the request that is currently being processed.
46  T* active_request();
47
48  // Returns the number of times the current request has been retried already.
49  int active_request_failure_count();
50
51  // Signals RequestQueue that processing of the current request has completed.
52  scoped_ptr<T> reset_active_request();
53
54  // Add the given request to the queue, and starts the next request if no
55  // request is currently being processed.
56  void ScheduleRequest(scoped_ptr<T> request);
57
58  bool empty() const;
59  size_t size() const;
60
61  // Returns the earliest release time of all requests currently in the queue.
62  base::TimeTicks NextReleaseTime() const;
63
64  // Starts the next request, if no request is currently active. This will
65  // synchronously call the start_request_callback if the release time of the
66  // earliest available request is in the past, otherwise it will call that
67  // callback asynchronously after enough time has passed.
68  void StartNextRequest();
69
70  // Tell RequestQueue to put the current request back in the queue, after
71  // applying the backoff policy to determine when to next try this request.
72  // If the policy results in a backoff delay smaller than |min_backoff_delay|,
73  // that delay is used instead.
74  void RetryRequest(const base::TimeDelta& min_backoff_delay);
75
76  iterator begin();
77  iterator end();
78
79  // Change the backoff policy used by the queue.
80  void set_backoff_policy(const net::BackoffEntry::Policy* backoff_policy);
81
82 private:
83  struct Request {
84    Request(net::BackoffEntry* backoff_entry, T* request)
85        : backoff_entry(backoff_entry), request(request) {}
86    linked_ptr<net::BackoffEntry> backoff_entry;
87    linked_ptr<T> request;
88  };
89
90  // Compares the release time of two pending requests.
91  static bool CompareRequests(const Request& a,
92                              const Request& b);
93
94  // Pushes a request with a given backoff entry onto the queue.
95  void PushImpl(scoped_ptr<T> request,
96                scoped_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::Closure 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  std::deque<Request> pending_requests_;
107
108  // Active request and its associated backoff entry.
109  scoped_ptr<T> active_request_;
110  scoped_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::Timer timer_;
115};
116
117// Iterator class that wraps a std::deque<> iterator, only giving access to the
118// actual request part of each item.
119template<typename T>
120class RequestQueue<T>::iterator {
121 public:
122  iterator() {}
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 {
131    return it_ != b.it_;
132  }
133
134 private:
135  friend class RequestQueue<T>;
136  typedef std::deque<typename RequestQueue<T>::Request> Container;
137
138  explicit iterator(const typename Container::iterator& it)
139      : it_(it) {}
140
141  typename Container::iterator it_;
142};
143
144
145}  // namespace extensions
146
147#endif  // CHROME_BROWSER_EXTENSIONS_UPDATER_REQUEST_QUEUE_H_
148