• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
139 // Create a new task ID and check its status on a separate thread
140 // before and after canceling.  The is-canceled callback should be
141 // thread-safe (i.e., nothing should blow up).
TEST_F(CancelableTaskTrackerTest,NewTrackedTaskIdDifferentThread)142 TEST_F(CancelableTaskTrackerTest, NewTrackedTaskIdDifferentThread) {
143   CancelableTaskTracker::IsCanceledCallback is_canceled;
144   CancelableTaskTracker::TaskId task_id =
145       task_tracker_.NewTrackedTaskId(&is_canceled);
146 
147   EXPECT_FALSE(is_canceled.Run());
148 
149   Thread other_thread("other thread");
150   ASSERT_TRUE(other_thread.Start());
151   other_thread.task_runner()->PostTask(
152       FROM_HERE, is_canceled.Then(
153                      BindRepeating([](bool result) { EXPECT_FALSE(result); })));
154   other_thread.Stop();
155 
156   task_tracker_.TryCancel(task_id);
157 
158   ASSERT_TRUE(other_thread.Start());
159   other_thread.task_runner()->PostTask(
160       FROM_HERE, is_canceled.Then(
161                      BindRepeating([](bool result) { EXPECT_TRUE(result); })));
162   other_thread.Stop();
163 }
164 
165 // With the task tracker, post a task, a task with a reply, get a new
166 // task id, and then cancel all of them.  None of the tasks nor the
167 // reply should run and the "is canceled" callback should return
168 // true.
TEST_F(CancelableTaskTrackerTest,CancelAll)169 TEST_F(CancelableTaskTrackerTest, CancelAll) {
170   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
171       new TestSimpleTaskRunner());
172 
173   std::ignore = task_tracker_.PostTask(test_task_runner.get(), FROM_HERE,
174                                        MakeExpectedNotRunClosure(FROM_HERE));
175 
176   std::ignore = task_tracker_.PostTaskAndReply(
177       test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE),
178       MakeExpectedNotRunClosure(FROM_HERE));
179 
180   CancelableTaskTracker::IsCanceledCallback is_canceled;
181   std::ignore = task_tracker_.NewTrackedTaskId(&is_canceled);
182 
183   task_tracker_.TryCancelAll();
184 
185   test_task_runner->RunUntilIdle();
186 
187   RunLoop().RunUntilIdle();
188 
189   EXPECT_TRUE(is_canceled.Run());
190 }
191 
192 // With the task tracker, post a task, a task with a reply, get a new
193 // task id, and then cancel all of them.  None of the tasks nor the
194 // reply should run and the "is canceled" callback should return
195 // true.
TEST_F(CancelableTaskTrackerTest,DestructionCancelsAll)196 TEST_F(CancelableTaskTrackerTest, DestructionCancelsAll) {
197   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
198       new TestSimpleTaskRunner());
199 
200   CancelableTaskTracker::IsCanceledCallback is_canceled;
201 
202   {
203     // Create another task tracker with a smaller scope.
204     CancelableTaskTracker task_tracker;
205 
206     std::ignore = task_tracker.PostTask(test_task_runner.get(), FROM_HERE,
207                                         MakeExpectedNotRunClosure(FROM_HERE));
208 
209     std::ignore = task_tracker.PostTaskAndReply(
210         test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE),
211         MakeExpectedNotRunClosure(FROM_HERE));
212 
213     std::ignore = task_tracker_.NewTrackedTaskId(&is_canceled);
214   }
215 
216   test_task_runner->RunUntilIdle();
217 
218   RunLoop().RunUntilIdle();
219 
220   EXPECT_FALSE(is_canceled.Run());
221 }
222 
223 // Post a task and cancel it. HasTrackedTasks() should return false as soon as
224 // TryCancel() returns, otherwise we may have leaked per-task state.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksCancelById)225 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksCancelById) {
226   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
227       new TestSimpleTaskRunner());
228 
229   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
230 
231   CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask(
232       test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE));
233   EXPECT_TRUE(task_tracker_.HasTrackedTasks());
234 
235   task_tracker_.TryCancel(task_id);
236   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
237 
238   test_task_runner->RunUntilIdle();
239   RunLoop().RunUntilIdle();
240 }
241 
242 // Post a task and then cancel all tasks. HasTrackedTasks() should return false
243 // as soon as TryCancelAll() is called.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksPostCancelAll)244 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPostCancelAll) {
245   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
246       new TestSimpleTaskRunner());
247 
248   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
249 
250   std::ignore = task_tracker_.PostTask(test_task_runner.get(), FROM_HERE,
251                                        MakeExpectedNotRunClosure(FROM_HERE));
252 
253   task_tracker_.TryCancelAll();
254 
255   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
256 
257   test_task_runner->RunUntilIdle();
258   RunLoop().RunUntilIdle();
259 }
260 
261 // Post a task with a reply and cancel it. HasTrackedTasks() should return false
262 // as soon as TryCancelAll() is called.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksPostWithReplyCancelAll)263 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPostWithReplyCancelAll) {
264   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
265       new TestSimpleTaskRunner());
266 
267   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
268 
269   std::ignore = task_tracker_.PostTaskAndReply(
270       test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE),
271       MakeExpectedNotRunClosure(FROM_HERE));
272 
273   task_tracker_.TryCancelAll();
274 
275   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
276 
277   test_task_runner->RunUntilIdle();
278   RunLoop().RunUntilIdle();
279 }
280 
281 // Create a new tracked task ID. HasTrackedTasks() should return false as soon
282 // as TryCancelAll() is called.
TEST_F(CancelableTaskTrackerTest,HasTrackedTasksIsCancelledCancelAll)283 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksIsCancelledCancelAll) {
284   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
285 
286   CancelableTaskTracker::IsCanceledCallback is_canceled;
287   std::ignore = task_tracker_.NewTrackedTaskId(&is_canceled);
288 
289   task_tracker_.TryCancelAll();
290 
291   EXPECT_FALSE(task_tracker_.HasTrackedTasks());
292 }
293 
294 // The death tests below make sure that calling task tracker member
295 // functions from a thread different from its owner thread DCHECKs in
296 // debug mode.
297 
298 class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest {
299  protected:
CancelableTaskTrackerDeathTest()300   CancelableTaskTrackerDeathTest() {
301     // The default style "fast" does not support multi-threaded tests.
302     GTEST_FLAG_SET(death_test_style, "threadsafe");
303   }
304 };
305 
306 // Runs |fn| with |task_tracker|, expecting it to crash in debug mode.
MaybeRunDeadlyTaskTrackerMemberFunction(CancelableTaskTracker * task_tracker,OnceCallback<void (CancelableTaskTracker *)> fn)307 void MaybeRunDeadlyTaskTrackerMemberFunction(
308     CancelableTaskTracker* task_tracker,
309     OnceCallback<void(CancelableTaskTracker*)> fn) {
310   EXPECT_DCHECK_DEATH(std::move(fn).Run(task_tracker));
311 }
312 
TEST_F(CancelableTaskTrackerDeathTest,PostFromDifferentThread)313 TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) {
314   Thread bad_thread("bad thread");
315   ASSERT_TRUE(bad_thread.Start());
316 
317   bad_thread.task_runner()->PostTask(
318       FROM_HERE,
319       BindOnce(
320           &MaybeRunDeadlyTaskTrackerMemberFunction, Unretained(&task_tracker_),
321           BindOnce([](CancelableTaskTracker* task_tracker) {
322             std::ignore = task_tracker->PostTask(
323                 scoped_refptr<TestSimpleTaskRunner>(new TestSimpleTaskRunner())
324                     .get(),
325                 FROM_HERE, DoNothing());
326           })));
327 }
328 
TEST_F(CancelableTaskTrackerDeathTest,CancelOnDifferentThread)329 TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) {
330   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
331       new TestSimpleTaskRunner());
332 
333   Thread bad_thread("bad thread");
334   ASSERT_TRUE(bad_thread.Start());
335 
336   CancelableTaskTracker::TaskId task_id =
337       task_tracker_.PostTask(test_task_runner.get(), FROM_HERE, DoNothing());
338   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
339 
340   bad_thread.task_runner()->PostTask(
341       FROM_HERE, BindOnce(&MaybeRunDeadlyTaskTrackerMemberFunction,
342                           Unretained(&task_tracker_),
343                           BindOnce(
344                               [](CancelableTaskTracker::TaskId task_id,
345                                  CancelableTaskTracker* task_tracker) {
346                                 task_tracker->TryCancel(task_id);
347                               },
348                               task_id)));
349 
350   test_task_runner->RunUntilIdle();
351 }
352 
TEST_F(CancelableTaskTrackerDeathTest,CancelAllOnDifferentThread)353 TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) {
354   scoped_refptr<TestSimpleTaskRunner> test_task_runner(
355       new TestSimpleTaskRunner());
356 
357   Thread bad_thread("bad thread");
358   ASSERT_TRUE(bad_thread.Start());
359 
360   CancelableTaskTracker::TaskId task_id =
361       task_tracker_.PostTask(test_task_runner.get(), FROM_HERE, DoNothing());
362   EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id);
363 
364   bad_thread.task_runner()->PostTask(
365       FROM_HERE, BindOnce(&MaybeRunDeadlyTaskTrackerMemberFunction,
366                           Unretained(&task_tracker_),
367                           BindOnce([](CancelableTaskTracker* task_tracker) {
368                             task_tracker->TryCancelAll();
369                           })));
370 
371   test_task_runner->RunUntilIdle();
372 }
373 
374 }  // namespace base
375