diff options
author | dmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-08 18:55:16 +0000 |
---|---|---|
committer | dmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-08 18:55:16 +0000 |
commit | 77c3417c441feac4bdcbad88f5a6bbf7f77bfe8f (patch) | |
tree | de665a63fc4d8137139cb976a45a938fc02b0d72 | |
parent | e92a3618d401cd78886f0a404aa28a7e7947bc37 (diff) | |
download | chromium_src-77c3417c441feac4bdcbad88f5a6bbf7f77bfe8f.zip chromium_src-77c3417c441feac4bdcbad88f5a6bbf7f77bfe8f.tar.gz chromium_src-77c3417c441feac4bdcbad88f5a6bbf7f77bfe8f.tar.bz2 |
PPAPI: Make CompletionCallbacks work right on background threads.
Now, TrackedCallback::Run will:
-Run the callback immediately if it is on the right thread.
-PostRun to the correct thread if it is not.
This was preceded by https://chromiumcodereview.appspot.com/10909244/, which removed ClearAndRun and does some other little cleanups to TrackedCallback to make it usable on background threads.
BUG=92909
Review URL: https://chromiumcodereview.appspot.com/10910099
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@166719 0039d316-1c4b-4281-b951-d872f2087c98
26 files changed, 450 insertions, 116 deletions
diff --git a/ppapi/api/pp_completion_callback.idl b/ppapi/api/pp_completion_callback.idl index 0dff263..6db9e2e 100644 --- a/ppapi/api/pp_completion_callback.idl +++ b/ppapi/api/pp_completion_callback.idl @@ -32,7 +32,13 @@ enum PP_CompletionCallback_Flag { * without blocking. * * The method taking such callback will always return PP_OK_COMPLETIONPENDING. - * The callback will be invoked on the main thread of PPAPI execution. + * The callback will be invoked on the same thread on which the method was + * invoked. + * + * NOTE: If the method taking the callback is invoked on a background + * thread that has no valid PPB_MessageLoop resource attached, the system has + * no way to run the callback on the correct thread. In this case, a log + * message will be emitted and the plugin will be made to crash. */ PP_COMPLETIONCALLBACK_FLAG_NONE = 0 << 0, /** @@ -44,32 +50,70 @@ enum PP_CompletionCallback_Flag { * On synchronous method completion, the completion result will be returned * by the method itself. Otherwise, the method will return * PP_OK_COMPLETIONPENDING, and the callback will be invoked asynchronously on - * the main thread of PPAPI execution. + * the same thread on which the method was invoked. If there is no valid + * PPB_MessageLoop attached to that thread, and the callback would normally + * run asynchronously, the invoked method will return + * PP_ERROR_NO_MESSAGE_LOOP. */ PP_COMPLETIONCALLBACK_FLAG_OPTIONAL = 1 << 0 }; /** - * Any method that takes a <code>PP_CompletionCallback</code> has the option of - * completing asynchronously if the operation would block. Such a method - * should return <code>PP_OK_COMPLETIONPENDING</code> to indicate that the - * method will complete asynchronously and notify the caller and will always be - * invoked from the main thread of PPAPI execution. If the completion callback - * is NULL, then the operation will block if necessary to complete its work. - * <code>PP_BlockUntilComplete()</code> provides a convenient way to specify - * blocking behavior. Refer to <code>PP_BlockUntilComplete</code> for more - * information. - * - * The result parameter passed to <code>func</code> is an int32_t that, if - * negative indicates an error code whose meaning is specific to the calling - * method (refer to <code>pp_error.h</code> for further information). A - * positive or 0 value is a return result indicating success whose meaning - * depends on the calling method (e.g. number of bytes read). + * <code>PP_CompletionCallback</code> is a common mechanism for supporting + * potentially asynchronous calls in browser interfaces. Any method that takes a + * <code>PP_CompletionCallback</code> can be used in one of three different + * ways: + * - Required: The callback will always be invoked asynchronously on the + * thread where the associated PPB method was invoked. The method + * will always return <code>PP_OK_COMPLETIONPENDING when a + * required callback, and the callback will be invoked later + * (barring system or thread shutdown; see PPB_MessageLoop for + * details). Required callbacks are the default. + * + * NOTE: If you use a required callback on a background thread, + * you must have created and attached a PPB_MessageLoop. + * Otherwise, the system can not run your callback on that thread, + * and will instead emit a log message and crash your plugin to + * make the problem more obvious. + * + * - Optional: The callback may be invoked asynchronously, or the PPB method + * may complete synchronously if it can do so without blocking. + * If the method will complete asynchronously, it will return + * PP_OK_COMPLETIONPENDING. Otherwise, it will complete + * synchronously and return an appropriate code (see below for + * more information on the return code). Optional callbacks are + * generally more difficult to use correctly than Required + * callbacks, but can provide better performance for some APIs + * (especially APIs with buffered reads, such as PPB_URLLoader or + * PPB_FileIO). + * + * NOTE: If you use an optional callback on a background thread, + * and you have not created and attached a PPB_MessageLoop, then + * the method you invoke will fail without running and return + * PP_ERROR_NO_MESSAGE_LOOP. + * + * - Blocking: In this case, the callback's function pointer is NULL, and the + * invoked method must complete synchronously. The method will + * run to completion and return an appropriate code when finished + * (see below for more information). Blocking completion + * callbacks are only supported on background threads. + * + * <code>PP_BlockUntilComplete()</code> provides a convenient way + * to specify blocking behavior. Refer to + * <code>PP_BlockUntilComplete</code> for more information. + * + * When the callback is run asynchronously, the result parameter passed to + * <code>func</code> is an int32_t that, if negative indicates an error code + * whose meaning is specific to the calling method (refer to + * <code>pp_error.h</code> for further information). A positive or 0 value is a + * return result indicating success whose meaning depends on the calling method + * (e.g. number of bytes read). */ [passByValue] struct PP_CompletionCallback { /** - * This value is a callback function that will be called. + * This value is a callback function that will be called, or NULL if this is + * a blocking completion callback. */ PP_CompletionCallback_Func func; /** @@ -95,7 +139,13 @@ enum PP_CompletionCallback_Flag { * PP_MakeCompletionCallback() is used to create a * <code>PP_CompletionCallback</code>. * - * <strong>Example:</strong> + * <strong>Example, creating a Required callback:</strong> + * + * <code> + * struct PP_CompletionCallback cc = PP_MakeCompletionCallback(Foo, NULL); + * </code> + * + * <strong>Example, creating an Optional callback:</strong> * * <code> * struct PP_CompletionCallback cc = PP_MakeCompletionCallback(Foo, NULL); diff --git a/ppapi/c/pp_completion_callback.h b/ppapi/c/pp_completion_callback.h index 911302f..6619ca4 100644 --- a/ppapi/c/pp_completion_callback.h +++ b/ppapi/c/pp_completion_callback.h @@ -3,7 +3,7 @@ * found in the LICENSE file. */ -/* From pp_completion_callback.idl modified Wed Oct 5 14:06:02 2011. */ +/* From pp_completion_callback.idl modified Wed Nov 7 11:20:18 2012. */ #ifndef PPAPI_C_PP_COMPLETION_CALLBACK_H_ #define PPAPI_C_PP_COMPLETION_CALLBACK_H_ @@ -51,7 +51,13 @@ typedef enum { * without blocking. * * The method taking such callback will always return PP_OK_COMPLETIONPENDING. - * The callback will be invoked on the main thread of PPAPI execution. + * The callback will be invoked on the same thread on which the method was + * invoked. + * + * NOTE: If the method taking the callback is invoked on a background + * thread that has no valid PPB_MessageLoop resource attached, the system has + * no way to run the callback on the correct thread. In this case, a log + * message will be emitted and the plugin will be made to crash. */ PP_COMPLETIONCALLBACK_FLAG_NONE = 0 << 0, /** @@ -63,7 +69,10 @@ typedef enum { * On synchronous method completion, the completion result will be returned * by the method itself. Otherwise, the method will return * PP_OK_COMPLETIONPENDING, and the callback will be invoked asynchronously on - * the main thread of PPAPI execution. + * the same thread on which the method was invoked. If there is no valid + * PPB_MessageLoop attached to that thread, and the callback would normally + * run asynchronously, the invoked method will return + * PP_ERROR_NO_MESSAGE_LOOP. */ PP_COMPLETIONCALLBACK_FLAG_OPTIONAL = 1 << 0 } PP_CompletionCallback_Flag; @@ -77,25 +86,60 @@ PP_COMPILE_ASSERT_SIZE_IN_BYTES(PP_CompletionCallback_Flag, 4); * @{ */ /** - * Any method that takes a <code>PP_CompletionCallback</code> has the option of - * completing asynchronously if the operation would block. Such a method - * should return <code>PP_OK_COMPLETIONPENDING</code> to indicate that the - * method will complete asynchronously and notify the caller and will always be - * invoked from the main thread of PPAPI execution. If the completion callback - * is NULL, then the operation will block if necessary to complete its work. - * <code>PP_BlockUntilComplete()</code> provides a convenient way to specify - * blocking behavior. Refer to <code>PP_BlockUntilComplete</code> for more - * information. + * <code>PP_CompletionCallback</code> is a common mechanism for supporting + * potentially asynchronous calls in browser interfaces. Any method that takes a + * <code>PP_CompletionCallback</code> can be used in one of three different + * ways: + * - Required: The callback will always be invoked asynchronously on the + * thread where the associated PPB method was invoked. The method + * will always return <code>PP_OK_COMPLETIONPENDING when a + * required callback, and the callback will be invoked later + * (barring system or thread shutdown; see PPB_MessageLoop for + * details). Required callbacks are the default. + * + * NOTE: If you use a required callback on a background thread, + * you must have created and attached a PPB_MessageLoop. + * Otherwise, the system can not run your callback on that thread, + * and will instead emit a log message and crash your plugin to + * make the problem more obvious. + * + * - Optional: The callback may be invoked asynchronously, or the PPB method + * may complete synchronously if it can do so without blocking. + * If the method will complete asynchronously, it will return + * PP_OK_COMPLETIONPENDING. Otherwise, it will complete + * synchronously and return an appropriate code (see below for + * more information on the return code). Optional callbacks are + * generally more difficult to use correctly than Required + * callbacks, but can provide better performance for some APIs + * (especially APIs with buffered reads, such as PPB_URLLoader or + * PPB_FileIO). + * + * NOTE: If you use an optional callback on a background thread, + * and you have not created and attached a PPB_MessageLoop, then + * the method you invoke will fail without running and return + * PP_ERROR_NO_MESSAGE_LOOP. + * + * - Blocking: In this case, the callback's function pointer is NULL, and the + * invoked method must complete synchronously. The method will + * run to completion and return an appropriate code when finished + * (see below for more information). Blocking completion + * callbacks are only supported on background threads. + * + * <code>PP_BlockUntilComplete()</code> provides a convenient way + * to specify blocking behavior. Refer to + * <code>PP_BlockUntilComplete</code> for more information. * - * The result parameter passed to <code>func</code> is an int32_t that, if - * negative indicates an error code whose meaning is specific to the calling - * method (refer to <code>pp_error.h</code> for further information). A - * positive or 0 value is a return result indicating success whose meaning - * depends on the calling method (e.g. number of bytes read). + * When the callback is run asynchronously, the result parameter passed to + * <code>func</code> is an int32_t that, if negative indicates an error code + * whose meaning is specific to the calling method (refer to + * <code>pp_error.h</code> for further information). A positive or 0 value is a + * return result indicating success whose meaning depends on the calling method + * (e.g. number of bytes read). */ struct PP_CompletionCallback { /** - * This value is a callback function that will be called. + * This value is a callback function that will be called, or NULL if this is + * a blocking completion callback. */ PP_CompletionCallback_Func func; /** @@ -122,7 +166,13 @@ struct PP_CompletionCallback { * PP_MakeCompletionCallback() is used to create a * <code>PP_CompletionCallback</code>. * - * <strong>Example:</strong> + * <strong>Example, creating a Required callback:</strong> + * + * <code> + * struct PP_CompletionCallback cc = PP_MakeCompletionCallback(Foo, NULL); + * </code> + * + * <strong>Example, creating an Optional callback:</strong> * * <code> * struct PP_CompletionCallback cc = PP_MakeCompletionCallback(Foo, NULL); diff --git a/ppapi/cpp/completion_callback.h b/ppapi/cpp/completion_callback.h index 2391a3a..ec892f6 100644 --- a/ppapi/cpp/completion_callback.h +++ b/ppapi/cpp/completion_callback.h @@ -99,7 +99,7 @@ class CompletionCallback { /// On synchronous method completion, the completion result will be returned /// by the method itself. Otherwise, the method will return /// PP_OK_COMPLETIONPENDING, and the callback will be invoked asynchronously - /// on the main thread of Pepper execution. + /// on the same thread where the PPB method was invoked. /// /// @return true if this callback is optional, otherwise false. bool IsOptional() const { @@ -150,6 +150,7 @@ class CompletionCallback { int32_t MayForce(int32_t result) const { if (result == PP_OK_COMPLETIONPENDING || IsOptional()) return result; + // FIXME(dmichael): Use pp::MessageLoop here once it's out of Dev. Module::Get()->core()->CallOnMainThread(0, *this, result); return PP_OK_COMPLETIONPENDING; } diff --git a/ppapi/cpp/resource.h b/ppapi/cpp/resource.h index f214672..8f5a92d 100644 --- a/ppapi/cpp/resource.h +++ b/ppapi/cpp/resource.h @@ -91,4 +91,8 @@ inline bool operator==(const pp::Resource& lhs, const pp::Resource& rhs) { return lhs.pp_resource() == rhs.pp_resource(); } +inline bool operator!=(const pp::Resource& lhs, const pp::Resource& rhs) { + return !(lhs == rhs); +} + #endif // PPAPI_CPP_RESOURCE_H_ diff --git a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c index 9c63608..7dc2ff3 100644 --- a/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c +++ b/ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c @@ -3,7 +3,7 @@ * found in the LICENSE file. */ -/* Last generated from IDL: Wed Nov 7 13:45:01 2012. */ +/* Last generated from IDL: Thu Nov 8 08:56:35 2012. */ #include "ppapi/generators/pnacl_shim.h" #include "ppapi/c/ppb.h" diff --git a/ppapi/ppapi_shared.gypi b/ppapi/ppapi_shared.gypi index fffc465..1205f09 100644 --- a/ppapi/ppapi_shared.gypi +++ b/ppapi/ppapi_shared.gypi @@ -70,6 +70,8 @@ 'shared_impl/ppb_instance_shared.cc', 'shared_impl/ppb_instance_shared.h', 'shared_impl/ppb_memory_shared.cc', + 'shared_impl/ppb_message_loop_shared.cc', + 'shared_impl/ppb_message_loop_shared.h', 'shared_impl/ppb_network_list_private_shared.cc', 'shared_impl/ppb_network_list_private_shared.h', 'shared_impl/ppb_opengles2_shared.cc', diff --git a/ppapi/proxy/plugin_globals.cc b/ppapi/proxy/plugin_globals.cc index 4932d32..75369d0 100644 --- a/ppapi/proxy/plugin_globals.cc +++ b/ppapi/proxy/plugin_globals.cc @@ -105,6 +105,10 @@ void PluginGlobals::BroadcastLogWithSource(PP_Module /* module */, LogWithSource(0, level, source, value); } +MessageLoopShared* PluginGlobals::GetCurrentMessageLoop() { + return MessageLoopResource::GetCurrent(); +} + MessageLoopResource* PluginGlobals::loop_for_main_thread() { return loop_for_main_thread_.get(); } diff --git a/ppapi/proxy/plugin_globals.h b/ppapi/proxy/plugin_globals.h index 2bbc3f9..215ad63 100644 --- a/ppapi/proxy/plugin_globals.h +++ b/ppapi/proxy/plugin_globals.h @@ -56,6 +56,7 @@ class PPAPI_PROXY_EXPORT PluginGlobals : public PpapiGlobals { PP_LogLevel_Dev level, const std::string& source, const std::string& value) OVERRIDE; + virtual MessageLoopShared* GetCurrentMessageLoop() OVERRIDE; // Getters for the plugin-specific versions. PluginResourceTracker* plugin_resource_tracker() { diff --git a/ppapi/proxy/ppb_message_loop_proxy.cc b/ppapi/proxy/ppb_message_loop_proxy.cc index 916002d..b86ecf0 100644 --- a/ppapi/proxy/ppb_message_loop_proxy.cc +++ b/ppapi/proxy/ppb_message_loop_proxy.cc @@ -27,15 +27,15 @@ typedef thunk::EnterResource<PPB_MessageLoop_API> EnterMessageLoop; } MessageLoopResource::MessageLoopResource(PP_Instance instance) - : Resource(OBJECT_IS_PROXY, instance), + : MessageLoopShared(instance), nested_invocations_(0), destroyed_(false), should_destroy_(false), is_main_thread_loop_(false) { } -MessageLoopResource::MessageLoopResource(ForMainThread) - : Resource(Resource::Untracked()), +MessageLoopResource::MessageLoopResource(ForMainThread for_main_thread) + : MessageLoopShared(for_main_thread), nested_invocations_(0), destroyed_(false), should_destroy_(false), @@ -80,7 +80,7 @@ int32_t MessageLoopResource::AttachToCurrentThread() { if (slot->Get()) return PP_ERROR_INPROGRESS; } - // TODO(brettw) check that the current thread can support a message loop. + // TODO(dmichael) check that the current thread can support a message loop. // Take a ref to the MessageLoop on behalf of the TLS. Note that this is an // internal ref and not a plugin ref so the plugin can't accidentally @@ -140,20 +140,24 @@ int32_t MessageLoopResource::PostQuit(PP_Bool should_destroy) { if (PP_ToBool(should_destroy)) should_destroy_ = true; - if (IsCurrent()) + if (IsCurrent() && nested_invocations_ > 0) loop_->Quit(); else PostClosure(FROM_HERE, MessageLoop::QuitClosure(), 0); return PP_OK; } -void MessageLoopResource::DetachFromThread() { - // Never detach the main thread from its loop resource. Other plugin instances - // might need it. - if (is_main_thread_loop_) - return; +// static +MessageLoopResource* MessageLoopResource::GetCurrent() { + PluginGlobals* globals = PluginGlobals::Get(); + if (!globals->msg_loop_slot()) + return NULL; + return reinterpret_cast<MessageLoopResource*>( + globals->msg_loop_slot()->Get()); +} - // Note that the message loop must be destroyed on the thread is was created +void MessageLoopResource::DetachFromThread() { + // Note that the message loop must be destroyed on the thread it was created // on. loop_proxy_ = NULL; loop_.reset(); @@ -208,12 +212,10 @@ PP_Resource GetForMainThread() { } PP_Resource GetCurrent() { - PluginGlobals* globals = PluginGlobals::Get(); - if (!globals->msg_loop_slot()) - return 0; - MessageLoopResource* loop = reinterpret_cast<MessageLoopResource*>( - globals->msg_loop_slot()->Get()); - return loop->GetReference(); + Resource* resource = MessageLoopResource::GetCurrent(); + if (resource) + return resource->GetReference(); + return 0; } int32_t AttachToCurrentThread(PP_Resource message_loop) { diff --git a/ppapi/proxy/ppb_message_loop_proxy.h b/ppapi/proxy/ppb_message_loop_proxy.h index 325be78..689e439 100644 --- a/ppapi/proxy/ppb_message_loop_proxy.h +++ b/ppapi/proxy/ppb_message_loop_proxy.h @@ -11,7 +11,7 @@ #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" #include "ppapi/proxy/interface_proxy.h" -#include "ppapi/shared_impl/resource.h" +#include "ppapi/shared_impl/ppb_message_loop_shared.h" #include "ppapi/thunk/ppb_message_loop_api.h" struct PPB_MessageLoop_Dev_0_1; @@ -19,13 +19,12 @@ struct PPB_MessageLoop_Dev_0_1; namespace ppapi { namespace proxy { -class MessageLoopResource : public Resource, public thunk::PPB_MessageLoop_API { +class MessageLoopResource : public MessageLoopShared { public: explicit MessageLoopResource(PP_Instance instance); // Construct the one MessageLoopResource for the main thread. This must be // invoked on the main thread. - struct ForMainThread {}; - MessageLoopResource(ForMainThread); + explicit MessageLoopResource(ForMainThread); virtual ~MessageLoopResource(); // Resource overrides. @@ -38,6 +37,7 @@ class MessageLoopResource : public Resource, public thunk::PPB_MessageLoop_API { int64_t delay_ms) OVERRIDE; virtual int32_t PostQuit(PP_Bool should_destroy) OVERRIDE; + static MessageLoopResource* GetCurrent(); void DetachFromThread(); bool is_main_thread_loop() const { return is_main_thread_loop_; @@ -58,9 +58,9 @@ class MessageLoopResource : public Resource, public thunk::PPB_MessageLoop_API { // NOTE: The given closure will be run *WITHOUT* acquiring the Proxy lock. // This only makes sense for user code and completely thread-safe // proxy operations (e.g., MessageLoop::QuitClosure). - void PostClosure(const tracked_objects::Location& from_here, - const base::Closure& closure, - int64 delay_ms); + virtual void PostClosure(const tracked_objects::Location& from_here, + const base::Closure& closure, + int64 delay_ms) OVERRIDE; // TLS destructor function. static void ReleaseMessageLoop(void* value); diff --git a/ppapi/proxy/ppb_testing_proxy.cc b/ppapi/proxy/ppb_testing_proxy.cc index b8c9ce0..576302f 100644 --- a/ppapi/proxy/ppb_testing_proxy.cc +++ b/ppapi/proxy/ppb_testing_proxy.cc @@ -53,12 +53,14 @@ PP_Bool ReadImageData(PP_Resource graphics_2d, void RunMessageLoop(PP_Instance instance) { MessageLoop::ScopedNestableTaskAllower allow(MessageLoop::current()); - // TODO(dmichael): We should probably assert that this is the main thread. + CHECK(PpapiGlobals::Get()->GetMainThreadMessageLoop()-> + BelongsToCurrentThread()); MessageLoop::current()->Run(); } void QuitMessageLoop(PP_Instance instance) { - // TODO(dmichael): We should probably assert that this is the main thread. + CHECK(PpapiGlobals::Get()->GetMainThreadMessageLoop()-> + BelongsToCurrentThread()); MessageLoop::current()->QuitNow(); } diff --git a/ppapi/shared_impl/ppapi_globals.cc b/ppapi/shared_impl/ppapi_globals.cc index 0336e0c..270d7e8 100644 --- a/ppapi/shared_impl/ppapi_globals.cc +++ b/ppapi/shared_impl/ppapi_globals.cc @@ -23,12 +23,12 @@ PpapiGlobals* PpapiGlobals::ppapi_globals_ = NULL; PpapiGlobals::PpapiGlobals() { DCHECK(!ppapi_globals_); ppapi_globals_ = this; - message_loop_proxy_ = base::MessageLoopProxy::current(); + main_loop_proxy_ = base::MessageLoopProxy::current(); } PpapiGlobals::PpapiGlobals(ForTest) { DCHECK(!ppapi_globals_); - message_loop_proxy_ = base::MessageLoopProxy::current(); + main_loop_proxy_ = base::MessageLoopProxy::current(); } PpapiGlobals::~PpapiGlobals() { @@ -45,7 +45,7 @@ void PpapiGlobals::SetPpapiGlobalsOnThreadForTest(PpapiGlobals* ptr) { } base::MessageLoopProxy* PpapiGlobals::GetMainThreadMessageLoop() { - return message_loop_proxy_.get(); + return main_loop_proxy_.get(); } bool PpapiGlobals::IsHostGlobals() const { diff --git a/ppapi/shared_impl/ppapi_globals.h b/ppapi/shared_impl/ppapi_globals.h index e95cf48..b03d35c 100644 --- a/ppapi/shared_impl/ppapi_globals.h +++ b/ppapi/shared_impl/ppapi_globals.h @@ -24,6 +24,7 @@ class MessageLoopProxy; namespace ppapi { class CallbackTracker; +class MessageLoopShared; class ResourceTracker; class VarTracker; @@ -35,6 +36,7 @@ class ResourceCreationAPI; // Abstract base class class PPAPI_SHARED_EXPORT PpapiGlobals { public: + // Must be created on the main thread. PpapiGlobals(); // This constructor is to be used only for making a PpapiGlobal for testing @@ -42,7 +44,7 @@ class PPAPI_SHARED_EXPORT PpapiGlobals { // tests that use this feature, the "test" PpapiGlobals should be constructed // using this method. See SetPpapiGlobalsOnThreadForTest for more information. struct ForTest {}; - PpapiGlobals(ForTest); + explicit PpapiGlobals(ForTest); virtual ~PpapiGlobals(); @@ -71,6 +73,7 @@ class PPAPI_SHARED_EXPORT PpapiGlobals { virtual VarTracker* GetVarTracker() = 0; virtual CallbackTracker* GetCallbackTrackerForInstance( PP_Instance instance) = 0; + virtual base::Lock* GetProxyLock() = 0; // Logs the given string to the JS console. If "source" is empty, the name of @@ -103,11 +106,15 @@ class PPAPI_SHARED_EXPORT PpapiGlobals { // failure. virtual PP_Module GetModuleForInstance(PP_Instance instance) = 0; - // Returns the base::MessageLoopProxy for the main thread. Note that this must - // be called on the main thread the first time so that it can initialize - // its static data. + // Returns the base::MessageLoopProxy for the main thread. This is set in the + // constructor, so PpapiGlobals must be created on the main thread. base::MessageLoopProxy* GetMainThreadMessageLoop(); + // Return the MessageLoopShared of the current thread, if any. This will + // always return NULL on the host side, where PPB_MessageLoop is not + // supported. + virtual MessageLoopShared* GetCurrentMessageLoop() = 0; + // Returns the command line for the process. virtual std::string GetCmdLine() = 0; @@ -127,7 +134,7 @@ class PPAPI_SHARED_EXPORT PpapiGlobals { static PpapiGlobals* ppapi_globals_; - scoped_refptr<base::MessageLoopProxy> message_loop_proxy_; + scoped_refptr<base::MessageLoopProxy> main_loop_proxy_; DISALLOW_COPY_AND_ASSIGN(PpapiGlobals); }; diff --git a/ppapi/shared_impl/ppb_message_loop_shared.cc b/ppapi/shared_impl/ppb_message_loop_shared.cc new file mode 100644 index 0000000..9c0c92d --- /dev/null +++ b/ppapi/shared_impl/ppb_message_loop_shared.cc @@ -0,0 +1,20 @@ +// Copyright (c) 2012 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/ppb_message_loop_shared.h" + +namespace ppapi { + +MessageLoopShared::MessageLoopShared(PP_Instance instance) + : Resource(OBJECT_IS_PROXY, instance) { +} + +MessageLoopShared::MessageLoopShared(ForMainThread) + : Resource(Resource::Untracked()) { +} + +MessageLoopShared::~MessageLoopShared() { +} + +} // namespace ppapi diff --git a/ppapi/shared_impl/ppb_message_loop_shared.h b/ppapi/shared_impl/ppb_message_loop_shared.h new file mode 100644 index 0000000..fd6d161 --- /dev/null +++ b/ppapi/shared_impl/ppb_message_loop_shared.h @@ -0,0 +1,52 @@ +// Copyright (c) 2012 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_PPB_MESSAGE_LOOP_SHARED_H_ +#define PPAPI_SHARED_IMPL_PPB_MESSAGE_LOOP_SHARED_H_ + +#include "base/basictypes.h" +#include "base/callback_forward.h" +#include "base/location.h" +#include "ppapi/c/pp_instance.h" +#include "ppapi/shared_impl/ppapi_shared_export.h" +#include "ppapi/shared_impl/resource.h" +#include "ppapi/thunk/ppb_message_loop_api.h" + +namespace tracked_objects { +class Location; +} + +namespace ppapi { + +// MessageLoopShared doesn't really do anything interesting. It exists so that +// shared code (in particular, TrackedCallback) can keep a pointer to a +// MessageLoop resource. In the host side, there is not a concrete class that +// implements this. So pointers to MessageLoopShared can only really be valid +// on the plugin side. +class PPAPI_SHARED_EXPORT MessageLoopShared + : public Resource, + public thunk::PPB_MessageLoop_API { + public: + explicit MessageLoopShared(PP_Instance instance); + // Construct the one MessageLoopShared for the main thread. This must be + // invoked on the main thread. + struct ForMainThread {}; + explicit MessageLoopShared(ForMainThread); + virtual ~MessageLoopShared(); + + // Handles posting to the message loop if there is one, or the pending queue + // if there isn't. + // NOTE: The given closure will be run *WITHOUT* acquiring the Proxy lock. + // This only makes sense for user code and completely thread-safe + // proxy operations (e.g., MessageLoop::QuitClosure). + virtual void PostClosure(const tracked_objects::Location& from_here, + const base::Closure& closure, + int64 delay_ms) = 0; + + DISALLOW_COPY_AND_ASSIGN(MessageLoopShared); +}; + +} // namespace ppapi + +#endif // PPAPI_SHARED_IMPL_PPB_MESSAGE_LOOP_SHARED_H_ diff --git a/ppapi/shared_impl/test_globals.cc b/ppapi/shared_impl/test_globals.cc index f8997dc..b1dc0fe 100644 --- a/ppapi/shared_impl/test_globals.cc +++ b/ppapi/shared_impl/test_globals.cc @@ -69,4 +69,14 @@ void TestGlobals::BroadcastLogWithSource(PP_Module module, const std::string& value) { } +MessageLoopShared* TestGlobals::GetCurrentMessageLoop() { + return NULL; +} + +bool TestGlobals::IsHostGlobals() const { + // Pretend to be the host-side, for code that expects one or the other. + // TODO(dmichael): just make it settable which one we're pretending to be? + return true; +} + } // namespace ppapi diff --git a/ppapi/shared_impl/test_globals.h b/ppapi/shared_impl/test_globals.h index 3675347..ca67e77 100644 --- a/ppapi/shared_impl/test_globals.h +++ b/ppapi/shared_impl/test_globals.h @@ -53,6 +53,10 @@ class TestGlobals : public PpapiGlobals { PP_LogLevel_Dev level, const std::string& source, const std::string& value) OVERRIDE; + virtual MessageLoopShared* GetCurrentMessageLoop() OVERRIDE; + + // PpapiGlobals overrides: + virtual bool IsHostGlobals() const OVERRIDE; private: ResourceTracker resource_tracker_; diff --git a/ppapi/shared_impl/tracked_callback.cc b/ppapi/shared_impl/tracked_callback.cc index 65d94be..bd011df 100644 --- a/ppapi/shared_impl/tracked_callback.cc +++ b/ppapi/shared_impl/tracked_callback.cc @@ -14,11 +14,21 @@ #include "ppapi/c/pp_errors.h" #include "ppapi/shared_impl/callback_tracker.h" #include "ppapi/shared_impl/ppapi_globals.h" +#include "ppapi/shared_impl/ppb_message_loop_shared.h" #include "ppapi/shared_impl/proxy_lock.h" #include "ppapi/shared_impl/resource.h" namespace ppapi { +namespace { + +bool IsMainThread() { + return + PpapiGlobals::Get()->GetMainThreadMessageLoop()->BelongsToCurrentThread(); +} + +} // namespace + // TrackedCallback ------------------------------------------------------------- // Note: don't keep a Resource* since it may go out of scope before us. @@ -30,7 +40,12 @@ TrackedCallback::TrackedCallback( completed_(false), aborted_(false), callback_(callback), + target_loop_(PpapiGlobals::Get()->GetCurrentMessageLoop()), result_for_blocked_callback_(PP_OK) { + // Note that target_loop_ may be NULL at this point, if the plugin has not + // attached a loop to this thread, or if this is an in-process plugin. + // The Enter class should handle checking this for us. + // TODO(dmichael): Add tracking at the instance level, for callbacks that only // have an instance (e.g. for MouseLock). if (resource) { @@ -40,19 +55,27 @@ TrackedCallback::TrackedCallback( } base::Lock* proxy_lock = PpapiGlobals::Get()->GetProxyLock(); - // We only need a ConditionVariable if the lock is valid (i.e., we're out-of- - // process) and the callback is blocking. - if (proxy_lock && is_blocking()) - operation_completed_condvar_.reset(new base::ConditionVariable(proxy_lock)); + if (proxy_lock) { + // If the proxy_lock is valid, we're running out-of-process, and locking + // is enabled. + if (is_blocking()) { + // This is a blocking completion callback, so we will need a condition + // variable for blocking & signalling the calling thread. + operation_completed_condvar_.reset( + new base::ConditionVariable(proxy_lock)); + } else { + // It's a non-blocking callback, so we should have a MessageLoopResource + // to dispatch to. Note that we don't error check here, though. Later, + // EnterResource::SetResult will check to make sure the callback is valid + // and take appropriate action. + } + } } TrackedCallback::~TrackedCallback() { } void TrackedCallback::Abort() { - // It doesn't make sense to abort a callback that's not associated with a - // resource. - DCHECK(resource_id_); Run(PP_ERROR_ABORTED); } @@ -71,12 +94,10 @@ void TrackedCallback::Run(int32_t result) { if (result == PP_ERROR_ABORTED) aborted_ = true; - // Copy |callback_| and look at |aborted()| now, since |MarkAsCompleted()| - // may delete us. - PP_CompletionCallback callback = callback_; // Note that this call of Run() may have been scheduled prior to Abort() or // PostAbort() being called. If we have been told to Abort, that always - // trumps a result that was scheduled before. + // trumps a result that was scheduled before, so we should make sure to pass + // PP_ERROR_ABORTED. if (aborted()) result = PP_ERROR_ABORTED; @@ -100,6 +121,15 @@ void TrackedCallback::Run(int32_t result) { // Wait()s. operation_completed_condvar_->Signal(); } else { + // If there's a target_loop_, and we're not on the right thread, we need to + // post to target_loop_. + if (target_loop_ && + target_loop_ != PpapiGlobals::Get()->GetCurrentMessageLoop()) { + PostRun(result); + return; + } + // Copy |callback_| now, since |MarkAsCompleted()| may delete us. + PP_CompletionCallback callback = callback_; // Do this before running the callback in case of reentrancy (which // shouldn't happen, but avoid strange failures). MarkAsCompleted(); @@ -122,7 +152,15 @@ void TrackedCallback::PostRun(int32_t result) { base::Closure callback_closure( RunWhileLocked(base::Bind(&TrackedCallback::Run, this, result))); - MessageLoop::current()->PostTask(FROM_HERE, callback_closure); + if (!target_loop_) { + // We must be running in-process and on the main thread (the Enter + // classes protect against having a null target_loop_ otherwise). + DCHECK(IsMainThread()); + DCHECK(PpapiGlobals::Get()->IsHostGlobals()); + MessageLoop::current()->PostTask(FROM_HERE, callback_closure); + } else { + target_loop_->PostClosure(FROM_HERE, callback_closure, 0); + } is_scheduled_ = true; } diff --git a/ppapi/shared_impl/tracked_callback.h b/ppapi/shared_impl/tracked_callback.h index 1fa7b1b..03bf162 100644 --- a/ppapi/shared_impl/tracked_callback.h +++ b/ppapi/shared_impl/tracked_callback.h @@ -16,10 +16,12 @@ #include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_resource.h" #include "ppapi/shared_impl/ppapi_shared_export.h" +#include "ppapi/shared_impl/ppb_message_loop_shared.h" namespace ppapi { class CallbackTracker; +class MessageLoopShared; class Resource; namespace thunk { @@ -76,7 +78,6 @@ class PPAPI_SHARED_EXPORT TrackedCallback // Run() will invoke the call immediately, if invoked from the target thread // (as determined by target_loop_). If invoked on a different thread, the // callback will be scheduled to run later on target_loop_. - // TODO(dmichael): Make the above part about different threads actually true. void Run(int32_t result); // PostRun is like Run(), except it guarantees that the callback will be run // later. In particular, if you invoke PostRun on the same thread on which the @@ -116,6 +117,9 @@ class PPAPI_SHARED_EXPORT TrackedCallback return (callback_.func && (callback_.flags & PP_COMPLETIONCALLBACK_FLAG_OPTIONAL)); } + bool has_null_target_loop() const { + return target_loop_ == NULL; + } private: // TrackedCallback and EnterBase manage dealing with how to invoke callbacks @@ -146,6 +150,10 @@ class PPAPI_SHARED_EXPORT TrackedCallback bool aborted_; PP_CompletionCallback callback_; + // The MessageLoopShared on which this callback should be run. This will be + // NULL if we're in-process. + scoped_refptr<MessageLoopShared> target_loop_; + int32_t result_for_blocked_callback_; // Used for pausing/waking the blocked thread if this is a blocking completion // callback. Note that in-process, there is no lock, blocking callbacks are diff --git a/ppapi/tests/test_case.h b/ppapi/tests/test_case.h index 2cf2ddf..aa35046 100644 --- a/ppapi/tests/test_case.h +++ b/ppapi/tests/test_case.h @@ -122,11 +122,12 @@ class TestCase { "Chrome, use the --enable-pepper-testing flag."; } // These tests are only valid if running out-of-process (threading is not - // supported in-process). Just consider it a pass. + // supported in-process). For in-process, just consider it a pass. if (!testing_interface_->IsOutOfProcess()) return std::string(); + pp::MessageLoop_Dev background_loop(instance_); ThreadedTestRunner<T> runner(instance_->pp_instance(), - static_cast<T*>(this), test_to_run); + static_cast<T*>(this), test_to_run, background_loop); RunOnThreadInternal(&ThreadedTestRunner<T>::ThreadFunction, &runner, testing_interface_); return runner.result(); @@ -162,10 +163,12 @@ class TestCase { typedef std::string(T::*TestMethodType)(); ThreadedTestRunner(PP_Instance instance, T* test_case, - TestMethodType test_to_run) + TestMethodType test_to_run, + pp::MessageLoop_Dev loop) : instance_(instance), test_case_(test_case), - test_to_run_(test_to_run) { + test_to_run_(test_to_run), + loop_(loop) { } const std::string& result() { return result_; } static void ThreadFunction(void* runner) { @@ -174,9 +177,11 @@ class TestCase { private: void Run() { - // TODO(dmichael): Create and attach a pp::MessageLoop for this thread so - // nested loops work. + PP_DCHECK(PP_OK == loop_.AttachToCurrentThread()); result_ = (test_case_->*test_to_run_)(); + // Now give the loop a chance to clean up. + loop_.PostQuit(true /* should_destroy */); + loop_.Run(); // Tell the main thread to quit its nested message loop, now that the test // is complete. TestCase::QuitMainMessageLoop(instance_); @@ -186,6 +191,7 @@ class TestCase { PP_Instance instance_; T* test_case_; TestMethodType test_to_run_; + pp::MessageLoop_Dev loop_; }; // The internals for RunOnThread. This allows us to avoid including @@ -275,6 +281,12 @@ class TestCaseFactory { CheckResourcesAndVars(RunOnThread(&test_case::Test##name))); \ } +#define RUN_TEST_BACKGROUND(test_case, name, test_filter) \ + if (MatchesFilter(#name, test_filter)) { \ + instance_->LogTest(#name"Background", \ + CheckResourcesAndVars(RunOnThread(&test_case::Test##name))); \ + } + #define RUN_TEST_FORCEASYNC_AND_NOT(name, test_filter) \ do { \ RUN_TEST_FORCEASYNC(name, test_filter); \ @@ -287,6 +299,7 @@ class TestCaseFactory { RUN_TEST_FORCEASYNC(name, test_filter); \ RUN_TEST(name, test_filter); \ RUN_TEST_BLOCKING(test_case, name, test_filter); \ + RUN_TEST_BACKGROUND(test_case, name, test_filter); \ } while (false) #define RUN_TEST_WITH_REFERENCE_CHECK(name, test_filter) \ diff --git a/ppapi/tests/test_utils.cc b/ppapi/tests/test_utils.cc index 9776e34..20a8a81 100644 --- a/ppapi/tests/test_utils.cc +++ b/ppapi/tests/test_utils.cc @@ -13,6 +13,7 @@ #endif #include "ppapi/c/pp_errors.h" +#include "ppapi/cpp/dev/message_loop_dev.h" #include "ppapi/cpp/module.h" #include "ppapi/cpp/var.h" @@ -138,7 +139,7 @@ int32_t TestCompletionCallback::WaitForResult() { errors_.clear(); if (!have_result_) { post_quit_task_ = true; - GetTestingInterface()->RunMessageLoop(instance_); + RunMessageLoop(); } return result_; } @@ -150,7 +151,7 @@ void TestCompletionCallback::WaitForResult(int32_t result) { if (result == PP_OK_COMPLETIONPENDING) { if (!have_result_) { post_quit_task_ = true; - GetTestingInterface()->RunMessageLoop(instance_); + RunMessageLoop(); } if (callback_type_ == PP_BLOCKING) { errors_.assign( @@ -200,6 +201,7 @@ pp::CompletionCallback TestCompletionCallback::GetCallback() { return pp::CompletionCallback(); else if (callback_type_ == PP_OPTIONAL) flags = PP_COMPLETIONCALLBACK_FLAG_OPTIONAL; + target_loop_ = pp::MessageLoop_Dev::GetCurrent(); return pp::CompletionCallback(&TestCompletionCallback::Handler, const_cast<TestCompletionCallback*>(this), flags); @@ -229,7 +231,36 @@ void TestCompletionCallback::Handler(void* user_data, int32_t result) { callback->delegate_->OnCallback(user_data, result); if (callback->post_quit_task_) { callback->post_quit_task_ = false; - GetTestingInterface()->QuitMessageLoop(callback->instance_); + callback->QuitMessageLoop(); + } + if (callback->target_loop_ != pp::MessageLoop_Dev::GetCurrent()) { + // Note, in-process, loop_ and GetCurrent() will both be NULL, so should + // still be equal. + callback->errors_.assign( + ReportError("TestCompletionCallback: Callback ran on the wrong message " + "loop!", + result)); } } +void TestCompletionCallback::RunMessageLoop() { + pp::MessageLoop_Dev loop(pp::MessageLoop_Dev::GetCurrent()); + // If we don't have a message loop, we're probably running in process, where + // PPB_MessageLoop is not supported. Just use the Testing message loop. + if (loop.is_null() || loop == pp::MessageLoop_Dev::GetForMainThread()) + GetTestingInterface()->RunMessageLoop(instance_); + else + loop.Run(); +} + +void TestCompletionCallback::QuitMessageLoop() { + pp::MessageLoop_Dev loop(pp::MessageLoop_Dev::GetCurrent()); + // If we don't have a message loop, we're probably running in process, where + // PPB_MessageLoop is not supported. Just use the Testing message loop. + if (loop.is_null() || loop == pp::MessageLoop_Dev::GetForMainThread()) { + GetTestingInterface()->QuitMessageLoop(instance_); + } else { + const bool should_quit = false; + loop.PostQuit(should_quit); + } +} diff --git a/ppapi/tests/test_utils.h b/ppapi/tests/test_utils.h index ab87dd3..60f7fc4 100644 --- a/ppapi/tests/test_utils.h +++ b/ppapi/tests/test_utils.h @@ -11,6 +11,7 @@ #include "ppapi/c/pp_instance.h" #include "ppapi/c/pp_stdint.h" #include "ppapi/cpp/completion_callback.h" +#include "ppapi/cpp/dev/message_loop_dev.h" #include "ppapi/utility/completion_callback_factory.h" // Timeout to wait for some action to complete. @@ -152,6 +153,8 @@ class TestCompletionCallback { private: static void Handler(void* user_data, int32_t result); + void RunMessageLoop(); + void QuitMessageLoop(); // Used to check that WaitForResult is only called once for each usage of the // callback. @@ -166,6 +169,7 @@ class TestCompletionCallback { unsigned run_count_; PP_Instance instance_; Delegate* delegate_; + pp::MessageLoop_Dev target_loop_; }; // Verifies that the callback didn't record any errors. If the callback is run diff --git a/ppapi/thunk/enter.cc b/ppapi/thunk/enter.cc index c89c969..296a8b4 100644 --- a/ppapi/thunk/enter.cc +++ b/ppapi/thunk/enter.cc @@ -87,8 +87,6 @@ int32_t EnterBase::SetResult(int32_t result) { // 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 { @@ -108,23 +106,52 @@ Resource* EnterBase::GetResource(PP_Resource 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); - } + if (PpapiGlobals::Get()->IsHostGlobals()) { + // In-process plugins can't make PPAPI calls off the main thread. + CHECK(IsMainThread()); } -} + if (callback_) { + if (callback_->is_blocking() && IsMainThread()) { + // Blocking callbacks are never allowed on the main thread. + 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); + } + } else if (!IsMainThread() && + callback_->has_null_target_loop() && + !callback_->is_blocking()) { + // On a non-main thread, there must be a valid target loop for non- + // blocking callbacks, or we will have no place to run them. + + // If the callback is required, there's no nice way to tell the plugin. + // We can't run their callback asynchronously without a message loop, and + // the plugin won't expect any return code other than + // PP_OK_COMPLETIONPENDING. So we crash to make the problem more obvious. + if (callback_->is_required()) { + std::string message("Attempted to use a required callback, but there" + "is no attached message loop on which to run the" + "callback."); + PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, + std::string(), message); + LOG(FATAL) << 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(); + callback_->MarkAsCompleted(); + callback_ = NULL; + retval_ = PP_ERROR_NO_MESSAGE_LOOP; + if (report_error) { + std::string message( + "The calling thread must have a message loop attached."); + PpapiGlobals::Get()->BroadcastLogWithSource(0, PP_LOGLEVEL_ERROR, + std::string(), message); + } + } + } } void EnterBase::ClearCallback() { @@ -145,8 +172,6 @@ void EnterBase::SetStateForResourceError(PP_Resource pp_resource, return; // Everything worked. 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; diff --git a/ppapi/thunk/ppb_message_loop_api.h b/ppapi/thunk/ppb_message_loop_api.h index 52660ca..efc1862 100644 --- a/ppapi/thunk/ppb_message_loop_api.h +++ b/ppapi/thunk/ppb_message_loop_api.h @@ -9,6 +9,7 @@ #include "ppapi/c/pp_bool.h" #include "ppapi/c/pp_completion_callback.h" #include "ppapi/c/pp_stdint.h" +#include "ppapi/thunk/ppapi_thunk_export.h" namespace ppapi { @@ -16,7 +17,7 @@ class TrackedCallback; namespace thunk { -class PPB_MessageLoop_API { +class PPAPI_THUNK_EXPORT PPB_MessageLoop_API { public: virtual ~PPB_MessageLoop_API() {} diff --git a/webkit/plugins/ppapi/host_globals.cc b/webkit/plugins/ppapi/host_globals.cc index 643ea8c..7ab611d 100644 --- a/webkit/plugins/ppapi/host_globals.cc +++ b/webkit/plugins/ppapi/host_globals.cc @@ -178,6 +178,10 @@ void HostGlobals::BroadcastLogWithSource(PP_Module pp_module, (*i)->element().document().frame()->addMessageToConsole(message); } +::ppapi::MessageLoopShared* HostGlobals::GetCurrentMessageLoop() { + return NULL; +} + PP_Module HostGlobals::AddModule(PluginModule* module) { #ifndef NDEBUG // Make sure we're not adding one more than once. diff --git a/webkit/plugins/ppapi/host_globals.h b/webkit/plugins/ppapi/host_globals.h index fb46d70..2cc98c7 100644 --- a/webkit/plugins/ppapi/host_globals.h +++ b/webkit/plugins/ppapi/host_globals.h @@ -54,6 +54,7 @@ class HostGlobals : public ::ppapi::PpapiGlobals { PP_LogLevel_Dev level, const std::string& source, const std::string& value) OVERRIDE; + virtual ::ppapi::MessageLoopShared* GetCurrentMessageLoop() OVERRIDE; HostVarTracker* host_var_tracker() { return &host_var_tracker_; |