1 // Copyright (c) 2011 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 "chrome/common/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 { 23 24 class CancelableTaskTrackerTest : public testing::Test { 25 protected: 26 virtual ~CancelableTaskTrackerTest() { 27 RunCurrentLoopUntilIdle(); 28 } 29 30 void RunCurrentLoopUntilIdle() { 31 base::RunLoop run_loop; 32 run_loop.RunUntilIdle(); 33 } 34 35 CancelableTaskTracker task_tracker_; 36 37 private: 38 // Needed by CancelableTaskTracker methods. 39 base::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 base::Closure MakeExpectedNotRunClosure( 48 const tracked_objects::Location& location) { 49 return base::Bind(&AddFailureAt, location); 50 } 51 52 // A helper class for MakeExpectedRunClosure() that fails if it is 53 // destroyed without Run() having been called. This class may be used 54 // from multiple threads as long as Run() is called at most once 55 // before destruction. 56 class RunChecker { 57 public: 58 explicit RunChecker(const tracked_objects::Location& location) 59 : location_(location), 60 called_(false) {} 61 62 ~RunChecker() { 63 if (!called_) { 64 ADD_FAILURE_AT(location_.file_name(), location_.line_number()); 65 } 66 } 67 68 void Run() { 69 called_ = true; 70 } 71 72 private: 73 tracked_objects::Location location_; 74 bool called_; 75 }; 76 77 // Returns a closure that fails on destruction if it hasn't been run. 78 base::Closure MakeExpectedRunClosure( 79 const tracked_objects::Location& location) { 80 return base::Bind(&RunChecker::Run, base::Owned(new RunChecker(location))); 81 } 82 83 // With the task tracker, post a task, a task with a reply, and get a 84 // new task id without canceling any of them. The tasks and the reply 85 // should run and the "is canceled" callback should return false. 86 TEST_F(CancelableTaskTrackerTest, NoCancel) { 87 base::Thread worker_thread("worker thread"); 88 ASSERT_TRUE(worker_thread.Start()); 89 90 ignore_result(task_tracker_.PostTask(worker_thread.message_loop_proxy().get(), 91 FROM_HERE, 92 MakeExpectedRunClosure(FROM_HERE))); 93 94 ignore_result( 95 task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(), 96 FROM_HERE, 97 MakeExpectedRunClosure(FROM_HERE), 98 MakeExpectedRunClosure(FROM_HERE))); 99 100 CancelableTaskTracker::IsCanceledCallback is_canceled; 101 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); 102 103 worker_thread.Stop(); 104 105 RunCurrentLoopUntilIdle(); 106 107 EXPECT_FALSE(is_canceled.Run()); 108 } 109 110 // Post a task with the task tracker but cancel it before running the 111 // task runner. The task should not run. 112 TEST_F(CancelableTaskTrackerTest, CancelPostedTask) { 113 scoped_refptr<base::TestSimpleTaskRunner> test_task_runner( 114 new base::TestSimpleTaskRunner()); 115 116 CancelableTaskTracker::TaskId task_id = 117 task_tracker_.PostTask( 118 test_task_runner.get(), 119 FROM_HERE, 120 MakeExpectedNotRunClosure(FROM_HERE)); 121 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 122 123 EXPECT_EQ(1U, test_task_runner->GetPendingTasks().size()); 124 125 task_tracker_.TryCancel(task_id); 126 127 test_task_runner->RunUntilIdle(); 128 } 129 130 // Post a task with reply with the task tracker and cancel it before 131 // running the task runner. Neither the task nor the reply should 132 // run. 133 TEST_F(CancelableTaskTrackerTest, CancelPostedTaskAndReply) { 134 scoped_refptr<base::TestSimpleTaskRunner> test_task_runner( 135 new base::TestSimpleTaskRunner()); 136 137 CancelableTaskTracker::TaskId task_id = 138 task_tracker_.PostTaskAndReply( 139 test_task_runner.get(), 140 FROM_HERE, 141 MakeExpectedNotRunClosure(FROM_HERE), 142 MakeExpectedNotRunClosure(FROM_HERE)); 143 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 144 145 task_tracker_.TryCancel(task_id); 146 147 test_task_runner->RunUntilIdle(); 148 } 149 150 // Post a task with reply with the task tracker and cancel it after 151 // running the task runner but before running the current message 152 // loop. The task should run but the reply should not. 153 TEST_F(CancelableTaskTrackerTest, CancelReply) { 154 scoped_refptr<base::TestSimpleTaskRunner> test_task_runner( 155 new base::TestSimpleTaskRunner()); 156 157 CancelableTaskTracker::TaskId task_id = 158 task_tracker_.PostTaskAndReply( 159 test_task_runner.get(), 160 FROM_HERE, 161 MakeExpectedRunClosure(FROM_HERE), 162 MakeExpectedNotRunClosure(FROM_HERE)); 163 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 164 165 test_task_runner->RunUntilIdle(); 166 167 task_tracker_.TryCancel(task_id); 168 } 169 170 // Post a task with reply with the task tracker on a worker thread and 171 // cancel it before running the current message loop. The task should 172 // run but the reply should not. 173 TEST_F(CancelableTaskTrackerTest, CancelReplyDifferentThread) { 174 base::Thread worker_thread("worker thread"); 175 ASSERT_TRUE(worker_thread.Start()); 176 177 CancelableTaskTracker::TaskId task_id = 178 task_tracker_.PostTaskAndReply(worker_thread.message_loop_proxy().get(), 179 FROM_HERE, 180 base::Bind(&base::DoNothing), 181 MakeExpectedNotRunClosure(FROM_HERE)); 182 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 183 184 task_tracker_.TryCancel(task_id); 185 186 worker_thread.Stop(); 187 } 188 189 void ExpectIsCanceled( 190 const CancelableTaskTracker::IsCanceledCallback& is_canceled, 191 bool expected_is_canceled) { 192 EXPECT_EQ(expected_is_canceled, is_canceled.Run()); 193 } 194 195 // Create a new task ID and check its status on a separate thread 196 // before and after canceling. The is-canceled callback should be 197 // thread-safe (i.e., nothing should blow up). 198 TEST_F(CancelableTaskTrackerTest, NewTrackedTaskIdDifferentThread) { 199 CancelableTaskTracker::IsCanceledCallback is_canceled; 200 CancelableTaskTracker::TaskId task_id = 201 task_tracker_.NewTrackedTaskId(&is_canceled); 202 203 EXPECT_FALSE(is_canceled.Run()); 204 205 base::Thread other_thread("other thread"); 206 ASSERT_TRUE(other_thread.Start()); 207 other_thread.message_loop_proxy()->PostTask( 208 FROM_HERE, 209 base::Bind(&ExpectIsCanceled, is_canceled, false)); 210 other_thread.Stop(); 211 212 task_tracker_.TryCancel(task_id); 213 214 ASSERT_TRUE(other_thread.Start()); 215 other_thread.message_loop_proxy()->PostTask( 216 FROM_HERE, 217 base::Bind(&ExpectIsCanceled, is_canceled, true)); 218 other_thread.Stop(); 219 } 220 221 // With the task tracker, post a task, a task with a reply, get a new 222 // task id, and then cancel all of them. None of the tasks nor the 223 // reply should run and the "is canceled" callback should return 224 // true. 225 TEST_F(CancelableTaskTrackerTest, CancelAll) { 226 scoped_refptr<base::TestSimpleTaskRunner> test_task_runner( 227 new base::TestSimpleTaskRunner()); 228 229 ignore_result(task_tracker_.PostTask( 230 test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE))); 231 232 ignore_result( 233 task_tracker_.PostTaskAndReply(test_task_runner.get(), 234 FROM_HERE, 235 MakeExpectedNotRunClosure(FROM_HERE), 236 MakeExpectedNotRunClosure(FROM_HERE))); 237 238 CancelableTaskTracker::IsCanceledCallback is_canceled; 239 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); 240 241 task_tracker_.TryCancelAll(); 242 243 test_task_runner->RunUntilIdle(); 244 245 RunCurrentLoopUntilIdle(); 246 247 EXPECT_TRUE(is_canceled.Run()); 248 } 249 250 // With the task tracker, post a task, a task with a reply, get a new 251 // task id, and then cancel all of them. None of the tasks nor the 252 // reply should run and the "is canceled" callback should return 253 // true. 254 TEST_F(CancelableTaskTrackerTest, DestructionCancelsAll) { 255 scoped_refptr<base::TestSimpleTaskRunner> test_task_runner( 256 new base::TestSimpleTaskRunner()); 257 258 CancelableTaskTracker::IsCanceledCallback is_canceled; 259 260 { 261 // Create another task tracker with a smaller scope. 262 CancelableTaskTracker task_tracker; 263 264 ignore_result(task_tracker.PostTask(test_task_runner.get(), 265 FROM_HERE, 266 MakeExpectedNotRunClosure(FROM_HERE))); 267 268 ignore_result( 269 task_tracker.PostTaskAndReply(test_task_runner.get(), 270 FROM_HERE, 271 MakeExpectedNotRunClosure(FROM_HERE), 272 MakeExpectedNotRunClosure(FROM_HERE))); 273 274 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); 275 } 276 277 test_task_runner->RunUntilIdle(); 278 279 RunCurrentLoopUntilIdle(); 280 281 EXPECT_FALSE(is_canceled.Run()); 282 } 283 284 // Post a task and cancel it. HasTrackedTasks() should return true 285 // from when the task is posted until the (do-nothing) reply task is 286 // flushed. 287 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPost) { 288 scoped_refptr<base::TestSimpleTaskRunner> test_task_runner( 289 new base::TestSimpleTaskRunner()); 290 291 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 292 293 ignore_result(task_tracker_.PostTask( 294 test_task_runner.get(), FROM_HERE, MakeExpectedNotRunClosure(FROM_HERE))); 295 296 task_tracker_.TryCancelAll(); 297 298 test_task_runner->RunUntilIdle(); 299 300 EXPECT_TRUE(task_tracker_.HasTrackedTasks()); 301 302 RunCurrentLoopUntilIdle(); 303 304 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 305 } 306 307 // Post a task with a reply and cancel it. HasTrackedTasks() should 308 // return true from when the task is posted until it is canceled. 309 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksPostWithReply) { 310 scoped_refptr<base::TestSimpleTaskRunner> test_task_runner( 311 new base::TestSimpleTaskRunner()); 312 313 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 314 315 ignore_result( 316 task_tracker_.PostTaskAndReply(test_task_runner.get(), 317 FROM_HERE, 318 MakeExpectedNotRunClosure(FROM_HERE), 319 MakeExpectedNotRunClosure(FROM_HERE))); 320 321 task_tracker_.TryCancelAll(); 322 323 test_task_runner->RunUntilIdle(); 324 325 EXPECT_TRUE(task_tracker_.HasTrackedTasks()); 326 327 RunCurrentLoopUntilIdle(); 328 329 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 330 } 331 332 // Create a new tracked task ID. HasTrackedTasks() should return true 333 // until the IsCanceledCallback is destroyed. 334 TEST_F(CancelableTaskTrackerTest, HasTrackedTasksIsCancelled) { 335 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 336 337 CancelableTaskTracker::IsCanceledCallback is_canceled; 338 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); 339 340 task_tracker_.TryCancelAll(); 341 342 EXPECT_TRUE(task_tracker_.HasTrackedTasks()); 343 344 is_canceled.Reset(); 345 346 EXPECT_FALSE(task_tracker_.HasTrackedTasks()); 347 } 348 349 // The death tests below make sure that calling task tracker member 350 // functions from a thread different from its owner thread DCHECKs in 351 // debug mode. 352 353 class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest { 354 protected: 355 CancelableTaskTrackerDeathTest() { 356 // The default style "fast" does not support multi-threaded tests. 357 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; 358 } 359 360 virtual ~CancelableTaskTrackerDeathTest() {} 361 }; 362 363 // Duplicated from base/threading/thread_checker.h so that we can be 364 // good citizens there and undef the macro. 365 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) 366 #define ENABLE_THREAD_CHECKER 1 367 #else 368 #define ENABLE_THREAD_CHECKER 0 369 #endif 370 371 // Runs |fn| with |task_tracker|, expecting it to crash in debug mode. 372 void MaybeRunDeadlyTaskTrackerMemberFunction( 373 CancelableTaskTracker* task_tracker, 374 const base::Callback<void(CancelableTaskTracker*)>& fn) { 375 // CancelableTask uses DCHECKs with its ThreadChecker (itself only 376 // enabled in debug mode). 377 #if ENABLE_THREAD_CHECKER 378 EXPECT_DEATH_IF_SUPPORTED(fn.Run(task_tracker), ""); 379 #endif 380 } 381 382 void PostDoNothingTask(CancelableTaskTracker* task_tracker) { 383 ignore_result( 384 task_tracker->PostTask(scoped_refptr<base::TestSimpleTaskRunner>( 385 new base::TestSimpleTaskRunner()) 386 .get(), 387 FROM_HERE, 388 base::Bind(&base::DoNothing))); 389 } 390 391 TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) { 392 base::Thread bad_thread("bad thread"); 393 ASSERT_TRUE(bad_thread.Start()); 394 395 bad_thread.message_loop_proxy()->PostTask( 396 FROM_HERE, 397 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, 398 base::Unretained(&task_tracker_), 399 base::Bind(&PostDoNothingTask))); 400 } 401 402 void TryCancel(CancelableTaskTracker::TaskId task_id, 403 CancelableTaskTracker* task_tracker) { 404 task_tracker->TryCancel(task_id); 405 } 406 407 TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) { 408 scoped_refptr<base::TestSimpleTaskRunner> test_task_runner( 409 new base::TestSimpleTaskRunner()); 410 411 base::Thread bad_thread("bad thread"); 412 ASSERT_TRUE(bad_thread.Start()); 413 414 CancelableTaskTracker::TaskId task_id = 415 task_tracker_.PostTask( 416 test_task_runner.get(), 417 FROM_HERE, 418 base::Bind(&base::DoNothing)); 419 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 420 421 bad_thread.message_loop_proxy()->PostTask( 422 FROM_HERE, 423 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, 424 base::Unretained(&task_tracker_), 425 base::Bind(&TryCancel, task_id))); 426 427 test_task_runner->RunUntilIdle(); 428 } 429 430 TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) { 431 scoped_refptr<base::TestSimpleTaskRunner> test_task_runner( 432 new base::TestSimpleTaskRunner()); 433 434 base::Thread bad_thread("bad thread"); 435 ASSERT_TRUE(bad_thread.Start()); 436 437 CancelableTaskTracker::TaskId task_id = 438 task_tracker_.PostTask( 439 test_task_runner.get(), 440 FROM_HERE, 441 base::Bind(&base::DoNothing)); 442 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); 443 444 bad_thread.message_loop_proxy()->PostTask( 445 FROM_HERE, 446 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, 447 base::Unretained(&task_tracker_), 448 base::Bind(&CancelableTaskTracker::TryCancelAll))); 449 450 test_task_runner->RunUntilIdle(); 451 } 452 453 } // namespace 454