diff options
author | hidehiko@chromium.org <hidehiko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-21 10:36:21 +0000 |
---|---|---|
committer | hidehiko@chromium.org <hidehiko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-04-21 10:36:21 +0000 |
commit | a4a01e47a657b2a74fa6f2748006efcb01f63523 (patch) | |
tree | df08272594c974115fdadf892e12f445ec527a0d /ppapi | |
parent | ad6d13372a73c90fc24059b22b2fa0ea927f2151 (diff) | |
download | chromium_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.h | 8 | ||||
-rw-r--r-- | ppapi/proxy/ppb_audio_proxy.cc | 2 | ||||
-rw-r--r-- | ppapi/shared_impl/ppb_audio_shared.cc | 16 | ||||
-rw-r--r-- | ppapi/shared_impl/ppb_audio_shared.h | 6 | ||||
-rw-r--r-- | ppapi/tests/DEPS | 5 | ||||
-rw-r--r-- | ppapi/tests/test_audio.cc | 167 | ||||
-rw-r--r-- | ppapi/tests/test_audio.h | 5 |
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, |