1 // Copyright (c) 2011 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.
5 #include "chrome/common/cancelable_task_tracker.h"
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/message_loop.h"
17 #include "base/run_loop.h"
18 #include "base/test/test_simple_task_runner.h"
19 #include "base/threading/thread.h"
20 #include "testing/gtest/include/gtest/gtest.h"
24 class CancelableTaskTrackerTest
: public testing::Test
{
26 virtual ~CancelableTaskTrackerTest() {
27 RunCurrentLoopUntilIdle();
30 void RunCurrentLoopUntilIdle() {
31 base::RunLoop run_loop
;
32 run_loop
.RunUntilIdle();
35 CancelableTaskTracker task_tracker_
;
38 // Needed by CancelableTaskTracker methods.
39 MessageLoop message_loop_
;
42 void AddFailureAt(const tracked_objects::Location
& location
) {
43 ADD_FAILURE_AT(location
.file_name(), location
.line_number());
46 // Returns a closure that fails if run.
47 base::Closure
MakeExpectedNotRunClosure(
48 const tracked_objects::Location
& location
) {
49 return base::Bind(&AddFailureAt
, location
);
52 // A helper class for MakeExpectedRunClosure() that fails if it is
53 // destroyed without Run() having been called. This class may be used
54 // from multiple threads as long as Run() is called at most once
55 // before destruction.
58 explicit RunChecker(const tracked_objects::Location
& location
)
59 : location_(location
),
64 ADD_FAILURE_AT(location_
.file_name(), location_
.line_number());
73 tracked_objects::Location location_
;
77 // Returns a closure that fails on destruction if it hasn't been run.
78 base::Closure
MakeExpectedRunClosure(
79 const tracked_objects::Location
& location
) {
80 return base::Bind(&RunChecker::Run
, base::Owned(new RunChecker(location
)));
83 // With the task tracker, post a task, a task with a reply, and get a
84 // new task id without canceling any of them. The tasks and the reply
85 // should run and the "is canceled" callback should return false.
86 TEST_F(CancelableTaskTrackerTest
, NoCancel
) {
87 base::Thread
worker_thread("worker thread");
88 ASSERT_TRUE(worker_thread
.Start());
91 task_tracker_
.PostTask(
92 worker_thread
.message_loop_proxy(),
94 MakeExpectedRunClosure(FROM_HERE
)));
97 task_tracker_
.PostTaskAndReply(
98 worker_thread
.message_loop_proxy(),
100 MakeExpectedRunClosure(FROM_HERE
),
101 MakeExpectedRunClosure(FROM_HERE
)));
103 CancelableTaskTracker::IsCanceledCallback is_canceled
;
104 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
106 worker_thread
.Stop();
108 RunCurrentLoopUntilIdle();
110 EXPECT_FALSE(is_canceled
.Run());
113 // Post a task with the task tracker but cancel it before running the
114 // task runner. The task should not run.
115 TEST_F(CancelableTaskTrackerTest
, CancelPostedTask
) {
116 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
117 new base::TestSimpleTaskRunner());
119 CancelableTaskTracker::TaskId task_id
=
120 task_tracker_
.PostTask(
121 test_task_runner
.get(),
123 MakeExpectedNotRunClosure(FROM_HERE
));
124 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
126 EXPECT_EQ(1U, test_task_runner
->GetPendingTasks().size());
128 task_tracker_
.TryCancel(task_id
);
130 test_task_runner
->RunUntilIdle();
133 // Post a task with reply with the task tracker and cancel it before
134 // running the task runner. Neither the task nor the reply should
136 TEST_F(CancelableTaskTrackerTest
, CancelPostedTaskAndReply
) {
137 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
138 new base::TestSimpleTaskRunner());
140 CancelableTaskTracker::TaskId task_id
=
141 task_tracker_
.PostTaskAndReply(
142 test_task_runner
.get(),
144 MakeExpectedNotRunClosure(FROM_HERE
),
145 MakeExpectedNotRunClosure(FROM_HERE
));
146 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
148 task_tracker_
.TryCancel(task_id
);
150 test_task_runner
->RunUntilIdle();
153 // Post a task with reply with the task tracker and cancel it after
154 // running the task runner but before running the current message
155 // loop. The task should run but the reply should not.
156 TEST_F(CancelableTaskTrackerTest
, CancelReply
) {
157 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
158 new base::TestSimpleTaskRunner());
160 CancelableTaskTracker::TaskId task_id
=
161 task_tracker_
.PostTaskAndReply(
162 test_task_runner
.get(),
164 MakeExpectedRunClosure(FROM_HERE
),
165 MakeExpectedNotRunClosure(FROM_HERE
));
166 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
168 test_task_runner
->RunUntilIdle();
170 task_tracker_
.TryCancel(task_id
);
173 // Post a task with reply with the task tracker on a worker thread and
174 // cancel it before running the current message loop. The task should
175 // run but the reply should not.
176 TEST_F(CancelableTaskTrackerTest
, CancelReplyDifferentThread
) {
177 base::Thread
worker_thread("worker thread");
178 ASSERT_TRUE(worker_thread
.Start());
180 CancelableTaskTracker::TaskId task_id
=
181 task_tracker_
.PostTaskAndReply(
182 worker_thread
.message_loop_proxy(),
184 base::Bind(&base::DoNothing
),
185 MakeExpectedNotRunClosure(FROM_HERE
));
186 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
188 task_tracker_
.TryCancel(task_id
);
190 worker_thread
.Stop();
193 void ExpectIsCanceled(
194 const CancelableTaskTracker::IsCanceledCallback
& is_canceled
,
195 bool expected_is_canceled
) {
196 EXPECT_EQ(expected_is_canceled
, is_canceled
.Run());
199 // Create a new task ID and check its status on a separate thread
200 // before and after canceling. The is-canceled callback should be
201 // thread-safe (i.e., nothing should blow up).
202 TEST_F(CancelableTaskTrackerTest
, NewTrackedTaskIdDifferentThread
) {
203 CancelableTaskTracker::IsCanceledCallback is_canceled
;
204 CancelableTaskTracker::TaskId task_id
=
205 task_tracker_
.NewTrackedTaskId(&is_canceled
);
207 EXPECT_FALSE(is_canceled
.Run());
209 base::Thread
other_thread("other thread");
210 ASSERT_TRUE(other_thread
.Start());
211 other_thread
.message_loop_proxy()->PostTask(
213 base::Bind(&ExpectIsCanceled
, is_canceled
, false));
216 task_tracker_
.TryCancel(task_id
);
218 ASSERT_TRUE(other_thread
.Start());
219 other_thread
.message_loop_proxy()->PostTask(
221 base::Bind(&ExpectIsCanceled
, is_canceled
, true));
225 // With the task tracker, post a task, a task with a reply, get a new
226 // task id, and then cancel all of them. None of the tasks nor the
227 // reply should run and the "is canceled" callback should return
229 TEST_F(CancelableTaskTrackerTest
, CancelAll
) {
230 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
231 new base::TestSimpleTaskRunner());
234 task_tracker_
.PostTask(
237 MakeExpectedNotRunClosure(FROM_HERE
)));
240 task_tracker_
.PostTaskAndReply(
243 MakeExpectedNotRunClosure(FROM_HERE
),
244 MakeExpectedNotRunClosure(FROM_HERE
)));
246 CancelableTaskTracker::IsCanceledCallback is_canceled
;
247 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
249 task_tracker_
.TryCancelAll();
251 test_task_runner
->RunUntilIdle();
253 RunCurrentLoopUntilIdle();
255 EXPECT_TRUE(is_canceled
.Run());
258 // With the task tracker, post a task, a task with a reply, get a new
259 // task id, and then cancel all of them. None of the tasks nor the
260 // reply should run and the "is canceled" callback should return
262 TEST_F(CancelableTaskTrackerTest
, DestructionCancelsAll
) {
263 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
264 new base::TestSimpleTaskRunner());
266 CancelableTaskTracker::IsCanceledCallback is_canceled
;
269 // Create another task tracker with a smaller scope.
270 CancelableTaskTracker task_tracker
;
273 task_tracker
.PostTask(
276 MakeExpectedNotRunClosure(FROM_HERE
)));
279 task_tracker
.PostTaskAndReply(
282 MakeExpectedNotRunClosure(FROM_HERE
),
283 MakeExpectedNotRunClosure(FROM_HERE
)));
285 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
288 test_task_runner
->RunUntilIdle();
290 RunCurrentLoopUntilIdle();
292 EXPECT_FALSE(is_canceled
.Run());
295 // Post a task and cancel it. HasTrackedTasks() should return true
296 // from when the task is posted until the (do-nothing) reply task is
298 TEST_F(CancelableTaskTrackerTest
, HasTrackedTasksPost
) {
299 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
300 new base::TestSimpleTaskRunner());
302 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
305 task_tracker_
.PostTask(
308 MakeExpectedNotRunClosure(FROM_HERE
)));
310 task_tracker_
.TryCancelAll();
312 test_task_runner
->RunUntilIdle();
314 EXPECT_TRUE(task_tracker_
.HasTrackedTasks());
316 RunCurrentLoopUntilIdle();
318 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
321 // Post a task with a reply and cancel it. HasTrackedTasks() should
322 // return true from when the task is posted until it is canceled.
323 TEST_F(CancelableTaskTrackerTest
, HasTrackedTasksPostWithReply
) {
324 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
325 new base::TestSimpleTaskRunner());
327 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
330 task_tracker_
.PostTaskAndReply(
333 MakeExpectedNotRunClosure(FROM_HERE
),
334 MakeExpectedNotRunClosure(FROM_HERE
)));
336 task_tracker_
.TryCancelAll();
338 test_task_runner
->RunUntilIdle();
340 EXPECT_TRUE(task_tracker_
.HasTrackedTasks());
342 RunCurrentLoopUntilIdle();
344 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
347 // Create a new tracked task ID. HasTrackedTasks() should return true
348 // until the IsCanceledCallback is destroyed.
349 TEST_F(CancelableTaskTrackerTest
, HasTrackedTasksIsCancelled
) {
350 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
352 CancelableTaskTracker::IsCanceledCallback is_canceled
;
353 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
355 task_tracker_
.TryCancelAll();
357 EXPECT_TRUE(task_tracker_
.HasTrackedTasks());
361 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
364 // The death tests below make sure that calling task tracker member
365 // functions from a thread different from its owner thread DCHECKs in
368 class CancelableTaskTrackerDeathTest
: public CancelableTaskTrackerTest
{
370 CancelableTaskTrackerDeathTest() {
371 // The default style "fast" does not support multi-threaded tests.
372 ::testing::FLAGS_gtest_death_test_style
= "threadsafe";
375 virtual ~CancelableTaskTrackerDeathTest() {}
378 // Duplicated from base/threading/thread_checker.h so that we can be
379 // good citizens there and undef the macro.
380 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
381 #define ENABLE_THREAD_CHECKER 1
383 #define ENABLE_THREAD_CHECKER 0
386 // Runs |fn| with |task_tracker|, expecting it to crash in debug mode.
387 void MaybeRunDeadlyTaskTrackerMemberFunction(
388 CancelableTaskTracker
* task_tracker
,
389 const base::Callback
<void(CancelableTaskTracker
*)>& fn
) {
390 // CancelableTask uses DCHECKs with its ThreadChecker (itself only
391 // enabled in debug mode).
392 #if ENABLE_THREAD_CHECKER
393 EXPECT_DEATH_IF_SUPPORTED(fn
.Run(task_tracker
), "");
397 void PostDoNothingTask(CancelableTaskTracker
* task_tracker
) {
399 task_tracker
->PostTask(
400 scoped_refptr
<base::TestSimpleTaskRunner
>(
401 new base::TestSimpleTaskRunner()),
402 FROM_HERE
, base::Bind(&base::DoNothing
)));
405 TEST_F(CancelableTaskTrackerDeathTest
, PostFromDifferentThread
) {
406 base::Thread
bad_thread("bad thread");
407 ASSERT_TRUE(bad_thread
.Start());
409 bad_thread
.message_loop_proxy()->PostTask(
411 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction
,
412 base::Unretained(&task_tracker_
),
413 base::Bind(&PostDoNothingTask
)));
416 void TryCancel(CancelableTaskTracker::TaskId task_id
,
417 CancelableTaskTracker
* task_tracker
) {
418 task_tracker
->TryCancel(task_id
);
421 TEST_F(CancelableTaskTrackerDeathTest
, CancelOnDifferentThread
) {
422 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
423 new base::TestSimpleTaskRunner());
425 base::Thread
bad_thread("bad thread");
426 ASSERT_TRUE(bad_thread
.Start());
428 CancelableTaskTracker::TaskId task_id
=
429 task_tracker_
.PostTask(
430 test_task_runner
.get(),
432 base::Bind(&base::DoNothing
));
433 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
435 bad_thread
.message_loop_proxy()->PostTask(
437 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction
,
438 base::Unretained(&task_tracker_
),
439 base::Bind(&TryCancel
, task_id
)));
441 test_task_runner
->RunUntilIdle();
444 TEST_F(CancelableTaskTrackerDeathTest
, CancelAllOnDifferentThread
) {
445 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
446 new base::TestSimpleTaskRunner());
448 base::Thread
bad_thread("bad thread");
449 ASSERT_TRUE(bad_thread
.Start());
451 CancelableTaskTracker::TaskId task_id
=
452 task_tracker_
.PostTask(
453 test_task_runner
.get(),
455 base::Bind(&base::DoNothing
));
456 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
458 bad_thread
.message_loop_proxy()->PostTask(
460 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction
,
461 base::Unretained(&task_tracker_
),
462 base::Bind(&CancelableTaskTracker::TryCancelAll
)));
464 test_task_runner
->RunUntilIdle();