summaryrefslogtreecommitdiffstats
path: root/chrome/browser/cancelable_request.h
diff options
context:
space:
mode:
authorinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
committerinitial.commit <initial.commit@0039d316-1c4b-4281-b951-d872f2087c98>2008-07-26 23:55:29 +0000
commit09911bf300f1a419907a9412154760efd0b7abc3 (patch)
treef131325fb4e2ad12c6d3504ab75b16dd92facfed /chrome/browser/cancelable_request.h
parent586acc5fe142f498261f52c66862fa417c3d52d2 (diff)
downloadchromium_src-09911bf300f1a419907a9412154760efd0b7abc3.zip
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.gz
chromium_src-09911bf300f1a419907a9412154760efd0b7abc3.tar.bz2
Add chrome to the repository.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@15 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/cancelable_request.h')
-rw-r--r--chrome/browser/cancelable_request.h553
1 files changed, 553 insertions, 0 deletions
diff --git a/chrome/browser/cancelable_request.h b/chrome/browser/cancelable_request.h
new file mode 100644
index 0000000..f1455b7
--- /dev/null
+++ b/chrome/browser/cancelable_request.h
@@ -0,0 +1,553 @@
+// Copyright 2008, Google Inc.
+// All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+// CancelableRequestProviders and Consumers work together to make requests that
+// execute on a background thread in the provider and return data to the
+// consumer. These class collaborate to keep a list of open requests and to
+// make sure that requests to 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, this,
+// NewCallback(this, &MyClass:RequestComplete));
+// }
+//
+// void RequestComplete(int status) {
+// ...
+// }
+//
+// private:
+// CallbackConsumer 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,
+// CallbackConsumer* 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,
+// NewRunnableMethod(backend_, &Backend::DoRequest, 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_CANCELABLE_REQUEST_H__
+#define CHROME_BROWSER_CANCELABLE_REQUEST_H__
+
+#include <map>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/lock.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "base/ref_counted.h"
+#include "base/task.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();
+ virtual ~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).
+ void CancelRequest(Handle handle);
+
+ protected:
+ // 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:
+ friend class CancelableRequestBase;
+
+ typedef std::map<Handle, scoped_refptr<CancelableRequestBase> >
+ CancelableRequestMap;
+
+ 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_EVIL_CONSTRUCTORS(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 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;
+};
+
+// Template for clients to use. It allows them to associate random "client
+// data" with a specific requst. The default value for this type is given in
+// |initial_t|. The type T should be small and easily copyable (like a pointer
+// or an integer).
+template<class T, T initial_t>
+class CancelableRequestConsumerT : public CancelableRequestConsumerBase {
+ public:
+ CancelableRequestConsumerT() {
+ }
+
+ // 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 ~CancelableRequestConsumerT() {
+ CancelAllRequests();
+ }
+
+ // 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) {
+ PendingRequest request(p, h);
+ DCHECK(pending_requests_.find(request) != pending_requests_.end());
+ pending_requests_[request] = 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) {
+ PendingRequest request(p, h);
+ DCHECK(pending_requests_.find(request) != pending_requests_.end());
+ return pending_requests_[request];
+ }
+
+ // Returns true if there are any pending requests.
+ bool HasPendingRequests() const {
+ return !pending_requests_.empty();
+ }
+
+ // Returns the number of pending requests.
+ size_t PendingRequestCount() const {
+ return pending_requests_.size();
+ }
+
+ // Cancels all requests outstanding.
+ void CancelAllRequests() {
+ PendingRequestList copied_requests(pending_requests_);
+ for (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());
+ }
+
+ // Gets the client data for all pending requests.
+ void GetAllClientData(std::vector<T>* data) {
+ DCHECK(data);
+ for (PendingRequestList::iterator i = pending_requests_.begin();
+ i != pending_requests_.end(); ++i)
+ data->push_back(i->second);
+ }
+
+ protected:
+ struct PendingRequest {
+ PendingRequest(CancelableRequestProvider* p,
+ CancelableRequestProvider::Handle h)
+ : provider(p), handle(h) {
+ }
+
+ // Comparison operator for stl.
+ bool operator<(const PendingRequest& other) const {
+ if (provider != other.provider)
+ return provider < other.provider;
+ return handle < other.handle;
+ }
+
+ CancelableRequestProvider* provider;
+ CancelableRequestProvider::Handle handle;
+ };
+ typedef std::map<PendingRequest, T> PendingRequestList;
+
+ virtual void OnRequestAdded(CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle) {
+ DCHECK(pending_requests_.find(PendingRequest(provider, handle)) ==
+ pending_requests_.end());
+ pending_requests_[PendingRequest(provider, handle)] = initial_t;
+ }
+
+ virtual void OnRequestRemoved(CancelableRequestProvider* provider,
+ CancelableRequestProvider::Handle handle) {
+ PendingRequestList::iterator i =
+ pending_requests_.find(PendingRequest(provider, handle));
+ if (i == pending_requests_.end()) {
+ NOTREACHED() << "Got a complete notification for a nonexistant request";
+ return;
+ }
+
+ pending_requests_.erase(i);
+ }
+
+ // Lists all outstanding requests.
+ PendingRequestList pending_requests_;
+};
+
+// 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;
+
+// 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()
+ : provider_(NULL),
+ consumer_(NULL),
+ handle_(0),
+ canceled_(false) {
+ callback_thread_ = MessageLoop::current();
+ }
+ virtual ~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.
+ void set_canceled() {
+ canceled_ = true;
+ }
+ bool canceled() {
+ return canceled_;
+ }
+
+ protected:
+ // 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) {
+ DCHECK(handle_ == 0 && provider_ == NULL && consumer_ == NULL);
+ provider_ = provider;
+ consumer_ = consumer;
+ handle_ = handle;
+ }
+
+ // Tells the provider that the request is complete, which then tells the
+ // consumer.
+ void NotifyCompleted() const {
+ provider_->RequestCompleted(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_, 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.
+ bool canceled_;
+
+ private:
+ DISALLOW_EVIL_CONSTRUCTORS(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) {
+// }
+//
+// int input_arg1;
+// std::wstring input_arg2;
+// };
+template<typename CB>
+class CancelableRequest : public CancelableRequestBase {
+ public:
+ 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";
+ }
+ virtual ~CancelableRequest() {
+ }
+
+ // 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, NewRunnableMethod(this,
+ &CancelableRequest<CB>::ExecuteCallback, param));
+ }
+ }
+ }
+
+ 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_) {
+ // Execute the callback.
+ callback_->RunWithParams(param);
+
+ // Notify the provider that the request is complete. The provider will
+ // notify the consumer for us.
+ NotifyCompleted();
+ }
+ }
+
+ // This should only be executed if !canceled_, otherwise the pointers may be
+ // invalid.
+ scoped_ptr<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 fillin 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(CallbackType* callback)
+ : CancelableRequest(callback) {
+ }
+
+ virtual ~CancelableRequest1() {
+ }
+
+ // The value.
+ Type value;
+};
+
+#endif // CHROME_BROWSER_CANCELABLE_REQUEST_H__