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_IMPL_H_
6 #define EXTENSIONS_BROWSER_UPDATER_REQUEST_QUEUE_IMPL_H_
7 
8 #include <stddef.h>
9 
10 #include <algorithm>
11 #include <memory>
12 #include <utility>
13 
14 #include "base/bind.h"
15 #include "base/compiler_specific.h"
16 #include "extensions/browser/updater/request_queue.h"
17 
18 namespace extensions {
19 
20 template <typename T>
RequestQueue(const net::BackoffEntry::Policy * const backoff_policy,const base::RepeatingClosure & start_request_callback)21 RequestQueue<T>::RequestQueue(
22     const net::BackoffEntry::Policy* const backoff_policy,
23     const base::RepeatingClosure& start_request_callback)
24     : backoff_policy_(backoff_policy),
25       start_request_callback_(start_request_callback) {}
26 
27 template <typename T>
28 RequestQueue<T>::~RequestQueue() = default;
29 
30 template <typename T>
active_request()31 T* RequestQueue<T>::active_request() {
32   return active_request_.get();
33 }
34 
35 template <typename T>
active_request_failure_count()36 int RequestQueue<T>::active_request_failure_count() {
37   return active_backoff_entry_->failure_count();
38 }
39 
40 template <typename T>
reset_active_request()41 std::unique_ptr<T> RequestQueue<T>::reset_active_request() {
42   active_backoff_entry_.reset();
43   return std::move(active_request_);
44 }
45 
46 template <typename T>
ScheduleRequest(std::unique_ptr<T> request)47 void RequestQueue<T>::ScheduleRequest(std::unique_ptr<T> request) {
48   PushImpl(std::move(request), std::unique_ptr<net::BackoffEntry>(
49                                    new net::BackoffEntry(backoff_policy_)));
50   StartNextRequest();
51 }
52 
53 template <typename T>
PushImpl(std::unique_ptr<T> request,std::unique_ptr<net::BackoffEntry> backoff_entry)54 void RequestQueue<T>::PushImpl(
55     std::unique_ptr<T> request,
56     std::unique_ptr<net::BackoffEntry> backoff_entry) {
57   pending_requests_.push_back(
58       Request(backoff_entry.release(), request.release()));
59   std::push_heap(
60       pending_requests_.begin(), pending_requests_.end(), CompareRequests);
61 }
62 
63 template <typename T>
empty()64 bool RequestQueue<T>::empty() const {
65   return pending_requests_.empty();
66 }
67 
68 template <typename T>
size()69 size_t RequestQueue<T>::size() const {
70   return pending_requests_.size();
71 }
72 
73 template <typename T>
NextReleaseTime()74 base::TimeTicks RequestQueue<T>::NextReleaseTime() const {
75   return pending_requests_.front().backoff_entry->GetReleaseTime();
76 }
77 
78 template <typename T>
StartNextRequest()79 void RequestQueue<T>::StartNextRequest() {
80   if (active_request_)
81     // Already running a request, assume this method will be called again when
82     // the request is done.
83     return;
84 
85   if (empty())
86     // No requests in the queue, so we're done.
87     return;
88 
89   base::TimeTicks next_release = NextReleaseTime();
90   base::TimeTicks now = base::TimeTicks::Now();
91   if (next_release > now) {
92     // Not ready for the next update check yet, call this method when it is
93     // time.
94     timer_.Start(FROM_HERE, next_release - now,
95                  base::BindOnce(&RequestQueue<T>::StartNextRequest,
96                                 base::Unretained(this)));
97     return;
98   }
99 
100   // pop_heap swaps the first and last elements of pending_requests_, and after
101   // that assures that the rest of pending_requests_ (excluding the
102   // now last/formerly first element) forms a proper heap. After pop_heap
103   // [begin, end-1) is a valid heap, and *(end - 1) contains the element that
104   // used to be at the top of the heap. Since no elements are actually
105   // removed from the container it is safe to read the entry being removed after
106   // pop_heap is called (but before pop_back is called).
107   std::pop_heap(
108       pending_requests_.begin(), pending_requests_.end(), CompareRequests);
109 
110   active_backoff_entry_ = std::move(pending_requests_.back().backoff_entry);
111   active_request_ = std::move(pending_requests_.back().request);
112 
113   pending_requests_.pop_back();
114 
115   start_request_callback_.Run();
116 }
117 
118 template <typename T>
RetryRequest(const base::TimeDelta & min_backoff_delay)119 void RequestQueue<T>::RetryRequest(const base::TimeDelta& min_backoff_delay) {
120   active_backoff_entry_->InformOfRequest(false);
121   if (active_backoff_entry_->GetTimeUntilRelease() < min_backoff_delay) {
122     active_backoff_entry_->SetCustomReleaseTime(base::TimeTicks::Now() +
123                                                 min_backoff_delay);
124   }
125   PushImpl(std::move(active_request_), std::move(active_backoff_entry_));
126 }
127 
128 template <typename T>
begin()129 typename RequestQueue<T>::iterator RequestQueue<T>::begin() {
130   return iterator(pending_requests_.begin());
131 }
132 
133 template <typename T>
end()134 typename RequestQueue<T>::iterator RequestQueue<T>::end() {
135   return iterator(pending_requests_.end());
136 }
137 
138 template <typename T>
set_backoff_policy(const net::BackoffEntry::Policy * backoff_policy)139 void RequestQueue<T>::set_backoff_policy(
140     const net::BackoffEntry::Policy* backoff_policy) {
141   backoff_policy_ = backoff_policy;
142 }
143 
144 // static
145 template <typename T>
CompareRequests(const Request & a,const Request & b)146 bool RequestQueue<T>::CompareRequests(const Request& a, const Request& b) {
147   return a.backoff_entry->GetReleaseTime() > b.backoff_entry->GetReleaseTime();
148 }
149 
150 }  // namespace extensions
151 
152 #endif  // EXTENSIONS_BROWSER_UPDATER_REQUEST_QUEUE_IMPL_H_
153