summaryrefslogtreecommitdiffstats
path: root/ppapi
diff options
context:
space:
mode:
authorhidehiko@chromium.org <hidehiko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-21 10:36:21 +0000
committerhidehiko@chromium.org <hidehiko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-04-21 10:36:21 +0000
commita4a01e47a657b2a74fa6f2748006efcb01f63523 (patch)
treedf08272594c974115fdadf892e12f445ec527a0d /ppapi
parentad6d13372a73c90fc24059b22b2fa0ea927f2151 (diff)
downloadchromium_src-a4a01e47a657b2a74fa6f2748006efcb01f63523.zip
chromium_src-a4a01e47a657b2a74fa6f2748006efcb01f63523.tar.gz
chromium_src-a4a01e47a657b2a74fa6f2748006efcb01f63523.tar.bz2
Add test to make sure if PPB_Audio_Shared::StartThread() works.
To NaCl in either SFI or non-SFI mode, PPB_Audio_Shraed::StartThread requires ppapi_register_thread_creator invoked in advance. Currently no test about it. This CL adds the test. Tests are guarded by #ifdef __native_client__, because it won't work and is meaningless for trusted plugins. TEST=Ran browser_tests --gtest_filter=*AudioThreadCreation locally, and ran trybots. BUG=359710 Review URL: https://codereview.chromium.org/240523002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@264974 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'ppapi')
-rw-r--r--ppapi/native_client/src/untrusted/irt_stub/thread_creator.h8
-rw-r--r--ppapi/proxy/ppb_audio_proxy.cc2
-rw-r--r--ppapi/shared_impl/ppb_audio_shared.cc16
-rw-r--r--ppapi/shared_impl/ppb_audio_shared.h6
-rw-r--r--ppapi/tests/DEPS5
-rw-r--r--ppapi/tests/test_audio.cc167
-rw-r--r--ppapi/tests/test_audio.h5
7 files changed, 205 insertions, 4 deletions
diff --git a/ppapi/native_client/src/untrusted/irt_stub/thread_creator.h b/ppapi/native_client/src/untrusted/irt_stub/thread_creator.h
index ac4258f..7adbabe 100644
--- a/ppapi/native_client/src/untrusted/irt_stub/thread_creator.h
+++ b/ppapi/native_client/src/untrusted/irt_stub/thread_creator.h
@@ -10,6 +10,14 @@
#include "native_client/src/untrusted/irt/irt.h"
#include "ppapi/nacl_irt/public/irt_ppapi.h"
+#ifdef __cplusplus
+extern "C" {
+#endif
+
void __nacl_register_thread_creator(const struct nacl_irt_ppapihook *hooks);
+#ifdef __cplusplus
+} // extern "C"
+#endif
+
#endif
diff --git a/ppapi/proxy/ppb_audio_proxy.cc b/ppapi/proxy/ppb_audio_proxy.cc
index 58f5abc..d84c3cb 100644
--- a/ppapi/proxy/ppb_audio_proxy.cc
+++ b/ppapi/proxy/ppb_audio_proxy.cc
@@ -95,6 +95,8 @@ PP_Resource Audio::GetCurrentConfig() {
PP_Bool Audio::StartPlayback() {
if (playing())
return PP_TRUE;
+ if (!PPB_Audio_Shared::IsThreadFunctionReady())
+ return PP_FALSE;
SetStartPlaybackState();
PluginDispatcher::GetForResource(this)->Send(
new PpapiHostMsg_PPBAudio_StartOrStop(
diff --git a/ppapi/shared_impl/ppb_audio_shared.cc b/ppapi/shared_impl/ppb_audio_shared.cc
index ced1146..c8c97d4 100644
--- a/ppapi/shared_impl/ppb_audio_shared.cc
+++ b/ppapi/shared_impl/ppb_audio_shared.cc
@@ -146,10 +146,10 @@ void PPB_Audio_Shared::StartThread() {
#else
// Use NaCl's special API for IRT code that creates threads that call back
// into user code.
- if (NULL == thread_functions.thread_create ||
- NULL == thread_functions.thread_join)
+ if (!IsThreadFunctionReady())
return;
+ DCHECK(!thread_active_);
int result = thread_functions.thread_create(&thread_id_, CallRun, this);
DCHECK_EQ(result, 0);
thread_active_ = true;
@@ -179,12 +179,20 @@ void PPB_Audio_Shared::StopThread() {
#endif
}
+// static
+bool PPB_Audio_Shared::IsThreadFunctionReady() {
+#if defined(OS_NACL)
+ if (thread_functions.thread_create == NULL ||
+ thread_functions.thread_join == NULL)
+ return false;
+#endif
+ return true;
+}
+
#if defined(OS_NACL)
// static
void PPB_Audio_Shared::SetThreadFunctions(
const struct PP_ThreadFunctions* functions) {
- DCHECK(thread_functions.thread_create == NULL);
- DCHECK(thread_functions.thread_join == NULL);
thread_functions = *functions;
}
diff --git a/ppapi/shared_impl/ppb_audio_shared.h b/ppapi/shared_impl/ppb_audio_shared.h
index 6ba8cac..c9fbd9c 100644
--- a/ppapi/shared_impl/ppb_audio_shared.h
+++ b/ppapi/shared_impl/ppb_audio_shared.h
@@ -79,6 +79,12 @@ class PPAPI_SHARED_EXPORT PPB_Audio_Shared
PP_AudioSampleRate sample_rate,
int sample_frame_count);
+ // Returns whether a thread can be created on the client context.
+ // In trusted plugin, this should always return true, as it uses Chrome's
+ // thread library. In NaCl plugin, this returns whether SetThreadFunctions
+ // was invoked properly.
+ static bool IsThreadFunctionReady();
+
#if defined(OS_NACL)
// NaCl has a special API for IRT code to create threads that can call back
// into user code.
diff --git a/ppapi/tests/DEPS b/ppapi/tests/DEPS
index 4f75714..06b64ab 100644
--- a/ppapi/tests/DEPS
+++ b/ppapi/tests/DEPS
@@ -12,6 +12,11 @@ include_rules = [
"+ppapi/lib",
"+ppapi/tests",
"+ppapi/utility",
+
+ # Test to confirm whether PPB_Audio_Shared works properly in NaCl needs to
+ # call nacl_irt_ppapihook internally to emulate some erroneous situation.
+ "+native_client/src/untrusted/irt/irt.h",
+ "+ppapi/native_client/src/untrusted/irt_stub/thread_creator.h",
]
# checkdeps.py shouldn't check include paths for files in clang, which aren't
# part of the chrome build.
diff --git a/ppapi/tests/test_audio.cc b/ppapi/tests/test_audio.cc
index 4fea6e8..1c1c9b5 100644
--- a/ppapi/tests/test_audio.cc
+++ b/ppapi/tests/test_audio.cc
@@ -12,10 +12,87 @@
#include "ppapi/tests/testing_instance.h"
#include "ppapi/tests/test_utils.h"
+#if defined(__native_client__)
+#include "native_client/src/untrusted/irt/irt.h"
+#include "ppapi/native_client/src/untrusted/irt_stub/thread_creator.h"
+#endif
+
#define ARRAYSIZE_UNSAFE(a) \
((sizeof(a) / sizeof(*(a))) / \
static_cast<size_t>(!(sizeof(a) % sizeof(*(a)))))
+#if defined(__native_client__)
+namespace {
+
+void GetNaClIrtPpapiHook(struct nacl_irt_ppapihook* hooks) {
+ nacl_interface_query(NACL_IRT_PPAPIHOOK_v0_1, hooks, sizeof(*hooks));
+}
+
+struct PP_ThreadFunctions g_thread_funcs = {};
+
+void ThreadFunctionsGetter(const struct PP_ThreadFunctions* thread_funcs) {
+ g_thread_funcs = *thread_funcs;
+}
+
+// In order to check if the thread_create is called, CountingThreadCreate()
+// increments this variable. Callers can check if the function is actually
+// called by looking at this value.
+int g_num_thread_create_called = 0;
+int g_num_thread_join_called = 0;
+
+int CountingThreadCreate(uintptr_t* tid,
+ void (*func)(void* thread_argument),
+ void* thread_argument) {
+ ++g_num_thread_create_called;
+ return g_thread_funcs.thread_create(tid, func, thread_argument);
+}
+
+int CountingThreadJoin(uintptr_t tid) {
+ ++g_num_thread_join_called;
+ return g_thread_funcs.thread_join(tid);
+}
+
+// Sets NULL for PP_ThreadFunctions to emulate the situation that
+// ppapi_register_thread_creator() is not yet called.
+void SetNullThreadFunctions() {
+ nacl_irt_ppapihook hooks;
+ GetNaClIrtPpapiHook(&hooks);
+ PP_ThreadFunctions thread_functions = {};
+ hooks.ppapi_register_thread_creator(&thread_functions);
+}
+
+void InjectCountingThreadFunctions() {
+ // First of all, we extract the system default thread functions.
+ // Internally, __nacl_register_thread_creator calls
+ // hooks.ppapi_register_thread_creator with default PP_ThreadFunctions
+ // instance. ThreadFunctionGetter stores it to g_thread_funcs.
+ nacl_irt_ppapihook hooks = { NULL, ThreadFunctionsGetter };
+ __nacl_register_thread_creator(&hooks);
+
+ // Here g_thread_funcs stores the thread functions.
+ // Inject the CountingThreadCreate.
+ PP_ThreadFunctions thread_functions = {
+ CountingThreadCreate,
+ CountingThreadJoin,
+ };
+ GetNaClIrtPpapiHook(&hooks);
+ hooks.ppapi_register_thread_creator(&thread_functions);
+}
+
+// Resets the PP_ThreadFunctions on exit from the scope.
+class ScopedThreadFunctionsResetter {
+ public:
+ ScopedThreadFunctionsResetter() {}
+ ~ScopedThreadFunctionsResetter() {
+ nacl_irt_ppapihook hooks;
+ GetNaClIrtPpapiHook(&hooks);
+ __nacl_register_thread_creator(&hooks);
+ }
+};
+
+} // namespace
+#endif // __native_client__
+
REGISTER_TEST_CASE(Audio);
TestAudio::TestAudio(TestingInstance* instance)
@@ -53,6 +130,11 @@ void TestAudio::RunTests(const std::string& filter) {
RUN_TEST(AudioCallback2, filter);
RUN_TEST(AudioCallback3, filter);
RUN_TEST(AudioCallback4, filter);
+
+#if defined(__native_client__)
+ RUN_TEST(AudioThreadCreatorIsRequired, filter);
+ RUN_TEST(AudioThreadCreatorIsCalled, filter);
+#endif
}
// Test creating audio resources for all guaranteed sample rates and various
@@ -319,6 +401,91 @@ std::string TestAudio::TestAudioCallback4() {
PASS();
}
+#if defined(__native_client__)
+// Tests the behavior of the thread_create functions.
+// For PPB_Audio_Shared to work properly, the user code must call
+// ppapi_register_thread_creator(). This test checks the error handling for the
+// case when user code doesn't call ppapi_register_thread_creator().
+std::string TestAudio::TestAudioThreadCreatorIsRequired() {
+ // We'll inject some thread functions in this test case.
+ // Reset them at the end of this case.
+ ScopedThreadFunctionsResetter thread_resetter;
+
+ // Set the thread functions to NULLs to emulate the situation where
+ // ppapi_register_thread_creator() is not called by user code.
+ SetNullThreadFunctions();
+
+ PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024);
+ ASSERT_TRUE(ac);
+ audio_callback_method_ = NULL;
+ PP_Resource audio = audio_interface_->Create(
+ instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
+ core_interface_->ReleaseResource(ac);
+ ac = 0;
+
+ // StartPlayback() fails, because no thread creating function
+ // is available.
+ ASSERT_FALSE(audio_interface_->StartPlayback(audio));
+
+ // If any more audio callbacks are generated,
+ // we should crash (which is good).
+ audio_callback_method_ = NULL;
+
+ core_interface_->ReleaseResource(audio);
+
+ PASS();
+}
+
+// Tests whether the thread functions passed from the user code are actually
+// called.
+std::string TestAudio::TestAudioThreadCreatorIsCalled() {
+ // We'll inject some thread functions in this test case.
+ // Reset them at the end of this case.
+ ScopedThreadFunctionsResetter thread_resetter;
+
+ // Inject the thread counting function. In the injected function,
+ // when called, g_num_thread_create_called is incremented.
+ g_num_thread_create_called = 0;
+ g_num_thread_join_called = 0;
+ InjectCountingThreadFunctions();
+
+ PP_Resource ac = CreateAudioConfig(PP_AUDIOSAMPLERATE_44100, 1024);
+ ASSERT_TRUE(ac);
+ audio_callback_method_ = NULL;
+ PP_Resource audio = audio_interface_->Create(
+ instance_->pp_instance(), ac, AudioCallbackTrampoline, this);
+ core_interface_->ReleaseResource(ac);
+ ac = 0;
+
+ audio_callback_event_.Reset();
+ test_done_ = false;
+
+ audio_callback_method_ = &TestAudio::AudioCallbackTest;
+ ASSERT_TRUE(audio_interface_->StartPlayback(audio));
+
+ // Wait for the audio callback to be called.
+ audio_callback_event_.Wait();
+ // Here, the injected thread_create is called, but thread_join is not yet.
+ ASSERT_EQ(1, g_num_thread_create_called);
+ ASSERT_EQ(0, g_num_thread_join_called);
+
+ ASSERT_TRUE(audio_interface_->StopPlayback(audio));
+
+ test_done_ = true;
+
+ // Here, the injected thread_join is called.
+ ASSERT_EQ(1, g_num_thread_join_called);
+
+ // If any more audio callbacks are generated,
+ // we should crash (which is good).
+ audio_callback_method_ = NULL;
+
+ core_interface_->ReleaseResource(audio);
+
+ PASS();
+}
+#endif
+
// TODO(raymes): Test that actually playback happens correctly, etc.
static void Crash() {
diff --git a/ppapi/tests/test_audio.h b/ppapi/tests/test_audio.h
index 56e6254..63e06f4 100644
--- a/ppapi/tests/test_audio.h
+++ b/ppapi/tests/test_audio.h
@@ -30,6 +30,11 @@ class TestAudio : public TestCase {
std::string TestAudioCallback3();
std::string TestAudioCallback4();
+#if defined(__native_client__)
+ std::string TestAudioThreadCreatorIsRequired();
+ std::string TestAudioThreadCreatorIsCalled();
+#endif
+
// Calls |audio_callback_method_| (where |user_data| is "this").
static void AudioCallbackTrampoline(void* sample_buffer,
uint32_t buffer_size_in_bytes,