summaryrefslogtreecommitdiffstats
path: root/ppapi
diff options
context:
space:
mode:
authordmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-08 18:55:16 +0000
committerdmichael@chromium.org <dmichael@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-11-08 18:55:16 +0000
commit77c3417c441feac4bdcbad88f5a6bbf7f77bfe8f (patch)
treede665a63fc4d8137139cb976a45a938fc02b0d72 /ppapi
parente92a3618d401cd78886f0a404aa28a7e7947bc37 (diff)
downloadchromium_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
Diffstat (limited to 'ppapi')
-rw-r--r--ppapi/api/pp_completion_callback.idl88
-rw-r--r--ppapi/c/pp_completion_callback.h88
-rw-r--r--ppapi/cpp/completion_callback.h3
-rw-r--r--ppapi/cpp/resource.h4
-rw-r--r--ppapi/native_client/src/untrusted/pnacl_irt_shim/pnacl_shim.c2
-rw-r--r--ppapi/ppapi_shared.gypi2
-rw-r--r--ppapi/proxy/plugin_globals.cc4
-rw-r--r--ppapi/proxy/plugin_globals.h1
-rw-r--r--ppapi/proxy/ppb_message_loop_proxy.cc36
-rw-r--r--ppapi/proxy/ppb_message_loop_proxy.h14
-rw-r--r--ppapi/proxy/ppb_testing_proxy.cc6
-rw-r--r--ppapi/shared_impl/ppapi_globals.cc6
-rw-r--r--ppapi/shared_impl/ppapi_globals.h17
-rw-r--r--ppapi/shared_impl/ppb_message_loop_shared.cc20
-rw-r--r--ppapi/shared_impl/ppb_message_loop_shared.h52
-rw-r--r--ppapi/shared_impl/test_globals.cc10
-rw-r--r--ppapi/shared_impl/test_globals.h4
-rw-r--r--ppapi/shared_impl/tracked_callback.cc62
-rw-r--r--ppapi/shared_impl/tracked_callback.h10
-rw-r--r--ppapi/tests/test_case.h25
-rw-r--r--ppapi/tests/test_utils.cc37
-rw-r--r--ppapi/tests/test_utils.h4
-rw-r--r--ppapi/thunk/enter.cc63
-rw-r--r--ppapi/thunk/ppb_message_loop_api.h3
24 files changed, 445 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() {}