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