Commit d4d57df4 authored by jhawkins@chromium.org's avatar jhawkins@chromium.org

base::Bind: Implement a 1-arity CancelableCallback and use this to implement

net::CancelableCompletionClosure.

BUG=none
TEST=none

R=ajwong@chromium.org

Review URL: http://codereview.chromium.org/8662047

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@112279 0039d316-1c4b-4281-b951-d872f2087c98
parent 43cb919a
......@@ -61,7 +61,6 @@
'callback_internal.cc',
'callback_internal.h',
'callback_old.h',
'cancelable_callback.cc',
'cancelable_callback.h',
'command_line.cc',
'command_line.h',
......
// Copyright (c) 2011 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 "base/cancelable_callback.h"
#include "base/bind.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
namespace base {
CancelableCallback::CancelableCallback()
: ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {
}
CancelableCallback::CancelableCallback(const base::Closure& callback)
: ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
callback_(callback) {
DCHECK(!callback.is_null());
InitializeForwarder();
}
CancelableCallback::~CancelableCallback() {}
void CancelableCallback::Cancel() {
weak_factory_.InvalidateWeakPtrs();
callback_.Reset();
}
bool CancelableCallback::IsCancelled() const {
return callback_.is_null();
}
void CancelableCallback::Reset(const base::Closure& callback) {
DCHECK(!callback.is_null());
// Outstanding tasks (e.g., posted to a message loop) must not be called.
Cancel();
// |forwarder_| is no longer valid after Cancel(), so re-bind.
InitializeForwarder();
callback_ = callback;
}
const base::Closure& CancelableCallback::callback() const {
return forwarder_;
}
void CancelableCallback::RunCallback() {
callback_.Run();
}
void CancelableCallback::InitializeForwarder() {
forwarder_ = base::Bind(&CancelableCallback::RunCallback,
weak_factory_.GetWeakPtr());
}
} // namespace bind
......@@ -7,7 +7,13 @@
// wrapped callback until this object is destroyed or Reset()/Cancel() are
// called.
//
// Thread-safety notes:
// NOTE:
//
// Calling CancellableCallback::Cancel() brings the object back to its natural,
// default-constructed state, i.e., CancellableCallback::callback() will return
// a null callback.
//
// THREAD-SAFETY:
//
// CancelableCallback objects must be created on, posted to, cancelled on, and
// destroyed on the same thread.
......@@ -25,7 +31,7 @@
// MessageLoop::current()->Quit();
// }
//
// CancelableCallback timeout(base::Bind(&TimeoutCallback, "Test timed out."));
// CancelableClosure timeout(base::Bind(&TimeoutCallback, "Test timed out."));
// MessageLoop::current()->PostDelayedTask(FROM_HERE, timeout.callback(),
// 4000) // 4 seconds to run.
// RunIntensiveTest();
......@@ -37,53 +43,162 @@
#define BASE_CANCELABLE_CALLBACK_H_
#pragma once
#include "base/callback.h"
#include "base/base_export.h"
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_internal.h"
#include "base/compiler_specific.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
namespace base {
class BASE_EXPORT CancelableCallback {
template <typename Sig>
class CancelableCallback;
template <>
class CancelableCallback<void(void)> {
public:
CancelableCallback();
CancelableCallback() : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {}
// |callback| must not be null.
explicit CancelableCallback(const base::Closure& callback);
explicit CancelableCallback(const base::Callback<void(void)>& callback)
: ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
callback_(callback) {
DCHECK(!callback.is_null());
InitializeForwarder();
}
~CancelableCallback();
~CancelableCallback() {}
// Cancels and drops the reference to the wrapped callback.
void Cancel();
void Cancel() {
weak_factory_.InvalidateWeakPtrs();
forwarder_.Reset();
callback_.Reset();
}
// Returns true if the wrapped callback has been cancelled.
bool IsCancelled() const;
bool IsCancelled() const {
return callback_.is_null();
}
// Sets |callback| as the closure that may be cancelled. |callback| may not
// be null. Outstanding and any previously wrapped callbacks are cancelled.
void Reset(const base::Closure& callback);
void Reset(const base::Callback<void(void)>& callback) {
DCHECK(!callback.is_null());
// Outstanding tasks (e.g., posted to a message loop) must not be called.
Cancel();
// |forwarder_| is no longer valid after Cancel(), so re-bind.
InitializeForwarder();
callback_ = callback;
}
// Returns a callback that can be disabled by calling Cancel().
const base::Closure& callback() const;
const base::Callback<void(void)>& callback() const {
return forwarder_;
}
private:
void RunCallback();
void Forward() {
callback_.Run();
}
// Helper method to bind |forwarder_| using a weak pointer from
// |weak_factory_|.
void InitializeForwarder();
void InitializeForwarder() {
forwarder_ = base::Bind(&CancelableCallback<void(void)>::Forward,
weak_factory_.GetWeakPtr());
}
// Used to ensure RunCallback() is not run when this object is destroyed.
base::WeakPtrFactory<CancelableCallback> weak_factory_;
// Used to ensure Forward() is not run when this object is destroyed.
base::WeakPtrFactory<CancelableCallback<void(void)> > weak_factory_;
// The wrapper closure.
base::Closure forwarder_;
base::Callback<void(void)> forwarder_;
// The stored closure that may be cancelled.
base::Closure callback_;
base::Callback<void(void)> callback_;
DISALLOW_COPY_AND_ASSIGN(CancelableCallback);
};
template <typename A1>
class CancelableCallback<void(A1)> {
public:
CancelableCallback() : ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)) {}
// |callback| must not be null.
explicit CancelableCallback(const base::Callback<void(A1)>& callback)
: ALLOW_THIS_IN_INITIALIZER_LIST(weak_factory_(this)),
callback_(callback) {
DCHECK(!callback.is_null());
InitializeForwarder();
}
~CancelableCallback() {}
// Cancels and drops the reference to the wrapped callback.
void Cancel() {
weak_factory_.InvalidateWeakPtrs();
forwarder_.Reset();
callback_.Reset();
}
// Returns true if the wrapped callback has been cancelled.
bool IsCancelled() const {
return callback_.is_null();
}
// Sets |callback| as the closure that may be cancelled. |callback| may not
// be null. Outstanding and any previously wrapped callbacks are cancelled.
void Reset(const base::Callback<void(A1)>& callback) {
DCHECK(!callback.is_null());
// Outstanding tasks (e.g., posted to a message loop) must not be called.
Cancel();
// |forwarder_| is no longer valid after Cancel(), so re-bind.
InitializeForwarder();
callback_ = callback;
}
// Returns a callback that can be disabled by calling Cancel().
const base::Callback<void(A1)>& callback() const {
return forwarder_;
}
private:
void Forward(
typename internal::CallbackParamTraits<A1>::ForwardType a1) const {
callback_.Run(a1);
}
// Helper method to bind |forwarder_| using a weak pointer from
// |weak_factory_|.
void InitializeForwarder() {
forwarder_ = base::Bind(&CancelableCallback<void(A1)>::Forward,
weak_factory_.GetWeakPtr());
}
// Used to ensure Forward() is not run when this object is destroyed.
base::WeakPtrFactory<CancelableCallback<void(A1)> > weak_factory_;
// The wrapper closure.
base::Callback<void(A1)> forwarder_;
// The stored closure that may be cancelled.
base::Callback<void(A1)> callback_;
DISALLOW_COPY_AND_ASSIGN(CancelableCallback);
};
typedef CancelableCallback<void(void)> CancelableClosure;
} // namespace base
#endif // BASE_CANCELABLE_CALLBACK_H_
......@@ -28,7 +28,7 @@ void RefCountedParam(const scoped_refptr<TestRefCounted>& ref_counted) {}
// - After Cancel(), Run() completes but has no effect.
TEST(CancelableCallbackTest, Cancel) {
int count = 0;
CancelableCallback cancelable(
CancelableClosure cancelable(
base::Bind(&Increment, base::Unretained(&count)));
base::Closure callback = cancelable.callback();
......@@ -45,26 +45,29 @@ TEST(CancelableCallbackTest, Cancel) {
// Cancel() called multiple times.
// - Cancel() cancels all copies of the wrapped callback.
// - Calling Cancel() more than once has no effect.
// - After Cancel(), callback() returns a null callback.
TEST(CancelableCallbackTest, MultipleCancel) {
int count = 0;
CancelableCallback cancelable(
CancelableClosure cancelable(
base::Bind(&Increment, base::Unretained(&count)));
base::Closure callback1 = cancelable.callback();
base::Closure callback2 = cancelable.callback();
cancelable.Cancel();
callback1.Run();
EXPECT_EQ(0, count);
base::Closure callback2 = cancelable.callback();
callback2.Run();
EXPECT_EQ(0, count);
// Cancel() should have no effect, but callback() is still runnable.
base::Closure callback3 = cancelable.callback();
// Calling Cancel() again has no effect.
cancelable.Cancel();
callback3.Run();
EXPECT_EQ(0, count);
// callback() of a cancelled callback is null.
base::Closure callback3 = cancelable.callback();
EXPECT_TRUE(callback3.is_null());
}
// CancelableCallback destroyed before callback is run.
......@@ -74,7 +77,7 @@ TEST(CancelableCallbackTest, CallbackCanceledOnDestruction) {
base::Closure callback;
{
CancelableCallback cancelable(
CancelableClosure cancelable(
base::Bind(&Increment, base::Unretained(&count)));
callback = cancelable.callback();
......@@ -92,7 +95,7 @@ TEST(CancelableCallbackTest, CancelDropsCallback) {
scoped_refptr<TestRefCounted> ref_counted = new TestRefCounted;
EXPECT_TRUE(ref_counted->HasOneRef());
CancelableCallback cancelable(base::Bind(RefCountedParam, ref_counted));
CancelableClosure cancelable(base::Bind(RefCountedParam, ref_counted));
EXPECT_FALSE(cancelable.IsCancelled());
EXPECT_TRUE(ref_counted.get());
EXPECT_FALSE(ref_counted->HasOneRef());
......@@ -109,7 +112,7 @@ TEST(CancelableCallbackTest, CancelDropsCallback) {
// - Reset() deactivates outstanding callbacks.
TEST(CancelableCallbackTest, Reset) {
int count = 0;
CancelableCallback cancelable(
CancelableClosure cancelable(
base::Bind(&Increment, base::Unretained(&count)));
base::Closure callback = cancelable.callback();
......@@ -140,7 +143,7 @@ TEST(CancelableCallbackTest, Reset) {
// IsCanceled().
// - Cancel() transforms the CancelableCallback into a cancelled state.
TEST(CancelableCallbackTest, IsNull) {
CancelableCallback cancelable;
CancelableClosure cancelable;
EXPECT_TRUE(cancelable.IsCancelled());
int count = 0;
......@@ -158,7 +161,7 @@ TEST(CancelableCallbackTest, PostTask) {
MessageLoop loop(MessageLoop::TYPE_DEFAULT);
int count = 0;
CancelableCallback cancelable(base::Bind(&Increment,
CancelableClosure cancelable(base::Bind(&Increment,
base::Unretained(&count)));
MessageLoop::current()->PostTask(FROM_HERE, cancelable.callback());
......
......@@ -188,7 +188,7 @@ class DevToolsExtensionDebugTest : public DevToolsSanityTest,
content::NotificationRegistrar registrar;
registrar.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED,
content::NotificationService::AllSources());
base::CancelableCallback timeout(
base::CancelableClosure timeout(
base::Bind(&TimeoutCallback, "Extension load timed out."));
MessageLoop::current()->PostDelayedTask(
FROM_HERE, timeout.callback(), 4 * 1000);
......@@ -211,7 +211,7 @@ class DevToolsExtensionDebugTest : public DevToolsSanityTest,
content::NotificationRegistrar registrar;
registrar.Add(this, chrome::NOTIFICATION_EXTENSION_HOST_DID_STOP_LOADING,
content::NotificationService::AllSources());
base::CancelableCallback timeout(
base::CancelableClosure timeout(
base::Bind(&TimeoutCallback, "Extension host load timed out."));
MessageLoop::current()->PostDelayedTask(
FROM_HERE, timeout.callback(), 4 * 1000);
......
......@@ -45,7 +45,7 @@ OfflineResourceHandler::OfflineResourceHandler(
OfflineResourceHandler::~OfflineResourceHandler() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
DCHECK(!appcache_completion_callback_.get());
DCHECK(appcache_completion_callback_.IsCancelled());
}
bool OfflineResourceHandler::OnUploadProgress(int request_id,
......@@ -77,26 +77,24 @@ bool OfflineResourceHandler::OnResponseCompleted(
}
void OfflineResourceHandler::OnRequestClosed() {
if (appcache_completion_callback_) {
appcache_completion_callback_->Cancel();
appcache_completion_callback_.release();
Release(); // Balanced with OnWillStart
}
if (!appcache_completion_callback_.IsCancelled())
appcache_completion_callback_.Cancel();
next_handler_->OnRequestClosed();
}
void OfflineResourceHandler::OnCanHandleOfflineComplete(int rv) {
CHECK(appcache_completion_callback_);
appcache_completion_callback_ = NULL;
// Cancel() to break the circular reference cycle.
appcache_completion_callback_.Cancel();
if (deferred_request_id_ == -1) {
LOG(WARNING) << "OnCanHandleOfflineComplete called after completion: "
<< " this=" << this;
NOTREACHED();
DLOG(FATAL) << "OnCanHandleOfflineComplete called after completion: "
<< " this=" << this;
return;
}
if (rv == net::OK) {
Resume();
Release(); // Balanced with OnWillStart
} else {
BrowserThread::PostTask(
BrowserThread::UI, FROM_HERE,
......@@ -107,24 +105,29 @@ void OfflineResourceHandler::OnCanHandleOfflineComplete(int rv) {
bool OfflineResourceHandler::OnWillStart(int request_id,
const GURL& url,
bool* defer) {
if (ShouldShowOfflinePage(url)) {
deferred_request_id_ = request_id;
deferred_url_ = url;
DVLOG(1) << "OnWillStart: this=" << this << ", request id=" << request_id
<< ", url=" << url;
AddRef(); // Balanced with OnCanHandleOfflineComplete
DCHECK(!appcache_completion_callback_);
appcache_completion_callback_ =
new net::CancelableOldCompletionCallback<OfflineResourceHandler>(
this, &OfflineResourceHandler::OnCanHandleOfflineComplete);
appcache_service_->CanHandleMainResourceOffline(
url, request_->first_party_for_cookies(),
appcache_completion_callback_);
*defer = true;
return true;
}
return next_handler_->OnWillStart(request_id, url, defer);
if (!ShouldShowOfflinePage(url))
return next_handler_->OnWillStart(request_id, url, defer);
deferred_request_id_ = request_id;
deferred_url_ = url;
DVLOG(1) << "OnWillStart: this=" << this << ", request id=" << request_id
<< ", url=" << url;
DCHECK(appcache_completion_callback_.IsCancelled());
// |appcache_completion_callback_| holds a reference to |this|, so there is a
// circular reference; however, either
// OfflineResourceHandler::OnCanHandleOfflineComplete cancels the callback
// (thus dropping the reference), or CanHandleMainResourceOffline calls the
// callback which Resets it.
appcache_completion_callback_.Reset(
base::Bind(&OfflineResourceHandler::OnCanHandleOfflineComplete, this));
appcache_service_->CanHandleMainResourceOffline(
url, request_->first_party_for_cookies(),
appcache_completion_callback_.callback());
*defer = true;
return true;
}
// We'll let the original event handler provide a buffer, and reuse it for
......@@ -161,7 +164,6 @@ void OfflineResourceHandler::OnBlockingPageComplete(bool proceed) {
ClearRequestInfo();
rdh_->CancelRequest(process_host_id_, request_id, false);
}
Release(); // Balanced with OnWillStart
}
void OfflineResourceHandler::ClearRequestInfo() {
......
......@@ -52,7 +52,7 @@ class OfflineResourceHandler : public ResourceHandler {
void OnBlockingPageComplete(bool proceed);
private:
// Erase the state assocaited with a deferred load request.
// Erase the state associated with a deferred load request.
void ClearRequestInfo();
bool IsRemote(const GURL& url) const;
......@@ -80,8 +80,7 @@ class OfflineResourceHandler : public ResourceHandler {
int deferred_request_id_;
GURL deferred_url_;
scoped_refptr<net::CancelableOldCompletionCallback<OfflineResourceHandler> >
appcache_completion_callback_;
net::CancelableCompletionCallback appcache_completion_callback_;
DISALLOW_COPY_AND_ASSIGN(OfflineResourceHandler);
};
......
......@@ -8,6 +8,7 @@
#include "base/callback_old.h"
#include "base/callback.h"
#include "base/cancelable_callback.h"
namespace net {
......@@ -27,6 +28,8 @@ class OldCompletionCallbackImpl :
}
};
typedef base::CancelableCallback<void(int)> CancelableCompletionCallback;
// CancelableOldCompletionCallback is used for completion callbacks
// which may outlive the target for the method dispatch. In such a case, the
// provider of the callback calls Cancel() to mark the callback as
......
......@@ -147,7 +147,7 @@ class APPCACHE_EXPORT AppCacheGroup : public base::RefCounted<AppCacheGroup> {
// Updates that have been queued for the next run.
QueuedUpdates queued_updates_;
ObserverList<UpdateObserver> queued_observers_;
base::CancelableCallback restart_update_task_;
base::CancelableClosure restart_update_task_;
scoped_ptr<HostObserver> host_observer_;
// True if we're in our destructor.
......
......@@ -28,6 +28,52 @@ AppCacheInfoCollection::~AppCacheInfoCollection() {}
// AsyncHelper -------
class AppCacheService::NewAsyncHelper
: public AppCacheStorage::Delegate {
public:
NewAsyncHelper(AppCacheService* service,
const net::CompletionCallback& callback)
: service_(service), callback_(callback) {
service_->pending_new_helpers_.insert(this);
}
virtual ~NewAsyncHelper() {
if (service_)
service_->pending_new_helpers_.erase(this);
}
virtual void Start() = 0;
virtual void Cancel();
protected:
void CallCallback(int rv) {
if (!callback_.is_null()) {
// Defer to guarantee async completion.
MessageLoop::current()->PostTask(
FROM_HERE, base::Bind(&DeferredCallCallback, callback_, rv));
}
callback_.Reset();
}
static void DeferredCallCallback(const net::CompletionCallback& callback,
int rv) {
callback.Run(rv);
}
AppCacheService* service_;
net::CompletionCallback callback_;
};
void AppCacheService::NewAsyncHelper::Cancel() {
if (!callback_.is_null()) {
callback_.Run(net::ERR_ABORTED);
callback_.Reset();
}
service_->storage()->CancelDelegateCallbacks(this);
service_ = NULL;
}
class AppCacheService::AsyncHelper
: public AppCacheStorage::Delegate {
public:
......@@ -75,12 +121,14 @@ void AppCacheService::AsyncHelper::Cancel() {
// CanHandleOfflineHelper -------
class AppCacheService::CanHandleOfflineHelper : AsyncHelper {
class AppCacheService::CanHandleOfflineHelper : NewAsyncHelper {
public:
CanHandleOfflineHelper(
AppCacheService* service, const GURL& url,
const GURL& first_party, net::OldCompletionCallback* callback)
: AsyncHelper(service, callback), url_(url), first_party_(first_party) {
const GURL& first_party, const net::CompletionCallback& callback)
: NewAsyncHelper(service, callback),
url_(url),
first_party_(first_party) {
}
virtual void Start() {
......@@ -90,6 +138,7 @@ class AppCacheService::CanHandleOfflineHelper : AsyncHelper {
delete this;
return;
}
service_->storage()->FindResponseForMainRequest(url_, GURL(), this);
}
......@@ -102,6 +151,7 @@ class AppCacheService::CanHandleOfflineHelper : AsyncHelper {
GURL url_;
GURL first_party_;
DISALLOW_COPY_AND_ASSIGN(CanHandleOfflineHelper);
};
......@@ -426,6 +476,10 @@ AppCacheService::~AppCacheService() {
pending_helpers_.end(),
std::mem_fun(&AsyncHelper::Cancel));
STLDeleteElements(&pending_helpers_);
std::for_each(pending_new_helpers_.begin(),
pending_new_helpers_.end(),
std::mem_fun(&NewAsyncHelper::Cancel));
STLDeleteElements(&pending_new_helpers_);
if (quota_client_)
quota_client_->NotifyAppCacheDestroyed();
......@@ -446,7 +500,7 @@ void AppCacheService::Initialize(const FilePath& cache_directory,
void AppCacheService::CanHandleMainResourceOffline(
const GURL& url,
const GURL& first_party,
net::OldCompletionCallback* callback) {
const net::CompletionCallback& callback) {
CanHandleOfflineHelper* helper =
new CanHandleOfflineHelper(this, url, first_party, callback);
helper->Start();
......
......@@ -71,7 +71,7 @@ class APPCACHE_EXPORT AppCacheService {
// This method always completes asynchronously.
void CanHandleMainResourceOffline(const GURL& url,
const GURL& first_party,
net::OldCompletionCallback* callback);
const net::CompletionCallback& callback);
// Populates 'collection' with info about all of the appcaches stored
// within the service, 'callback' is invoked upon completion. The service
......@@ -149,6 +149,7 @@ class APPCACHE_EXPORT AppCacheService {
friend class AppCacheServiceTest;
class AsyncHelper;
class NewAsyncHelper;
class CanHandleOfflineHelper;
class DeleteHelper;
class DeleteOriginHelper;
......@@ -156,6 +157,7 @@ class APPCACHE_EXPORT AppCacheService {
class CheckResponseHelper;
typedef std::set<AsyncHelper*> PendingAsyncHelpers;
typedef std::set<NewAsyncHelper*> PendingNewAsyncHelpers;
typedef std::map<int, AppCacheBackendImpl*> BackendMap;
AppCachePolicy* appcache_policy_;
......@@ -164,6 +166,7 @@ class APPCACHE_EXPORT AppCacheService {
scoped_refptr<quota::SpecialStoragePolicy> special_storage_policy_;
scoped_refptr<quota::QuotaManagerProxy> quota_manager_proxy_;
PendingAsyncHelpers pending_helpers_;
PendingNewAsyncHelpers pending_new_helpers_;
BackendMap backends_; // One 'backend' per child process.
// Context for use during cache updates.
net::URLRequestContext* request_context_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment