1 // Copyright 2014 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 "base/task/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/run_loop.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/test/test_simple_task_runner.h"
19 #include "base/threading/thread.h"
20 #include "testing/gtest/include/gtest/gtest.h"
26 class CancelableTaskTrackerTest
: public testing::Test
{
28 ~CancelableTaskTrackerTest() override
{ RunCurrentLoopUntilIdle(); }
30 void RunCurrentLoopUntilIdle() {
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 Closure
MakeExpectedNotRunClosure(const tracked_objects::Location
& location
) {
48 return Bind(&AddFailureAt
, location
);
51 // A helper class for MakeExpectedRunClosure() that fails if it is
52 // destroyed without Run() having been called. This class may be used
53 // from multiple threads as long as Run() is called at most once
54 // before destruction.
57 explicit RunChecker(const tracked_objects::Location
& location
)
58 : location_(location
), called_(false) {}
62 ADD_FAILURE_AT(location_
.file_name(), location_
.line_number());
66 void Run() { called_
= true; }
69 tracked_objects::Location location_
;
73 // Returns a closure that fails on destruction if it hasn't been run.
74 Closure
MakeExpectedRunClosure(const tracked_objects::Location
& location
) {
75 return Bind(&RunChecker::Run
, Owned(new RunChecker(location
)));
80 // With the task tracker, post a task, a task with a reply, and get a
81 // new task id without canceling any of them. The tasks and the reply
82 // should run and the "is canceled" callback should return false.
83 TEST_F(CancelableTaskTrackerTest
, NoCancel
) {
84 Thread
worker_thread("worker thread");
85 ASSERT_TRUE(worker_thread
.Start());
87 ignore_result(task_tracker_
.PostTask(worker_thread
.task_runner().get(),
89 MakeExpectedRunClosure(FROM_HERE
)));
91 ignore_result(task_tracker_
.PostTaskAndReply(
92 worker_thread
.task_runner().get(), FROM_HERE
,
93 MakeExpectedRunClosure(FROM_HERE
), MakeExpectedRunClosure(FROM_HERE
)));
95 CancelableTaskTracker::IsCanceledCallback is_canceled
;
96 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
100 RunCurrentLoopUntilIdle();
102 EXPECT_FALSE(is_canceled
.Run());
105 // Post a task with the task tracker but cancel it before running the
106 // task runner. The task should not run.
107 TEST_F(CancelableTaskTrackerTest
, CancelPostedTask
) {
108 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
109 new TestSimpleTaskRunner());
111 CancelableTaskTracker::TaskId task_id
= task_tracker_
.PostTask(
112 test_task_runner
.get(), FROM_HERE
, MakeExpectedNotRunClosure(FROM_HERE
));
113 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
115 EXPECT_EQ(1U, test_task_runner
->GetPendingTasks().size());
117 task_tracker_
.TryCancel(task_id
);
119 test_task_runner
->RunUntilIdle();
122 // Post a task with reply with the task tracker and cancel it before
123 // running the task runner. Neither the task nor the reply should
125 TEST_F(CancelableTaskTrackerTest
, CancelPostedTaskAndReply
) {
126 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
127 new TestSimpleTaskRunner());
129 CancelableTaskTracker::TaskId task_id
=
130 task_tracker_
.PostTaskAndReply(test_task_runner
.get(),
132 MakeExpectedNotRunClosure(FROM_HERE
),
133 MakeExpectedNotRunClosure(FROM_HERE
));
134 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
136 task_tracker_
.TryCancel(task_id
);
138 test_task_runner
->RunUntilIdle();
141 // Post a task with reply with the task tracker and cancel it after
142 // running the task runner but before running the current message
143 // loop. The task should run but the reply should not.
144 TEST_F(CancelableTaskTrackerTest
, CancelReply
) {
145 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
146 new TestSimpleTaskRunner());
148 CancelableTaskTracker::TaskId task_id
=
149 task_tracker_
.PostTaskAndReply(test_task_runner
.get(),
151 MakeExpectedRunClosure(FROM_HERE
),
152 MakeExpectedNotRunClosure(FROM_HERE
));
153 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
155 test_task_runner
->RunUntilIdle();
157 task_tracker_
.TryCancel(task_id
);
160 // Post a task with reply with the task tracker on a worker thread and
161 // cancel it before running the current message loop. The task should
162 // run but the reply should not.
163 TEST_F(CancelableTaskTrackerTest
, CancelReplyDifferentThread
) {
164 Thread
worker_thread("worker thread");
165 ASSERT_TRUE(worker_thread
.Start());
167 CancelableTaskTracker::TaskId task_id
= task_tracker_
.PostTaskAndReply(
168 worker_thread
.task_runner().get(), FROM_HERE
, Bind(&DoNothing
),
169 MakeExpectedNotRunClosure(FROM_HERE
));
170 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
172 task_tracker_
.TryCancel(task_id
);
174 worker_thread
.Stop();
177 void ExpectIsCanceled(
178 const CancelableTaskTracker::IsCanceledCallback
& is_canceled
,
179 bool expected_is_canceled
) {
180 EXPECT_EQ(expected_is_canceled
, is_canceled
.Run());
183 // Create a new task ID and check its status on a separate thread
184 // before and after canceling. The is-canceled callback should be
185 // thread-safe (i.e., nothing should blow up).
186 TEST_F(CancelableTaskTrackerTest
, NewTrackedTaskIdDifferentThread
) {
187 CancelableTaskTracker::IsCanceledCallback is_canceled
;
188 CancelableTaskTracker::TaskId task_id
=
189 task_tracker_
.NewTrackedTaskId(&is_canceled
);
191 EXPECT_FALSE(is_canceled
.Run());
193 Thread
other_thread("other thread");
194 ASSERT_TRUE(other_thread
.Start());
195 other_thread
.task_runner()->PostTask(
196 FROM_HERE
, Bind(&ExpectIsCanceled
, is_canceled
, false));
199 task_tracker_
.TryCancel(task_id
);
201 ASSERT_TRUE(other_thread
.Start());
202 other_thread
.task_runner()->PostTask(
203 FROM_HERE
, Bind(&ExpectIsCanceled
, is_canceled
, true));
207 // With the task tracker, post a task, a task with a reply, get a new
208 // task id, and then cancel all of them. None of the tasks nor the
209 // reply should run and the "is canceled" callback should return
211 TEST_F(CancelableTaskTrackerTest
, CancelAll
) {
212 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
213 new TestSimpleTaskRunner());
215 ignore_result(task_tracker_
.PostTask(
216 test_task_runner
.get(), FROM_HERE
, MakeExpectedNotRunClosure(FROM_HERE
)));
219 task_tracker_
.PostTaskAndReply(test_task_runner
.get(),
221 MakeExpectedNotRunClosure(FROM_HERE
),
222 MakeExpectedNotRunClosure(FROM_HERE
)));
224 CancelableTaskTracker::IsCanceledCallback is_canceled
;
225 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
227 task_tracker_
.TryCancelAll();
229 test_task_runner
->RunUntilIdle();
231 RunCurrentLoopUntilIdle();
233 EXPECT_TRUE(is_canceled
.Run());
236 // With the task tracker, post a task, a task with a reply, get a new
237 // task id, and then cancel all of them. None of the tasks nor the
238 // reply should run and the "is canceled" callback should return
240 TEST_F(CancelableTaskTrackerTest
, DestructionCancelsAll
) {
241 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
242 new TestSimpleTaskRunner());
244 CancelableTaskTracker::IsCanceledCallback is_canceled
;
247 // Create another task tracker with a smaller scope.
248 CancelableTaskTracker task_tracker
;
250 ignore_result(task_tracker
.PostTask(test_task_runner
.get(),
252 MakeExpectedNotRunClosure(FROM_HERE
)));
255 task_tracker
.PostTaskAndReply(test_task_runner
.get(),
257 MakeExpectedNotRunClosure(FROM_HERE
),
258 MakeExpectedNotRunClosure(FROM_HERE
)));
260 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
263 test_task_runner
->RunUntilIdle();
265 RunCurrentLoopUntilIdle();
267 EXPECT_FALSE(is_canceled
.Run());
270 // Post a task and cancel it. HasTrackedTasks() should return true
271 // from when the task is posted until the (do-nothing) reply task is
273 TEST_F(CancelableTaskTrackerTest
, HasTrackedTasksPost
) {
274 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
275 new TestSimpleTaskRunner());
277 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
279 ignore_result(task_tracker_
.PostTask(
280 test_task_runner
.get(), FROM_HERE
, MakeExpectedNotRunClosure(FROM_HERE
)));
282 task_tracker_
.TryCancelAll();
284 test_task_runner
->RunUntilIdle();
286 EXPECT_TRUE(task_tracker_
.HasTrackedTasks());
288 RunCurrentLoopUntilIdle();
290 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
293 // Post a task with a reply and cancel it. HasTrackedTasks() should
294 // return true from when the task is posted until it is canceled.
295 TEST_F(CancelableTaskTrackerTest
, HasTrackedTasksPostWithReply
) {
296 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
297 new TestSimpleTaskRunner());
299 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
302 task_tracker_
.PostTaskAndReply(test_task_runner
.get(),
304 MakeExpectedNotRunClosure(FROM_HERE
),
305 MakeExpectedNotRunClosure(FROM_HERE
)));
307 task_tracker_
.TryCancelAll();
309 test_task_runner
->RunUntilIdle();
311 EXPECT_TRUE(task_tracker_
.HasTrackedTasks());
313 RunCurrentLoopUntilIdle();
315 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
318 // Create a new tracked task ID. HasTrackedTasks() should return true
319 // until the IsCanceledCallback is destroyed.
320 TEST_F(CancelableTaskTrackerTest
, HasTrackedTasksIsCancelled
) {
321 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
323 CancelableTaskTracker::IsCanceledCallback is_canceled
;
324 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
326 task_tracker_
.TryCancelAll();
328 EXPECT_TRUE(task_tracker_
.HasTrackedTasks());
332 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
335 // The death tests below make sure that calling task tracker member
336 // functions from a thread different from its owner thread DCHECKs in
339 class CancelableTaskTrackerDeathTest
: public CancelableTaskTrackerTest
{
341 CancelableTaskTrackerDeathTest() {
342 // The default style "fast" does not support multi-threaded tests.
343 ::testing::FLAGS_gtest_death_test_style
= "threadsafe";
347 // Duplicated from base/threading/thread_checker.h so that we can be
348 // good citizens there and undef the macro.
349 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
350 #define ENABLE_THREAD_CHECKER 1
352 #define ENABLE_THREAD_CHECKER 0
355 // Runs |fn| with |task_tracker|, expecting it to crash in debug mode.
356 void MaybeRunDeadlyTaskTrackerMemberFunction(
357 CancelableTaskTracker
* task_tracker
,
358 const Callback
<void(CancelableTaskTracker
*)>& fn
) {
359 // CancelableTask uses DCHECKs with its ThreadChecker (itself only
360 // enabled in debug mode).
361 #if ENABLE_THREAD_CHECKER
362 EXPECT_DEATH_IF_SUPPORTED(fn
.Run(task_tracker
), "");
366 void PostDoNothingTask(CancelableTaskTracker
* task_tracker
) {
367 ignore_result(task_tracker
->PostTask(
368 scoped_refptr
<TestSimpleTaskRunner
>(new TestSimpleTaskRunner()).get(),
373 TEST_F(CancelableTaskTrackerDeathTest
, PostFromDifferentThread
) {
374 Thread
bad_thread("bad thread");
375 ASSERT_TRUE(bad_thread
.Start());
377 bad_thread
.task_runner()->PostTask(
378 FROM_HERE
, Bind(&MaybeRunDeadlyTaskTrackerMemberFunction
,
379 Unretained(&task_tracker_
), Bind(&PostDoNothingTask
)));
382 void TryCancel(CancelableTaskTracker::TaskId task_id
,
383 CancelableTaskTracker
* task_tracker
) {
384 task_tracker
->TryCancel(task_id
);
387 TEST_F(CancelableTaskTrackerDeathTest
, CancelOnDifferentThread
) {
388 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
389 new TestSimpleTaskRunner());
391 Thread
bad_thread("bad thread");
392 ASSERT_TRUE(bad_thread
.Start());
394 CancelableTaskTracker::TaskId task_id
= task_tracker_
.PostTask(
395 test_task_runner
.get(), FROM_HERE
, Bind(&DoNothing
));
396 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
398 bad_thread
.task_runner()->PostTask(
399 FROM_HERE
, Bind(&MaybeRunDeadlyTaskTrackerMemberFunction
,
400 Unretained(&task_tracker_
), Bind(&TryCancel
, task_id
)));
402 test_task_runner
->RunUntilIdle();
405 TEST_F(CancelableTaskTrackerDeathTest
, CancelAllOnDifferentThread
) {
406 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
407 new TestSimpleTaskRunner());
409 Thread
bad_thread("bad thread");
410 ASSERT_TRUE(bad_thread
.Start());
412 CancelableTaskTracker::TaskId task_id
= task_tracker_
.PostTask(
413 test_task_runner
.get(), FROM_HERE
, Bind(&DoNothing
));
414 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
416 bad_thread
.task_runner()->PostTask(
418 Bind(&MaybeRunDeadlyTaskTrackerMemberFunction
, Unretained(&task_tracker_
),
419 Bind(&CancelableTaskTracker::TryCancelAll
)));
421 test_task_runner
->RunUntilIdle();