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/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" 21 22 namespace base { 23 24 namespace { 25 26 class CancelableTaskTrackerTest : public testing::Test { 27 protected: 28 virtual ~CancelableTaskTrackerTest() { 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.message_loop_proxy().get(), 88 FROM_HERE, 89 MakeExpectedRunClosure(FROM_HERE))); 90 91 ignore_result( 92 task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(), 93 FROM_HERE, 94 MakeExpectedRunClosure(FROM_HERE), 95 MakeExpectedRunClosure(FROM_HERE))); 96 97 CancelableTaskTracker::IsCanceledCallback is_canceled; 98 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); 99 100 worker_thread.Stop(); 101 102 RunCurrentLoopUntilIdle(); 103 104 EXPECT_FALSE(is_canceled.Run()); 105 } 106 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()); 112 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); 116 117 EXPECT_EQ(1U, test_task_runner->GetPendingTasks().size()); 118 119 task_tracker_.TryCancel(task_id); 120 121 test_task_runner->RunUntilIdle(); 122 } 123 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 126 // run. 127 TEST_F(CancelableTaskTrackerTest, CancelPostedTaskAndReply) { 128 scoped_refptr<TestSimpleTaskRunner> test_task_runner( 129 new TestSimpleTaskRunner()); 130 131 CancelableTaskTracker::TaskId task_id = 132 task_tracker_.PostTaskAndReply(test_task_runner.get(), 133 FROM_HERE, 134 MakeExpectedNotRunClosure(FROM_HERE), 135 MakeExpectedNotRunClosure(FROM_HERE)); 136 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 137 138 task_tracker_.TryCancel(task_id); 139 140 test_task_runner->RunUntilIdle(); 141 } 142 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()); 149 150 CancelableTaskTracker::TaskId task_id = 151 task_tracker_.PostTaskAndReply(test_task_runner.get(), 152 FROM_HERE, 153 MakeExpectedRunClosure(FROM_HERE), 154 MakeExpectedNotRunClosure(FROM_HERE)); 155 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 156 157 test_task_runner->RunUntilIdle(); 158 159 task_tracker_.TryCancel(task_id); 160 } 161 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()); 168 169 CancelableTaskTracker::TaskId task_id = 170 task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(), 171 FROM_HERE, 172 Bind(&DoNothing), 173 MakeExpectedNotRunClosure(FROM_HERE)); 174 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 175 176 task_tracker_.TryCancel(task_id); 177 178 worker_thread.Stop(); 179 } 180 181 void ExpectIsCanceled( 182 const CancelableTaskTracker::IsCanceledCallback& is_canceled, 183 bool expected_is_canceled) { 184 EXPECT_EQ(expected_is_canceled, is_canceled.Run()); 185 } 186 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); 194 195 EXPECT_FALSE(is_canceled.Run()); 196 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)); 201 other_thread.Stop(); 202 203 task_tracker_.TryCancel(task_id); 204 205 ASSERT_TRUE(other_thread.Start()); 206 other_thread.message_loop_proxy()->PostTask( 207 FROM_HERE, Bind(&ExpectIsCanceled, is_canceled, true)); 208 other_thread.Stop(); 209 } 210 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 214 // true. 215 TEST_F(CancelableTaskTrackerTest, CancelAll) { 216 scoped_refptr<TestSimpleTaskRunner> test_task_runner( 217 new TestSimpleTaskRunner()); 218 219 ignore_result(task_tracker_.PostTask( 220 test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE))); 221 222 ignore_result( 223 task_tracker_.PostTaskAndReply(test_task_runner.get(), 224 FROM_HERE, 225 MakeExpectedNotRunClosure(FROM_HERE), 226 MakeExpectedNotRunClosure(FROM_HERE))); 227 228 CancelableTaskTracker::IsCanceledCallback is_canceled; 229 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); 230 231 task_tracker_.TryCancelAll(); 232 233 test_task_runner->RunUntilIdle(); 234 235 RunCurrentLoopUntilIdle(); 236 237 EXPECT_TRUE(is_canceled.Run()); 238 } 239 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 243 // true. 244 TEST_F(CancelableTaskTrackerTest, DestructionCancelsAll) { 245 scoped_refptr<TestSimpleTaskRunner> test_task_runner( 246 new TestSimpleTaskRunner()); 247 248 CancelableTaskTracker::IsCanceledCallback is_canceled; 249 250 { 251 // Create another task tracker with a smaller scope. 252 CancelableTaskTracker task_tracker; 253 254 ignore_result(task_tracker.PostTask(test_task_runner.get(), 255 FROM_HERE, 256 MakeExpectedNotRunClosure(FROM_HERE))); 257 258 ignore_result( 259 task_tracker.PostTaskAndReply(test_task_runner.get(), 260 FROM_HERE, 261 MakeExpectedNotRunClosure(FROM_HERE), 262 MakeExpectedNotRunClosure(FROM_HERE))); 263 264 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); 265 } 266 267 test_task_runner->RunUntilIdle(); 268 269 RunCurrentLoopUntilIdle(); 270 271 EXPECT_FALSE(is_canceled.Run()); 272 } 273 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 276 // flushed. 277 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPost) { 278 scoped_refptr<TestSimpleTaskRunner> test_task_runner( 279 new TestSimpleTaskRunner()); 280 281 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 282 283 ignore_result(task_tracker_.PostTask( 284 test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE))); 285 286 task_tracker_.TryCancelAll(); 287 288 test_task_runner->RunUntilIdle(); 289 290 EXPECT_TRUE(task_tracker_.HasTrackedTasks()); 291 292 RunCurrentLoopUntilIdle(); 293 294 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 295 } 296 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()); 302 303 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 304 305 ignore_result( 306 task_tracker_.PostTaskAndReply(test_task_runner.get(), 307 FROM_HERE, 308 MakeExpectedNotRunClosure(FROM_HERE), 309 MakeExpectedNotRunClosure(FROM_HERE))); 310 311 task_tracker_.TryCancelAll(); 312 313 test_task_runner->RunUntilIdle(); 314 315 EXPECT_TRUE(task_tracker_.HasTrackedTasks()); 316 317 RunCurrentLoopUntilIdle(); 318 319 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 320 } 321 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()); 326 327 CancelableTaskTracker::IsCanceledCallback is_canceled; 328 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); 329 330 task_tracker_.TryCancelAll(); 331 332 EXPECT_TRUE(task_tracker_.HasTrackedTasks()); 333 334 is_canceled.Reset(); 335 336 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 337 } 338 339 // The death tests below make sure that calling task tracker member 340 // functions from a thread different from its owner thread DCHECKs in 341 // debug mode. 342 343 class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest { 344 protected: 345 CancelableTaskTrackerDeathTest() { 346 // The default style "fast" does not support multi-threaded tests. 347 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; 348 } 349 350 virtual ~CancelableTaskTrackerDeathTest() {} 351 }; 352 353 // Duplicated from base/threading/thread_checker.h so that we can be 354 // good citizens there and undef the macro. 355 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) 356 #define ENABLE_THREAD_CHECKER 1 357 #else 358 #define ENABLE_THREAD_CHECKER 0 359 #endif 360 361 // Runs |fn| with |task_tracker|, expecting it to crash in debug mode. 362 void MaybeRunDeadlyTaskTrackerMemberFunction( 363 CancelableTaskTracker* task_tracker, 364 const Callback<void(CancelableTaskTracker*)>& fn) { 365 // CancelableTask uses DCHECKs with its ThreadChecker (itself only 366 // enabled in debug mode). 367 #if ENABLE_THREAD_CHECKER 368 EXPECT_DEATH_IF_SUPPORTED(fn.Run(task_tracker), ""); 369 #endif 370 } 371 372 void PostDoNothingTask(CancelableTaskTracker* task_tracker) { 373 ignore_result(task_tracker->PostTask( 374 scoped_refptr<TestSimpleTaskRunner>(new TestSimpleTaskRunner()).get(), 375 FROM_HERE, 376 Bind(&DoNothing))); 377 } 378 379 TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) { 380 Thread bad_thread("bad thread"); 381 ASSERT_TRUE(bad_thread.Start()); 382 383 bad_thread.message_loop_proxy()->PostTask( 384 FROM_HERE, 385 Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, 386 Unretained(&task_tracker_), 387 Bind(&PostDoNothingTask))); 388 } 389 390 void TryCancel(CancelableTaskTracker::TaskId task_id, 391 CancelableTaskTracker* task_tracker) { 392 task_tracker->TryCancel(task_id); 393 } 394 395 TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) { 396 scoped_refptr<TestSimpleTaskRunner> test_task_runner( 397 new TestSimpleTaskRunner()); 398 399 Thread bad_thread("bad thread"); 400 ASSERT_TRUE(bad_thread.Start()); 401 402 CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask( 403 test_task_runner.get(), FROM_HERE, Bind(&DoNothing)); 404 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 405 406 bad_thread.message_loop_proxy()->PostTask( 407 FROM_HERE, 408 Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, 409 Unretained(&task_tracker_), 410 Bind(&TryCancel, task_id))); 411 412 test_task_runner->RunUntilIdle(); 413 } 414 415 TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) { 416 scoped_refptr<TestSimpleTaskRunner> test_task_runner( 417 new TestSimpleTaskRunner()); 418 419 Thread bad_thread("bad thread"); 420 ASSERT_TRUE(bad_thread.Start()); 421 422 CancelableTaskTracker::TaskId task_id = task_tracker_.PostTask( 423 test_task_runner.get(), FROM_HERE, Bind(&DoNothing)); 424 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 425 426 bad_thread.message_loop_proxy()->PostTask( 427 FROM_HERE, 428 Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, 429 Unretained(&task_tracker_), 430 Bind(&CancelableTaskTracker::TryCancelAll))); 431 432 test_task_runner->RunUntilIdle(); 433 } 434 435 } // namespace base 436