summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ppapi/ppapi_shared.gypi4
-rw-r--r--ppapi/ppapi_tests.gypi1
-rw-r--r--ppapi/proxy/plugin_globals.cc10
-rw-r--r--ppapi/proxy/plugin_globals.h4
-rw-r--r--ppapi/shared_impl/callback_tracker.cc75
-rw-r--r--ppapi/shared_impl/callback_tracker.h101
-rw-r--r--ppapi/shared_impl/ppapi_globals.h3
-rw-r--r--ppapi/shared_impl/resource_tracker.cc4
-rw-r--r--ppapi/shared_impl/test_globals.cc9
-rw-r--r--ppapi/shared_impl/test_globals.h4
-rw-r--r--ppapi/shared_impl/tracked_callback.cc87
-rw-r--r--ppapi/shared_impl/tracked_callback.h106
-rw-r--r--ppapi/shared_impl/tracked_callback_unittest.cc266
-rw-r--r--webkit/plugins/ppapi/host_globals.cc10
-rw-r--r--webkit/plugins/ppapi/host_globals.h3
-rw-r--r--webkit/plugins/ppapi/plugin_module.cc9
-rw-r--r--webkit/plugins/ppapi/plugin_module.h8
17 files changed, 700 insertions, 4 deletions
diff --git a/ppapi/ppapi_shared.gypi b/ppapi/ppapi_shared.gypi
index f7a01d9..a497a63 100644
--- a/ppapi/ppapi_shared.gypi
+++ b/ppapi/ppapi_shared.gypi
@@ -30,6 +30,8 @@
'../base/base.gyp:base',
],
'sources': [
+ 'shared_impl/callback_tracker.cc',
+ 'shared_impl/callback_tracker.h',
'shared_impl/file_type_conversion.cc',
'shared_impl/file_type_conversion.h',
'shared_impl/function_group_base.cc',
@@ -89,6 +91,8 @@
'shared_impl/scoped_pp_resource.h',
'shared_impl/time_conversion.cc',
'shared_impl/time_conversion.h',
+ 'shared_impl/tracked_callback.cc',
+ 'shared_impl/tracked_callback.h',
'shared_impl/var.cc',
'shared_impl/var.h',
'shared_impl/var_tracker.cc',
diff --git a/ppapi/ppapi_tests.gypi b/ppapi/ppapi_tests.gypi
index 14e3d3c..26d2527 100644
--- a/ppapi/ppapi_tests.gypi
+++ b/ppapi/ppapi_tests.gypi
@@ -152,6 +152,7 @@
'shared_impl/resource_tracker_unittest.cc',
'shared_impl/test_globals.cc',
'shared_impl/test_globals.h',
+ 'shared_impl/tracked_callback_unittest.cc',
'shared_impl/var_tracker_unittest.cc',
],
},
diff --git a/ppapi/proxy/plugin_globals.cc b/ppapi/proxy/plugin_globals.cc
index e5acc4c0..664fbeb 100644
--- a/ppapi/proxy/plugin_globals.cc
+++ b/ppapi/proxy/plugin_globals.cc
@@ -13,7 +13,8 @@ PluginGlobals* PluginGlobals::plugin_globals_ = NULL;
PluginGlobals::PluginGlobals()
: ppapi::PpapiGlobals(),
- plugin_proxy_delegate_(NULL) {
+ plugin_proxy_delegate_(NULL),
+ callback_tracker_(new CallbackTracker) {
DCHECK(!plugin_globals_);
plugin_globals_ = this;
}
@@ -31,6 +32,13 @@ VarTracker* PluginGlobals::GetVarTracker() {
return &plugin_var_tracker_;
}
+CallbackTracker* PluginGlobals::GetCallbackTrackerForInstance(
+ PP_Instance instance) {
+ // In the plugin process, the callback tracker is always the same, regardless
+ // of the instance.
+ return callback_tracker_.get();
+}
+
FunctionGroupBase* PluginGlobals::GetFunctionAPI(PP_Instance inst, ApiID id) {
PluginDispatcher* dispatcher = PluginDispatcher::GetForInstance(inst);
if (dispatcher)
diff --git a/ppapi/proxy/plugin_globals.h b/ppapi/proxy/plugin_globals.h
index a76b7f0..ffcf01e 100644
--- a/ppapi/proxy/plugin_globals.h
+++ b/ppapi/proxy/plugin_globals.h
@@ -9,6 +9,7 @@
#include "ppapi/proxy/plugin_resource_tracker.h"
#include "ppapi/proxy/plugin_var_tracker.h"
#include "ppapi/proxy/ppapi_proxy_export.h"
+#include "ppapi/shared_impl/callback_tracker.h"
#include "ppapi/shared_impl/ppapi_globals.h"
namespace ppapi {
@@ -29,6 +30,8 @@ class PPAPI_PROXY_EXPORT PluginGlobals : public PpapiGlobals {
// PpapiGlobals implementation.
virtual ResourceTracker* GetResourceTracker() OVERRIDE;
virtual VarTracker* GetVarTracker() OVERRIDE;
+ virtual CallbackTracker* GetCallbackTrackerForInstance(
+ PP_Instance instance) OVERRIDE;
virtual FunctionGroupBase* GetFunctionAPI(PP_Instance inst,
ApiID id) OVERRIDE;
virtual PP_Module GetModuleForInstance(PP_Instance instance) OVERRIDE;
@@ -55,6 +58,7 @@ class PPAPI_PROXY_EXPORT PluginGlobals : public PpapiGlobals {
PluginProxyDelegate* plugin_proxy_delegate_;
PluginResourceTracker plugin_resource_tracker_;
PluginVarTracker plugin_var_tracker_;
+ scoped_refptr<CallbackTracker> callback_tracker_;
DISALLOW_COPY_AND_ASSIGN(PluginGlobals);
};
diff --git a/ppapi/shared_impl/callback_tracker.cc b/ppapi/shared_impl/callback_tracker.cc
new file mode 100644
index 0000000..1f22119
--- /dev/null
+++ b/ppapi/shared_impl/callback_tracker.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2010 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 "ppapi/shared_impl/callback_tracker.h"
+
+#include <algorithm>
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "ppapi/c/pp_completion_callback.h"
+#include "ppapi/shared_impl/tracked_callback.h"
+
+namespace ppapi {
+
+// CallbackTracker -------------------------------------------------------------
+
+CallbackTracker::CallbackTracker() {
+}
+
+void CallbackTracker::AbortAll() {
+ // Iterate over a copy since |Abort()| calls |Remove()| (indirectly).
+ // TODO(viettrungluu): This obviously isn't so efficient.
+ CallbackSetMap pending_callbacks_copy = pending_callbacks_;
+ for (CallbackSetMap::iterator it1 = pending_callbacks_copy.begin();
+ it1 != pending_callbacks_copy.end(); ++it1) {
+ for (CallbackSet::iterator it2 = it1->second.begin();
+ it2 != it1->second.end(); ++it2) {
+ (*it2)->Abort();
+ }
+ }
+}
+
+void CallbackTracker::PostAbortForResource(PP_Resource resource_id) {
+ CHECK(resource_id != 0);
+ CallbackSetMap::iterator it1 = pending_callbacks_.find(resource_id);
+ if (it1 == pending_callbacks_.end())
+ return;
+ for (CallbackSet::iterator it2 = it1->second.begin();
+ it2 != it1->second.end(); ++it2) {
+ // Post the abort.
+ (*it2)->PostAbort();
+ }
+}
+
+CallbackTracker::~CallbackTracker() {
+ // All callbacks must be aborted before destruction.
+ CHECK_EQ(0u, pending_callbacks_.size());
+}
+
+void CallbackTracker::Add(
+ const scoped_refptr<TrackedCallback>& tracked_callback) {
+ PP_Resource resource_id = tracked_callback->resource_id();
+ DCHECK(pending_callbacks_[resource_id].find(tracked_callback) ==
+ pending_callbacks_[resource_id].end());
+ pending_callbacks_[resource_id].insert(tracked_callback);
+}
+
+void CallbackTracker::Remove(
+ const scoped_refptr<TrackedCallback>& tracked_callback) {
+ CallbackSetMap::iterator map_it =
+ pending_callbacks_.find(tracked_callback->resource_id());
+ DCHECK(map_it != pending_callbacks_.end());
+ CallbackSet::iterator it = map_it->second.find(tracked_callback);
+ DCHECK(it != map_it->second.end());
+ map_it->second.erase(it);
+
+ // If there are no pending callbacks left for this ID, get rid of the entry.
+ if (map_it->second.empty())
+ pending_callbacks_.erase(map_it);
+}
+
+} // namespace ppapi
diff --git a/ppapi/shared_impl/callback_tracker.h b/ppapi/shared_impl/callback_tracker.h
new file mode 100644
index 0000000..5e354fb
--- /dev/null
+++ b/ppapi/shared_impl/callback_tracker.h
@@ -0,0 +1,101 @@
+// 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.
+
+#ifndef PPAPI_SHARED_IMPL_CALLBACK_TRACKER_H_
+#define PPAPI_SHARED_IMPL_CALLBACK_TRACKER_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/task.h"
+#include "ppapi/c/pp_resource.h"
+#include "ppapi/shared_impl/ppapi_shared_export.h"
+
+namespace ppapi {
+
+class TrackedCallback;
+
+// Pepper callbacks have the following semantics (unless otherwise specified;
+// in particular, the below apply to all completion callbacks):
+// - Callbacks are always run on the main thread.
+// - Callbacks are always called from the main message loop. In particular,
+// calling into Pepper will not result in the plugin being re-entered via a
+// synchronously-run callback.
+// - Each callback will be executed (a.k.a. completed) exactly once.
+// - Each callback may be *aborted*, which means that it will be executed with
+// result |PP_ERROR_ABORTED| (in the case of completion callbacks).
+// - Before |PPP_ShutdownModule()| is called, every pending callback (for that
+// module) will be aborted.
+// - Callbacks are usually associated to a resource, whose "deletion" provides
+// a "cancellation" (or abort) mechanism -- see below.
+// - When a plugin releases its last reference to resource, all callbacks
+// associated to that resource are aborted. Even if a non-abortive completion
+// of such a callback had previously been scheduled (i.e., posted), that
+// callback must now be aborted. The aborts should be scheduled immediately
+// (upon the last reference being given up) and should not rely on anything
+// else (e.g., a background task to complete or further action from the
+// plugin).
+// - Abortive completion gives no information about the status of the
+// asynchronous operation: The operation may have not yet begun, may be in
+// progress, or may be completed (successfully or not). In fact, the
+// operation may still proceed after the callback has been aborted.
+// - Any output data buffers provided to Pepper are associated with a resource.
+// Once that resource is released, no subsequent writes to those buffers. (If
+// background threads are set up to write into such buffers, the final
+// release operation should not return into the plugin until it can
+// guaranteed that those threads will no longer write into the buffers.)
+//
+// Thread-safety notes:
+// Currently, everything should happen on the main thread. The objects are
+// thread-safe ref-counted, so objects which live on different threads may keep
+// references. Releasing a reference to |TrackedCallback| on a different thread
+// (possibly causing destruction) is also okay. Otherwise, all methods should be
+// called only from the main thread.
+
+// |CallbackTracker| tracks pending Pepper callbacks for a single module. It
+// also tracks, for each resource ID, which callbacks are pending. When a
+// callback is (just about to be) completed, it is removed from the tracker. We
+// use |CallbackTracker| for two things: (1) to ensure that all callbacks are
+// properly aborted before module shutdown, and (2) to ensure that all callbacks
+// associated to a given resource are aborted when a plugin (module) releases
+// its last reference to that resource.
+class PPAPI_SHARED_EXPORT CallbackTracker
+ : public base::RefCountedThreadSafe<CallbackTracker> {
+ public:
+ CallbackTracker();
+
+ // Abort all callbacks (synchronously).
+ void AbortAll();
+
+ // Abort all callbacks associated to the given resource ID (which must be
+ // valid, i.e., nonzero) by posting a task (or tasks).
+ void PostAbortForResource(PP_Resource resource_id);
+
+ private:
+ friend class base::RefCountedThreadSafe<CallbackTracker>;
+ ~CallbackTracker();
+
+ // |TrackedCallback| are expected to automatically add and
+ // remove themselves from their provided |CallbackTracker|.
+ friend class TrackedCallback;
+ void Add(const scoped_refptr<TrackedCallback>& tracked_callback);
+ void Remove(const scoped_refptr<TrackedCallback>& tracked_callback);
+
+ // For each resource ID with a pending callback, store a set with its pending
+ // callbacks. (Resource ID 0 is used for callbacks not associated to a valid
+ // resource.) If a resource ID is re-used for another resource, there may be
+ // aborted callbacks corresponding to the original resource in that set; these
+ // will be removed when they are completed (abortively).
+ typedef std::set<scoped_refptr<TrackedCallback> > CallbackSet;
+ typedef std::map<PP_Resource, CallbackSet> CallbackSetMap;
+ CallbackSetMap pending_callbacks_;
+
+ DISALLOW_COPY_AND_ASSIGN(CallbackTracker);
+};
+
+} // namespace ppapi
+
+#endif // PPAPI_SHARED_IMPL_CALLBACK_TRACKER_H_
diff --git a/ppapi/shared_impl/ppapi_globals.h b/ppapi/shared_impl/ppapi_globals.h
index 56ec6bf..b3d7d55 100644
--- a/ppapi/shared_impl/ppapi_globals.h
+++ b/ppapi/shared_impl/ppapi_globals.h
@@ -13,6 +13,7 @@
namespace ppapi {
+class CallbackTracker;
class FunctionGroupBase;
class ResourceTracker;
class VarTracker;
@@ -29,6 +30,8 @@ class PPAPI_SHARED_EXPORT PpapiGlobals {
// Retrieves the corresponding tracker.
virtual ResourceTracker* GetResourceTracker() = 0;
virtual VarTracker* GetVarTracker() = 0;
+ virtual CallbackTracker* GetCallbackTrackerForInstance(
+ PP_Instance instance) = 0;
// Returns the function object corresponding to the given ID, or NULL if
// there isn't one.
diff --git a/ppapi/shared_impl/resource_tracker.cc b/ppapi/shared_impl/resource_tracker.cc
index 2b3cb9f..3b66db8 100644
--- a/ppapi/shared_impl/resource_tracker.cc
+++ b/ppapi/shared_impl/resource_tracker.cc
@@ -4,7 +4,9 @@
#include "ppapi/shared_impl/resource_tracker.h"
+#include "ppapi/shared_impl/callback_tracker.h"
#include "ppapi/shared_impl/id_assignment.h"
+#include "ppapi/shared_impl/ppapi_globals.h"
#include "ppapi/shared_impl/resource.h"
namespace ppapi {
@@ -168,6 +170,8 @@ void ResourceTracker::RemoveResource(Resource* object) {
}
void ResourceTracker::LastPluginRefWasDeleted(Resource* object) {
+ PpapiGlobals::Get()->GetCallbackTrackerForInstance(object->pp_instance())->
+ PostAbortForResource(object->pp_resource());
object->LastPluginRefWasDeleted();
}
diff --git a/ppapi/shared_impl/test_globals.cc b/ppapi/shared_impl/test_globals.cc
index 624b85b..e870e5c 100644
--- a/ppapi/shared_impl/test_globals.cc
+++ b/ppapi/shared_impl/test_globals.cc
@@ -6,7 +6,9 @@
namespace ppapi {
-TestGlobals::TestGlobals() : ppapi::PpapiGlobals() {
+TestGlobals::TestGlobals()
+ : ppapi::PpapiGlobals(),
+ callback_tracker_(new CallbackTracker) {
}
TestGlobals::~TestGlobals() {
@@ -20,6 +22,11 @@ VarTracker* TestGlobals::GetVarTracker() {
return &var_tracker_;
}
+CallbackTracker* TestGlobals::GetCallbackTrackerForInstance(
+ PP_Instance instance) {
+ return callback_tracker_.get();
+}
+
FunctionGroupBase* TestGlobals::GetFunctionAPI(PP_Instance inst, ApiID id) {
return NULL;
}
diff --git a/ppapi/shared_impl/test_globals.h b/ppapi/shared_impl/test_globals.h
index 49f189f..9c58fd3 100644
--- a/ppapi/shared_impl/test_globals.h
+++ b/ppapi/shared_impl/test_globals.h
@@ -6,6 +6,7 @@
#define PPAPI_SHARED_IMPL_TEST_GLOBALS_H_
#include "base/compiler_specific.h"
+#include "ppapi/shared_impl/callback_tracker.h"
#include "ppapi/shared_impl/ppapi_globals.h"
#include "ppapi/shared_impl/resource_tracker.h"
#include "ppapi/shared_impl/var_tracker.h"
@@ -31,6 +32,8 @@ class TestGlobals : public PpapiGlobals {
// PpapiGlobals implementation.
virtual ResourceTracker* GetResourceTracker() OVERRIDE;
virtual VarTracker* GetVarTracker() OVERRIDE;
+ virtual CallbackTracker* GetCallbackTrackerForInstance(
+ PP_Instance instance) OVERRIDE;
virtual FunctionGroupBase* GetFunctionAPI(PP_Instance inst,
ApiID id) OVERRIDE;
virtual PP_Module GetModuleForInstance(PP_Instance instance) OVERRIDE;
@@ -38,6 +41,7 @@ class TestGlobals : public PpapiGlobals {
private:
ResourceTracker resource_tracker_;
TestVarTracker var_tracker_;
+ scoped_refptr<CallbackTracker> callback_tracker_;
DISALLOW_COPY_AND_ASSIGN(TestGlobals);
};
diff --git a/ppapi/shared_impl/tracked_callback.cc b/ppapi/shared_impl/tracked_callback.cc
new file mode 100644
index 0000000..b36569d
--- /dev/null
+++ b/ppapi/shared_impl/tracked_callback.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2010 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 "ppapi/shared_impl/tracked_callback.h"
+
+#include "base/bind.h"
+#include "base/compiler_specific.h"
+#include "base/logging.h"
+#include "base/message_loop.h"
+#include "ppapi/c/pp_completion_callback.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/shared_impl/callback_tracker.h"
+#include "ppapi/shared_impl/ppapi_globals.h"
+#include "ppapi/shared_impl/resource.h"
+
+namespace ppapi {
+
+// TrackedCallback -------------------------------------------------------------
+
+// Note: don't keep a Resource* since it may go out of scope before us.
+TrackedCallback::TrackedCallback(
+ Resource* resource,
+ const PP_CompletionCallback& callback)
+ : ALLOW_THIS_IN_INITIALIZER_LIST(abort_impl_factory_(this)),
+ resource_id_(resource->pp_resource()),
+ completed_(false),
+ aborted_(false),
+ callback_(callback) {
+ tracker_ = PpapiGlobals::Get()->GetCallbackTrackerForInstance(
+ resource->pp_instance()),
+ tracker_->Add(make_scoped_refptr(this));
+}
+
+TrackedCallback::~TrackedCallback() {
+}
+
+void TrackedCallback::Abort() {
+ if (!completed()) {
+ aborted_ = true;
+ Run(PP_ERROR_ABORTED);
+ }
+}
+
+void TrackedCallback::PostAbort() {
+ if (!completed()) {
+ aborted_ = true;
+ // Post a task for the abort (only if necessary).
+ if (!abort_impl_factory_.HasWeakPtrs()) {
+ MessageLoop::current()->PostTask(
+ FROM_HERE,
+ base::Bind(&TrackedCallback::Abort,
+ abort_impl_factory_.GetWeakPtr()));
+ }
+ }
+}
+
+void TrackedCallback::Run(int32_t result) {
+ if (!completed()) {
+ // Cancel any pending calls.
+ abort_impl_factory_.InvalidateWeakPtrs();
+
+ // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()|
+ // may delete us.
+ PP_CompletionCallback callback = callback_;
+ if (aborted())
+ result = PP_ERROR_ABORTED;
+
+ // Do this before running the callback in case of reentrancy (which
+ // shouldn't happen, but avoid strange failures).
+ MarkAsCompleted();
+ PP_RunCompletionCallback(&callback, result);
+ }
+}
+
+void TrackedCallback::MarkAsCompleted() {
+ DCHECK(!completed());
+
+ // We will be removed; maintain a reference to ensure we won't be deleted
+ // until we're done.
+ scoped_refptr<TrackedCallback> thiz = this;
+ completed_ = true;
+ tracker_->Remove(thiz);
+ tracker_ = NULL;
+}
+
+} // namespace ppapi
diff --git a/ppapi/shared_impl/tracked_callback.h b/ppapi/shared_impl/tracked_callback.h
new file mode 100644
index 0000000..ed42a59
--- /dev/null
+++ b/ppapi/shared_impl/tracked_callback.h
@@ -0,0 +1,106 @@
+// 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.
+
+#ifndef PPAPI_SHARED_IMPL_TRACKED_CALLBACK_H_
+#define PPAPI_SHARED_IMPL_TRACKED_CALLBACK_H_
+
+#include <map>
+#include <set>
+
+#include "base/basictypes.h"
+#include "base/memory/ref_counted.h"
+#include "base/task.h"
+#include "ppapi/c/pp_completion_callback.h"
+#include "ppapi/c/pp_resource.h"
+#include "ppapi/shared_impl/ppapi_shared_export.h"
+
+namespace ppapi {
+
+class CallbackTracker;
+class Resource;
+
+// |TrackedCallback| represents a tracked Pepper callback (from the browser to
+// the plugin), typically still pending. Such callbacks have the standard Pepper
+// callback semantics. Execution (i.e., completion) of callbacks happens through
+// objects of subclasses of |TrackedCallback|. Two things are ensured: (1) that
+// the callback is executed at most once, and (2) once a callback is marked to
+// be aborted, any subsequent completion is abortive (even if a non-abortive
+// completion had previously been scheduled).
+//
+// The details of non-abortive completion depend on the type of callback (e.g.,
+// different parameters may be required), but basic abort functionality is core.
+// The ability to post aborts is needed in many situations to ensure that the
+// plugin is not re-entered into. (Note that posting a task to just run
+// |Abort()| is different and not correct; calling |PostAbort()| additionally
+// guarantees that all subsequent completions will be abortive.)
+//
+// This class is reference counted so that different things can hang on to it,
+// and not worry too much about ensuring Pepper callback semantics. Note that
+// the "owning" |CallbackTracker| will keep a reference until the callback is
+// completed.
+//
+// Subclasses must do several things:
+// - They must ensure that the callback is executed at most once (by looking at
+// |completed()| before running the callback).
+// - They must ensure that the callback is run abortively if it is marked as to
+// be aborted (by looking at |aborted()| before running the callback).
+// - They must call |MarkAsCompleted()| immediately before actually running the
+// callback; see the comment for |MarkAsCompleted()| for a caveat.
+class PPAPI_SHARED_EXPORT TrackedCallback
+ : public base::RefCountedThreadSafe<TrackedCallback> {
+ public:
+ // Create a tracked completion callback and register it with the tracker. The
+ // resource pointer is not stored.
+ TrackedCallback(Resource* resource,
+ const PP_CompletionCallback& callback);
+
+ // These run the callback in an abortive manner, or post a task to do so (but
+ // immediately marking the callback as to be aborted).
+ void Abort();
+ void PostAbort();
+
+ // Run the callback with the given result. If the callback had previously been
+ // marked as to be aborted (by |PostAbort()|), |result| will be ignored and
+ // the callback will be run with result |PP_ERROR_ABORTED|.
+ void Run(int32_t result);
+
+ // Returns the ID of the resource which "owns" the callback, or 0 if the
+ // callback is not associated with any resource.
+ PP_Resource resource_id() const { return resource_id_; }
+
+ // Returns true if the callback was completed (possibly aborted).
+ bool completed() const { return completed_; }
+
+ // Returns true if the callback was or should be aborted; this will be the
+ // case whenever |Abort()| or |PostAbort()| is called before a non-abortive
+ // completion.
+ bool aborted() const { return aborted_; }
+
+ private:
+ // This class is ref counted.
+ friend class base::RefCountedThreadSafe<TrackedCallback>;
+ virtual ~TrackedCallback();
+
+ // Mark this object as complete and remove it from the tracker. This must only
+ // be called once. Note that running this may result in this object being
+ // deleted (so keep a reference if it'll still be needed).
+ void MarkAsCompleted();
+
+ // Factory used by |PostAbort()|. Note that it's safe to cancel any pending
+ // posted aborts on destruction -- before it's destroyed, the "owning"
+ // |CallbackTracker| must have gone through and done (synchronous) |Abort()|s.
+ base::WeakPtrFactory<TrackedCallback> abort_impl_factory_;
+
+ scoped_refptr<CallbackTracker> tracker_;
+ PP_Resource resource_id_;
+ bool completed_;
+ bool aborted_;
+ PP_CompletionCallback callback_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(TrackedCallback);
+};
+
+} // namespace ppapi
+
+#endif // PPAPI_SHARED_IMPL_TRACKED_CALLBACK_H_
diff --git a/ppapi/shared_impl/tracked_callback_unittest.cc b/ppapi/shared_impl/tracked_callback_unittest.cc
new file mode 100644
index 0000000..62da396
--- /dev/null
+++ b/ppapi/shared_impl/tracked_callback_unittest.cc
@@ -0,0 +1,266 @@
+// 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/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "ppapi/c/pp_completion_callback.h"
+#include "ppapi/c/pp_errors.h"
+#include "ppapi/shared_impl/callback_tracker.h"
+#include "ppapi/shared_impl/resource.h"
+#include "ppapi/shared_impl/resource_tracker.h"
+#include "ppapi/shared_impl/test_globals.h"
+#include "ppapi/shared_impl/tracked_callback.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace ppapi {
+
+namespace {
+
+class TrackedCallbackTest : public testing::Test {
+ public:
+ TrackedCallbackTest()
+ : message_loop_(MessageLoop::TYPE_DEFAULT),
+ pp_instance_(1234) {}
+
+ PP_Instance pp_instance() const { return pp_instance_; }
+
+ virtual void SetUp() OVERRIDE {
+ globals_.GetResourceTracker()->DidCreateInstance(pp_instance_);
+ }
+ virtual void TearDown() OVERRIDE {
+ globals_.GetResourceTracker()->DidDeleteInstance(pp_instance_);
+ }
+
+ private:
+ MessageLoop message_loop_;
+ TestGlobals globals_;
+ PP_Instance pp_instance_;
+};
+
+struct CallbackRunInfo {
+ // All valid results (PP_OK, PP_ERROR_...) are nonpositive.
+ CallbackRunInfo() : run_count(0), result(1) {}
+ unsigned run_count;
+ int32_t result;
+};
+
+void TestCallback(void* user_data, int32_t result) {
+ CallbackRunInfo* info = reinterpret_cast<CallbackRunInfo*>(user_data);
+ info->run_count++;
+ if (info->run_count == 1)
+ info->result = result;
+}
+
+} // namespace
+
+// CallbackShutdownTest --------------------------------------------------------
+
+namespace {
+
+class CallbackShutdownTest : public TrackedCallbackTest {
+ public:
+ CallbackShutdownTest() {}
+
+ // Cases:
+ // (1) A callback which is run (so shouldn't be aborted on shutdown).
+ // (2) A callback which is aborted (so shouldn't be aborted on shutdown).
+ // (3) A callback which isn't run (so should be aborted on shutdown).
+ CallbackRunInfo& info_did_run() { return info_did_run_; } // (1)
+ CallbackRunInfo& info_did_abort() { return info_did_abort_; } // (2)
+ CallbackRunInfo& info_didnt_run() { return info_didnt_run_; } // (3)
+
+ private:
+ CallbackRunInfo info_did_run_;
+ CallbackRunInfo info_did_abort_;
+ CallbackRunInfo info_didnt_run_;
+};
+
+} // namespace
+
+// Tests that callbacks are properly aborted on module shutdown.
+TEST_F(CallbackShutdownTest, AbortOnShutdown) {
+ scoped_refptr<Resource> resource(new Resource(pp_instance()));
+
+ // Set up case (1) (see above).
+ EXPECT_EQ(0U, info_did_run().run_count);
+ scoped_refptr<TrackedCallback> callback_did_run = new TrackedCallback(
+ resource.get(),
+ PP_MakeCompletionCallback(&TestCallback, &info_did_run()));
+ EXPECT_EQ(0U, info_did_run().run_count);
+ callback_did_run->Run(PP_OK);
+ EXPECT_EQ(1U, info_did_run().run_count);
+ EXPECT_EQ(PP_OK, info_did_run().result);
+
+ // Set up case (2).
+ EXPECT_EQ(0U, info_did_abort().run_count);
+ scoped_refptr<TrackedCallback> callback_did_abort = new TrackedCallback(
+ resource.get(),
+ PP_MakeCompletionCallback(&TestCallback, &info_did_abort()));
+ EXPECT_EQ(0U, info_did_abort().run_count);
+ callback_did_abort->Abort();
+ EXPECT_EQ(1U, info_did_abort().run_count);
+ EXPECT_EQ(PP_ERROR_ABORTED, info_did_abort().result);
+
+ // Set up case (3).
+ EXPECT_EQ(0U, info_didnt_run().run_count);
+ scoped_refptr<TrackedCallback> callback_didnt_run = new TrackedCallback(
+ resource.get(),
+ PP_MakeCompletionCallback(&TestCallback, &info_didnt_run()));
+ EXPECT_EQ(0U, info_didnt_run().run_count);
+
+ PpapiGlobals::Get()->GetCallbackTrackerForInstance(pp_instance())->AbortAll();
+
+ // Check case (1).
+ EXPECT_EQ(1U, info_did_run().run_count);
+
+ // Check case (2).
+ EXPECT_EQ(1U, info_did_abort().run_count);
+
+ // Check case (3).
+ EXPECT_EQ(1U, info_didnt_run().run_count);
+ EXPECT_EQ(PP_ERROR_ABORTED, info_didnt_run().result);
+}
+
+// CallbackResourceTest --------------------------------------------------------
+
+namespace {
+
+class CallbackResourceTest : public TrackedCallbackTest {
+ public:
+ CallbackResourceTest() {}
+};
+
+class CallbackMockResource : public Resource {
+ public:
+ CallbackMockResource(PP_Instance instance) : Resource(instance) {}
+ ~CallbackMockResource() {}
+
+ PP_Resource SetupForTest() {
+ PP_Resource resource_id = GetReference();
+ EXPECT_NE(0, resource_id);
+
+ callback_did_run_ = new TrackedCallback(
+ this,
+ PP_MakeCompletionCallback(&TestCallback, &info_did_run_));
+ EXPECT_EQ(0U, info_did_run_.run_count);
+
+ callback_did_abort_ = new TrackedCallback(
+ this,
+ PP_MakeCompletionCallback(&TestCallback, &info_did_abort_));
+ EXPECT_EQ(0U, info_did_abort_.run_count);
+
+ callback_didnt_run_ = new TrackedCallback(
+ this,
+ PP_MakeCompletionCallback(&TestCallback, &info_didnt_run_));
+ EXPECT_EQ(0U, info_didnt_run_.run_count);
+
+ callback_did_run_->Run(PP_OK);
+ callback_did_abort_->Abort();
+
+ CheckIntermediateState();
+
+ return resource_id;
+ }
+
+ void CheckIntermediateState() {
+ EXPECT_EQ(1U, info_did_run_.run_count);
+ EXPECT_EQ(PP_OK, info_did_run_.result);
+
+ EXPECT_EQ(1U, info_did_abort_.run_count);
+ EXPECT_EQ(PP_ERROR_ABORTED, info_did_abort_.result);
+
+ EXPECT_EQ(0U, info_didnt_run_.run_count);
+ }
+
+ void CheckFinalState() {
+ EXPECT_EQ(1U, info_did_run_.run_count);
+ EXPECT_EQ(PP_OK, info_did_run_.result);
+ EXPECT_EQ(1U, info_did_abort_.run_count);
+ EXPECT_EQ(PP_ERROR_ABORTED, info_did_abort_.result);
+ EXPECT_EQ(1U, info_didnt_run_.run_count);
+ EXPECT_EQ(PP_ERROR_ABORTED, info_didnt_run_.result);
+ }
+
+ scoped_refptr<TrackedCallback> callback_did_run_;
+ CallbackRunInfo info_did_run_;
+
+ scoped_refptr<TrackedCallback> callback_did_abort_;
+ CallbackRunInfo info_did_abort_;
+
+ scoped_refptr<TrackedCallback> callback_didnt_run_;
+ CallbackRunInfo info_didnt_run_;
+};
+
+} // namespace
+
+// Test that callbacks get aborted on the last resource unref.
+TEST_F(CallbackResourceTest, AbortOnNoRef) {
+ ResourceTracker* resource_tracker =
+ PpapiGlobals::Get()->GetResourceTracker();
+
+ // Test several things: Unref-ing a resource (to zero refs) with callbacks
+ // which (1) have been run, (2) have been aborted, (3) haven't been completed.
+ // Check that the uncompleted one gets aborted, and that the others don't get
+ // called again.
+ scoped_refptr<CallbackMockResource> resource_1(
+ new CallbackMockResource(pp_instance()));
+ PP_Resource resource_1_id = resource_1->SetupForTest();
+
+ // Also do the same for a second resource, and make sure that unref-ing the
+ // first resource doesn't much up the second resource.
+ scoped_refptr<CallbackMockResource> resource_2(
+ new CallbackMockResource(pp_instance()));
+ PP_Resource resource_2_id = resource_2->SetupForTest();
+
+ // Double-check that resource #1 is still okay.
+ resource_1->CheckIntermediateState();
+
+ // Kill resource #1, spin the message loop to run posted calls, and check that
+ // things are in the expected states.
+ resource_tracker->ReleaseResource(resource_1_id);
+ MessageLoop::current()->RunAllPending();
+ resource_1->CheckFinalState();
+ resource_2->CheckIntermediateState();
+
+ // Kill resource #2.
+ resource_tracker->ReleaseResource(resource_2_id);
+ MessageLoop::current()->RunAllPending();
+ resource_1->CheckFinalState();
+ resource_2->CheckFinalState();
+
+ // This shouldn't be needed, but make sure there are no stranded tasks.
+ MessageLoop::current()->RunAllPending();
+}
+
+// Test that "resurrecting" a resource (getting a new ID for a |Resource|)
+// doesn't resurrect callbacks.
+TEST_F(CallbackResourceTest, Resurrection) {
+ ResourceTracker* resource_tracker =
+ PpapiGlobals::Get()->GetResourceTracker();
+
+ scoped_refptr<CallbackMockResource> resource(
+ new CallbackMockResource(pp_instance()));
+ PP_Resource resource_id = resource->SetupForTest();
+
+ // Unref it, spin the message loop to run posted calls, and check that things
+ // are in the expected states.
+ resource_tracker->ReleaseResource(resource_id);
+ MessageLoop::current()->RunAllPending();
+ resource->CheckFinalState();
+
+ // "Resurrect" it and check that the callbacks are still dead.
+ PP_Resource new_resource_id = resource->GetReference();
+ MessageLoop::current()->RunAllPending();
+ resource->CheckFinalState();
+
+ // Unref it again and do the same.
+ resource_tracker->ReleaseResource(new_resource_id);
+ MessageLoop::current()->RunAllPending();
+ resource->CheckFinalState();
+
+ // This shouldn't be needed, but make sure there are no stranded tasks.
+ MessageLoop::current()->RunAllPending();
+}
+
+} // namespace ppapi
diff --git a/webkit/plugins/ppapi/host_globals.cc b/webkit/plugins/ppapi/host_globals.cc
index 80f538c..56eb90a 100644
--- a/webkit/plugins/ppapi/host_globals.cc
+++ b/webkit/plugins/ppapi/host_globals.cc
@@ -58,6 +58,16 @@ HostGlobals::~HostGlobals() {
return &host_var_tracker_;
}
+::ppapi::CallbackTracker* HostGlobals::GetCallbackTrackerForInstance(
+ PP_Instance instance) {
+ std::map<PP_Instance, linked_ptr<InstanceData> >::iterator found =
+ instance_map_.find(instance);
+ if (found == instance_map_.end())
+ return NULL;
+
+ return found->second->instance->module()->GetNewCallbackTracker();
+}
+
::ppapi::FunctionGroupBase* HostGlobals::GetFunctionAPI(PP_Instance pp_instance,
::ppapi::ApiID id) {
// Get the instance object. This also ensures that the instance data is in
diff --git a/webkit/plugins/ppapi/host_globals.h b/webkit/plugins/ppapi/host_globals.h
index 51b7827..914be3a 100644
--- a/webkit/plugins/ppapi/host_globals.h
+++ b/webkit/plugins/ppapi/host_globals.h
@@ -6,6 +6,7 @@
#define WEBKIT_PLUGINS_PPAPI_HOST_GLOBALS_H_
#include "base/compiler_specific.h"
+#include "ppapi/shared_impl/callback_tracker.h"
#include "ppapi/shared_impl/ppapi_globals.h"
#include "ppapi/shared_impl/var_tracker.h"
#include "webkit/plugins/ppapi/host_resource_tracker.h"
@@ -31,6 +32,8 @@ class HostGlobals : public ::ppapi::PpapiGlobals {
// PpapiGlobals implementation.
virtual ::ppapi::ResourceTracker* GetResourceTracker() OVERRIDE;
virtual ::ppapi::VarTracker* GetVarTracker() OVERRIDE;
+ virtual ::ppapi::CallbackTracker* GetCallbackTrackerForInstance(
+ PP_Instance instance) OVERRIDE;
virtual ::ppapi::FunctionGroupBase* GetFunctionAPI(
PP_Instance inst,
::ppapi::ApiID id) OVERRIDE;
diff --git a/webkit/plugins/ppapi/plugin_module.cc b/webkit/plugins/ppapi/plugin_module.cc
index 9934666..3e08385d 100644
--- a/webkit/plugins/ppapi/plugin_module.cc
+++ b/webkit/plugins/ppapi/plugin_module.cc
@@ -84,6 +84,7 @@
#include "ppapi/c/trusted/ppb_graphics_3d_trusted.h"
#include "ppapi/c/trusted/ppb_image_data_trusted.h"
#include "ppapi/c/trusted/ppb_url_loader_trusted.h"
+#include "ppapi/shared_impl/callback_tracker.h"
#include "ppapi/shared_impl/ppb_input_event_shared.h"
#include "ppapi/shared_impl/ppb_opengles2_shared.h"
#include "ppapi/shared_impl/ppb_var_shared.h"
@@ -415,7 +416,8 @@ PluginModule::PluginModule(const std::string& name,
const FilePath& path,
PluginDelegate::ModuleLifetime* lifetime_delegate)
: lifetime_delegate_(lifetime_delegate),
- callback_tracker_(new CallbackTracker),
+ old_callback_tracker_(new CallbackTracker),
+ callback_tracker_(new ::ppapi::CallbackTracker),
is_in_destructor_(false),
is_crashed_(false),
broker_(NULL),
@@ -445,6 +447,7 @@ PluginModule::~PluginModule() {
GetLivePluginSet()->erase(this);
+ old_callback_tracker_->AbortAll();
callback_tracker_->AbortAll();
if (entry_points_.shutdown_module)
@@ -550,6 +553,10 @@ void PluginModule::InstanceDeleted(PluginInstance* instance) {
}
scoped_refptr<CallbackTracker> PluginModule::GetCallbackTracker() {
+ return old_callback_tracker_;
+}
+
+scoped_refptr< ::ppapi::CallbackTracker> PluginModule::GetNewCallbackTracker() {
return callback_tracker_;
}
diff --git a/webkit/plugins/ppapi/plugin_module.h b/webkit/plugins/ppapi/plugin_module.h
index dc2eaff..708c8ac 100644
--- a/webkit/plugins/ppapi/plugin_module.h
+++ b/webkit/plugins/ppapi/plugin_module.h
@@ -28,6 +28,7 @@ struct PPB_Core;
typedef void* NPIdentifier;
namespace ppapi {
+class CallbackTracker;
class WebKitForwarding;
} // namespace ppapi
@@ -117,6 +118,7 @@ class WEBKIT_PLUGINS_EXPORT PluginModule :
void InstanceDeleted(PluginInstance* instance);
scoped_refptr<CallbackTracker> GetCallbackTracker();
+ scoped_refptr< ::ppapi::CallbackTracker> GetNewCallbackTracker();
// Called when running out of process and the plugin crashed. This will
// release relevant resources and update all affected instances.
@@ -154,7 +156,11 @@ class WEBKIT_PLUGINS_EXPORT PluginModule :
// Tracker for completion callbacks, used mainly to ensure that all callbacks
// are properly aborted on module shutdown.
- scoped_refptr<CallbackTracker> callback_tracker_;
+ //
+ // TODO(brettw) remove this in favor of the ::ppapi:: one below.
+ scoped_refptr<CallbackTracker> old_callback_tracker_;
+
+ scoped_refptr< ::ppapi::CallbackTracker> callback_tracker_;
PP_Module pp_module_;