diff options
author | rockot <rockot@chromium.org> | 2014-10-15 13:29:37 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-10-15 20:30:49 +0000 |
commit | 568502632b4e42a143bbe713c08e7a7aba221d5a (patch) | |
tree | ea37d23c54e3f09d8d6c17b508fe9e1dd8455023 /extensions/browser/updater | |
parent | 31f0530105bfc993e7d949a4a33bc70bb023acb1 (diff) | |
download | chromium_src-568502632b4e42a143bbe713c08e7a7aba221d5a.zip chromium_src-568502632b4e42a143bbe713c08e7a7aba221d5a.tar.gz chromium_src-568502632b4e42a143bbe713c08e7a7aba221d5a.tar.bz2 |
Move request_queue/_impl to //extensions
BUG=398671
Review URL: https://codereview.chromium.org/657693002
Cr-Commit-Position: refs/heads/master@{#299757}
Diffstat (limited to 'extensions/browser/updater')
-rw-r--r-- | extensions/browser/updater/request_queue.h | 142 | ||||
-rw-r--r-- | extensions/browser/updater/request_queue_impl.h | 154 |
2 files changed, 296 insertions, 0 deletions
diff --git a/extensions/browser/updater/request_queue.h b/extensions/browser/updater/request_queue.h new file mode 100644 index 0000000..0e3d2eb --- /dev/null +++ b/extensions/browser/updater/request_queue.h @@ -0,0 +1,142 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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 <deque> +#include <utility> + +#include "base/callback.h" +#include "base/memory/linked_ptr.h" +#include "base/memory/scoped_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: + class iterator; + + RequestQueue(const net::BackoffEntry::Policy* backoff_policy, + const base::Closure& 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. + scoped_ptr<T> reset_active_request(); + + // Add the given request to the queue, and starts the next request if no + // request is currently being processed. + void ScheduleRequest(scoped_ptr<T> request); + + 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(); + + // Change the backoff policy used by the queue. + void set_backoff_policy(const net::BackoffEntry::Policy* backoff_policy); + + private: + struct Request { + Request(net::BackoffEntry* backoff_entry, T* request) + : backoff_entry(backoff_entry), request(request) {} + linked_ptr<net::BackoffEntry> backoff_entry; + linked_ptr<T> request; + }; + + // 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(scoped_ptr<T> request, + scoped_ptr<net::BackoffEntry> backoff_entry); + + // The backoff policy used to determine backoff delays. + const net::BackoffEntry::Policy* backoff_policy_; + + // Callback to call when a new request has become the active request. + base::Closure 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. + std::deque<Request> pending_requests_; + + // Active request and its associated backoff entry. + scoped_ptr<T> active_request_; + scoped_ptr<net::BackoffEntry> active_backoff_entry_; + + // Timer to schedule calls to StartNextRequest, if the first pending request + // hasn't passed its release time yet. + base::Timer timer_; +}; + +// Iterator class that wraps a std::deque<> iterator, only giving access to the +// actual request part of each item. +template <typename T> +class RequestQueue<T>::iterator { + public: + iterator() {} + + T* operator*() { return it_->request.get(); } + T* operator->() { return it_->request.get(); } + iterator& operator++() { + ++it_; + return *this; + } + bool operator!=(const iterator& b) const { return it_ != b.it_; } + + private: + friend class RequestQueue<T>; + typedef std::deque<typename RequestQueue<T>::Request> Container; + + explicit iterator(const typename Container::iterator& it) : it_(it) {} + + typename Container::iterator it_; +}; + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_UPDATER_REQUEST_QUEUE_H_ diff --git a/extensions/browser/updater/request_queue_impl.h b/extensions/browser/updater/request_queue_impl.h new file mode 100644 index 0000000..309dc82 --- /dev/null +++ b/extensions/browser/updater/request_queue_impl.h @@ -0,0 +1,154 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// 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_IMPL_H_ +#define EXTENSIONS_BROWSER_UPDATER_REQUEST_QUEUE_IMPL_H_ + +#include <algorithm> + +#include "base/bind.h" +#include "base/compiler_specific.h" +#include "base/message_loop/message_loop.h" +#include "base/stl_util.h" +#include "extensions/browser/updater/request_queue.h" + +namespace extensions { + +template <typename T> +RequestQueue<T>::RequestQueue( + const net::BackoffEntry::Policy* const backoff_policy, + const base::Closure& start_request_callback) + : backoff_policy_(backoff_policy), + start_request_callback_(start_request_callback), + timer_(false, false) { +} + +template <typename T> +RequestQueue<T>::~RequestQueue() { +} + +template <typename T> +T* RequestQueue<T>::active_request() { + return active_request_.get(); +} + +template <typename T> +int RequestQueue<T>::active_request_failure_count() { + return active_backoff_entry_->failure_count(); +} + +template <typename T> +scoped_ptr<T> RequestQueue<T>::reset_active_request() { + active_backoff_entry_.reset(); + return active_request_.Pass(); +} + +template <typename T> +void RequestQueue<T>::ScheduleRequest(scoped_ptr<T> request) { + PushImpl( + request.Pass(), + scoped_ptr<net::BackoffEntry>(new net::BackoffEntry(backoff_policy_))); + StartNextRequest(); +} + +template <typename T> +void RequestQueue<T>::PushImpl(scoped_ptr<T> request, + scoped_ptr<net::BackoffEntry> backoff_entry) { + pending_requests_.push_back( + Request(backoff_entry.release(), request.release())); + std::push_heap( + pending_requests_.begin(), pending_requests_.end(), CompareRequests); +} + +template <typename T> +bool RequestQueue<T>::empty() const { + return pending_requests_.empty(); +} + +template <typename T> +size_t RequestQueue<T>::size() const { + return pending_requests_.size(); +} + +template <typename T> +base::TimeTicks RequestQueue<T>::NextReleaseTime() const { + return pending_requests_.front().backoff_entry->GetReleaseTime(); +} + +template <typename T> +void RequestQueue<T>::StartNextRequest() { + if (active_request_) + // Already running a request, assume this method will be called again when + // the request is done. + return; + + if (empty()) + // No requests in the queue, so we're done. + return; + + base::TimeTicks next_release = NextReleaseTime(); + base::TimeTicks now = base::TimeTicks::Now(); + if (next_release > now) { + // Not ready for the next update check yet, call this method when it is + // time. + timer_.Start( + FROM_HERE, + next_release - now, + base::Bind(&RequestQueue<T>::StartNextRequest, base::Unretained(this))); + return; + } + + // pop_heap swaps the first and last elements of pending_requests_, and after + // that assures that the rest of pending_requests_ (excluding the + // now last/formerly first element) forms a proper heap. After pop_heap + // [begin, end-1) is a valid heap, and *(end - 1) contains the element that + // used to be at the top of the heap. Since no elements are actually + // removed from the container it is safe to read the entry being removed after + // pop_heap is called (but before pop_back is called). + std::pop_heap( + pending_requests_.begin(), pending_requests_.end(), CompareRequests); + + active_backoff_entry_.reset(pending_requests_.back().backoff_entry.release()); + active_request_.reset(pending_requests_.back().request.release()); + + pending_requests_.pop_back(); + + start_request_callback_.Run(); +} + +template <typename T> +void RequestQueue<T>::RetryRequest(const base::TimeDelta& min_backoff_delay) { + active_backoff_entry_->InformOfRequest(false); + if (active_backoff_entry_->GetTimeUntilRelease() < min_backoff_delay) { + active_backoff_entry_->SetCustomReleaseTime(base::TimeTicks::Now() + + min_backoff_delay); + } + PushImpl(active_request_.Pass(), active_backoff_entry_.Pass()); +} + +template <typename T> +typename RequestQueue<T>::iterator RequestQueue<T>::begin() { + return iterator(pending_requests_.begin()); +} + +template <typename T> +typename RequestQueue<T>::iterator RequestQueue<T>::end() { + return iterator(pending_requests_.end()); +} + +template <typename T> +void RequestQueue<T>::set_backoff_policy( + const net::BackoffEntry::Policy* backoff_policy) { + backoff_policy_ = backoff_policy; +} + +// static +template <typename T> +bool RequestQueue<T>::CompareRequests(const Request& a, const Request& b) { + return a.backoff_entry->GetReleaseTime() > b.backoff_entry->GetReleaseTime(); +} + +} // namespace extensions + +#endif // EXTENSIONS_BROWSER_UPDATER_REQUEST_QUEUE_IMPL_H_ |