diff options
Diffstat (limited to 'ppapi/thunk/enter.cc')
-rw-r--r-- | ppapi/thunk/enter.cc | 166 |
1 files changed, 121 insertions, 45 deletions
diff --git a/ppapi/thunk/enter.cc b/ppapi/thunk/enter.cc index a511711..dcfe2aa 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); } |