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/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 base::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());
90 ignore_result(task_tracker_
.PostTask(worker_thread
.message_loop_proxy().get(),
92 MakeExpectedRunClosure(FROM_HERE
)));
95 task_tracker_
.PostTaskAndReply(worker_thread
.message_loop_proxy().get(),
97 MakeExpectedRunClosure(FROM_HERE
),
98 MakeExpectedRunClosure(FROM_HERE
)));
100 CancelableTaskTracker::IsCanceledCallback is_canceled
;
101 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
103 worker_thread
.Stop();
105 RunCurrentLoopUntilIdle();
107 EXPECT_FALSE(is_canceled
.Run());
110 // Post a task with the task tracker but cancel it before running the
111 // task runner. The task should not run.
112 TEST_F(CancelableTaskTrackerTest
, CancelPostedTask
) {
113 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
114 new base::TestSimpleTaskRunner());
116 CancelableTaskTracker::TaskId task_id
=
117 task_tracker_
.PostTask(
118 test_task_runner
.get(),
120 MakeExpectedNotRunClosure(FROM_HERE
));
121 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
123 EXPECT_EQ(1U, test_task_runner
->GetPendingTasks().size());
125 task_tracker_
.TryCancel(task_id
);
127 test_task_runner
->RunUntilIdle();
130 // Post a task with reply with the task tracker and cancel it before
131 // running the task runner. Neither the task nor the reply should
133 TEST_F(CancelableTaskTrackerTest
, CancelPostedTaskAndReply
) {
134 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
135 new base::TestSimpleTaskRunner());
137 CancelableTaskTracker::TaskId task_id
=
138 task_tracker_
.PostTaskAndReply(
139 test_task_runner
.get(),
141 MakeExpectedNotRunClosure(FROM_HERE
),
142 MakeExpectedNotRunClosure(FROM_HERE
));
143 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
145 task_tracker_
.TryCancel(task_id
);
147 test_task_runner
->RunUntilIdle();
150 // Post a task with reply with the task tracker and cancel it after
151 // running the task runner but before running the current message
152 // loop. The task should run but the reply should not.
153 TEST_F(CancelableTaskTrackerTest
, CancelReply
) {
154 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
155 new base::TestSimpleTaskRunner());
157 CancelableTaskTracker::TaskId task_id
=
158 task_tracker_
.PostTaskAndReply(
159 test_task_runner
.get(),
161 MakeExpectedRunClosure(FROM_HERE
),
162 MakeExpectedNotRunClosure(FROM_HERE
));
163 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
165 test_task_runner
->RunUntilIdle();
167 task_tracker_
.TryCancel(task_id
);
170 // Post a task with reply with the task tracker on a worker thread and
171 // cancel it before running the current message loop. The task should
172 // run but the reply should not.
173 TEST_F(CancelableTaskTrackerTest
, CancelReplyDifferentThread
) {
174 base::Thread
worker_thread("worker thread");
175 ASSERT_TRUE(worker_thread
.Start());
177 CancelableTaskTracker::TaskId task_id
=
178 task_tracker_
.PostTaskAndReply(worker_thread
.message_loop_proxy().get(),
180 base::Bind(&base::DoNothing
),
181 MakeExpectedNotRunClosure(FROM_HERE
));
182 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
184 task_tracker_
.TryCancel(task_id
);
186 worker_thread
.Stop();
189 void ExpectIsCanceled(
190 const CancelableTaskTracker::IsCanceledCallback
& is_canceled
,
191 bool expected_is_canceled
) {
192 EXPECT_EQ(expected_is_canceled
, is_canceled
.Run());
195 // Create a new task ID and check its status on a separate thread
196 // before and after canceling. The is-canceled callback should be
197 // thread-safe (i.e., nothing should blow up).
198 TEST_F(CancelableTaskTrackerTest
, NewTrackedTaskIdDifferentThread
) {
199 CancelableTaskTracker::IsCanceledCallback is_canceled
;
200 CancelableTaskTracker::TaskId task_id
=
201 task_tracker_
.NewTrackedTaskId(&is_canceled
);
203 EXPECT_FALSE(is_canceled
.Run());
205 base::Thread
other_thread("other thread");
206 ASSERT_TRUE(other_thread
.Start());
207 other_thread
.message_loop_proxy()->PostTask(
209 base::Bind(&ExpectIsCanceled
, is_canceled
, false));
212 task_tracker_
.TryCancel(task_id
);
214 ASSERT_TRUE(other_thread
.Start());
215 other_thread
.message_loop_proxy()->PostTask(
217 base::Bind(&ExpectIsCanceled
, is_canceled
, true));
221 // With the task tracker, post a task, a task with a reply, get a new
222 // task id, and then cancel all of them. None of the tasks nor the
223 // reply should run and the "is canceled" callback should return
225 TEST_F(CancelableTaskTrackerTest
, CancelAll
) {
226 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
227 new base::TestSimpleTaskRunner());
229 ignore_result(task_tracker_
.PostTask(
230 test_task_runner
.get(), FROM_HERE
, MakeExpectedNotRunClosure(FROM_HERE
)));
233 task_tracker_
.PostTaskAndReply(test_task_runner
.get(),
235 MakeExpectedNotRunClosure(FROM_HERE
),
236 MakeExpectedNotRunClosure(FROM_HERE
)));
238 CancelableTaskTracker::IsCanceledCallback is_canceled
;
239 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
241 task_tracker_
.TryCancelAll();
243 test_task_runner
->RunUntilIdle();
245 RunCurrentLoopUntilIdle();
247 EXPECT_TRUE(is_canceled
.Run());
250 // With the task tracker, post a task, a task with a reply, get a new
251 // task id, and then cancel all of them. None of the tasks nor the
252 // reply should run and the "is canceled" callback should return
254 TEST_F(CancelableTaskTrackerTest
, DestructionCancelsAll
) {
255 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
256 new base::TestSimpleTaskRunner());
258 CancelableTaskTracker::IsCanceledCallback is_canceled
;
261 // Create another task tracker with a smaller scope.
262 CancelableTaskTracker task_tracker
;
264 ignore_result(task_tracker
.PostTask(test_task_runner
.get(),
266 MakeExpectedNotRunClosure(FROM_HERE
)));
269 task_tracker
.PostTaskAndReply(test_task_runner
.get(),
271 MakeExpectedNotRunClosure(FROM_HERE
),
272 MakeExpectedNotRunClosure(FROM_HERE
)));
274 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
277 test_task_runner
->RunUntilIdle();
279 RunCurrentLoopUntilIdle();
281 EXPECT_FALSE(is_canceled
.Run());
284 // Post a task and cancel it. HasTrackedTasks() should return true
285 // from when the task is posted until the (do-nothing) reply task is
287 TEST_F(CancelableTaskTrackerTest
, HasTrackedTasksPost
) {
288 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
289 new base::TestSimpleTaskRunner());
291 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
293 ignore_result(task_tracker_
.PostTask(
294 test_task_runner
.get(), FROM_HERE
, MakeExpectedNotRunClosure(FROM_HERE
)));
296 task_tracker_
.TryCancelAll();
298 test_task_runner
->RunUntilIdle();
300 EXPECT_TRUE(task_tracker_
.HasTrackedTasks());
302 RunCurrentLoopUntilIdle();
304 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
307 // Post a task with a reply and cancel it. HasTrackedTasks() should
308 // return true from when the task is posted until it is canceled.
309 TEST_F(CancelableTaskTrackerTest
, HasTrackedTasksPostWithReply
) {
310 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
311 new base::TestSimpleTaskRunner());
313 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
316 task_tracker_
.PostTaskAndReply(test_task_runner
.get(),
318 MakeExpectedNotRunClosure(FROM_HERE
),
319 MakeExpectedNotRunClosure(FROM_HERE
)));
321 task_tracker_
.TryCancelAll();
323 test_task_runner
->RunUntilIdle();
325 EXPECT_TRUE(task_tracker_
.HasTrackedTasks());
327 RunCurrentLoopUntilIdle();
329 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
332 // Create a new tracked task ID. HasTrackedTasks() should return true
333 // until the IsCanceledCallback is destroyed.
334 TEST_F(CancelableTaskTrackerTest
, HasTrackedTasksIsCancelled
) {
335 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
337 CancelableTaskTracker::IsCanceledCallback is_canceled
;
338 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
340 task_tracker_
.TryCancelAll();
342 EXPECT_TRUE(task_tracker_
.HasTrackedTasks());
346 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
349 // The death tests below make sure that calling task tracker member
350 // functions from a thread different from its owner thread DCHECKs in
353 class CancelableTaskTrackerDeathTest
: public CancelableTaskTrackerTest
{
355 CancelableTaskTrackerDeathTest() {
356 // The default style "fast" does not support multi-threaded tests.
357 ::testing::FLAGS_gtest_death_test_style
= "threadsafe";
360 virtual ~CancelableTaskTrackerDeathTest() {}
363 // Duplicated from base/threading/thread_checker.h so that we can be
364 // good citizens there and undef the macro.
365 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
366 #define ENABLE_THREAD_CHECKER 1
368 #define ENABLE_THREAD_CHECKER 0
371 // Runs |fn| with |task_tracker|, expecting it to crash in debug mode.
372 void MaybeRunDeadlyTaskTrackerMemberFunction(
373 CancelableTaskTracker
* task_tracker
,
374 const base::Callback
<void(CancelableTaskTracker
*)>& fn
) {
375 // CancelableTask uses DCHECKs with its ThreadChecker (itself only
376 // enabled in debug mode).
377 #if ENABLE_THREAD_CHECKER
378 EXPECT_DEATH_IF_SUPPORTED(fn
.Run(task_tracker
), "");
382 void PostDoNothingTask(CancelableTaskTracker
* task_tracker
) {
384 task_tracker
->PostTask(scoped_refptr
<base::TestSimpleTaskRunner
>(
385 new base::TestSimpleTaskRunner())
388 base::Bind(&base::DoNothing
)));
391 TEST_F(CancelableTaskTrackerDeathTest
, PostFromDifferentThread
) {
392 base::Thread
bad_thread("bad thread");
393 ASSERT_TRUE(bad_thread
.Start());
395 bad_thread
.message_loop_proxy()->PostTask(
397 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction
,
398 base::Unretained(&task_tracker_
),
399 base::Bind(&PostDoNothingTask
)));
402 void TryCancel(CancelableTaskTracker::TaskId task_id
,
403 CancelableTaskTracker
* task_tracker
) {
404 task_tracker
->TryCancel(task_id
);
407 TEST_F(CancelableTaskTrackerDeathTest
, CancelOnDifferentThread
) {
408 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
409 new base::TestSimpleTaskRunner());
411 base::Thread
bad_thread("bad thread");
412 ASSERT_TRUE(bad_thread
.Start());
414 CancelableTaskTracker::TaskId task_id
=
415 task_tracker_
.PostTask(
416 test_task_runner
.get(),
418 base::Bind(&base::DoNothing
));
419 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
421 bad_thread
.message_loop_proxy()->PostTask(
423 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction
,
424 base::Unretained(&task_tracker_
),
425 base::Bind(&TryCancel
, task_id
)));
427 test_task_runner
->RunUntilIdle();
430 TEST_F(CancelableTaskTrackerDeathTest
, CancelAllOnDifferentThread
) {
431 scoped_refptr
<base::TestSimpleTaskRunner
> test_task_runner(
432 new base::TestSimpleTaskRunner());
434 base::Thread
bad_thread("bad thread");
435 ASSERT_TRUE(bad_thread
.Start());
437 CancelableTaskTracker::TaskId task_id
=
438 task_tracker_
.PostTask(
439 test_task_runner
.get(),
441 base::Bind(&base::DoNothing
));
442 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
444 bad_thread
.message_loop_proxy()->PostTask(
446 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction
,
447 base::Unretained(&task_tracker_
),
448 base::Bind(&CancelableTaskTracker::TryCancelAll
)));
450 test_task_runner
->RunUntilIdle();