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/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"
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
.message_loop_proxy().get(),
89 MakeExpectedRunClosure(FROM_HERE
)));
92 task_tracker_
.PostTaskAndReply(worker_thread
.message_loop_proxy().get(),
94 MakeExpectedRunClosure(FROM_HERE
),
95 MakeExpectedRunClosure(FROM_HERE
)));
97 CancelableTaskTracker::IsCanceledCallback is_canceled
;
98 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
100 worker_thread
.Stop();
102 RunCurrentLoopUntilIdle();
104 EXPECT_FALSE(is_canceled
.Run());
107 // Post a task with the task tracker but cancel it before running the
108 // task runner. The task should not run.
109 TEST_F(CancelableTaskTrackerTest
, CancelPostedTask
) {
110 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
111 new TestSimpleTaskRunner());
113 CancelableTaskTracker::TaskId task_id
= task_tracker_
.PostTask(
114 test_task_runner
.get(), FROM_HERE
, MakeExpectedNotRunClosure(FROM_HERE
));
115 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
117 EXPECT_EQ(1U, test_task_runner
->GetPendingTasks().size());
119 task_tracker_
.TryCancel(task_id
);
121 test_task_runner
->RunUntilIdle();
124 // Post a task with reply with the task tracker and cancel it before
125 // running the task runner. Neither the task nor the reply should
127 TEST_F(CancelableTaskTrackerTest
, CancelPostedTaskAndReply
) {
128 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
129 new TestSimpleTaskRunner());
131 CancelableTaskTracker::TaskId task_id
=
132 task_tracker_
.PostTaskAndReply(test_task_runner
.get(),
134 MakeExpectedNotRunClosure(FROM_HERE
),
135 MakeExpectedNotRunClosure(FROM_HERE
));
136 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
138 task_tracker_
.TryCancel(task_id
);
140 test_task_runner
->RunUntilIdle();
143 // Post a task with reply with the task tracker and cancel it after
144 // running the task runner but before running the current message
145 // loop. The task should run but the reply should not.
146 TEST_F(CancelableTaskTrackerTest
, CancelReply
) {
147 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
148 new TestSimpleTaskRunner());
150 CancelableTaskTracker::TaskId task_id
=
151 task_tracker_
.PostTaskAndReply(test_task_runner
.get(),
153 MakeExpectedRunClosure(FROM_HERE
),
154 MakeExpectedNotRunClosure(FROM_HERE
));
155 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
157 test_task_runner
->RunUntilIdle();
159 task_tracker_
.TryCancel(task_id
);
162 // Post a task with reply with the task tracker on a worker thread and
163 // cancel it before running the current message loop. The task should
164 // run but the reply should not.
165 TEST_F(CancelableTaskTrackerTest
, CancelReplyDifferentThread
) {
166 Thread
worker_thread("worker thread");
167 ASSERT_TRUE(worker_thread
.Start());
169 CancelableTaskTracker::TaskId task_id
=
170 task_tracker_
.PostTaskAndReply(worker_thread
.message_loop_proxy().get(),
173 MakeExpectedNotRunClosure(FROM_HERE
));
174 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
176 task_tracker_
.TryCancel(task_id
);
178 worker_thread
.Stop();
181 void ExpectIsCanceled(
182 const CancelableTaskTracker::IsCanceledCallback
& is_canceled
,
183 bool expected_is_canceled
) {
184 EXPECT_EQ(expected_is_canceled
, is_canceled
.Run());
187 // Create a new task ID and check its status on a separate thread
188 // before and after canceling. The is-canceled callback should be
189 // thread-safe (i.e., nothing should blow up).
190 TEST_F(CancelableTaskTrackerTest
, NewTrackedTaskIdDifferentThread
) {
191 CancelableTaskTracker::IsCanceledCallback is_canceled
;
192 CancelableTaskTracker::TaskId task_id
=
193 task_tracker_
.NewTrackedTaskId(&is_canceled
);
195 EXPECT_FALSE(is_canceled
.Run());
197 Thread
other_thread("other thread");
198 ASSERT_TRUE(other_thread
.Start());
199 other_thread
.message_loop_proxy()->PostTask(
200 FROM_HERE
, Bind(&ExpectIsCanceled
, is_canceled
, false));
203 task_tracker_
.TryCancel(task_id
);
205 ASSERT_TRUE(other_thread
.Start());
206 other_thread
.message_loop_proxy()->PostTask(
207 FROM_HERE
, Bind(&ExpectIsCanceled
, is_canceled
, true));
211 // With the task tracker, post a task, a task with a reply, get a new
212 // task id, and then cancel all of them. None of the tasks nor the
213 // reply should run and the "is canceled" callback should return
215 TEST_F(CancelableTaskTrackerTest
, CancelAll
) {
216 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
217 new TestSimpleTaskRunner());
219 ignore_result(task_tracker_
.PostTask(
220 test_task_runner
.get(), FROM_HERE
, MakeExpectedNotRunClosure(FROM_HERE
)));
223 task_tracker_
.PostTaskAndReply(test_task_runner
.get(),
225 MakeExpectedNotRunClosure(FROM_HERE
),
226 MakeExpectedNotRunClosure(FROM_HERE
)));
228 CancelableTaskTracker::IsCanceledCallback is_canceled
;
229 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
231 task_tracker_
.TryCancelAll();
233 test_task_runner
->RunUntilIdle();
235 RunCurrentLoopUntilIdle();
237 EXPECT_TRUE(is_canceled
.Run());
240 // With the task tracker, post a task, a task with a reply, get a new
241 // task id, and then cancel all of them. None of the tasks nor the
242 // reply should run and the "is canceled" callback should return
244 TEST_F(CancelableTaskTrackerTest
, DestructionCancelsAll
) {
245 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
246 new TestSimpleTaskRunner());
248 CancelableTaskTracker::IsCanceledCallback is_canceled
;
251 // Create another task tracker with a smaller scope.
252 CancelableTaskTracker task_tracker
;
254 ignore_result(task_tracker
.PostTask(test_task_runner
.get(),
256 MakeExpectedNotRunClosure(FROM_HERE
)));
259 task_tracker
.PostTaskAndReply(test_task_runner
.get(),
261 MakeExpectedNotRunClosure(FROM_HERE
),
262 MakeExpectedNotRunClosure(FROM_HERE
)));
264 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
267 test_task_runner
->RunUntilIdle();
269 RunCurrentLoopUntilIdle();
271 EXPECT_FALSE(is_canceled
.Run());
274 // Post a task and cancel it. HasTrackedTasks() should return true
275 // from when the task is posted until the (do-nothing) reply task is
277 TEST_F(CancelableTaskTrackerTest
, HasTrackedTasksPost
) {
278 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
279 new TestSimpleTaskRunner());
281 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
283 ignore_result(task_tracker_
.PostTask(
284 test_task_runner
.get(), FROM_HERE
, MakeExpectedNotRunClosure(FROM_HERE
)));
286 task_tracker_
.TryCancelAll();
288 test_task_runner
->RunUntilIdle();
290 EXPECT_TRUE(task_tracker_
.HasTrackedTasks());
292 RunCurrentLoopUntilIdle();
294 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
297 // Post a task with a reply and cancel it. HasTrackedTasks() should
298 // return true from when the task is posted until it is canceled.
299 TEST_F(CancelableTaskTrackerTest
, HasTrackedTasksPostWithReply
) {
300 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
301 new TestSimpleTaskRunner());
303 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
306 task_tracker_
.PostTaskAndReply(test_task_runner
.get(),
308 MakeExpectedNotRunClosure(FROM_HERE
),
309 MakeExpectedNotRunClosure(FROM_HERE
)));
311 task_tracker_
.TryCancelAll();
313 test_task_runner
->RunUntilIdle();
315 EXPECT_TRUE(task_tracker_
.HasTrackedTasks());
317 RunCurrentLoopUntilIdle();
319 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
322 // Create a new tracked task ID. HasTrackedTasks() should return true
323 // until the IsCanceledCallback is destroyed.
324 TEST_F(CancelableTaskTrackerTest
, HasTrackedTasksIsCancelled
) {
325 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
327 CancelableTaskTracker::IsCanceledCallback is_canceled
;
328 ignore_result(task_tracker_
.NewTrackedTaskId(&is_canceled
));
330 task_tracker_
.TryCancelAll();
332 EXPECT_TRUE(task_tracker_
.HasTrackedTasks());
336 EXPECT_FALSE(task_tracker_
.HasTrackedTasks());
339 // The death tests below make sure that calling task tracker member
340 // functions from a thread different from its owner thread DCHECKs in
343 class CancelableTaskTrackerDeathTest
: public CancelableTaskTrackerTest
{
345 CancelableTaskTrackerDeathTest() {
346 // The default style "fast" does not support multi-threaded tests.
347 ::testing::FLAGS_gtest_death_test_style
= "threadsafe";
351 // Duplicated from base/threading/thread_checker.h so that we can be
352 // good citizens there and undef the macro.
353 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)
354 #define ENABLE_THREAD_CHECKER 1
356 #define ENABLE_THREAD_CHECKER 0
359 // Runs |fn| with |task_tracker|, expecting it to crash in debug mode.
360 void MaybeRunDeadlyTaskTrackerMemberFunction(
361 CancelableTaskTracker
* task_tracker
,
362 const Callback
<void(CancelableTaskTracker
*)>& fn
) {
363 // CancelableTask uses DCHECKs with its ThreadChecker (itself only
364 // enabled in debug mode).
365 #if ENABLE_THREAD_CHECKER
366 EXPECT_DEATH_IF_SUPPORTED(fn
.Run(task_tracker
), "");
370 void PostDoNothingTask(CancelableTaskTracker
* task_tracker
) {
371 ignore_result(task_tracker
->PostTask(
372 scoped_refptr
<TestSimpleTaskRunner
>(new TestSimpleTaskRunner()).get(),
377 TEST_F(CancelableTaskTrackerDeathTest
, PostFromDifferentThread
) {
378 Thread
bad_thread("bad thread");
379 ASSERT_TRUE(bad_thread
.Start());
381 bad_thread
.message_loop_proxy()->PostTask(
383 Bind(&MaybeRunDeadlyTaskTrackerMemberFunction
,
384 Unretained(&task_tracker_
),
385 Bind(&PostDoNothingTask
)));
388 void TryCancel(CancelableTaskTracker::TaskId task_id
,
389 CancelableTaskTracker
* task_tracker
) {
390 task_tracker
->TryCancel(task_id
);
393 TEST_F(CancelableTaskTrackerDeathTest
, CancelOnDifferentThread
) {
394 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
395 new TestSimpleTaskRunner());
397 Thread
bad_thread("bad thread");
398 ASSERT_TRUE(bad_thread
.Start());
400 CancelableTaskTracker::TaskId task_id
= task_tracker_
.PostTask(
401 test_task_runner
.get(), FROM_HERE
, Bind(&DoNothing
));
402 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
404 bad_thread
.message_loop_proxy()->PostTask(
406 Bind(&MaybeRunDeadlyTaskTrackerMemberFunction
,
407 Unretained(&task_tracker_
),
408 Bind(&TryCancel
, task_id
)));
410 test_task_runner
->RunUntilIdle();
413 TEST_F(CancelableTaskTrackerDeathTest
, CancelAllOnDifferentThread
) {
414 scoped_refptr
<TestSimpleTaskRunner
> test_task_runner(
415 new TestSimpleTaskRunner());
417 Thread
bad_thread("bad thread");
418 ASSERT_TRUE(bad_thread
.Start());
420 CancelableTaskTracker::TaskId task_id
= task_tracker_
.PostTask(
421 test_task_runner
.get(), FROM_HERE
, Bind(&DoNothing
));
422 EXPECT_NE(CancelableTaskTracker::kBadTaskId
, task_id
);
424 bad_thread
.message_loop_proxy()->PostTask(
426 Bind(&MaybeRunDeadlyTaskTrackerMemberFunction
,
427 Unretained(&task_tracker_
),
428 Bind(&CancelableTaskTracker::TryCancelAll
)));
430 test_task_runner
->RunUntilIdle();