diff options
author | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-22 02:16:44 +0000 |
---|---|---|
committer | kaiwang@chromium.org <kaiwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-22 02:16:44 +0000 |
commit | 01b654b4bbee5186b3e379bc2324e571a1c8c4fd (patch) | |
tree | 2132e78faee00b70e7a0c551da345d526dc7f476 /chrome/browser/common | |
parent | 57d4066b07153ea621a44c5e6f20a29f732cee9b (diff) | |
download | chromium_src-01b654b4bbee5186b3e379bc2324e571a1c8c4fd.zip chromium_src-01b654b4bbee5186b3e379bc2324e571a1c8c4fd.tar.gz chromium_src-01b654b4bbee5186b3e379bc2324e571a1c8c4fd.tar.bz2 |
Move cancelable_request.cc/h to chrome/browser/common.
It is a commonly shared utility.
Also move "!chrome/browser/browser_process.h" to test section in bookmarks/DEPS
BUG=144783
Review URL: https://codereview.chromium.org/10958031
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@158151 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/common')
-rw-r--r-- | chrome/browser/common/cancelable_request.cc | 140 | ||||
-rw-r--r-- | chrome/browser/common/cancelable_request.h | 1016 |
2 files changed, 1156 insertions, 0 deletions
diff --git a/chrome/browser/common/cancelable_request.cc b/chrome/browser/common/cancelable_request.cc new file mode 100644 index 0000000..39fb29a --- /dev/null +++ b/chrome/browser/common/cancelable_request.cc @@ -0,0 +1,140 @@ +// 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. + +#include "chrome/browser/common/cancelable_request.h" + +CancelableRequestProvider::CancelableRequestProvider() + : next_handle_(1) { +} + +CancelableRequestProvider::Handle CancelableRequestProvider::AddRequest( + CancelableRequestBase* request, + CancelableRequestConsumerBase* consumer) { + Handle handle; + { + base::AutoLock lock(pending_request_lock_); + + handle = next_handle_; + pending_requests_[next_handle_] = request; + ++next_handle_; + DCHECK(next_handle_) + << "next_handle_ may have wrapped around to invalid state."; + } + + consumer->OnRequestAdded(this, handle); + + request->Init(this, handle, consumer); + return handle; +} + +void CancelableRequestProvider::CancelRequest(Handle handle) { + base::AutoLock lock(pending_request_lock_); + CancelRequestLocked(pending_requests_.find(handle)); +} + +CancelableRequestProvider::~CancelableRequestProvider() { + // There may be requests whose result callback has not been run yet. We need + // to cancel them otherwise they may try and call us back after we've been + // deleted, or do other bad things. This can occur on shutdown (or browser + // context destruction) when a request is scheduled, completed (but not + // dispatched), then the BrowserContext is deleted. + base::AutoLock lock(pending_request_lock_); + while (!pending_requests_.empty()) + CancelRequestLocked(pending_requests_.begin()); +} + +void CancelableRequestProvider::CancelRequestLocked( + const CancelableRequestMap::iterator& item) { + pending_request_lock_.AssertAcquired(); + if (item == pending_requests_.end()) { + NOTREACHED() << "Trying to cancel an unknown request"; + return; + } + + item->second->consumer()->OnRequestRemoved(this, item->first); + item->second->set_canceled(); + pending_requests_.erase(item); +} + +void CancelableRequestProvider::RequestCompleted(Handle handle) { + CancelableRequestConsumerBase* consumer = NULL; + { + base::AutoLock lock(pending_request_lock_); + + CancelableRequestMap::iterator i = pending_requests_.find(handle); + if (i == pending_requests_.end()) { + NOTREACHED() << "Trying to cancel an unknown request"; + return; + } + consumer = i->second->consumer(); + + // This message should only get sent if the class is not cancelled, or + // else the consumer might be gone). + DCHECK(!i->second->canceled()); + + pending_requests_.erase(i); + } + + // Notify the consumer that the request is gone + consumer->OnRequestRemoved(this, handle); +} + +// MSVC doesn't like complex extern templates and DLLs. +#if !defined(COMPILER_MSVC) +// Emit our most common CancelableRequestConsumer. +template class CancelableRequestConsumerTSimple<int>; + +// And the most common subclass of it. +template class CancelableRequestConsumerT<int, 0>; +#endif + +CancelableRequestBase::CancelableRequestBase() + : provider_(NULL), + consumer_(NULL), + handle_(0) { + callback_thread_ = MessageLoop::current(); +} + +CancelableRequestBase::~CancelableRequestBase() { +} + +void CancelableRequestBase::Init(CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle, + CancelableRequestConsumerBase* consumer) { + DCHECK(handle_ == 0 && provider_ == NULL && consumer_ == NULL); + provider_ = provider; + consumer_ = consumer; + handle_ = handle; +} + +void CancelableRequestBase::DoForward(const base::Closure& forwarded_call, + bool force_async) { + if (force_async || callback_thread_ != MessageLoop::current()) { + callback_thread_->PostTask( + FROM_HERE, + base::Bind(&CancelableRequestBase::ExecuteCallback, this, + forwarded_call)); + } else { + // We can do synchronous callbacks when we're on the same thread. + ExecuteCallback(forwarded_call); + } +} + +void CancelableRequestBase::ExecuteCallback( + const base::Closure& forwarded_call) { + DCHECK_EQ(callback_thread_, MessageLoop::current()); + + if (!canceled_.IsSet()) { + WillExecute(); + + // Execute the callback. + forwarded_call.Run(); + } + + // Notify the provider that the request is complete. The provider will + // notify the consumer for us. Note that it is possible for the callback to + // cancel this request; we must check canceled again. + if (!canceled_.IsSet()) + NotifyCompleted(); +} diff --git a/chrome/browser/common/cancelable_request.h b/chrome/browser/common/cancelable_request.h new file mode 100644 index 0000000..a43e60b --- /dev/null +++ b/chrome/browser/common/cancelable_request.h @@ -0,0 +1,1016 @@ +// 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. + +// CancelableRequestProviders and Consumers work together to make requests that +// execute on a background thread in the provider and return data to the +// consumer. These classes collaborate to keep a list of open requests and to +// make sure that requests do not outlive either of the objects involved in the +// transaction. +// +// If you do not need to return data to the consumer, do not use this system, +// just use the regular Task/RunnableMethod stuff. +// +// The CancelableRequest object is used internally to each provider to track +// request data and callback information. +// +// Example consumer calling |StartRequest| on a frontend service: +// +// class MyClass { +// void MakeRequest() { +// frontend_service->StartRequest( +// some_input1, some_input2, +// &callback_consumer_, +// base::Bind(&MyClass:RequestComplete, base::Unretained(this))); +// // StartRequest() returns a Handle which may be retained for use with +// // CancelRequest() if required, e.g. in MyClass's destructor. +// } +// +// void RequestComplete(int status) { +// ... +// } +// +// private: +// CancelableRequestConsumer callback_consumer_; +// }; +// +// +// Example frontend provider. It receives requests and forwards them to the +// backend on another thread: +// +// class Frontend : public CancelableRequestProvider { +// typedef Callback1<int>::Type RequestCallbackType; +// +// Handle StartRequest(int some_input1, int some_input2, +// CancelableRequestConsumerBase* consumer, +// RequestCallbackType* callback) { +// scoped_refptr<CancelableRequest<RequestCallbackType> > request( +// new CancelableRequest<RequestCallbackType>(callback)); +// AddRequest(request, consumer); +// +// // Send the parameters and the request to the backend thread. +// backend_thread_->PostTask( +// FROM_HERE, +// base::Bind(&Backend::DoRequest, backend_, request, +// some_input1, some_input2)); +// +// // The handle will have been set by AddRequest. +// return request->handle(); +// } +// }; +// +// +// Example backend provider that does work and dispatches the callback back +// to the original thread. Note that we need to pass it as a scoped_refptr so +// that the object will be kept alive if the request is canceled (releasing +// the provider's reference to it). +// +// class Backend { +// void DoRequest( +// scoped_refptr< CancelableRequest<Frontend::RequestCallbackType> > +// request, +// int some_input1, int some_input2) { +// if (request->canceled()) +// return; +// +// ... do your processing ... +// +// // Depending on your typedefs, one of these two forms will be more +// // convenient: +// request->ForwardResult(Tuple1<int>(return_value)); +// +// // -- or -- (inferior in this case) +// request->ForwardResult(Frontend::RequestCallbackType::TupleType( +// return_value)); +// } +// }; + +#ifndef CHROME_BROWSER_COMMON_CANCELABLE_REQUEST_H_ +#define CHROME_BROWSER_COMMON_CANCELABLE_REQUEST_H_ + +#include <map> +#include <vector> + +#include "base/basictypes.h" +#include "base/bind.h" +#include "base/callback.h" +#include "base/callback_internal.h" +#include "base/logging.h" +#include "base/memory/ref_counted.h" +#include "base/memory/scoped_ptr.h" +#include "base/message_loop.h" +#include "base/synchronization/cancellation_flag.h" +#include "base/synchronization/lock.h" +#include "build/build_config.h" + +class CancelableRequestBase; +class CancelableRequestConsumerBase; + +// CancelableRequestProvider -------------------------------------------------- +// +// This class is threadsafe. Requests may be added or canceled from any thread, +// but a task must only be canceled from the same thread it was initially run +// on. +// +// It is intended that providers inherit from this class to provide the +// necessary functionality. + +class CancelableRequestProvider { + public: + // Identifies a specific request from this provider. + typedef int Handle; + + CancelableRequestProvider(); + + // Called by the enduser of the request to cancel it. This MUST be called on + // the same thread that originally issued the request (which is also the same + // thread that would have received the callback if it was not canceled). + // handle must be for a valid pending (not yet complete or cancelled) request. + void CancelRequest(Handle handle); + + protected: + virtual ~CancelableRequestProvider(); + + // Adds a new request and initializes it. This is called by a derived class + // to add a new request. The request's Init() will be called (which is why + // the consumer is required. The handle to the new request is returned. + Handle AddRequest(CancelableRequestBase* request, + CancelableRequestConsumerBase* consumer); + + // Called by the CancelableRequest when the request has executed. It will + // be removed from the list of pending requests (as opposed to canceling, + // which will also set some state on the request). + void RequestCompleted(Handle handle); + + private: + typedef std::map<Handle, scoped_refptr<CancelableRequestBase> > + CancelableRequestMap; + + // Only call this when you already have acquired pending_request_lock_. + void CancelRequestLocked(const CancelableRequestMap::iterator& item); + + friend class CancelableRequestBase; + + base::Lock pending_request_lock_; + + // Lists all outstanding requests. Protected by the |lock_|. + CancelableRequestMap pending_requests_; + + // The next handle value we will return. Protected by the |lock_|. + int next_handle_; + + DISALLOW_COPY_AND_ASSIGN(CancelableRequestProvider); +}; + +// CancelableRequestConsumer -------------------------------------------------- +// +// Classes wishing to make requests on a provider should have an instance of +// this class. Callers will need to pass a pointer to this consumer object +// when they make the request. It will automatically track any pending +// requests, and will automatically cancel them on destruction to prevent the +// accidental calling of freed memory. +// +// It is recommended to just have this class as a member variable since there +// is nothing to be gained by inheriting from it other than polluting your +// namespace. +// +// THIS CLASS IS NOT THREADSAFE (unlike the provider). You must make requests +// and get callbacks all from the same thread. + +// Base class used to notify of new requests. +class CancelableRequestConsumerBase { + protected: + friend class CancelableRequestBase; + friend class CancelableRequestProvider; + + virtual ~CancelableRequestConsumerBase() { + } + + // Adds a new request to the list of requests that are being tracked. This + // is called by the provider when a new request is created. + virtual void OnRequestAdded(CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle) = 0; + + // Removes the given request from the list of pending requests. Called + // by the CancelableRequest immediately after the callback has executed for a + // given request, and by the provider when a request is canceled. + virtual void OnRequestRemoved(CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle) = 0; + + // Sent to provider before executing a callback. + virtual void WillExecute(CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle) = 0; + + // Sent after executing a callback. + virtual void DidExecute(CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle) = 0; +}; + +// Template for clients to use. It allows them to associate random "client +// data" with a specific request. The default value for this type is 0. +// The type T should be small and easily copyable (like a pointer +// or an integer). +template<class T> +class CancelableRequestConsumerTSimple + : public CancelableRequestConsumerBase { + public: + CancelableRequestConsumerTSimple(); + + // Cancel any outstanding requests so that we do not get called back after we + // are destroyed. As these requests are removed, the providers will call us + // back on OnRequestRemoved, which will then update the list. To iterate + // successfully while the list is changing out from under us, we make a copy. + virtual ~CancelableRequestConsumerTSimple(); + + // Associates some random data with a specified request. The request MUST be + // outstanding, or it will assert. This is intended to be called immediately + // after a request is issued. + void SetClientData(CancelableRequestProvider* p, + CancelableRequestProvider::Handle h, + T client_data); + + // Retrieves previously associated data for a specified request. The request + // MUST be outstanding, or it will assert. This is intended to be called + // during processing of a callback to retrieve extra data. + T GetClientData(CancelableRequestProvider* p, + CancelableRequestProvider::Handle h); + + // Returns the data associated with the current request being processed. This + // is only valid during the time a callback is being processed. + T GetClientDataForCurrentRequest(); + + // Returns true if there are any pending requests. + bool HasPendingRequests() const; + + // Returns the number of pending requests. + size_t PendingRequestCount() const; + + // Cancels all requests outstanding. + void CancelAllRequests(); + + // Cancels all requests outstanding matching the client data. + void CancelAllRequestsForClientData(T client_data); + + // Returns the handle for the first request that has the specified client data + // (in |handle|). Returns true if there is a request for the specified client + // data, false otherwise. + bool GetFirstHandleForClientData(T client_data, + CancelableRequestProvider::Handle* handle); + + // Gets the client data for all pending requests. + void GetAllClientData(std::vector<T>* data); + + protected: + struct PendingRequest { + PendingRequest(CancelableRequestProvider* p, + CancelableRequestProvider::Handle h) + : provider(p), handle(h) { + } + + PendingRequest() : provider(NULL), handle(0) {} + + // Comparison operator for stl. + bool operator<(const PendingRequest& other) const { + if (provider != other.provider) + return provider < other.provider; + return handle < other.handle; + } + + bool is_valid() const { return provider != NULL; } + + CancelableRequestProvider* provider; + CancelableRequestProvider::Handle handle; + }; + typedef std::map<PendingRequest, T> PendingRequestList; + + virtual T get_initial_t() const; + + virtual void OnRequestAdded(CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle); + + virtual void OnRequestRemoved(CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle); + + virtual void WillExecute(CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle); + + virtual void DidExecute(CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle); + + // Lists all outstanding requests. + PendingRequestList pending_requests_; + + // This is valid while processing a request and is used to identify the + // provider/handle of request. + PendingRequest current_request_; +}; + +template<class T> +CancelableRequestConsumerTSimple<T>::CancelableRequestConsumerTSimple() { +} + +template<class T> +CancelableRequestConsumerTSimple<T>::~CancelableRequestConsumerTSimple() { + CancelAllRequests(); +} + +template<class T> +void CancelableRequestConsumerTSimple<T>::SetClientData( + CancelableRequestProvider* p, + CancelableRequestProvider::Handle h, + T client_data) { + PendingRequest request(p, h); + DCHECK(pending_requests_.find(request) != pending_requests_.end()); + pending_requests_[request] = client_data; +} + +template<class T> +T CancelableRequestConsumerTSimple<T>::GetClientData( + CancelableRequestProvider* p, + CancelableRequestProvider::Handle h) { + PendingRequest request(p, h); + DCHECK(pending_requests_.find(request) != pending_requests_.end()); + return pending_requests_[request]; +} + +template<class T> +T CancelableRequestConsumerTSimple<T>::GetClientDataForCurrentRequest() { + DCHECK(current_request_.is_valid()); + return GetClientData(current_request_.provider, current_request_.handle); +} + +template<class T> +bool CancelableRequestConsumerTSimple<T>::HasPendingRequests() const { + return !pending_requests_.empty(); +} + +template<class T> +size_t CancelableRequestConsumerTSimple<T>::PendingRequestCount() const { + return pending_requests_.size(); +} + +template<class T> +void CancelableRequestConsumerTSimple<T>::CancelAllRequests() { + // TODO(atwilson): This code is not thread safe as it is called from the + // consumer thread (via the destructor) and accesses pending_requests_ + // without acquiring the provider lock (http://crbug.com/85970). + PendingRequestList copied_requests(pending_requests_); + for (typename PendingRequestList::iterator i = copied_requests.begin(); + i != copied_requests.end(); ++i) { + i->first.provider->CancelRequest(i->first.handle); + } + copied_requests.clear(); + + // That should have cleared all the pending items. + DCHECK(pending_requests_.empty()); +} + +template<class T> +void CancelableRequestConsumerTSimple<T>::CancelAllRequestsForClientData( + T client_data) { + PendingRequestList copied_requests(pending_requests_); + for (typename PendingRequestList::const_iterator i = copied_requests.begin(); + i != copied_requests.end(); ++i) { + if (i->second == client_data) + i->first.provider->CancelRequest(i->first.handle); + } + copied_requests.clear(); +} + +template<class T> +bool CancelableRequestConsumerTSimple<T>::GetFirstHandleForClientData( + T client_data, + CancelableRequestProvider::Handle* handle) { + for (typename PendingRequestList::const_iterator i = + pending_requests_.begin(); i != pending_requests_.end(); ++i) { + if (i->second == client_data) { + *handle = i->first.handle; + return true; + } + } + *handle = 0; + return false; +} + +template<class T> +void CancelableRequestConsumerTSimple<T>::GetAllClientData( + std::vector<T>* data) { + DCHECK(data); + for (typename PendingRequestList::iterator i = pending_requests_.begin(); + i != pending_requests_.end(); ++i) + data->push_back(i->second); +} + +template<class T> +T CancelableRequestConsumerTSimple<T>::get_initial_t() const { + return T(); +} + +template<class T> +void CancelableRequestConsumerTSimple<T>::OnRequestAdded( + CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle) { + // Make sure we're not mixing/matching providers (since the provider is + // responsible for providing thread safety until http://crbug.com/85970 is + // fixed, we can't use the same consumer with multiple providers). +#ifndef NDEBUG + if (!pending_requests_.empty()) + DCHECK(pending_requests_.begin()->first.provider == provider); +#endif + DCHECK(pending_requests_.find(PendingRequest(provider, handle)) == + pending_requests_.end()); + pending_requests_[PendingRequest(provider, handle)] = get_initial_t(); +} + +template<class T> +void CancelableRequestConsumerTSimple<T>::OnRequestRemoved( + CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle) { + typename PendingRequestList::iterator i = + pending_requests_.find(PendingRequest(provider, handle)); + if (i == pending_requests_.end()) { + NOTREACHED() << "Got a complete notification for a nonexistent request"; + return; + } + + pending_requests_.erase(i); +} + +template<class T> +void CancelableRequestConsumerTSimple<T>::WillExecute( + CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle) { + current_request_ = PendingRequest(provider, handle); +} + +template<class T> +void CancelableRequestConsumerTSimple<T>::DidExecute( + CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle) { + current_request_ = PendingRequest(); +} + +// See CancelableRequestConsumerTSimple. The default value for T +// is given in |initial_t|. +template<class T, T initial_t> +class CancelableRequestConsumerT + : public CancelableRequestConsumerTSimple<T> { + public: + CancelableRequestConsumerT(); + virtual ~CancelableRequestConsumerT(); + + protected: + virtual T get_initial_t() const; +}; + +template<class T, T initial_t> +CancelableRequestConsumerT<T, initial_t>::CancelableRequestConsumerT() { +} + +template<class T, T initial_t> +CancelableRequestConsumerT<T, initial_t>::~CancelableRequestConsumerT() { +} + +template<class T, T initial_t> +T CancelableRequestConsumerT<T, initial_t>::get_initial_t() const { + return initial_t; +} + +// Some clients may not want to store data. Rather than do some complicated +// thing with virtual functions to allow some consumers to store extra data and +// some not to, we just define a default one that stores some dummy data. +typedef CancelableRequestConsumerT<int, 0> CancelableRequestConsumer; + +// MSVC doesn't like complex extern templates and DLLs. +#if !defined(COMPILER_MSVC) +// The vast majority of CancelableRequestConsumers are instantiated on <int>, +// so prevent that template from being expanded in normal code. +extern template class CancelableRequestConsumerTSimple<int>; + +// We'll also want to extern-template the most common, typedef-ed +// CancelableRequestConsumerT. +extern template class CancelableRequestConsumerT<int, 0>; +#endif + +// CancelableRequest ---------------------------------------------------------- +// +// The request object that is used by a CancelableRequestProvider to send +// results to a CancelableRequestConsumer. This request handles the returning +// of results from a thread where the request is being executed to the thread +// and callback where the results are used. IT SHOULD BE PASSED AS A +// scoped_refptr TO KEEP IT ALIVE. +// +// It does not handle input parameters to the request. The caller must either +// transfer those separately or derive from this class to add the desired +// parameters. +// +// When the processing is complete on this message, the caller MUST call +// ForwardResult() with the return arguments that will be passed to the +// callback. If the request has been canceled, Return is optional (it will not +// do anything). If you do not have to return to the caller, the cancelable +// request system should not be used! (just use regular fire-and-forget tasks). +// +// Callback parameters are passed by value. In some cases, the request will +// want to return a large amount of data (for example, an image). One good +// approach is to derive from the CancelableRequest and make the data object +// (for example, a std::vector) owned by the CancelableRequest. The pointer +// to this data would be passed for the callback parameter. Since the +// CancelableRequest outlives the callback call, the data will be valid on the +// other thread for the callback, but will still be destroyed properly. + +// Non-templatized base class that provides cancellation +class CancelableRequestBase + : public base::RefCountedThreadSafe<CancelableRequestBase> { + public: + friend class CancelableRequestProvider; + + // Initializes most things to empty, Init() must be called to complete + // initialization of the object. This will be done by the provider when the + // request is dispatched. + // + // This must be called on the same thread the callback will be executed on, it + // will save that thread for later. + // + // This two-phase init is done so that the constructor can have no parameters, + // which makes it much more convenient for derived classes, which can be + // common. The derived classes need only declare the variables they provide in + // the constructor rather than many lines of internal tracking data that are + // passed to the base class (us). + // + // In addition, not all of the information (for example, the handle) is known + // at construction time. + CancelableRequestBase(); + + CancelableRequestConsumerBase* consumer() const { + return consumer_; + } + + CancelableRequestProvider::Handle handle() const { + return handle_; + } + + // The canceled flag indicates that the request should not be executed. + // A request can never be uncanceled, so only a setter for true is provided. + // This can be called multiple times, but only from one thread. + void set_canceled() { + canceled_.Set(); + } + bool canceled() { + return canceled_.IsSet(); + } + + protected: + friend class base::RefCountedThreadSafe<CancelableRequestBase>; + virtual ~CancelableRequestBase(); + + // Initializes the object with the particulars from the provider. It may only + // be called once (it is called by the provider, which is a friend). + void Init(CancelableRequestProvider* provider, + CancelableRequestProvider::Handle handle, + CancelableRequestConsumerBase* consumer); + + // Attempts to execute callback on |callback_thread_| if the request has not + // been canceled yet. + void DoForward(const base::Closure& forwarded_call, bool force_async); + + // Tells the provider that the request is complete, which then tells the + // consumer. + void NotifyCompleted() const { + provider_->RequestCompleted(handle()); + consumer_->DidExecute(provider_, handle_); + } + + // Cover method for CancelableRequestConsumerBase::WillExecute. + void WillExecute() { + consumer_->WillExecute(provider_, handle_); + } + + // The message loop that this request was created on. The callback will + // happen on the same thread. + MessageLoop* callback_thread_; + + // The provider for this request. When we execute, we will notify this that + // request is complete to it can remove us from the requests it tracks. + CancelableRequestProvider* provider_; + + // Notified after we execute that the request is complete. This should only + // be accessed if !canceled_.IsSet(), otherwise the pointer is invalid. + CancelableRequestConsumerBase* consumer_; + + // The handle to this request inside the provider. This will be initialized + // to 0 when the request is created, and the provider will set it once the + // request has been dispatched. + CancelableRequestProvider::Handle handle_; + + // Set if the caller cancels this request. No callbacks should be made when + // this is set. + base::CancellationFlag canceled_; + + private: + // Executes the |forwarded_call| and notifies the provider and the consumer + // that this request has been completed. This must be called on the + // |callback_thread_|. + void ExecuteCallback(const base::Closure& forwarded_call); + + DISALLOW_COPY_AND_ASSIGN(CancelableRequestBase); +}; + +// Templatized class. This is the one you should use directly or inherit from. +// The callback can be invoked by calling the ForwardResult() method. For this, +// you must either pack the parameters into a tuple, or use DispatchToMethod +// (in tuple.h). +// +// If you inherit to add additional input parameters or to do more complex +// memory management (see the bigger comment about this above), you can put +// those on a subclass of this. +// +// We have decided to allow users to treat derived classes of this as structs, +// so you can add members without getters and setters (which just makes the +// code harder to read). Don't use underscores after these vars. For example: +// +// typedef Callback1<int>::Type DoodieCallback; +// +// class DoodieRequest : public CancelableRequest<DoodieCallback> { +// public: +// DoodieRequest(CallbackType* callback) : CancelableRequest(callback) { +// } +// +// private: +// ~DoodieRequest() {} +// +// int input_arg1; +// std::wstring input_arg2; +// }; +template<typename CB> +class CancelableRequest : public CancelableRequestBase { + public: + // TODO(ajwong): After all calls have been migrated, remove this template in + // favor of the specialization on base::Callback<Sig>. + typedef CB CallbackType; // CallbackRunner<...> + typedef typename CB::TupleType TupleType; // Tuple of the callback args. + + // The provider MUST call Init() (on the base class) before this is valid. + // This class will take ownership of the callback object and destroy it when + // appropriate. + explicit CancelableRequest(CallbackType* callback) + : CancelableRequestBase(), + callback_(callback) { + DCHECK(callback) << "We should always have a callback"; + } + + // Dispatches the parameters to the correct thread so the callback can be + // executed there. The caller does not need to check for cancel before + // calling this. It is optional in the cancelled case. In the non-cancelled + // case, this MUST be called. + // + // If there are any pointers in the parameters, they must live at least as + // long as the request so that it can be forwarded to the other thread. + // For complex objects, this would typically be done by having a derived + // request own the data itself. + void ForwardResult(const TupleType& param) { + DCHECK(callback_.get()); + if (!canceled()) { + if (callback_thread_ == MessageLoop::current()) { + // We can do synchronous callbacks when we're on the same thread. + ExecuteCallback(param); + } else { + callback_thread_->PostTask( + FROM_HERE, + base::Bind(&CancelableRequest<CB>::ExecuteCallback, this, param)); + } + } + } + + // Like |ForwardResult| but this never does a synchronous callback. + void ForwardResultAsync(const TupleType& param) { + DCHECK(callback_.get()); + if (!canceled()) { + callback_thread_->PostTask( + FROM_HERE, + base::Bind(&CancelableRequest<CB>::ExecuteCallback, this, param)); + } + } + + protected: + virtual ~CancelableRequest() {} + + private: + // Executes the callback and notifies the provider and the consumer that this + // request has been completed. This must be called on the callback_thread_. + void ExecuteCallback(const TupleType& param) { + if (!canceled_.IsSet()) { + WillExecute(); + + // Execute the callback. + callback_->RunWithParams(param); + } + + // Notify the provider that the request is complete. The provider will + // notify the consumer for us. Note that it is possible for the callback to + // cancel this request; we must check canceled again. + if (!canceled_.IsSet()) + NotifyCompleted(); + } + + // This should only be executed if !canceled_.IsSet(), + // otherwise the pointers may be invalid. + scoped_ptr<CallbackType> callback_; +}; + +// CancelableRequest<> wraps a normal base::Callback<> for use in the +// CancelableRequest framework. +// +// When using this class, the provider MUST call Init() (on the base class) +// before this is valid. +// +// The two functions ForwardResult(), and ForwardResultAsync() are used to +// invoke the wrapped callback on the correct thread. They are the same +// except that ForwardResult() will run the callback synchronously if +// we are already on the right thread. +// +// The caller does not need to check for cancel before calling ForwardResult() +// or ForwardResultAsync(). If the request has not been canceled, one of the +// these MUST be called. +// +// If there are any pointers in the parameters, they must live at least as +// long as the request so that it can be forwarded to the other thread. +// For complex objects, this would typically be done by having a derived +// request own the data itself. +// +// The following specializations handle different arities of base::Callbacks<>. +template<> +class CancelableRequest<base::Closure> : public CancelableRequestBase { + public: + typedef base::Closure CallbackType; + + explicit CancelableRequest(const CallbackType& callback) + : CancelableRequestBase(), + callback_(callback) { + DCHECK(!callback.is_null()) << "Callback must be initialized."; + } + + void ForwardResult(void) { + if (canceled()) return; + DoForward(callback_, false); + } + + void ForwardResultAsync() { + if (canceled()) return; + DoForward(callback_, true); + } + + protected: + virtual ~CancelableRequest() {} + + private: + CallbackType callback_; +}; + +template<typename A1> +class CancelableRequest<base::Callback<void(A1)> > + : public CancelableRequestBase { + public: + typedef base::Callback<void(A1)> CallbackType; + + explicit CancelableRequest(const CallbackType& callback) + : CancelableRequestBase(), + callback_(callback) { + DCHECK(!callback.is_null()) << "Callback must be initialized."; + } + + void ForwardResult( + typename base::internal::CallbackParamTraits<A1>::ForwardType a1) { + if (canceled()) return; + DoForward(base::Bind(callback_, a1), false); + } + + void ForwardResultAsync( + typename base::internal::CallbackParamTraits<A1>::ForwardType a1) { + if (canceled()) return; + DoForward(base::Bind(callback_, a1), true); + } + + protected: + virtual ~CancelableRequest() {} + + private: + CallbackType callback_; +}; + +template<typename A1, typename A2> +class CancelableRequest<base::Callback<void(A1,A2)> > + : public CancelableRequestBase { + public: + typedef base::Callback<void(A1,A2)> CallbackType; + + explicit CancelableRequest(const CallbackType& callback) + : CancelableRequestBase(), + callback_(callback) { + DCHECK(!callback.is_null()) << "Callback must be initialized."; + } + + void ForwardResult( + typename base::internal::CallbackParamTraits<A1>::ForwardType a1, + typename base::internal::CallbackParamTraits<A2>::ForwardType a2) { + if (canceled()) return; + DoForward(base::Bind(callback_, a1, a2), false); + } + + void ForwardResultAsync( + typename base::internal::CallbackParamTraits<A1>::ForwardType a1, + typename base::internal::CallbackParamTraits<A2>::ForwardType a2) { + if (canceled()) return; + DoForward(base::Bind(callback_, a1, a2), true); + } + + protected: + virtual ~CancelableRequest() {} + + private: + CallbackType callback_; +}; + +template<typename A1, typename A2, typename A3> +class CancelableRequest<base::Callback<void(A1,A2,A3)> > + : public CancelableRequestBase { + public: + typedef base::Callback<void(A1,A2,A3)> CallbackType; + + explicit CancelableRequest(const CallbackType& callback) + : CancelableRequestBase(), + callback_(callback) { + DCHECK(!callback.is_null()) << "Callback must be initialized."; + } + + void ForwardResult( + typename base::internal::CallbackParamTraits<A1>::ForwardType a1, + typename base::internal::CallbackParamTraits<A2>::ForwardType a2, + typename base::internal::CallbackParamTraits<A3>::ForwardType a3) { + if (canceled()) return; + DoForward(base::Bind(callback_, a1, a2, a3), false); + } + + void ForwardResultAsync( + typename base::internal::CallbackParamTraits<A1>::ForwardType a1, + typename base::internal::CallbackParamTraits<A2>::ForwardType a2, + typename base::internal::CallbackParamTraits<A3>::ForwardType a3) { + if (canceled()) return; + DoForward(base::Bind(callback_, a1, a2, a3), true); + } + + protected: + virtual ~CancelableRequest() {} + + private: + CallbackType callback_; +}; + +template<typename A1, typename A2, typename A3, typename A4> +class CancelableRequest<base::Callback<void(A1, A2, A3, A4)> > + : public CancelableRequestBase { + public: + typedef base::Callback<void(A1, A2, A3, A4)> CallbackType; + + explicit CancelableRequest(const CallbackType& callback) + : CancelableRequestBase(), + callback_(callback) { + DCHECK(!callback.is_null()) << "Callback must be initialized."; + } + + void ForwardResult( + typename base::internal::CallbackParamTraits<A1>::ForwardType a1, + typename base::internal::CallbackParamTraits<A2>::ForwardType a2, + typename base::internal::CallbackParamTraits<A3>::ForwardType a3, + typename base::internal::CallbackParamTraits<A4>::ForwardType a4) { + if (canceled()) return; + DoForward(base::Bind(callback_, a1, a2, a3, a4), false); + } + + void ForwardResultAsync( + typename base::internal::CallbackParamTraits<A1>::ForwardType a1, + typename base::internal::CallbackParamTraits<A2>::ForwardType a2, + typename base::internal::CallbackParamTraits<A3>::ForwardType a3, + typename base::internal::CallbackParamTraits<A4>::ForwardType a4) { + if (canceled()) return; + DoForward(base::Bind(callback_, a1, a2, a3, a4), true); + } + + protected: + virtual ~CancelableRequest() {} + + private: + CallbackType callback_; +}; + +template<typename A1, typename A2, typename A3, typename A4, typename A5> +class CancelableRequest<base::Callback<void(A1, A2, A3, A4, A5)> > + : public CancelableRequestBase { + public: + typedef base::Callback<void(A1, A2, A3, A4, A5)> CallbackType; + + explicit CancelableRequest(const CallbackType& callback) + : CancelableRequestBase(), + callback_(callback) { + DCHECK(!callback.is_null()) << "Callback must be initialized."; + } + + void ForwardResult( + typename base::internal::CallbackParamTraits<A1>::ForwardType a1, + typename base::internal::CallbackParamTraits<A2>::ForwardType a2, + typename base::internal::CallbackParamTraits<A3>::ForwardType a3, + typename base::internal::CallbackParamTraits<A4>::ForwardType a4, + typename base::internal::CallbackParamTraits<A5>::ForwardType a5) { + if (canceled()) return; + DoForward(base::Bind(callback_, a1, a2, a3, a4, a5), false); + } + + void ForwardResultAsync( + typename base::internal::CallbackParamTraits<A1>::ForwardType a1, + typename base::internal::CallbackParamTraits<A2>::ForwardType a2, + typename base::internal::CallbackParamTraits<A3>::ForwardType a3, + typename base::internal::CallbackParamTraits<A4>::ForwardType a4, + typename base::internal::CallbackParamTraits<A5>::ForwardType a5) { + if (canceled()) return; + DoForward(base::Bind(callback_, a1, a2, a3, a4, a5), true); + } + + protected: + virtual ~CancelableRequest() {} + + private: + CallbackType callback_; +}; + +template<typename A1, typename A2, typename A3, typename A4, typename A5, + typename A6> +class CancelableRequest<base::Callback<void(A1, A2, A3, A4, A5, A6)> > + : public CancelableRequestBase { + public: + typedef base::Callback<void(A1, A2, A3, A4, A5, A6)> CallbackType; + + explicit CancelableRequest(const CallbackType& callback) + : CancelableRequestBase(), + callback_(callback) { + DCHECK(!callback.is_null()) << "Callback must be initialized."; + } + + void ForwardResult( + typename base::internal::CallbackParamTraits<A1>::ForwardType a1, + typename base::internal::CallbackParamTraits<A2>::ForwardType a2, + typename base::internal::CallbackParamTraits<A3>::ForwardType a3, + typename base::internal::CallbackParamTraits<A4>::ForwardType a4, + typename base::internal::CallbackParamTraits<A5>::ForwardType a5, + typename base::internal::CallbackParamTraits<A6>::ForwardType a6) { + if (canceled()) return; + DoForward(base::Bind(callback_, a1, a2, a3, a4, a5, a6), false); + } + + void ForwardResultAsync( + typename base::internal::CallbackParamTraits<A1>::ForwardType a1, + typename base::internal::CallbackParamTraits<A2>::ForwardType a2, + typename base::internal::CallbackParamTraits<A3>::ForwardType a3, + typename base::internal::CallbackParamTraits<A4>::ForwardType a4, + typename base::internal::CallbackParamTraits<A5>::ForwardType a5, + typename base::internal::CallbackParamTraits<A6>::ForwardType a6) { + if (canceled()) return; + DoForward(base::Bind(callback_, a1, a2, a3, a4, a5, a6), true); + } + + protected: + virtual ~CancelableRequest() {} + + private: + CallbackType callback_; +}; + +// A CancelableRequest with a single value. This is intended for use when +// the provider provides a single value. The provider fills the result into +// the value, and notifies the request with a pointer to the value. For example, +// HistoryService has many methods that callback with a vector. Use the +// following pattern for this: +// 1. Define the callback: +// typedef Callback2<Handle, std::vector<Foo>*>::Type FooCallback; +// 2. Define the CancelableRequest1 type. +// typedef CancelableRequest1<FooCallback, std::vector<Foo>> FooRequest; +// 3. The provider method should then fill in the contents of the vector, +// forwarding the result like so: +// request->ForwardResult(FooRequest::TupleType(request->handle(), +// &request->value)); +// +// Tip: for passing more than one value, use a Tuple for the value. +template<typename CB, typename Type> +class CancelableRequest1 : public CancelableRequest<CB> { + public: + explicit CancelableRequest1( + const typename CancelableRequest<CB>::CallbackType& callback) + : CancelableRequest<CB>(callback), value() { + } + + // The value. + Type value; + + protected: + virtual ~CancelableRequest1() {} +}; + +#endif // CHROME_BROWSER_COMMON_CANCELABLE_REQUEST_H_ |