Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(64)

Unified Diff: ppapi/thunk/enter.cc

Issue 10081020: PPAPI: Make blocking completion callbacks work. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Cleaned up for review. Created 8 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ppapi/thunk/enter.cc
diff --git a/ppapi/thunk/enter.cc b/ppapi/thunk/enter.cc
index d8d42915e98f8b6fc827047cbadb6489e3ba6cd0..3a5ef75d0822909d7b7d79d21f2bb2fb1552be03 100644
--- a/ppapi/thunk/enter.cc
+++ b/ppapi/thunk/enter.cc
@@ -10,60 +10,85 @@
#include "base/stringprintf.h"
#include "ppapi/c/pp_errors.h"
#include "ppapi/shared_impl/ppapi_globals.h"
+#include "ppapi/shared_impl/tracked_callback.h"
#include "ppapi/thunk/ppb_instance_api.h"
#include "ppapi/thunk/resource_creation_api.h"
namespace ppapi {
+namespace {
+
+bool IsMainThread() {
+ return
+ PpapiGlobals::Get()->GetMainThreadMessageLoop()->BelongsToCurrentThread();
+}
+
+} // namespace
+
namespace thunk {
namespace subtle {
-bool CallbackIsRequired(const PP_CompletionCallback& callback) {
- return callback.func != NULL &&
- (callback.flags & PP_COMPLETIONCALLBACK_FLAG_OPTIONAL) == 0;
+EnterBase::EnterBase()
+ : resource_(NULL),
+ retval_(PP_OK) {
+ // TODO(dmichael) validate that threads have an associated message loop.
}
-EnterBase::EnterBase()
- : callback_(PP_BlockUntilComplete()),
+EnterBase::EnterBase(PP_Resource resource)
+ : resource_(GetResource(resource)),
retval_(PP_OK) {
- // TODO(brettw) validate threads.
+ // TODO(dmichael) validate that threads have an associated message loop.
}
-EnterBase::EnterBase(const PP_CompletionCallback& callback)
- : callback_(CallbackIsRequired(callback) ? callback
- : PP_BlockUntilComplete()),
+EnterBase::EnterBase(PP_Resource resource,
+ const PP_CompletionCallback& callback)
+ : resource_(GetResource(resource)),
retval_(PP_OK) {
- // TODO(brettw) validate threads.
+ callback_ = new TrackedCallback(resource_, callback);
+
+ // TODO(dmichael) validate that threads have an associated message loop.
}
EnterBase::~EnterBase() {
- if (callback_.func) {
- // All async completions should have cleared the callback in SetResult().
- DCHECK(retval_ != PP_OK_COMPLETIONPENDING && retval_ != PP_OK);
- MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind(
- callback_.func, callback_.user_data, retval_)));
- }
+ // callback_ is cleared any time it is run, scheduled to be run, or once we
+ // know it will be completed asynchronously. So by this point it should be
+ // NULL.
+ DCHECK(!callback_);
}
int32_t EnterBase::SetResult(int32_t result) {
- if (!callback_.func || result == PP_OK_COMPLETIONPENDING) {
- // Easy case, we don't need to issue the callback (either none is
- // required, or the implementation will asynchronously issue it
- // for us), just store the result.
- callback_ = PP_BlockUntilComplete();
+ if (!callback_) {
+ // It doesn't make sense to call SetResult if there is no callback.
+ NOTREACHED();
retval_ = result;
- return retval_;
+ return result;
}
-
- // This is a required callback, asynchronously issue it.
- // TODO(brettw) make this work on different threads, etc.
- MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind(
- callback_.func, callback_.user_data, result)));
-
- // Now that the callback will be issued in the future, we should return
- // "pending" to the caller, and not issue the callback again.
- callback_ = PP_BlockUntilComplete();
- retval_ = PP_OK_COMPLETIONPENDING;
+ if (result == PP_OK_COMPLETIONPENDING) {
+ retval_ = result;
+ if (callback_->is_blocking()) {
+ DCHECK(!IsMainThread()); // We should have returned an error before this.
+ retval_ = callback_->BlockUntilComplete();
+ } else {
+ // The callback is not blocking and the operation will complete
+ // asynchronously, so there's nothing to do.
+ retval_ = result;
+ }
+ } else {
+ // The function completed synchronously.
+ if (callback_->is_required()) {
+ // This is a required callback, so we must issue it asynchronously.
+ // TODO(dmichael) make this work so that a call from a background thread
+ // goes back to that thread.
+ callback_->PostRun(result);
+ retval_ = PP_OK_COMPLETIONPENDING;
+ } else {
+ // The callback is blocking or optional, so all we need to do is mark
+ // the callback as completed so that it won't be issued later.
+ callback_->MarkAsCompleted();
+ retval_ = result;
+ }
+ }
+ callback_ = NULL;
return retval_;
}
@@ -72,10 +97,35 @@ FunctionGroupBase* EnterBase::GetFunctions(PP_Instance instance,
return PpapiGlobals::Get()->GetFunctionAPI(instance, id);
}
-Resource* EnterBase::GetResource(PP_Resource resource) const {
+// static
+Resource* EnterBase::GetResource(PP_Resource resource) {
return PpapiGlobals::Get()->GetResourceTracker()->GetResource(resource);
}
+void EnterBase::SetStateForCallbackError(bool report_error) {
+ if (!CallbackIsValid()) {
+ callback_->MarkAsCompleted();
+ callback_ = NULL;
+ retval_ = PP_ERROR_BLOCKS_MAIN_THREAD;
+ if (report_error) {
+ std::string message(
+ "Blocking callbacks are not allowed on the main thread.");
+ PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR,
+ std::string(), message);
+ }
+ }
+}
+
+bool EnterBase::CallbackIsValid() const {
+ // A callback is only considered invalid if it is blocking and we're on the
+ // main thread.
+ return !callback_ || !callback_->is_blocking() || !IsMainThread();
+}
+
+void EnterBase::ClearCallback() {
+ callback_ = NULL;
+}
+
void EnterBase::SetStateForResourceError(PP_Resource pp_resource,
Resource* resource_base,
void* object,
@@ -83,14 +133,16 @@ void EnterBase::SetStateForResourceError(PP_Resource pp_resource,
if (object)
return; // Everything worked.
- if (callback_.func) {
- // Required callback, issue the async completion.
- MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind(
- callback_.func, callback_.user_data,
- static_cast<int32_t>(PP_ERROR_BADRESOURCE))));
- callback_ = PP_BlockUntilComplete();
+ if (callback_ && callback_->is_required()) {
+ // TODO(dmichael) make this work so that a call from a background thread
+ // goes back to that thread.
+ callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADRESOURCE));
+ callback_ = NULL;
retval_ = PP_OK_COMPLETIONPENDING;
} else {
+ if (callback_)
+ callback_->MarkAsCompleted();
+ callback_ = NULL;
retval_ = PP_ERROR_BADRESOURCE;
}
@@ -119,14 +171,14 @@ void EnterBase::SetStateForFunctionError(PP_Instance pp_instance,
if (object)
return; // Everything worked.
- if (callback_.func) {
- // Required callback, issue the async completion.
- MessageLoop::current()->PostTask(FROM_HERE, RunWhileLocked(base::Bind(
- callback_.func, callback_.user_data,
- static_cast<int32_t>(PP_ERROR_BADARGUMENT))));
- callback_ = PP_BlockUntilComplete();
+ if (callback_ && callback_->is_required()) {
+ callback_->PostRun(static_cast<int32_t>(PP_ERROR_BADARGUMENT));
+ callback_ = NULL;
retval_ = PP_OK_COMPLETIONPENDING;
} else {
+ if (callback_)
+ callback_->MarkAsCompleted();
+ callback_ = NULL;
retval_ = PP_ERROR_BADARGUMENT;
}
@@ -163,5 +215,12 @@ EnterInstance::EnterInstance(PP_Instance instance,
EnterInstance::~EnterInstance() {
}
+EnterInstanceNoLock::EnterInstanceNoLock(PP_Instance instance)
+ : EnterFunctionNoLock<PPB_Instance_FunctionAPI>(instance, true) {
+}
+
+EnterInstanceNoLock::~EnterInstanceNoLock() {
+}
+
} // namespace thunk
} // namespace ppapi
« ppapi/thunk/enter.h ('K') | « ppapi/thunk/enter.h ('k') | ppapi/thunk/ppb_audio_api.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698