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. 4 5 #include "base/task/cancelable_task_tracker.h" 6 7 #include <cstddef> 8 #include <deque> 9 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" 21 22 namespace base { 23 24 namespace { 25 26 class CancelableTaskTrackerTest : public testing::Test { 27 protected: 28 ~CancelableTaskTrackerTest() override { RunCurrentLoopUntilIdle(); } 29 30 void RunCurrentLoopUntilIdle() { 31 RunLoop run_loop; 32 run_loop.RunUntilIdle(); 33 } 34 35 CancelableTaskTracker task_tracker_; 36 37 private: 38 // Needed by CancelableTaskTracker methods. 39 MessageLoop message_loop_; 40 }; 41 42 void AddFailureAt(const tracked_objects::Location& location) { 43 ADD_FAILURE_AT(location.file_name(), location.line_number()); 44 } 45 46 // Returns a closure that fails if run. 47 Closure MakeExpectedNotRunClosure(const tracked_objects::Location& location) { 48 return Bind(&AddFailureAt, location); 49 } 50 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. 55 class RunChecker { 56 public: 57 explicit RunChecker(const tracked_objects::Location& location) 58 : location_(location), called_(false) {} 59 60 ~RunChecker() { 61 if (!called_) { 62 ADD_FAILURE_AT(location_.file_name(), location_.line_number()); 63 } 64 } 65 66 void Run() { called_ = true; } 67 68 private: 69 tracked_objects::Location location_; 70 bool called_; 71 }; 72 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))); 76 } 77 78 } // namespace 79 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()); 86 87 ignore_result(task_tracker_.PostTask(worker_thread.task_runner().get(), 88 FROM_HERE, 89 MakeExpectedRunClosure(FROM_HERE))); 90 91 ignore_result(task_tracker_.PostTaskAndReply( 92 worker_thread.task_runner().get(), FROM_HERE, 93 MakeExpectedRunClosure(FROM_HERE), MakeExpectedRunClosure(FROM_HERE))); 94 95 CancelableTaskTracker::IsCanceledCallback is_canceled; 96 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); 97 98 worker_thread.Stop(); 99 100 RunCurrentLoopUntilIdle(); 101 102 EXPECT_FALSE(is_canceled.Run()); 103 } 104 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()); 110 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); 114 115 EXPECT_EQ(1U, test_task_runner->GetPendingTasks().size()); 116 117 task_tracker_.TryCancel(task_id); 118 119 test_task_runner->RunUntilIdle(); 120 } 121 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 124 // run. 125 TEST_F(CancelableTaskTrackerTest, CancelPostedTaskAndReply) { 126 scoped_refptr<TestSimpleTaskRunner> test_task_runner( 127 new TestSimpleTaskRunner()); 128 129 CancelableTaskTracker::TaskId task_id = 130 task_tracker_.PostTaskAndReply(test_task_runner.get(), 131 FROM_HERE, 132 MakeExpectedNotRunClosure(FROM_HERE), 133 MakeExpectedNotRunClosure(FROM_HERE)); 134 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 135 136 task_tracker_.TryCancel(task_id); 137 138 test_task_runner->RunUntilIdle(); 139 } 140 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()); 147 148 CancelableTaskTracker::TaskId task_id = 149 task_tracker_.PostTaskAndReply(test_task_runner.get(), 150 FROM_HERE, 151 MakeExpectedRunClosure(FROM_HERE), 152 MakeExpectedNotRunClosure(FROM_HERE)); 153 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 154 155 test_task_runner->RunUntilIdle(); 156 157 task_tracker_.TryCancel(task_id); 158 } 159 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()); 166 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); 171 172 task_tracker_.TryCancel(task_id); 173 174 worker_thread.Stop(); 175 } 176 177 void ExpectIsCanceled( 178 const CancelableTaskTracker::IsCanceledCallback& is_canceled, 179 bool expected_is_canceled) { 180 EXPECT_EQ(expected_is_canceled, is_canceled.Run()); 181 } 182 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); 190 191 EXPECT_FALSE(is_canceled.Run()); 192 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)); 197 other_thread.Stop(); 198 199 task_tracker_.TryCancel(task_id); 200 201 ASSERT_TRUE(other_thread.Start()); 202 other_thread.task_runner()->PostTask( 203 FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, true)); 204 other_thread.Stop(); 205 } 206 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 210 // true. 211 TEST_F(CancelableTaskTrackerTest, CancelAll) { 212 scoped_refptr<TestSimpleTaskRunner> test_task_runner( 213 new TestSimpleTaskRunner()); 214 215 ignore_result(task_tracker_.PostTask( 216 test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE))); 217 218 ignore_result( 219 task_tracker_.PostTaskAndReply(test_task_runner.get(), 220 FROM_HERE, 221 MakeExpectedNotRunClosure(FROM_HERE), 222 MakeExpectedNotRunClosure(FROM_HERE))); 223 224 CancelableTaskTracker::IsCanceledCallback is_canceled; 225 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); 226 227 task_tracker_.TryCancelAll(); 228 229 test_task_runner->RunUntilIdle(); 230 231 RunCurrentLoopUntilIdle(); 232 233 EXPECT_TRUE(is_canceled.Run()); 234 } 235 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 239 // true. 240 TEST_F(CancelableTaskTrackerTest, DestructionCancelsAll) { 241 scoped_refptr<TestSimpleTaskRunner> test_task_runner( 242 new TestSimpleTaskRunner()); 243 244 CancelableTaskTracker::IsCanceledCallback is_canceled; 245 246 { 247 // Create another task tracker with a smaller scope. 248 CancelableTaskTracker task_tracker; 249 250 ignore_result(task_tracker.PostTask(test_task_runner.get(), 251 FROM_HERE, 252 MakeExpectedNotRunClosure(FROM_HERE))); 253 254 ignore_result( 255 task_tracker.PostTaskAndReply(test_task_runner.get(), 256 FROM_HERE, 257 MakeExpectedNotRunClosure(FROM_HERE), 258 MakeExpectedNotRunClosure(FROM_HERE))); 259 260 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); 261 } 262 263 test_task_runner->RunUntilIdle(); 264 265 RunCurrentLoopUntilIdle(); 266 267 EXPECT_FALSE(is_canceled.Run()); 268 } 269 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 272 // flushed. 273 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPost) { 274 scoped_refptr<TestSimpleTaskRunner> test_task_runner( 275 new TestSimpleTaskRunner()); 276 277 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 278 279 ignore_result(task_tracker_.PostTask( 280 test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE))); 281 282 task_tracker_.TryCancelAll(); 283 284 test_task_runner->RunUntilIdle(); 285 286 EXPECT_TRUE(task_tracker_.HasTrackedTasks()); 287 288 RunCurrentLoopUntilIdle(); 289 290 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 291 } 292 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()); 298 299 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 300 301 ignore_result( 302 task_tracker_.PostTaskAndReply(test_task_runner.get(), 303 FROM_HERE, 304 MakeExpectedNotRunClosure(FROM_HERE), 305 MakeExpectedNotRunClosure(FROM_HERE))); 306 307 task_tracker_.TryCancelAll(); 308 309 test_task_runner->RunUntilIdle(); 310 311 EXPECT_TRUE(task_tracker_.HasTrackedTasks()); 312 313 RunCurrentLoopUntilIdle(); 314 315 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 316 } 317 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()); 322 323 CancelableTaskTracker::IsCanceledCallback is_canceled; 324 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); 325 326 task_tracker_.TryCancelAll(); 327 328 EXPECT_TRUE(task_tracker_.HasTrackedTasks()); 329 330 is_canceled.Reset(); 331 332 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 333 } 334 335 // The death tests below make sure that calling task tracker member 336 // functions from a thread different from its owner thread DCHECKs in 337 // debug mode. 338 339 class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest { 340 protected: 341 CancelableTaskTrackerDeathTest() { 342 // The default style "fast" does not support multi-threaded tests. 343 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; 344 } 345 }; 346 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 351 #else 352 #define ENABLE_THREAD_CHECKER 0 353 #endif 354 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), ""); 363 #endif 364 } 365 366 void PostDoNothingTask(CancelableTaskTracker* task_tracker) { 367 ignore_result(task_tracker->PostTask( 368 scoped_refptr<TestSimpleTaskRunner>(new TestSimpleTaskRunner()).get(), 369 FROM_HERE, 370 Bind(&DoNothing))); 371 } 372 373 TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) { 374 Thread bad_thread("bad thread"); 375 ASSERT_TRUE(bad_thread.Start()); 376 377 bad_thread.task_runner()->PostTask( 378 FROM_HERE, Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, 379 Unretained(&task_tracker_), Bind(&PostDoNothingTask))); 380 } 381 382 void TryCancel(CancelableTaskTracker::TaskId task_id, 383 CancelableTaskTracker* task_tracker) { 384 task_tracker->TryCancel(task_id); 385 } 386 387 TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) { 388 scoped_refptr<TestSimpleTaskRunner> test_task_runner( 389 new TestSimpleTaskRunner()); 390 391 Thread bad_thread("bad thread"); 392 ASSERT_TRUE(bad_thread.Start()); 393 394 CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask( 395 test_task_runner.get(), FROM_HERE, Bind(&DoNothing)); 396 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 397 398 bad_thread.task_runner()->PostTask( 399 FROM_HERE, Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, 400 Unretained(&task_tracker_), Bind(&TryCancel, task_id))); 401 402 test_task_runner->RunUntilIdle(); 403 } 404 405 TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) { 406 scoped_refptr<TestSimpleTaskRunner> test_task_runner( 407 new TestSimpleTaskRunner()); 408 409 Thread bad_thread("bad thread"); 410 ASSERT_TRUE(bad_thread.Start()); 411 412 CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask( 413 test_task_runner.get(), FROM_HERE, Bind(&DoNothing)); 414 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 415 416 bad_thread.task_runner()->PostTask( 417 FROM_HERE, 418 Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, Unretained(&task_tracker_), 419 Bind(&CancelableTaskTracker::TryCancelAll))); 420 421 test_task_runner->RunUntilIdle(); 422 } 423 424 } // namespace base 425