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