Chromium Code Reviews| OLD | NEW | 
|---|---|
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 The Chromium Authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "media/gpu/avda_codec_allocator.h" | 5 #include "media/gpu/avda_codec_allocator.h" | 
| 6 | 6 | 
| 7 #include <stdint.h> | 7 #include <stdint.h> | 
| 8 | 8 | 
| 9 #include <memory> | 9 #include <memory> | 
| 10 | 10 | 
| 11 #include "base/bind.h" | 11 #include "base/bind.h" | 
| 12 #include "base/logging.h" | 12 #include "base/logging.h" | 
| 13 #include "base/test/simple_test_tick_clock.h" | |
| 13 #include "base/time/tick_clock.h" | 14 #include "base/time/tick_clock.h" | 
| 14 #include "testing/gmock/include/gmock/gmock.h" | 15 #include "testing/gmock/include/gmock/gmock.h" | 
| 15 #include "testing/gtest/include/gtest/gtest.h" | 16 #include "testing/gtest/include/gtest/gtest.h" | 
| 16 | 17 | 
| 17 using testing::Invoke; | 18 using testing::Invoke; | 
| 18 using testing::NiceMock; | 19 using testing::NiceMock; | 
| 19 using testing::_; | 20 using testing::_; | 
| 20 | 21 | 
| 21 namespace media { | 22 namespace media { | 
| 22 namespace { | 23 namespace { | 
| (...skipping 11 matching lines...) Expand all Loading... | |
| 34 if (about_to_wait_event) | 35 if (about_to_wait_event) | 
| 35 about_to_wait_event->Signal(); | 36 about_to_wait_event->Signal(); | 
| 36 wait_event->Wait(); | 37 wait_event->Wait(); | 
| 37 } | 38 } | 
| 38 | 39 | 
| 39 void SignalImmediately(base::WaitableEvent* event) { | 40 void SignalImmediately(base::WaitableEvent* event) { | 
| 40 event->Signal(); | 41 event->Signal(); | 
| 41 } | 42 } | 
| 42 } | 43 } | 
| 43 | 44 | 
| 44 class MockTickClock : public base::TickClock { | |
| 45 public: | |
| 46 MockTickClock() { | |
| 47 // Don't start with the null time. | |
| 48 Advance(1000); | |
| 49 } | |
| 50 ~MockTickClock() override{}; | |
| 51 base::TimeTicks NowTicks() override { | |
| 52 base::AutoLock auto_lock(lock_); | |
| 53 return now_; | |
| 54 } | |
| 55 | |
| 56 // Handy utility. | |
| 57 void Advance(int msec) { | |
| 58 base::AutoLock auto_lock(lock_); | |
| 59 now_ += base::TimeDelta::FromMilliseconds(msec); | |
| 60 } | |
| 61 | |
| 62 private: | |
| 63 base::Lock lock_; | |
| 64 base::TimeTicks now_; | |
| 65 }; | |
| 66 | |
| 67 class MockClient : public AVDACodecAllocatorClient { | 45 class MockClient : public AVDACodecAllocatorClient { | 
| 68 public: | 46 public: | 
| 69 MOCK_METHOD1(OnSurfaceAvailable, void(bool success)); | 47 MOCK_METHOD1(OnSurfaceAvailable, void(bool success)); | 
| 70 MOCK_METHOD0(OnSurfaceDestroyed, void()); | 48 MOCK_METHOD0(OnSurfaceDestroyed, void()); | 
| 71 | 49 | 
| 72 // Gmock doesn't let us mock methods taking move-only types. | 50 // Gmock doesn't let us mock methods taking move-only types. | 
| 73 MOCK_METHOD1(OnCodecConfiguredMock, void(VideoCodecBridge* media_codec)); | 51 MOCK_METHOD1(OnCodecConfiguredMock, void(VideoCodecBridge* media_codec)); | 
| 74 void OnCodecConfigured( | 52 void OnCodecConfigured( | 
| 75 std::unique_ptr<VideoCodecBridge> media_codec) override { | 53 std::unique_ptr<VideoCodecBridge> media_codec) override { | 
| 76 OnCodecConfiguredMock(media_codec.get()); | 54 OnCodecConfiguredMock(media_codec.get()); | 
| 77 } | 55 } | 
| 78 }; | 56 }; | 
| 79 | 57 | 
| 80 class AVDACodecAllocatorTest : public testing::Test { | 58 class AVDACodecAllocatorTest : public testing::Test { | 
| 81 public: | 59 public: | 
| 82 AVDACodecAllocatorTest() : allocator_thread_("AllocatorThread") {} | 60 AVDACodecAllocatorTest() | 
| 61 : allocator_thread_("AllocatorThread"), | |
| 62 stop_event_(base::WaitableEvent::ResetPolicy::AUTOMATIC, | |
| 63 base::WaitableEvent::InitialState::NOT_SIGNALED) { | |
| 64 // Don't start the clock at null. | |
| 65 tick_clock_.Advance(base::TimeDelta::FromSeconds(1)); | |
| 66 } | |
| 67 | |
| 83 ~AVDACodecAllocatorTest() override {} | 68 ~AVDACodecAllocatorTest() override {} | 
| 84 | 69 | 
| 85 protected: | 70 protected: | 
| 86 void SetUp() override { | 71 void SetUp() override { | 
| 87 // Start the main thread for the allocator. This would normally be the GPU | 72 // Start the main thread for the allocator. This would normally be the GPU | 
| 88 // main thread. | 73 // main thread. | 
| 89 ASSERT_TRUE(allocator_thread_.Start()); | 74 ASSERT_TRUE(allocator_thread_.Start()); | 
| 90 | 75 | 
| 91 // AVDACodecAllocator likes to post tasks to the current thread. | 76 // Create the first allocator on the allocator thread. | 
| 92 | |
| 93 test_information_.reset(new AVDACodecAllocator::TestInformation()); | |
| 94 test_information_->tick_clock_.reset(new MockTickClock()); | |
| 95 test_information_->stop_event_.reset(new base::WaitableEvent( | |
| 96 base::WaitableEvent::ResetPolicy::AUTOMATIC, | |
| 97 base::WaitableEvent::InitialState::NOT_SIGNALED)); | |
| 98 | |
| 99 // Allocate the allocator on the appropriate thread. | |
| 100 allocator_ = PostAndWait( | 77 allocator_ = PostAndWait( | 
| 101 FROM_HERE, base::Bind( | 78 FROM_HERE, base::Bind( | 
| 102 [](AVDACodecAllocator::TestInformation* test_info) { | 79 [](base::TickClock* clock, base::WaitableEvent* event) { | 
| 103 return new AVDACodecAllocator(test_info); | 80 return new AVDACodecAllocator(clock, event); | 
| 104 }, | 81 }, | 
| 105 test_information_.get())); | 82 &tick_clock_, &stop_event_)); | 
| 106 allocator2_ = new AVDACodecAllocator(test_information_.get()); | 83 allocator2_ = new AVDACodecAllocator(); | 
| 107 | |
| 108 // All threads should be stopped | |
| 109 ASSERT_FALSE(IsThreadRunning(TaskType::AUTO_CODEC)); | |
| 110 ASSERT_FALSE(IsThreadRunning(TaskType::SW_CODEC)); | |
| 111 | |
| 112 // Register an AVDA instance to start the allocator's threads. | |
| 113 ASSERT_TRUE(StartThread(avda1_)); | |
| 114 | |
| 115 // Assert that at least the AUTO_CODEC thread is started. The other might | |
| 116 // not be. | |
| 117 ASSERT_TRUE(IsThreadRunning(TaskType::AUTO_CODEC)); | |
| 118 ASSERT_EQ(TaskType::AUTO_CODEC, TaskTypeForAllocation()); | |
| 119 } | 84 } | 
| 120 | 85 | 
| 121 void TearDown() override { | 86 void TearDown() override { | 
| 122 // Don't leave any threads hung, or this will hang too. | 87 // Don't leave any threads hung, or this will hang too. | 
| 123 // It would be nice if we could let a unique ptr handle this, but the | 88 // It would be nice if we could let a unique ptr handle this, but the | 
| 124 // destructor is private. We also have to destroy it on the right thread. | 89 // destructor is private. We also have to destroy it on the right thread. | 
| 125 PostAndWait(FROM_HERE, base::Bind( | 90 PostAndWait(FROM_HERE, base::Bind( | 
| 126 [](AVDACodecAllocator* allocator) { | 91 [](AVDACodecAllocator* allocator) { | 
| 127 delete allocator; | 92 delete allocator; | 
| 128 return true; | 93 return true; | 
| (...skipping 21 matching lines...) Expand all Loading... | |
| 150 // stop has completed. It's async with respect to the allocator thread. | 115 // stop has completed. It's async with respect to the allocator thread. | 
| 151 PostAndWait(FROM_HERE, base::Bind( | 116 PostAndWait(FROM_HERE, base::Bind( | 
| 152 [](AVDACodecAllocator* allocator, | 117 [](AVDACodecAllocator* allocator, | 
| 153 AVDACodecAllocatorClient* avda) { | 118 AVDACodecAllocatorClient* avda) { | 
| 154 allocator->StopThread(avda); | 119 allocator->StopThread(avda); | 
| 155 return true; | 120 return true; | 
| 156 }, | 121 }, | 
| 157 allocator_, avda)); | 122 allocator_, avda)); | 
| 158 // Note that we don't do this on the allocator thread, since that's the | 123 // Note that we don't do this on the allocator thread, since that's the | 
| 159 // thread that will signal it. | 124 // thread that will signal it. | 
| 160 test_information_->stop_event_->Wait(); | 125 stop_event_.Wait(); | 
| 161 } | 126 } | 
| 162 | 127 | 
| 163 // Return the running state of |task_type|, doing the necessary thread hops. | 128 // Return the running state of |task_type|, doing the necessary thread hops. | 
| 164 bool IsThreadRunning(TaskType task_type) { | 129 bool IsThreadRunning(TaskType task_type) { | 
| 165 return PostAndWait( | 130 return PostAndWait( | 
| 166 FROM_HERE, | 131 FROM_HERE, | 
| 167 base::Bind( | 132 base::Bind( | 
| 168 [](AVDACodecAllocator* allocator, TaskType task_type) { | 133 [](AVDACodecAllocator* allocator, TaskType task_type) { | 
| 169 return allocator->GetThreadForTesting(task_type).IsRunning(); | 134 return allocator->GetThreadForTesting(task_type).IsRunning(); | 
| 170 }, | 135 }, | 
| (...skipping 24 matching lines...) Expand all Loading... | |
| 195 ReturnType return_value = ReturnType(); | 160 ReturnType return_value = ReturnType(); | 
| 196 allocator_thread_.task_runner()->PostTask( | 161 allocator_thread_.task_runner()->PostTask( | 
| 197 from_here, | 162 from_here, | 
| 198 base::Bind(&RunAndSignalTask<ReturnType>, &event, &return_value, cb)); | 163 base::Bind(&RunAndSignalTask<ReturnType>, &event, &return_value, cb)); | 
| 199 event.Wait(); | 164 event.Wait(); | 
| 200 return return_value; | 165 return return_value; | 
| 201 } | 166 } | 
| 202 | 167 | 
| 203 base::Thread allocator_thread_; | 168 base::Thread allocator_thread_; | 
| 204 | 169 | 
| 205 // Test info that we provide to the codec allocator. | 170 // The test params for |allocator_|. | 
| 206 std::unique_ptr<AVDACodecAllocator::TestInformation> test_information_; | 171 base::SimpleTestTickClock tick_clock_; | 
| 172 base::WaitableEvent stop_event_; | |
| 207 | 173 | 
| 208 // Allocators that we own. The first is intialized to be used on the allocator | 174 // Allocators that we own. The first is intialized to be used on the allocator | 
| 209 // thread and the second one is initialized on the test thread. Each test | 175 // thread and the second one is initialized on the test thread. Each test | 
| 210 // should only be using one of the two. They are not unique_ptrs because the | 176 // should only be using one of the two. They are not unique_ptrs because the | 
| 211 // destructor is private and they need to be destructed on the right thread. | 177 // destructor is private and they need to be destructed on the right thread. | 
| 212 AVDACodecAllocator* allocator_ = nullptr; | 178 AVDACodecAllocator* allocator_ = nullptr; | 
| 213 AVDACodecAllocator* allocator2_ = nullptr; | 179 AVDACodecAllocator* allocator2_ = nullptr; | 
| 214 | 180 | 
| 215 NiceMock<MockClient> client1_, client2_, client3_; | 181 NiceMock<MockClient> client1_, client2_, client3_; | 
| 216 NiceMock<MockClient>* avda1_ = &client1_; | 182 NiceMock<MockClient>* avda1_ = &client1_; | 
| 217 NiceMock<MockClient>* avda2_ = &client2_; | 183 NiceMock<MockClient>* avda2_ = &client2_; | 
| 218 NiceMock<MockClient>* avda3_ = &client3_; | 184 NiceMock<MockClient>* avda3_ = &client3_; | 
| 219 }; | 185 }; | 
| 220 | 186 | 
| 221 TEST_F(AVDACodecAllocatorTest, TestMultiInstance) { | 187 TEST_F(AVDACodecAllocatorTest, ThreadsStartWhenClientsStart) { | 
| 222 // Add an avda instance. This one must succeed immediately, since the last | 188 ASSERT_FALSE(IsThreadRunning(TaskType::AUTO_CODEC)); | 
| 223 // one is still running. | 189 ASSERT_FALSE(IsThreadRunning(TaskType::SW_CODEC)); | 
| 224 ASSERT_TRUE(StartThread(avda2_)); | 190 ASSERT_TRUE(StartThread(avda1_)); | 
| 191 // Assert that at least the AUTO_CODEC thread is started. The other might | |
| 192 // not be. | |
| 193 ASSERT_TRUE(IsThreadRunning(TaskType::AUTO_CODEC)); | |
| 194 } | |
| 225 | 195 | 
| 226 // Stop the original avda instance. | 196 TEST_F(AVDACodecAllocatorTest, ThreadsStopAfterAllClientsStop) { | 
| 197 StartThread(avda1_); | |
| 198 StartThread(avda2_); | |
| 227 StopThread(avda1_); | 199 StopThread(avda1_); | 
| 228 | |
| 229 // Verify that the AUTO_CODEC thread is still running. | |
| 230 ASSERT_TRUE(IsThreadRunning(TaskType::AUTO_CODEC)); | 200 ASSERT_TRUE(IsThreadRunning(TaskType::AUTO_CODEC)); | 
| 231 ASSERT_EQ(TaskType::AUTO_CODEC, TaskTypeForAllocation()); | |
| 232 | |
| 233 // Remove the second instance and wait for it to stop. Remember that it | |
| 234 // stops after messages have been posted to the thread, so we don't know | |
| 235 // how long it will take. | |
| 236 StopThread(avda2_); | 201 StopThread(avda2_); | 
| 237 | |
| 238 // Verify that the threads have stopped. | |
| 239 ASSERT_FALSE(IsThreadRunning(TaskType::AUTO_CODEC)); | 202 ASSERT_FALSE(IsThreadRunning(TaskType::AUTO_CODEC)); | 
| 240 ASSERT_FALSE(IsThreadRunning(TaskType::SW_CODEC)); | 203 ASSERT_FALSE(IsThreadRunning(TaskType::SW_CODEC)); | 
| 
 
liberato (no reviews please)
2016/11/30 06:47:30
i'm not sure why this thread must be stopped by no
 
watk
2016/11/30 20:48:42
Oh genius! I think you've got it.
 
 | |
| 241 } | 204 } | 
| 242 | 205 | 
| 243 TEST_F(AVDACodecAllocatorTest, TestHangThread) { | 206 TEST_F(AVDACodecAllocatorTest, TestHangThread) { | 
| 207 StartThread(avda1_); | |
| 244 ASSERT_EQ(TaskType::AUTO_CODEC, TaskTypeForAllocation()); | 208 ASSERT_EQ(TaskType::AUTO_CODEC, TaskTypeForAllocation()); | 
| 245 | 209 | 
| 246 // Hang the AUTO_CODEC thread. | 210 // Hang the AUTO_CODEC thread. | 
| 247 base::WaitableEvent about_to_wait_event( | 211 base::WaitableEvent about_to_wait_event( | 
| 248 base::WaitableEvent::ResetPolicy::MANUAL, | 212 base::WaitableEvent::ResetPolicy::MANUAL, | 
| 249 base::WaitableEvent::InitialState::NOT_SIGNALED); | 213 base::WaitableEvent::InitialState::NOT_SIGNALED); | 
| 250 base::WaitableEvent wait_event( | 214 base::WaitableEvent wait_event( | 
| 251 base::WaitableEvent::ResetPolicy::MANUAL, | 215 base::WaitableEvent::ResetPolicy::MANUAL, | 
| 252 base::WaitableEvent::InitialState::NOT_SIGNALED); | 216 base::WaitableEvent::InitialState::NOT_SIGNALED); | 
| 253 TaskRunnerFor(TaskType::AUTO_CODEC) | 217 TaskRunnerFor(TaskType::AUTO_CODEC) | 
| 254 ->PostTask(FROM_HERE, base::Bind(&WaitUntilRestarted, | 218 ->PostTask(FROM_HERE, base::Bind(&WaitUntilRestarted, | 
| 255 &about_to_wait_event, &wait_event)); | 219 &about_to_wait_event, &wait_event)); | 
| 256 // Wait until the task starts, so that |allocator_| starts the hang timer. | 220 // Wait until the task starts, so that |allocator_| starts the hang timer. | 
| 257 about_to_wait_event.Wait(); | 221 about_to_wait_event.Wait(); | 
| 258 | 222 | 
| 259 // Verify that we've failed over after a long time has passed. | 223 // Verify that we've failed over after a long time has passed. | 
| 260 static_cast<MockTickClock*>(test_information_->tick_clock_.get()) | 224 tick_clock_.Advance(base::TimeDelta::FromSeconds(1)); | 
| 261 ->Advance(1000); | |
| 262 // Note that this should return the SW codec task type even if that thread | 225 // Note that this should return the SW codec task type even if that thread | 
| 263 // failed to start. TaskRunnerFor() will return the current thread in that | 226 // failed to start. TaskRunnerFor() will return the current thread in that | 
| 264 // case too. | 227 // case too. | 
| 265 ASSERT_EQ(TaskType::SW_CODEC, TaskTypeForAllocation()); | 228 ASSERT_EQ(TaskType::SW_CODEC, TaskTypeForAllocation()); | 
| 266 | 229 | 
| 267 // Un-hang the thread and wait for it to let another task run. This will | 230 // Un-hang the thread and wait for it to let another task run. This will | 
| 268 // notify |allocator_| that the thread is no longer hung. | 231 // notify |allocator_| that the thread is no longer hung. | 
| 269 base::WaitableEvent done_waiting_event( | 232 base::WaitableEvent done_waiting_event( | 
| 270 base::WaitableEvent::ResetPolicy::MANUAL, | 233 base::WaitableEvent::ResetPolicy::MANUAL, | 
| 271 base::WaitableEvent::InitialState::NOT_SIGNALED); | 234 base::WaitableEvent::InitialState::NOT_SIGNALED); | 
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 340 | 303 | 
| 341 TEST_F(AVDACodecAllocatorTest, DeallocatingIsSafeDuringSurfaceDestroyed) { | 304 TEST_F(AVDACodecAllocatorTest, DeallocatingIsSafeDuringSurfaceDestroyed) { | 
| 342 allocator2_->AllocateSurface(avda1_, 1); | 305 allocator2_->AllocateSurface(avda1_, 1); | 
| 343 EXPECT_CALL(*avda1_, OnSurfaceDestroyed()).WillOnce(Invoke([=]() { | 306 EXPECT_CALL(*avda1_, OnSurfaceDestroyed()).WillOnce(Invoke([=]() { | 
| 344 allocator2_->DeallocateSurface(avda1_, 1); | 307 allocator2_->DeallocateSurface(avda1_, 1); | 
| 345 })); | 308 })); | 
| 346 allocator2_->OnSurfaceDestroyed(1); | 309 allocator2_->OnSurfaceDestroyed(1); | 
| 347 } | 310 } | 
| 348 | 311 | 
| 349 } // namespace media | 312 } // namespace media | 
| OLD | NEW |