| OLD | NEW |
| (Empty) |
| 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 | |
| OLD | NEW |