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