Index: ppapi/thunk/enter.cc |
diff --git a/ppapi/thunk/enter.cc b/ppapi/thunk/enter.cc |
index a5117118c2e41f06a202fa5057d196cd84337e61..dcfe2aa7f0545e1c2675864557a34efa3cb8c52e 100644 |
--- a/ppapi/thunk/enter.cc |
+++ b/ppapi/thunk/enter.cc |
@@ -8,84 +8,151 @@ |
#include "base/logging.h" |
#include "base/message_loop.h" |
#include "base/stringprintf.h" |
+#include "base/synchronization/lock.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; |
+void AssertLockHeld() { |
+ base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock(); |
+ // The lock is only valid in the plugin side of the proxy, so it only makes |
+ // sense to assert there. Otherwise, silently succeed. |
+ if (proxy_lock) |
+ proxy_lock->AssertAcquired(); |
} |
EnterBase::EnterBase() |
- : callback_(PP_BlockUntilComplete()), |
+ : resource_(NULL), |
+ retval_(PP_OK) { |
+ // TODO(dmichael) validate that threads have an associated message loop. |
+} |
+ |
+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_; |
} |
-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, |
bool report_error) { |
+ // Check for callback errors. If we get any, SetStateForCallbackError will |
+ // emit a log message. But we also want to check for resource errors. If there |
+ // are both kinds of errors, we'll emit two log messages and return |
+ // PP_ERROR_BADRESOURCE. |
+ SetStateForCallbackError(report_error); |
+ |
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; |
} |
@@ -111,17 +178,23 @@ void EnterBase::SetStateForResourceError(PP_Resource pp_resource, |
void EnterBase::SetStateForFunctionError(PP_Instance pp_instance, |
void* object, |
bool report_error) { |
+ // Check for callback errors. If we get any, SetStateForCallbackError will |
+ // emit a log message. But we also want to check for instance errors. If there |
+ // are both kinds of errors, we'll emit two log messages and return |
+ // PP_ERROR_BADARGUMENT. |
+ SetStateForCallbackError(report_error); |
+ |
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; |
} |
@@ -147,7 +220,10 @@ EnterInstance::EnterInstance(PP_Instance instance) |
EnterInstance::EnterInstance(PP_Instance instance, |
const PP_CompletionCallback& callback) |
- : EnterBase(callback), |
+ : EnterBase(0 /* resource */, callback), |
+ // TODO(dmichael): This means that the callback_ we get is not associated |
+ // even with the instance, but we should handle that for |
+ // MouseLock (maybe others?). |
functions_(PpapiGlobals::Get()->GetInstanceAPI(instance)) { |
SetStateForFunctionError(instance, functions_, true); |
} |