• 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 
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