OLD | NEW |
(Empty) | |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "base/bind.h" |
| 6 #include "base/memory/ref_counted.h" |
| 7 #include "base/message_loop/message_loop.h" |
| 8 #include "base/run_loop.h" |
| 9 #include "base/synchronization/waitable_event.h" |
| 10 #include "base/threading/simple_thread.h" |
| 11 #include "ppapi/c/pp_completion_callback.h" |
| 12 #include "ppapi/c/pp_errors.h" |
| 13 #include "ppapi/proxy/ppapi_proxy_test.h" |
| 14 #include "ppapi/proxy/ppb_message_loop_proxy.h" |
| 15 #include "ppapi/shared_impl/callback_tracker.h" |
| 16 #include "ppapi/shared_impl/proxy_lock.h" |
| 17 #include "ppapi/shared_impl/resource.h" |
| 18 #include "ppapi/shared_impl/resource_tracker.h" |
| 19 #include "ppapi/shared_impl/scoped_pp_resource.h" |
| 20 #include "ppapi/shared_impl/test_globals.h" |
| 21 #include "ppapi/shared_impl/tracked_callback.h" |
| 22 #include "testing/gtest/include/gtest/gtest.h" |
| 23 |
| 24 // Note, this file tests TrackedCallback which lives in ppapi/shared_impl. |
| 25 // Unfortunately, we need the test to live in ppapi/proxy so that it can use |
| 26 // the thread support there. |
| 27 namespace ppapi { |
| 28 namespace proxy { |
| 29 |
| 30 namespace { |
| 31 |
| 32 class CallbackThread : public base::SimpleThread { |
| 33 public: |
| 34 explicit CallbackThread(PP_Instance instance) |
| 35 : SimpleThread("CallbackThread"), instance_(instance) {} |
| 36 ~CallbackThread() override {} |
| 37 |
| 38 // base::SimpleThread overrides. |
| 39 void Start() override { |
| 40 { |
| 41 ProxyAutoLock acquire; |
| 42 // Create the message loop here, after PpapiGlobals has been created. |
| 43 message_loop_ = new MessageLoopResource(instance_); |
| 44 } |
| 45 base::SimpleThread::Start(); |
| 46 } |
| 47 void Join() override { |
| 48 { |
| 49 ProxyAutoLock acquire; |
| 50 message_loop()->PostQuit(PP_TRUE); |
| 51 message_loop_ = nullptr; |
| 52 } |
| 53 base::SimpleThread::Join(); |
| 54 } |
| 55 void Run() override { |
| 56 ProxyAutoLock acquire; |
| 57 // Make a local copy of message_loop_ for this thread so we can interact |
| 58 // with it even after the main thread releases it. |
| 59 scoped_refptr<MessageLoopResource> message_loop(message_loop_); |
| 60 message_loop->AttachToCurrentThread(); |
| 61 // Note, run releases the lock to run events. |
| 62 message_loop->Run(); |
| 63 message_loop->DetachFromThread(); |
| 64 } |
| 65 |
| 66 MessageLoopResource* message_loop() { return message_loop_.get(); } |
| 67 |
| 68 private: |
| 69 PP_Instance instance_; |
| 70 scoped_refptr<MessageLoopResource> message_loop_; |
| 71 }; |
| 72 |
| 73 class TrackedCallbackTest : public PluginProxyTest { |
| 74 public: |
| 75 TrackedCallbackTest() : thread_(pp_instance()) {} |
| 76 CallbackThread& thread() { return thread_; } |
| 77 |
| 78 private: |
| 79 // PluginProxyTest overrides. |
| 80 void SetUp() override { |
| 81 PluginProxyTest::SetUp(); |
| 82 thread_.Start(); |
| 83 } |
| 84 void TearDown() override { |
| 85 thread_.Join(); |
| 86 PluginProxyTest::TearDown(); |
| 87 base::RunLoop run_loop; |
| 88 run_loop.RunUntilIdle(); |
| 89 } |
| 90 CallbackThread thread_; |
| 91 }; |
| 92 |
| 93 // All valid results (PP_OK, PP_ERROR_...) are nonpositive. |
| 94 const int32_t kInitializedResultValue = 1; |
| 95 const int32_t kOverrideResultValue = 2; |
| 96 |
| 97 struct CallbackRunInfo { |
| 98 explicit CallbackRunInfo(base::ThreadChecker* thread_checker) |
| 99 : run_count_(0), |
| 100 result_(kInitializedResultValue), |
| 101 completion_task_run_count_(0), |
| 102 completion_task_result_(kInitializedResultValue), |
| 103 thread_checker_(thread_checker), |
| 104 callback_did_run_event_(true, false) {} |
| 105 void CallbackDidRun(int32_t result) { |
| 106 CHECK(thread_checker_->CalledOnValidThread()); |
| 107 if (!run_count_) |
| 108 result_ = result; |
| 109 ++run_count_; |
| 110 callback_did_run_event_.Signal(); |
| 111 } |
| 112 void CompletionTaskDidRun(int32_t result) { |
| 113 CHECK(thread_checker_->CalledOnValidThread()); |
| 114 if (!completion_task_run_count_) |
| 115 completion_task_result_ = result; |
| 116 ++completion_task_run_count_; |
| 117 } |
| 118 void WaitUntilCompleted() { callback_did_run_event_.Wait(); } |
| 119 unsigned run_count() { return run_count_; } |
| 120 int32_t result() { return result_; } |
| 121 unsigned completion_task_run_count() { return completion_task_run_count_; } |
| 122 int32_t completion_task_result() { return completion_task_result_; } |
| 123 |
| 124 private: |
| 125 unsigned run_count_; |
| 126 int32_t result_; |
| 127 unsigned completion_task_run_count_; |
| 128 int32_t completion_task_result_; |
| 129 // Weak; owned by the creator of CallbackRunInfo. |
| 130 base::ThreadChecker* thread_checker_; |
| 131 |
| 132 base::WaitableEvent callback_did_run_event_; |
| 133 }; |
| 134 |
| 135 void TestCallback(void* user_data, int32_t result) { |
| 136 CallbackRunInfo* info = static_cast<CallbackRunInfo*>(user_data); |
| 137 info->CallbackDidRun(result); |
| 138 } |
| 139 |
| 140 // CallbackShutdownTest -------------------------------------------------------- |
| 141 |
| 142 class CallbackShutdownTest : public TrackedCallbackTest { |
| 143 public: |
| 144 CallbackShutdownTest() |
| 145 : info_did_run_(&thread_checker_), |
| 146 info_did_abort_(&thread_checker_), |
| 147 info_didnt_run_(&thread_checker_) {} |
| 148 |
| 149 // Cases: |
| 150 // (1) A callback which is run (so shouldn't be aborted on shutdown). |
| 151 // (2) A callback which is aborted (so shouldn't be aborted on shutdown). |
| 152 // (3) A callback which isn't run (so should be aborted on shutdown). |
| 153 CallbackRunInfo& info_did_run() { return info_did_run_; } // (1) |
| 154 CallbackRunInfo& info_did_abort() { return info_did_abort_; } // (2) |
| 155 CallbackRunInfo& info_didnt_run() { return info_didnt_run_; } // (3) |
| 156 |
| 157 private: |
| 158 base::ThreadChecker thread_checker_; |
| 159 CallbackRunInfo info_did_run_; |
| 160 CallbackRunInfo info_did_abort_; |
| 161 CallbackRunInfo info_didnt_run_; |
| 162 }; |
| 163 |
| 164 } // namespace |
| 165 |
| 166 // Tests that callbacks are properly aborted on module shutdown. |
| 167 TEST_F(CallbackShutdownTest, AbortOnShutdown) { |
| 168 ProxyAutoLock lock; |
| 169 scoped_refptr<Resource> resource( |
| 170 new Resource(OBJECT_IS_PROXY, pp_instance())); |
| 171 |
| 172 // Set up case (1) (see above). |
| 173 EXPECT_EQ(0U, info_did_run().run_count()); |
| 174 // TODO(dmichael): Test this on a background thread? |
| 175 scoped_refptr<TrackedCallback> callback_did_run = new TrackedCallback( |
| 176 resource.get(), |
| 177 PP_MakeCompletionCallback(&TestCallback, &info_did_run())); |
| 178 EXPECT_EQ(0U, info_did_run().run_count()); |
| 179 callback_did_run->Run(PP_OK); |
| 180 EXPECT_EQ(1U, info_did_run().run_count()); |
| 181 EXPECT_EQ(PP_OK, info_did_run().result()); |
| 182 |
| 183 // Set up case (2). |
| 184 EXPECT_EQ(0U, info_did_abort().run_count()); |
| 185 scoped_refptr<TrackedCallback> callback_did_abort = new TrackedCallback( |
| 186 resource.get(), |
| 187 PP_MakeCompletionCallback(&TestCallback, &info_did_abort())); |
| 188 EXPECT_EQ(0U, info_did_abort().run_count()); |
| 189 callback_did_abort->Abort(); |
| 190 EXPECT_EQ(1U, info_did_abort().run_count()); |
| 191 EXPECT_EQ(PP_ERROR_ABORTED, info_did_abort().result()); |
| 192 |
| 193 // Set up case (3). |
| 194 EXPECT_EQ(0U, info_didnt_run().run_count()); |
| 195 scoped_refptr<TrackedCallback> callback_didnt_run = new TrackedCallback( |
| 196 resource.get(), |
| 197 PP_MakeCompletionCallback(&TestCallback, &info_didnt_run())); |
| 198 EXPECT_EQ(0U, info_didnt_run().run_count()); |
| 199 |
| 200 GetGlobals()->GetCallbackTrackerForInstance(pp_instance())->AbortAll(); |
| 201 |
| 202 // Check case (1). |
| 203 EXPECT_EQ(1U, info_did_run().run_count()); |
| 204 |
| 205 // Check case (2). |
| 206 EXPECT_EQ(1U, info_did_abort().run_count()); |
| 207 |
| 208 // Check case (3). |
| 209 EXPECT_EQ(1U, info_didnt_run().run_count()); |
| 210 EXPECT_EQ(PP_ERROR_ABORTED, info_didnt_run().result()); |
| 211 } |
| 212 |
| 213 // CallbackResourceTest -------------------------------------------------------- |
| 214 |
| 215 namespace { |
| 216 |
| 217 class CallbackResourceTest : public TrackedCallbackTest { |
| 218 public: |
| 219 CallbackResourceTest() {} |
| 220 }; |
| 221 |
| 222 class CallbackMockResource : public Resource { |
| 223 public: |
| 224 static scoped_refptr<CallbackMockResource> Create(PP_Instance instance) { |
| 225 ProxyAutoLock acquire; |
| 226 return scoped_refptr<CallbackMockResource>( |
| 227 new CallbackMockResource(instance)); |
| 228 } |
| 229 ~CallbackMockResource() {} |
| 230 |
| 231 // Take a reference to this resource, which will add it to the tracker. |
| 232 void TakeRef() { |
| 233 ProxyAutoLock acquire; |
| 234 ScopedPPResource temp_resource(ScopedPPResource::PassRef(), GetReference()); |
| 235 EXPECT_NE(0, temp_resource.get()); |
| 236 reference_holder_ = temp_resource; |
| 237 } |
| 238 // Release it, removing it from the tracker. |
| 239 void ReleaseRef() { |
| 240 ProxyAutoLock acquire; |
| 241 reference_holder_ = 0; |
| 242 } |
| 243 |
| 244 // Create the test callbacks on a background thread, so that we can verify |
| 245 // they are run on the same thread where they were created. |
| 246 void CreateCallbacksOnLoop(MessageLoopResource* loop_resource) { |
| 247 ProxyAutoLock acquire; |
| 248 // |thread_checker_| will bind to the background thread. |
| 249 thread_checker_.DetachFromThread(); |
| 250 loop_resource->message_loop_proxy()->PostTask( |
| 251 FROM_HERE, RunWhileLocked(base::Bind( |
| 252 &CallbackMockResource::CreateCallbacks, this))); |
| 253 } |
| 254 |
| 255 int32_t CompletionTask(CallbackRunInfo* info, int32_t result) { |
| 256 // The completion task must run on the thread where the callback was |
| 257 // created, and must hold the proxy lock. |
| 258 CHECK(thread_checker_.CalledOnValidThread()); |
| 259 ProxyLock::AssertAcquired(); |
| 260 |
| 261 // We should run before the callback. |
| 262 CHECK_EQ(0U, info->run_count()); |
| 263 info->CompletionTaskDidRun(result); |
| 264 return kOverrideResultValue; |
| 265 } |
| 266 |
| 267 void CheckInitialState() { |
| 268 callbacks_created_event_.Wait(); |
| 269 EXPECT_EQ(0U, info_did_run_.run_count()); |
| 270 EXPECT_EQ(0U, info_did_run_.completion_task_run_count()); |
| 271 |
| 272 EXPECT_EQ(0U, info_did_run_with_completion_task_.run_count()); |
| 273 EXPECT_EQ(0U, |
| 274 info_did_run_with_completion_task_.completion_task_run_count()); |
| 275 |
| 276 EXPECT_EQ(0U, info_did_abort_.run_count()); |
| 277 EXPECT_EQ(0U, info_did_abort_.completion_task_run_count()); |
| 278 |
| 279 EXPECT_EQ(0U, info_didnt_run_.run_count()); |
| 280 EXPECT_EQ(0U, info_didnt_run_.completion_task_run_count()); |
| 281 } |
| 282 |
| 283 void RunCallbacks() { |
| 284 callback_did_run_->Run(PP_OK); |
| 285 callback_did_run_with_completion_task_->Run(PP_OK); |
| 286 callback_did_abort_->Abort(); |
| 287 info_did_run_.WaitUntilCompleted(); |
| 288 info_did_run_with_completion_task_.WaitUntilCompleted(); |
| 289 info_did_abort_.WaitUntilCompleted(); |
| 290 } |
| 291 |
| 292 void CheckIntermediateState() { |
| 293 EXPECT_EQ(1U, info_did_run_.run_count()); |
| 294 EXPECT_EQ(PP_OK, info_did_run_.result()); |
| 295 EXPECT_EQ(0U, info_did_run_.completion_task_run_count()); |
| 296 |
| 297 EXPECT_EQ(1U, info_did_run_with_completion_task_.run_count()); |
| 298 // completion task should override the result. |
| 299 EXPECT_EQ(kOverrideResultValue, |
| 300 info_did_run_with_completion_task_.result()); |
| 301 EXPECT_EQ(1U, |
| 302 info_did_run_with_completion_task_.completion_task_run_count()); |
| 303 EXPECT_EQ(PP_OK, |
| 304 info_did_run_with_completion_task_.completion_task_result()); |
| 305 |
| 306 EXPECT_EQ(1U, info_did_abort_.run_count()); |
| 307 // completion task shouldn't override an abort. |
| 308 EXPECT_EQ(PP_ERROR_ABORTED, info_did_abort_.result()); |
| 309 EXPECT_EQ(1U, info_did_abort_.completion_task_run_count()); |
| 310 EXPECT_EQ(PP_ERROR_ABORTED, info_did_abort_.completion_task_result()); |
| 311 |
| 312 EXPECT_EQ(0U, info_didnt_run_.completion_task_run_count()); |
| 313 EXPECT_EQ(0U, info_didnt_run_.run_count()); |
| 314 } |
| 315 |
| 316 void CheckFinalState() { |
| 317 info_didnt_run_.WaitUntilCompleted(); |
| 318 EXPECT_EQ(1U, info_did_run_with_completion_task_.run_count()); |
| 319 EXPECT_EQ(kOverrideResultValue, |
| 320 info_did_run_with_completion_task_.result()); |
| 321 callback_did_run_with_completion_task_ = nullptr; |
| 322 EXPECT_EQ(1U, info_did_run_.run_count()); |
| 323 EXPECT_EQ(PP_OK, info_did_run_.result()); |
| 324 callback_did_run_ = nullptr; |
| 325 EXPECT_EQ(1U, info_did_abort_.run_count()); |
| 326 EXPECT_EQ(PP_ERROR_ABORTED, info_did_abort_.result()); |
| 327 callback_did_abort_ = nullptr; |
| 328 EXPECT_EQ(1U, info_didnt_run_.run_count()); |
| 329 EXPECT_EQ(PP_ERROR_ABORTED, info_didnt_run_.result()); |
| 330 callback_didnt_run_ = nullptr; |
| 331 } |
| 332 |
| 333 private: |
| 334 explicit CallbackMockResource(PP_Instance instance) |
| 335 : Resource(OBJECT_IS_PROXY, instance), |
| 336 info_did_run_(&thread_checker_), |
| 337 info_did_run_with_completion_task_(&thread_checker_), |
| 338 info_did_abort_(&thread_checker_), |
| 339 info_didnt_run_(&thread_checker_), |
| 340 callbacks_created_event_(true, false) {} |
| 341 void CreateCallbacks() { |
| 342 // Bind thread_checker_ to the thread where we create the callbacks. |
| 343 // Later, when the callback runs, it will check that it was invoked on this |
| 344 // same thread. |
| 345 CHECK(thread_checker_.CalledOnValidThread()); |
| 346 |
| 347 callback_did_run_ = new TrackedCallback( |
| 348 this, PP_MakeCompletionCallback(&TestCallback, &info_did_run_)); |
| 349 |
| 350 // In order to test that the completion task can override the callback |
| 351 // result, we need to test callbacks with and without a completion task. |
| 352 callback_did_run_with_completion_task_ = new TrackedCallback( |
| 353 this, PP_MakeCompletionCallback(&TestCallback, |
| 354 &info_did_run_with_completion_task_)); |
| 355 callback_did_run_with_completion_task_->set_completion_task( |
| 356 Bind(&CallbackMockResource::CompletionTask, this, |
| 357 &info_did_run_with_completion_task_)); |
| 358 |
| 359 callback_did_abort_ = new TrackedCallback( |
| 360 this, PP_MakeCompletionCallback(&TestCallback, &info_did_abort_)); |
| 361 callback_did_abort_->set_completion_task( |
| 362 Bind(&CallbackMockResource::CompletionTask, this, &info_did_abort_)); |
| 363 |
| 364 callback_didnt_run_ = new TrackedCallback( |
| 365 this, PP_MakeCompletionCallback(&TestCallback, &info_didnt_run_)); |
| 366 callback_didnt_run_->set_completion_task( |
| 367 Bind(&CallbackMockResource::CompletionTask, this, &info_didnt_run_)); |
| 368 |
| 369 callbacks_created_event_.Signal(); |
| 370 } |
| 371 |
| 372 // Used to verify that the callback runs on the same thread where it is |
| 373 // created. |
| 374 base::ThreadChecker thread_checker_; |
| 375 |
| 376 scoped_refptr<TrackedCallback> callback_did_run_; |
| 377 CallbackRunInfo info_did_run_; |
| 378 |
| 379 scoped_refptr<TrackedCallback> callback_did_run_with_completion_task_; |
| 380 CallbackRunInfo info_did_run_with_completion_task_; |
| 381 |
| 382 scoped_refptr<TrackedCallback> callback_did_abort_; |
| 383 CallbackRunInfo info_did_abort_; |
| 384 |
| 385 scoped_refptr<TrackedCallback> callback_didnt_run_; |
| 386 CallbackRunInfo info_didnt_run_; |
| 387 |
| 388 base::WaitableEvent callbacks_created_event_; |
| 389 |
| 390 ScopedPPResource reference_holder_; |
| 391 }; |
| 392 |
| 393 } // namespace |
| 394 |
| 395 // Test that callbacks get aborted on the last resource unref. |
| 396 TEST_F(CallbackResourceTest, AbortOnNoRef) { |
| 397 // Test several things: Unref-ing a resource (to zero refs) with callbacks |
| 398 // which (1) have been run, (2) have been aborted, (3) haven't been completed. |
| 399 // Check that the uncompleted one gets aborted, and that the others don't get |
| 400 // called again. |
| 401 scoped_refptr<CallbackMockResource> resource_1( |
| 402 CallbackMockResource::Create(pp_instance())); |
| 403 resource_1->CreateCallbacksOnLoop(thread().message_loop()); |
| 404 resource_1->CheckInitialState(); |
| 405 resource_1->RunCallbacks(); |
| 406 resource_1->TakeRef(); |
| 407 resource_1->CheckIntermediateState(); |
| 408 |
| 409 // Also do the same for a second resource, and make sure that unref-ing the |
| 410 // first resource doesn't much up the second resource. |
| 411 scoped_refptr<CallbackMockResource> resource_2( |
| 412 CallbackMockResource::Create(pp_instance())); |
| 413 resource_2->CreateCallbacksOnLoop(thread().message_loop()); |
| 414 resource_2->CheckInitialState(); |
| 415 resource_2->RunCallbacks(); |
| 416 resource_2->TakeRef(); |
| 417 resource_2->CheckIntermediateState(); |
| 418 |
| 419 // Double-check that resource #1 is still okay. |
| 420 resource_1->CheckIntermediateState(); |
| 421 |
| 422 // Kill resource #1, spin the message loop to run posted calls, and check that |
| 423 // things are in the expected states. |
| 424 resource_1->ReleaseRef(); |
| 425 |
| 426 resource_1->CheckFinalState(); |
| 427 resource_2->CheckIntermediateState(); |
| 428 |
| 429 // Kill resource #2. |
| 430 resource_2->ReleaseRef(); |
| 431 |
| 432 resource_1->CheckFinalState(); |
| 433 resource_2->CheckFinalState(); |
| 434 |
| 435 { |
| 436 ProxyAutoLock lock; |
| 437 resource_1 = nullptr; |
| 438 resource_2 = nullptr; |
| 439 } |
| 440 } |
| 441 |
| 442 // Test that "resurrecting" a resource (getting a new ID for a |Resource|) |
| 443 // doesn't resurrect callbacks. |
| 444 TEST_F(CallbackResourceTest, Resurrection) { |
| 445 scoped_refptr<CallbackMockResource> resource( |
| 446 CallbackMockResource::Create(pp_instance())); |
| 447 resource->CreateCallbacksOnLoop(thread().message_loop()); |
| 448 resource->CheckInitialState(); |
| 449 resource->RunCallbacks(); |
| 450 resource->TakeRef(); |
| 451 resource->CheckIntermediateState(); |
| 452 |
| 453 // Unref it and check that things are in the expected states. |
| 454 resource->ReleaseRef(); |
| 455 resource->CheckFinalState(); |
| 456 |
| 457 // "Resurrect" it and check that the callbacks are still dead. |
| 458 resource->TakeRef(); |
| 459 resource->CheckFinalState(); |
| 460 |
| 461 // Unref it again and do the same. |
| 462 resource->ReleaseRef(); |
| 463 resource->CheckFinalState(); |
| 464 { |
| 465 ProxyAutoLock lock; |
| 466 resource = nullptr; |
| 467 } |
| 468 } |
| 469 |
| 470 } // namespace proxy |
| 471 } // namespace ppapi |
OLD | NEW |