| 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);
|
| }
|
|
|