1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/task/cancelable_task_tracker.h"
6
7 #include <cstddef>
8 #include <tuple>
9
10 #include "base/check_op.h"
11 #include "base/functional/bind.h"
12 #include "base/functional/callback_helpers.h"
13 #include "base/location.h"
14 #include "base/memory/ref_counted.h"
15 #include "base/memory/weak_ptr.h"
16 #include "base/run_loop.h"
17 #include "base/task/single_thread_task_runner.h"
18 #include "base/test/bind.h"
19 #include "base/test/gtest_util.h"
20 #include "base/test/task_environment.h"
21 #include "base/test/test_simple_task_runner.h"
22 #include "base/threading/thread.h"
23 #include "testing/gtest/include/gtest/gtest.h"
24
25 namespace base {
26
27 namespace {
28
29 class CancelableTaskTrackerTest : public testing::Test {
30 protected:
~CancelableTaskTrackerTest()31 ~CancelableTaskTrackerTest() override { RunLoop().RunUntilIdle(); }
32
33 CancelableTaskTracker task_tracker_;
34
35 private:
36 // Needed by CancelableTaskTracker methods.
37 test::TaskEnvironment task_environment_;
38 };
39
40 } // namespace
41
42 // With the task tracker, post a task, a task with a reply, and get a
43 // new task id without canceling any of them. The tasks and the reply
44 // should run and the "is canceled" callback should return false.
TEST_F(CancelableTaskTrackerTest,NoCancel)45 TEST_F(CancelableTaskTrackerTest, NoCancel) {
46 Thread worker_thread("worker thread");
47 ASSERT_TRUE(worker_thread.Start());
48
49 std::ignore =
50 task_tracker_.PostTask(worker_thread.task_runner().get(), FROM_HERE,
51 MakeExpectedRunClosure(FROM_HERE));
52
53 std::ignore = task_tracker_.PostTaskAndReply(
54 worker_thread.task_runner().get(), FROM_HERE,
55 MakeExpectedRunClosure(FROM_HERE), MakeExpectedRunClosure(FROM_HERE));
56
57 CancelableTaskTracker::IsCanceledCallback is_canceled;
58 std::ignore = task_tracker_.NewTrackedTaskId(&is_canceled);
59
60 worker_thread.Stop();
61
62 RunLoop().RunUntilIdle();
63
64 EXPECT_FALSE(is_canceled.Run());
65 }
66
67 // Post a task with the task tracker but cancel it before running the
68 // task runner. The task should not run.
TEST_F(CancelableTaskTrackerTest,CancelPostedTask)69 TEST_F(CancelableTaskTrackerTest, CancelPostedTask) {
70 scoped_refptr<TestSimpleTaskRunner> test_task_runner(
71 new TestSimpleTaskRunner());
72
73 CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask(
74 test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE));
75 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
76
77 EXPECT_EQ(1U, test_task_runner->NumPendingTasks());
78
79 task_tracker_.TryCancel(task_id);
80
81 test_task_runner->RunUntilIdle();
82 }
83
84 // Post a task with reply with the task tracker and cancel it before
85 // running the task runner. Neither the task nor the reply should
86 // run.
TEST_F(CancelableTaskTrackerTest,CancelPostedTaskAndReply)87 TEST_F(CancelableTaskTrackerTest, CancelPostedTaskAndReply) {
88 scoped_refptr<TestSimpleTaskRunner> test_task_runner(
89 new TestSimpleTaskRunner());
90
91 CancelableTaskTracker::TaskId task_id =
92 task_tracker_.PostTaskAndReply(test_task_runner.get(),
93 FROM_HERE,
94 MakeExpectedNotRunClosure(FROM_HERE),
95 MakeExpectedNotRunClosure(FROM_HERE));
96 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
97
98 task_tracker_.TryCancel(task_id);
99
100 test_task_runner->RunUntilIdle();
101 }
102
103 // Post a task with reply with the task tracker and cancel it after
104 // running the task runner but before running the current message
105 // loop. The task should run but the reply should not.
TEST_F(CancelableTaskTrackerTest,CancelReply)106 TEST_F(CancelableTaskTrackerTest, CancelReply) {
107 scoped_refptr<TestSimpleTaskRunner> test_task_runner(
108 new TestSimpleTaskRunner());
109
110 CancelableTaskTracker::TaskId task_id =
111 task_tracker_.PostTaskAndReply(test_task_runner.get(),
112 FROM_HERE,
113 MakeExpectedRunClosure(FROM_HERE),
114 MakeExpectedNotRunClosure(FROM_HERE));
115 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
116
117 test_task_runner->RunUntilIdle();
118
119 task_tracker_.TryCancel(task_id);
120 }
121
122 // Post a task with reply with the task tracker on a worker thread and
123 // cancel it before running the current message loop. The task should
124 // run but the reply should not.
TEST_F(CancelableTaskTrackerTest,CancelReplyDifferentThread)125 TEST_F(CancelableTaskTrackerTest, CancelReplyDifferentThread) {
126 Thread worker_thread("worker thread");
127 ASSERT_TRUE(worker_thread.Start());
128
129 CancelableTaskTracker::TaskId task_id = task_tracker_.PostTaskAndReply(
130 worker_thread.task_runner().get(), FROM_HERE, DoNothing(),
131 MakeExpectedNotRunClosure(FROM_HERE));
132 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
133
134 task_tracker_.TryCancel(task_id);
135
136 worker_thread.Stop();
137 }
138
ExpectIsCanceled(const CancelableTaskTracker::IsCanceledCallback & is_canceled,bool expected_is_canceled)139 void ExpectIsCanceled(
140 const CancelableTaskTracker::IsCanceledCallback& is_canceled,
141 bool expected_is_canceled) {
142 EXPECT_EQ(expected_is_canceled, is_canceled.Run());
143 }
144
145 // Create a new task ID and check its status on a separate thread
146 // before and after canceling. The is-canceled callback should be
147 // thread-safe (i.e., nothing should blow up).
TEST_F(CancelableTaskTrackerTest,NewTrackedTaskIdDifferentThread)148 TEST_F(CancelableTaskTrackerTest, NewTrackedTaskIdDifferentThread) {
149 CancelableTaskTracker::IsCanceledCallback is_canceled;
150 CancelableTaskTracker::TaskId task_id =
151 task_tracker_.NewTrackedTaskId(&is_canceled);
152
153 EXPECT_FALSE(is_canceled.Run());
154
155 Thread other_thread("other thread");
156 ASSERT_TRUE(other_thread.Start());
157 other_thread.task_runner()->PostTask(
158 FROM_HERE, BindOnce(&ExpectIsCanceled, is_canceled, false));
159 other_thread.Stop();
160
161 task_tracker_.TryCancel(task_id);
162
163 ASSERT_TRUE(other_thread.Start());
164 other_thread.task_runner()->PostTask(
165 FROM_HERE, BindOnce(&ExpectIsCanceled, is_canceled, true));
166 other_thread.Stop();
167 }
168
169 // With the task tracker, post a task, a task with a reply, get a new
170 // task id, and then cancel all of them. None of the tasks nor the
171 // reply should run and the "is canceled" callback should return
172 // true.
TEST_F(CancelableTaskTrackerTest,CancelAll)173 TEST_F(CancelableTaskTrackerTest, CancelAll) {
174 scoped_refptr<TestSimpleTaskRunner> test_task_runner(
175 new TestSimpleTaskRunner());
176
177 std::ignore = task_tracker_.PostTask(test_task_runner.get(), FROM_HERE,
178 MakeExpectedNotRunClosure(FROM_HERE));
179
180 std::ignore = task_tracker_.PostTaskAndReply(
181 test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE),
182 MakeExpectedNotRunClosure(FROM_HERE));
183
184 CancelableTaskTracker::IsCanceledCallback is_canceled;
185 std::ignore = task_tracker_.NewTrackedTaskId(&is_canceled);
186
187 task_tracker_.TryCancelAll();
188
189 test_task_runner->RunUntilIdle();
190
191 RunLoop().RunUntilIdle();
192
193 EXPECT_TRUE(is_canceled.Run());
194 }
195
196 // With the task tracker, post a task, a task with a reply, get a new
197 // task id, and then cancel all of them. None of the tasks nor the
198 // reply should run and the "is canceled" callback should return
199 // true.
TEST_F(CancelableTaskTrackerTest,DestructionCancelsAll)200 TEST_F(CancelableTaskTrackerTest, DestructionCancelsAll) {
201 scoped_refptr<TestSimpleTaskRunner> test_task_runner(
202 new TestSimpleTaskRunner());
203
204 CancelableTaskTracker::IsCanceledCallback is_canceled;
205
206 {
207 // Create another task tracker with a smaller scope.
208 CancelableTaskTracker task_tracker;
209
210 std::ignore = task_tracker.PostTask(test_task_runner.get(), FROM_HERE,
211 MakeExpectedNotRunClosure(FROM_HERE));
212
213 std::ignore = task_tracker.PostTaskAndReply(
214 test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE),
215 MakeExpectedNotRunClosure(FROM_HERE));
216
217 std::ignore = task_tracker_.NewTrackedTaskId(&is_canceled);
218 }
219
220 test_task_runner->RunUntilIdle();
221
222 RunLoop().RunUntilIdle();
223
224 EXPECT_FALSE(is_canceled.Run());
225 }
226
227 // Post a task and cancel it. HasTrackedTasks() should return false as soon as
228 // TryCancel() returns, otherwise we may have leaked per-task state.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksCancelById)229 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksCancelById) {
230 scoped_refptr<TestSimpleTaskRunner> test_task_runner(
231 new TestSimpleTaskRunner());
232
233 EXPECT_FALSE(task_tracker_.HasTrackedTasks());
234
235 CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask(
236 test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE));
237 EXPECT_TRUE(task_tracker_.HasTrackedTasks());
238
239 task_tracker_.TryCancel(task_id);
240 EXPECT_FALSE(task_tracker_.HasTrackedTasks());
241
242 test_task_runner->RunUntilIdle();
243 RunLoop().RunUntilIdle();
244 }
245
246 // Post a task and then cancel all tasks. HasTrackedTasks() should return false
247 // as soon as TryCancelAll() is called.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksPostCancelAll)248 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPostCancelAll) {
249 scoped_refptr<TestSimpleTaskRunner> test_task_runner(
250 new TestSimpleTaskRunner());
251
252 EXPECT_FALSE(task_tracker_.HasTrackedTasks());
253
254 std::ignore = task_tracker_.PostTask(test_task_runner.get(), FROM_HERE,
255 MakeExpectedNotRunClosure(FROM_HERE));
256
257 task_tracker_.TryCancelAll();
258
259 EXPECT_FALSE(task_tracker_.HasTrackedTasks());
260
261 test_task_runner->RunUntilIdle();
262 RunLoop().RunUntilIdle();
263 }
264
265 // Post a task with a reply and cancel it. HasTrackedTasks() should return false
266 // as soon as TryCancelAll() is called.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksPostWithReplyCancelAll)267 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPostWithReplyCancelAll) {
268 scoped_refptr<TestSimpleTaskRunner> test_task_runner(
269 new TestSimpleTaskRunner());
270
271 EXPECT_FALSE(task_tracker_.HasTrackedTasks());
272
273 std::ignore = task_tracker_.PostTaskAndReply(
274 test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE),
275 MakeExpectedNotRunClosure(FROM_HERE));
276
277 task_tracker_.TryCancelAll();
278
279 EXPECT_FALSE(task_tracker_.HasTrackedTasks());
280
281 test_task_runner->RunUntilIdle();
282 RunLoop().RunUntilIdle();
283 }
284
285 // Create a new tracked task ID. HasTrackedTasks() should return false as soon
286 // as TryCancelAll() is called.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksIsCancelledCancelAll)287 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksIsCancelledCancelAll) {
288 EXPECT_FALSE(task_tracker_.HasTrackedTasks());
289
290 CancelableTaskTracker::IsCanceledCallback is_canceled;
291 std::ignore = task_tracker_.NewTrackedTaskId(&is_canceled);
292
293 task_tracker_.TryCancelAll();
294
295 EXPECT_FALSE(task_tracker_.HasTrackedTasks());
296 }
297
298 // The death tests below make sure that calling task tracker member
299 // functions from a thread different from its owner thread DCHECKs in
300 // debug mode.
301
302 class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest {
303 protected:
CancelableTaskTrackerDeathTest()304 CancelableTaskTrackerDeathTest() {
305 // The default style "fast" does not support multi-threaded tests.
306 GTEST_FLAG_SET(death_test_style, "threadsafe");
307 }
308 };
309
310 // Runs |fn| with |task_tracker|, expecting it to crash in debug mode.
MaybeRunDeadlyTaskTrackerMemberFunction(CancelableTaskTracker * task_tracker,OnceCallback<void (CancelableTaskTracker *)> fn)311 void MaybeRunDeadlyTaskTrackerMemberFunction(
312 CancelableTaskTracker* task_tracker,
313 OnceCallback<void(CancelableTaskTracker*)> fn) {
314 EXPECT_DCHECK_DEATH(std::move(fn).Run(task_tracker));
315 }
316
PostDoNothingTask(CancelableTaskTracker * task_tracker)317 void PostDoNothingTask(CancelableTaskTracker* task_tracker) {
318 std::ignore = task_tracker->PostTask(
319 scoped_refptr<TestSimpleTaskRunner>(new TestSimpleTaskRunner()).get(),
320 FROM_HERE, DoNothing());
321 }
322
TEST_F(CancelableTaskTrackerDeathTest,PostFromDifferentThread)323 TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) {
324 Thread bad_thread("bad thread");
325 ASSERT_TRUE(bad_thread.Start());
326
327 bad_thread.task_runner()->PostTask(
328 FROM_HERE,
329 BindOnce(&MaybeRunDeadlyTaskTrackerMemberFunction,
330 Unretained(&task_tracker_), BindOnce(&PostDoNothingTask)));
331 }
332
TryCancel(CancelableTaskTracker::TaskId task_id,CancelableTaskTracker * task_tracker)333 void TryCancel(CancelableTaskTracker::TaskId task_id,
334 CancelableTaskTracker* task_tracker) {
335 task_tracker->TryCancel(task_id);
336 }
337
TEST_F(CancelableTaskTrackerDeathTest,CancelOnDifferentThread)338 TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) {
339 scoped_refptr<TestSimpleTaskRunner> test_task_runner(
340 new TestSimpleTaskRunner());
341
342 Thread bad_thread("bad thread");
343 ASSERT_TRUE(bad_thread.Start());
344
345 CancelableTaskTracker::TaskId task_id =
346 task_tracker_.PostTask(test_task_runner.get(), FROM_HERE, DoNothing());
347 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
348
349 bad_thread.task_runner()->PostTask(
350 FROM_HERE,
351 BindOnce(&MaybeRunDeadlyTaskTrackerMemberFunction,
352 Unretained(&task_tracker_), BindOnce(&TryCancel, task_id)));
353
354 test_task_runner->RunUntilIdle();
355 }
356
TEST_F(CancelableTaskTrackerDeathTest,CancelAllOnDifferentThread)357 TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) {
358 scoped_refptr<TestSimpleTaskRunner> test_task_runner(
359 new TestSimpleTaskRunner());
360
361 Thread bad_thread("bad thread");
362 ASSERT_TRUE(bad_thread.Start());
363
364 CancelableTaskTracker::TaskId task_id =
365 task_tracker_.PostTask(test_task_runner.get(), FROM_HERE, DoNothing());
366 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
367
368 bad_thread.task_runner()->PostTask(
369 FROM_HERE, BindOnce(&MaybeRunDeadlyTaskTrackerMemberFunction,
370 Unretained(&task_tracker_),
371 BindOnce(&CancelableTaskTracker::TryCancelAll)));
372
373 test_task_runner->RunUntilIdle();
374 }
375
376 } // namespace base
377