OLD | NEW |
---|---|
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/common/cancelable_task_tracker.h" | 5 #include "chrome/common/cancelable_task_tracker.h" |
6 | 6 |
7 #include "base/basictypes.h" | 7 #include <cstddef> |
8 #include <deque> | |
9 | |
8 #include "base/bind.h" | 10 #include "base/bind.h" |
9 #include "base/callback.h" | 11 #include "base/bind_helpers.h" |
10 #include "base/memory/scoped_ptr.h" | 12 #include "base/compiler_specific.h" |
11 #include "base/synchronization/waitable_event.h" | 13 #include "base/location.h" |
14 #include "base/logging.h" | |
15 #include "base/memory/ref_counted.h" | |
16 #include "base/memory/weak_ptr.h" | |
17 #include "base/message_loop.h" | |
18 #include "base/run_loop.h" | |
19 #include "base/task_runner.h" | |
12 #include "base/threading/thread.h" | 20 #include "base/threading/thread.h" |
13 #include "testing/gtest/include/gtest/gtest.h" | 21 #include "testing/gtest/include/gtest/gtest.h" |
14 | 22 |
15 using base::Bind; | |
16 using base::Closure; | |
17 using base::Owned; | |
18 using base::TaskRunner; | |
19 using base::Thread; | |
20 using base::Unretained; | |
21 using base::WaitableEvent; | |
22 | |
23 namespace { | 23 namespace { |
24 | 24 |
25 class WaitableEventScoper { | 25 // Test TaskRunner implementation that simply stores posted tasks in a |
26 // queue. | |
27 // | |
28 // TOOD(akalin): Pull this out into its own file once something else | |
29 // needs it. | |
30 class FakeNonThreadSafeTaskRunner : public base::TaskRunner { | |
26 public: | 31 public: |
27 explicit WaitableEventScoper(WaitableEvent* event) : event_(event) {} | 32 // base::TaskRunner implementation. |
28 ~WaitableEventScoper() { | 33 // Stores posted tasks in a FIFO, ignoring |delay|.p |
kaiwang
2012/11/22 02:05:54
Remove trailing p
akalin
2012/11/22 07:00:26
Done.
| |
29 if (event_) | 34 virtual bool PostDelayedTask(const tracked_objects::Location& from_here, |
30 event_->Signal(); | 35 const base::Closure& task, |
31 } | 36 base::TimeDelta delay) OVERRIDE { |
37 tasks_.push_back(task); | |
38 return true; | |
39 } | |
40 | |
41 virtual bool RunsTasksOnCurrentThread() const OVERRIDE { | |
42 return true; | |
43 } | |
44 | |
45 size_t GetPendingTaskCount() const { | |
46 return tasks_.size(); | |
47 } | |
48 | |
49 void RunUntilIdle() { | |
50 // Use a while loop since a task may post more tasks. | |
51 while (!tasks_.empty()) { | |
52 base::Closure task = tasks_.front(); | |
53 tasks_.pop_front(); | |
54 task.Run(); | |
55 } | |
56 } | |
57 | |
58 protected: | |
59 virtual ~FakeNonThreadSafeTaskRunner() {} | |
60 | |
32 private: | 61 private: |
33 WaitableEvent* event_; | 62 std::deque<base::Closure> tasks_; |
34 DISALLOW_COPY_AND_ASSIGN(WaitableEventScoper); | |
35 }; | 63 }; |
36 | 64 |
37 class CancelableTaskTrackerTest : public testing::Test { | 65 class CancelableTaskTrackerTest : public testing::Test { |
38 protected: | 66 protected: |
39 CancelableTaskTrackerTest() | 67 virtual ~CancelableTaskTrackerTest() { |
40 : task_id_(CancelableTaskTracker::kBadTaskId), | 68 base::RunLoop run_loop; |
41 test_data_(0), | 69 run_loop.RunUntilIdle(); |
42 task_thread_start_event_(true, false) {} | 70 } |
43 | 71 |
44 virtual void SetUp() { | 72 private: |
45 task_thread_.reset(new Thread("task thread")); | 73 // Needs to be initialized before |task_tracker_|. |
kaiwang
2012/11/22 02:05:54
Why?
akalin
2012/11/22 07:00:26
Oops, looks like that's wrong. Only the methods n
| |
46 client_thread_.reset(new Thread("client thread")); | 74 MessageLoop message_loop_; |
47 task_thread_->Start(); | 75 |
48 client_thread_->Start(); | 76 protected: |
49 | 77 CancelableTaskTracker task_tracker_; |
50 task_thread_runner_ = task_thread_->message_loop_proxy(); | 78 }; |
51 client_thread_runner_ = client_thread_->message_loop_proxy(); | 79 |
52 | 80 void AddFailureAt(const tracked_objects::Location& location) { |
53 // Create tracker on client thread. | 81 ADD_FAILURE_AT(location.file_name(), location.line_number()); |
54 WaitableEvent tracker_created(true, false); | 82 } |
55 client_thread_runner_->PostTask( | 83 |
56 FROM_HERE, | 84 // Returns a closure that fails if run. |
57 Bind(&CancelableTaskTrackerTest::CreateTrackerOnClientThread, | 85 base::Closure MakeExpectedNotRunClosure( |
58 Unretained(this), &tracker_created)); | 86 const tracked_objects::Location& location) { |
59 tracker_created.Wait(); | 87 return base::Bind(&AddFailureAt, location); |
60 | 88 } |
61 // Block server thread so we can prepare the test. | 89 |
62 task_thread_runner_->PostTask( | 90 // A helper class for MakeExpectedRunClosure() that fails if it is |
63 FROM_HERE, | 91 // destroyed without Run() having been called. This class may be used |
64 Bind(&WaitableEvent::Wait, Unretained(&task_thread_start_event_))); | 92 // from multiple threads as long as Run() is called at most once |
65 } | 93 // before destruction. |
66 | 94 class RunChecker { |
67 virtual void TearDown() { | |
68 UnblockTaskThread(); | |
69 | |
70 // Destroy tracker on client thread. | |
71 WaitableEvent tracker_destroyed(true, false); | |
72 client_thread_runner_->PostTask( | |
73 FROM_HERE, | |
74 Bind(&CancelableTaskTrackerTest::DestroyTrackerOnClientThread, | |
75 Unretained(this), &tracker_destroyed)); | |
76 | |
77 // This will also wait for any pending tasks on client thread. | |
78 tracker_destroyed.Wait(); | |
79 | |
80 client_thread_->Stop(); | |
81 task_thread_->Stop(); | |
82 } | |
83 | |
84 void RunOnClientAndWait( | |
85 void (*func)(CancelableTaskTrackerTest*, WaitableEvent*)) { | |
86 WaitableEvent event(true, false); | |
87 client_thread_runner_->PostTask(FROM_HERE, | |
88 Bind(func, Unretained(this), &event)); | |
89 event.Wait(); | |
90 } | |
91 | |
92 public: | 95 public: |
93 // Client thread posts tasks and runs replies. | 96 explicit RunChecker(const tracked_objects::Location& location) |
94 scoped_refptr<TaskRunner> client_thread_runner_; | 97 : location_(location), |
95 | 98 called_(false) {} |
96 // Task thread runs tasks. | 99 |
97 scoped_refptr<TaskRunner> task_thread_runner_; | 100 ~RunChecker() { |
98 | 101 if (!called_) { |
99 // |tracker_| can only live on client thread. | 102 ADD_FAILURE_AT(location_.file_name(), location_.line_number()); |
100 scoped_ptr<CancelableTaskTracker> tracker_; | 103 } |
101 | 104 } |
102 CancelableTaskTracker::TaskId task_id_; | 105 |
103 | 106 void Run() { |
104 void UnblockTaskThread() { | 107 called_ = true; |
105 task_thread_start_event_.Signal(); | |
106 } | |
107 | |
108 ////////////////////////////////////////////////////////////////////////////// | |
109 // Testing data and related functions | |
110 int test_data_; // Defaults to 0. | |
111 | |
112 Closure IncreaseTestDataAndSignalClosure(WaitableEvent* event) { | |
113 return Bind(&CancelableTaskTrackerTest::IncreaseDataAndSignal, | |
114 &test_data_, event); | |
115 } | |
116 | |
117 Closure IncreaseTestDataIfNotCanceledAndSignalClosure( | |
118 const CancelableTaskTracker::IsCanceledCallback& is_canceled_cb, | |
119 WaitableEvent* event) { | |
120 return Bind(&CancelableTaskTrackerTest::IncreaseDataIfNotCanceledAndSignal, | |
121 &test_data_, is_canceled_cb, event); | |
122 } | |
123 | |
124 Closure DecreaseTestDataClosure(WaitableEvent* event) { | |
125 return Bind(&CancelableTaskTrackerTest::DecreaseData, | |
126 Owned(new WaitableEventScoper(event)), &test_data_); | |
127 } | 108 } |
128 | 109 |
129 private: | 110 private: |
130 void CreateTrackerOnClientThread(WaitableEvent* event) { | 111 tracked_objects::Location location_; |
131 tracker_.reset(new CancelableTaskTracker()); | 112 bool called_; |
132 event->Signal(); | |
133 } | |
134 | |
135 void DestroyTrackerOnClientThread(WaitableEvent* event) { | |
136 tracker_.reset(); | |
137 event->Signal(); | |
138 } | |
139 | |
140 static void IncreaseDataAndSignal(int* data, WaitableEvent* event) { | |
141 (*data)++; | |
142 if (event) | |
143 event->Signal(); | |
144 } | |
145 | |
146 static void IncreaseDataIfNotCanceledAndSignal( | |
147 int* data, | |
148 const CancelableTaskTracker::IsCanceledCallback& is_canceled_cb, | |
149 WaitableEvent* event) { | |
150 if (!is_canceled_cb.Run()) | |
151 (*data)++; | |
152 if (event) | |
153 event->Signal(); | |
154 } | |
155 | |
156 static void DecreaseData(WaitableEventScoper* event_scoper, int* data) { | |
157 (*data) -= 2; | |
158 } | |
159 | |
160 scoped_ptr<Thread> client_thread_; | |
161 scoped_ptr<Thread> task_thread_; | |
162 | |
163 WaitableEvent task_thread_start_event_; | |
164 }; | 113 }; |
165 | 114 |
166 #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST | 115 // Returns a closure that fails on destruction if it hasn't been run. |
167 | 116 base::Closure MakeExpectedRunClosure( |
168 typedef CancelableTaskTrackerTest CancelableTaskTrackerDeathTest; | 117 const tracked_objects::Location& location) { |
118 return base::Bind(&RunChecker::Run, base::Owned(new RunChecker(location))); | |
119 } | |
120 | |
121 // With the task tracker, post a task, a task with a reply, and get a | |
122 // new task id without canceling any of them. The tasks and the reply | |
123 // should run and the "is canceled" callback should return false. | |
124 TEST_F(CancelableTaskTrackerTest, NoCancel) { | |
125 base::Thread worker_thread("worker thread"); | |
126 ASSERT_TRUE(worker_thread.Start()); | |
127 | |
128 ignore_result( | |
129 task_tracker_.PostTask( | |
130 worker_thread.message_loop_proxy(), | |
131 FROM_HERE, | |
132 MakeExpectedRunClosure(FROM_HERE))); | |
133 | |
134 ignore_result( | |
135 task_tracker_.PostTaskAndReply( | |
136 worker_thread.message_loop_proxy(), | |
137 FROM_HERE, | |
138 MakeExpectedRunClosure(FROM_HERE), | |
139 MakeExpectedRunClosure(FROM_HERE))); | |
140 | |
141 CancelableTaskTracker::IsCanceledCallback is_canceled; | |
142 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); | |
143 | |
144 worker_thread.Stop(); | |
145 | |
146 base::RunLoop run_loop; | |
147 run_loop.RunUntilIdle(); | |
kaiwang
2012/11/22 02:05:54
already have this in Test destructor, necessary to
akalin
2012/11/22 07:00:26
This one is necessary (since the EXPECT below depe
| |
148 | |
149 EXPECT_FALSE(is_canceled.Run()); | |
150 } | |
151 | |
152 // Post a task with the task tracker but cancel it before running the | |
153 // task runner. The task should not run. | |
154 TEST_F(CancelableTaskTrackerTest, CancelPostedTask) { | |
155 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( | |
156 new FakeNonThreadSafeTaskRunner()); | |
157 | |
158 CancelableTaskTracker::TaskId task_id = | |
159 task_tracker_.PostTask( | |
160 fake_task_runner.get(), | |
161 FROM_HERE, | |
162 MakeExpectedNotRunClosure(FROM_HERE)); | |
163 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); | |
164 | |
165 EXPECT_EQ(1U, fake_task_runner->GetPendingTaskCount()); | |
166 | |
167 task_tracker_.TryCancel(task_id); | |
168 | |
169 fake_task_runner->RunUntilIdle(); | |
170 } | |
171 | |
172 // Post a task with reply with the task tracker and cancel it before | |
173 // running the task runner. Neither the task nor the reply should | |
174 // run. | |
175 TEST_F(CancelableTaskTrackerTest, CancelPostedTaskAndReply) { | |
176 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( | |
177 new FakeNonThreadSafeTaskRunner()); | |
178 | |
179 CancelableTaskTracker::TaskId task_id = | |
180 task_tracker_.PostTaskAndReply( | |
181 fake_task_runner.get(), | |
182 FROM_HERE, | |
183 MakeExpectedNotRunClosure(FROM_HERE), | |
184 MakeExpectedNotRunClosure(FROM_HERE)); | |
185 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); | |
186 | |
187 task_tracker_.TryCancel(task_id); | |
188 | |
189 fake_task_runner->RunUntilIdle(); | |
190 } | |
191 | |
192 // Post a task with reply with the task tracker and cancel it after | |
193 // running the task runner but before running the current message | |
194 // loop. The task should run but the reply should not. | |
195 TEST_F(CancelableTaskTrackerTest, CancelReply) { | |
196 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( | |
197 new FakeNonThreadSafeTaskRunner()); | |
198 | |
199 CancelableTaskTracker::TaskId task_id = | |
200 task_tracker_.PostTaskAndReply( | |
201 fake_task_runner.get(), | |
202 FROM_HERE, | |
203 MakeExpectedRunClosure(FROM_HERE), | |
204 MakeExpectedNotRunClosure(FROM_HERE)); | |
205 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); | |
206 | |
207 fake_task_runner->RunUntilIdle(); | |
208 | |
209 task_tracker_.TryCancel(task_id); | |
210 | |
211 base::RunLoop run_loop; | |
212 run_loop.RunUntilIdle(); | |
kaiwang
2012/11/22 02:05:54
same here
akalin
2012/11/22 07:00:26
Done.
| |
213 } | |
214 | |
215 // Post a task with reply with the task tracker on a worker thread and | |
216 // cancel it before running the current message loop. The task should | |
217 // run but the reply should not. | |
218 TEST_F(CancelableTaskTrackerTest, CancelReplyDifferentThread) { | |
219 base::Thread worker_thread("worker thread"); | |
220 ASSERT_TRUE(worker_thread.Start()); | |
221 | |
222 CancelableTaskTracker::TaskId task_id = | |
223 task_tracker_.PostTaskAndReply( | |
224 worker_thread.message_loop_proxy(), | |
225 FROM_HERE, | |
226 base::Bind(&base::DoNothing), | |
227 MakeExpectedNotRunClosure(FROM_HERE)); | |
228 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); | |
229 | |
230 task_tracker_.TryCancel(task_id); | |
231 | |
232 worker_thread.Stop(); | |
233 | |
234 base::RunLoop run_loop; | |
235 run_loop.RunUntilIdle(); | |
236 } | |
237 | |
238 void ExpectIsCanceled( | |
239 const CancelableTaskTracker::IsCanceledCallback& is_canceled, | |
240 bool expected_is_canceled) { | |
241 EXPECT_EQ(expected_is_canceled, is_canceled.Run()); | |
242 } | |
243 | |
244 // Create a new task ID and check its status on a separate thread | |
245 // before and after canceling. The is-canceled callback should be | |
246 // thread-safe (i.e., nothing should blow up). | |
247 TEST_F(CancelableTaskTrackerTest, NewTrackedTaskIdDifferentThread) { | |
248 CancelableTaskTracker::IsCanceledCallback is_canceled; | |
249 CancelableTaskTracker::TaskId task_id = | |
250 task_tracker_.NewTrackedTaskId(&is_canceled); | |
251 | |
252 EXPECT_FALSE(is_canceled.Run()); | |
253 | |
254 base::Thread other_thread("other thread"); | |
255 ASSERT_TRUE(other_thread.Start()); | |
256 other_thread.message_loop_proxy()->PostTask( | |
257 FROM_HERE, | |
258 base::Bind(&ExpectIsCanceled, is_canceled, false)); | |
259 other_thread.Stop(); | |
260 | |
261 task_tracker_.TryCancel(task_id); | |
262 | |
263 ASSERT_TRUE(other_thread.Start()); | |
264 other_thread.message_loop_proxy()->PostTask( | |
265 FROM_HERE, | |
266 base::Bind(&ExpectIsCanceled, is_canceled, true)); | |
267 other_thread.Stop(); | |
268 } | |
269 | |
270 // With the task tracker, post a task, a task with a reply, get a new | |
271 // task id, and then cancel all of them. None of the tasks nor the | |
272 // reply should run and the "is canceled" callback should return | |
273 // true. | |
274 TEST_F(CancelableTaskTrackerTest, CancelAll) { | |
275 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( | |
276 new FakeNonThreadSafeTaskRunner()); | |
277 | |
278 ignore_result( | |
279 task_tracker_.PostTask( | |
280 fake_task_runner, | |
281 FROM_HERE, | |
282 MakeExpectedNotRunClosure(FROM_HERE))); | |
283 | |
284 ignore_result( | |
285 task_tracker_.PostTaskAndReply( | |
286 fake_task_runner, | |
287 FROM_HERE, | |
288 MakeExpectedNotRunClosure(FROM_HERE), | |
289 MakeExpectedNotRunClosure(FROM_HERE))); | |
290 | |
291 CancelableTaskTracker::IsCanceledCallback is_canceled; | |
292 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); | |
293 | |
294 task_tracker_.TryCancelAll(); | |
295 | |
296 fake_task_runner->RunUntilIdle(); | |
297 | |
298 base::RunLoop run_loop; | |
299 run_loop.RunUntilIdle(); | |
300 | |
301 EXPECT_TRUE(is_canceled.Run()); | |
302 } | |
303 | |
304 // With the task tracker, post a task, a task with a reply, get a new | |
305 // task id, and then cancel all of them. None of the tasks nor the | |
306 // reply should run and the "is canceled" callback should return | |
307 // true. | |
308 TEST_F(CancelableTaskTrackerTest, DestructionCancelsAll) { | |
309 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( | |
310 new FakeNonThreadSafeTaskRunner()); | |
311 | |
312 CancelableTaskTracker::IsCanceledCallback is_canceled; | |
313 | |
314 { | |
315 // Create another task tracker with a smaller scope. | |
316 CancelableTaskTracker task_tracker; | |
317 | |
318 ignore_result( | |
319 task_tracker.PostTask( | |
320 fake_task_runner, | |
321 FROM_HERE, | |
322 MakeExpectedNotRunClosure(FROM_HERE))); | |
323 | |
324 ignore_result( | |
325 task_tracker.PostTaskAndReply( | |
326 fake_task_runner, | |
327 FROM_HERE, | |
328 MakeExpectedNotRunClosure(FROM_HERE), | |
329 MakeExpectedNotRunClosure(FROM_HERE))); | |
330 | |
331 ignore_result(task_tracker_.NewTrackedTaskId(&is_canceled)); | |
332 } | |
333 | |
334 fake_task_runner->RunUntilIdle(); | |
335 | |
336 base::RunLoop run_loop; | |
337 run_loop.RunUntilIdle(); | |
338 | |
339 EXPECT_FALSE(is_canceled.Run()); | |
340 } | |
341 | |
342 // The death tests below make sure that calling task tracker member | |
343 // functions from a thread different from its owner thread DCHECKs in | |
344 // debug mode. | |
345 | |
346 class CancelableTaskTrackerDeathTest : public CancelableTaskTrackerTest { | |
347 protected: | |
348 CancelableTaskTrackerDeathTest() { | |
349 // The default style "fast" does not support multi-threaded tests. | |
350 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; | |
351 } | |
352 | |
353 virtual ~CancelableTaskTrackerDeathTest() {} | |
354 }; | |
355 | |
356 // Duplicated from base/threading/thread_checker.h so that we can be | |
357 // good citizens there and undef the macro. | |
358 #if !defined(NDEBUG) || defined(DCHECK_ALWAYS_ON) | |
359 #define ENABLE_THREAD_CHECKER 1 | |
360 #else | |
361 #define ENABLE_THREAD_CHECKER 0 | |
362 #endif | |
kaiwang
2012/11/22 02:05:54
I suggest to directly use ENABLE_THREAD_CHECKER an
akalin
2012/11/22 07:00:26
Can't; note that ENABLE_THREAD_CHECKER is #undef'e
| |
363 | |
364 // Runs |fn| with |task_tracker|, expecting it to crash in debug mode. | |
365 void MaybeRunDeadlyTaskTrackerMemberFunction( | |
366 CancelableTaskTracker* task_tracker, | |
367 const base::Callback<void(CancelableTaskTracker*)>& fn) { | |
368 // CancelableTask uses DCHECKs with its ThreadChecker (itself only | |
369 // enabled in debug mode). | |
370 #if ENABLE_THREAD_CHECKER | |
371 EXPECT_DEATH_IF_SUPPORTED(fn.Run(task_tracker), ""); | |
372 #endif | |
373 } | |
374 | |
375 void PostDoNothingTask(CancelableTaskTracker* task_tracker) { | |
376 ignore_result( | |
377 task_tracker->PostTask( | |
378 scoped_refptr<FakeNonThreadSafeTaskRunner>( | |
379 new FakeNonThreadSafeTaskRunner()), | |
380 FROM_HERE, base::Bind(&base::DoNothing))); | |
381 } | |
169 | 382 |
170 TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) { | 383 TEST_F(CancelableTaskTrackerDeathTest, PostFromDifferentThread) { |
171 // The default style "fast" does not support multi-threaded tests. | 384 base::Thread bad_thread("bad thread"); |
172 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; | 385 ASSERT_TRUE(bad_thread.Start()); |
173 | 386 |
174 EXPECT_DEATH( | 387 bad_thread.message_loop_proxy()->PostTask( |
175 tracker_->PostTask(task_thread_runner_, | |
176 FROM_HERE, | |
177 DecreaseTestDataClosure(NULL)), | |
178 ""); | |
179 } | |
180 | |
181 void CancelOnDifferentThread_Test(CancelableTaskTrackerTest* test, | |
182 WaitableEvent* event) { | |
183 test->task_id_ = test->tracker_->PostTask( | |
184 test->task_thread_runner_, | |
185 FROM_HERE, | 388 FROM_HERE, |
186 test->DecreaseTestDataClosure(event)); | 389 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, |
187 EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | 390 base::Unretained(&task_tracker_), |
188 | 391 base::Bind(&PostDoNothingTask))); |
189 // Canceling a non-existed task is noop. | 392 } |
190 test->tracker_->TryCancel(test->task_id_ + 1); | 393 |
191 | 394 void TryCancel(CancelableTaskTracker::TaskId task_id, |
192 test->UnblockTaskThread(); | 395 CancelableTaskTracker* task_tracker) { |
396 task_tracker->TryCancel(task_id); | |
193 } | 397 } |
194 | 398 |
195 TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) { | 399 TEST_F(CancelableTaskTrackerDeathTest, CancelOnDifferentThread) { |
196 // The default style "fast" does not support multi-threaded tests. | 400 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( |
197 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; | 401 new FakeNonThreadSafeTaskRunner()); |
198 | 402 |
199 // Post a task and we'll try canceling it on a different thread. | 403 base::Thread bad_thread("bad thread"); |
200 RunOnClientAndWait(&CancelOnDifferentThread_Test); | 404 ASSERT_TRUE(bad_thread.Start()); |
201 | 405 |
202 // Canceling on the wrong thread. | 406 CancelableTaskTracker::TaskId task_id = |
203 EXPECT_DEATH(tracker_->TryCancel(task_id_), ""); | 407 task_tracker_.PostTask( |
204 | 408 fake_task_runner.get(), |
205 // Even canceling a non-existant task will crash. | 409 FROM_HERE, |
206 EXPECT_DEATH(tracker_->TryCancel(task_id_ + 1), ""); | 410 base::Bind(&base::DoNothing)); |
207 } | 411 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); |
208 | 412 |
209 void TrackerCancelAllOnDifferentThread_Test( | 413 bad_thread.message_loop_proxy()->PostTask( |
210 CancelableTaskTrackerTest* test, WaitableEvent* event) { | |
211 test->task_id_ = test->tracker_->PostTask( | |
212 test->task_thread_runner_, | |
213 FROM_HERE, | 414 FROM_HERE, |
214 test->DecreaseTestDataClosure(event)); | 415 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, |
215 EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | 416 base::Unretained(&task_tracker_), |
216 test->UnblockTaskThread(); | 417 base::Bind(&TryCancel, task_id))); |
217 } | 418 |
218 | 419 fake_task_runner->RunUntilIdle(); |
219 TEST_F(CancelableTaskTrackerDeathTest, TrackerCancelAllOnDifferentThread) { | 420 } |
220 // The default style "fast" does not support multi-threaded tests. | 421 |
221 ::testing::FLAGS_gtest_death_test_style = "threadsafe"; | 422 TEST_F(CancelableTaskTrackerDeathTest, CancelAllOnDifferentThread) { |
222 | 423 scoped_refptr<FakeNonThreadSafeTaskRunner> fake_task_runner( |
223 // |tracker_| can only live on client thread. | 424 new FakeNonThreadSafeTaskRunner()); |
224 EXPECT_DEATH(tracker_.reset(), ""); | 425 |
225 | 426 base::Thread bad_thread("bad thread"); |
226 RunOnClientAndWait(&TrackerCancelAllOnDifferentThread_Test); | 427 ASSERT_TRUE(bad_thread.Start()); |
227 | 428 |
228 EXPECT_DEATH(tracker_->TryCancelAll(), ""); | 429 CancelableTaskTracker::TaskId task_id = |
229 EXPECT_DEATH(tracker_.reset(), ""); | 430 task_tracker_.PostTask( |
230 } | 431 fake_task_runner.get(), |
231 | 432 FROM_HERE, |
232 #endif // (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && | 433 base::Bind(&base::DoNothing)); |
233 // GTEST_HAS_DEATH_TEST | 434 EXPECT_NE(CancelableTaskTracker::kBadTaskId, task_id); |
234 | 435 |
235 void Canceled_Test(CancelableTaskTrackerTest* test, WaitableEvent* event) { | 436 bad_thread.message_loop_proxy()->PostTask( |
236 test->task_id_ = test->tracker_->PostTask( | |
237 test->task_thread_runner_, | |
238 FROM_HERE, | 437 FROM_HERE, |
239 test->DecreaseTestDataClosure(event)); | 438 base::Bind(&MaybeRunDeadlyTaskTrackerMemberFunction, |
240 EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | 439 base::Unretained(&task_tracker_), |
241 | 440 base::Bind(&CancelableTaskTracker::TryCancelAll))); |
242 test->tracker_->TryCancel(test->task_id_); | 441 |
243 test->UnblockTaskThread(); | 442 fake_task_runner->RunUntilIdle(); |
244 } | |
245 | |
246 TEST_F(CancelableTaskTrackerTest, Canceled) { | |
247 RunOnClientAndWait(&Canceled_Test); | |
248 EXPECT_EQ(0, test_data_); | |
249 } | |
250 | |
251 void SignalAndWaitThenIncrease(WaitableEvent* start_event, | |
252 WaitableEvent* continue_event, | |
253 int* data) { | |
254 start_event->Signal(); | |
255 continue_event->Wait(); | |
256 (*data)++; | |
257 } | |
258 | |
259 void CancelWhileTaskRunning_Test(CancelableTaskTrackerTest* test, | |
260 WaitableEvent* event) { | |
261 WaitableEvent task_start_event(true, false); | |
262 WaitableEvent* task_continue_event = new WaitableEvent(true, false); | |
263 | |
264 test->task_id_ = test->tracker_->PostTaskAndReply( | |
265 test->task_thread_runner_, | |
266 FROM_HERE, | |
267 Bind(&SignalAndWaitThenIncrease, | |
268 &task_start_event, Owned(task_continue_event), &test->test_data_), | |
269 test->DecreaseTestDataClosure(event)); | |
270 EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | |
271 | |
272 test->UnblockTaskThread(); | |
273 task_start_event.Wait(); | |
274 | |
275 // Now task is running. Let's try to cancel. | |
276 test->tracker_->TryCancel(test->task_id_); | |
277 | |
278 // Let task continue. | |
279 task_continue_event->Signal(); | |
280 } | |
281 | |
282 TEST_F(CancelableTaskTrackerTest, CancelWhileTaskRunning) { | |
283 RunOnClientAndWait(&CancelWhileTaskRunning_Test); | |
284 | |
285 // Task will continue running but reply will be canceled. | |
286 EXPECT_EQ(1, test_data_); | |
287 } | |
288 | |
289 void NotCanceled_Test(CancelableTaskTrackerTest* test, WaitableEvent* event) { | |
290 test->task_id_ = test->tracker_->PostTaskAndReply( | |
291 test->task_thread_runner_, | |
292 FROM_HERE, | |
293 test->IncreaseTestDataAndSignalClosure(NULL), | |
294 test->DecreaseTestDataClosure(event)); | |
295 EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | |
296 | |
297 test->UnblockTaskThread(); | |
298 } | |
299 | |
300 TEST_F(CancelableTaskTrackerTest, NotCanceled) { | |
301 RunOnClientAndWait(&NotCanceled_Test); | |
302 EXPECT_EQ(-1, test_data_); | |
303 } | |
304 | |
305 void TrackerDestructed_Test(CancelableTaskTrackerTest* test, | |
306 WaitableEvent* event) { | |
307 test->task_id_ = test->tracker_->PostTaskAndReply( | |
308 test->task_thread_runner_, | |
309 FROM_HERE, | |
310 test->IncreaseTestDataAndSignalClosure(NULL), | |
311 test->DecreaseTestDataClosure(event)); | |
312 EXPECT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | |
313 | |
314 test->tracker_.reset(); | |
315 test->UnblockTaskThread(); | |
316 } | |
317 | |
318 TEST_F(CancelableTaskTrackerTest, TrackerDestructed) { | |
319 RunOnClientAndWait(&TrackerDestructed_Test); | |
320 EXPECT_EQ(0, test_data_); | |
321 } | |
322 | |
323 void TrackerDestructedAfterTask_Test(CancelableTaskTrackerTest* test, | |
324 WaitableEvent* event) { | |
325 WaitableEvent task_done_event(true, false); | |
326 test->task_id_ = test->tracker_->PostTaskAndReply( | |
327 test->task_thread_runner_, | |
328 FROM_HERE, | |
329 test->IncreaseTestDataAndSignalClosure(&task_done_event), | |
330 test->DecreaseTestDataClosure(event)); | |
331 ASSERT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | |
332 | |
333 test->UnblockTaskThread(); | |
334 | |
335 task_done_event.Wait(); | |
336 | |
337 // At this point, task is already finished on task thread but reply has not | |
338 // started yet (because this function is still running on client thread). | |
339 // Now delete the tracker to cancel reply. | |
340 test->tracker_.reset(); | |
341 } | |
342 | |
343 TEST_F(CancelableTaskTrackerTest, TrackerDestructedAfterTask) { | |
344 RunOnClientAndWait(&TrackerDestructedAfterTask_Test); | |
345 EXPECT_EQ(1, test_data_); | |
346 } | |
347 | |
348 void CheckTrackedTaskIdOnSameThread_Test(CancelableTaskTrackerTest* test, | |
349 WaitableEvent* event) { | |
350 CancelableTaskTracker::IsCanceledCallback is_canceled_cb; | |
351 test->task_id_ = test->tracker_->NewTrackedTaskId(&is_canceled_cb); | |
352 ASSERT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | |
353 | |
354 EXPECT_FALSE(is_canceled_cb.Run()); | |
355 | |
356 test->tracker_->TryCancel(test->task_id_); | |
357 EXPECT_TRUE(is_canceled_cb.Run()); | |
358 | |
359 test->task_id_ = test->tracker_->NewTrackedTaskId(&is_canceled_cb); | |
360 EXPECT_FALSE(is_canceled_cb.Run()); | |
361 | |
362 // Destroy tracker will cancel all tasks. | |
363 test->tracker_.reset(); | |
364 EXPECT_TRUE(is_canceled_cb.Run()); | |
365 | |
366 event->Signal(); | |
367 } | |
368 | |
369 TEST_F(CancelableTaskTrackerTest, CheckTrackedTaskIdOnSameThread) { | |
370 RunOnClientAndWait(&CheckTrackedTaskIdOnSameThread_Test); | |
371 } | |
372 | |
373 void CheckTrackedTaskIdOnDifferentThread_Test(CancelableTaskTrackerTest* test, | |
374 WaitableEvent* event) { | |
375 CancelableTaskTracker::IsCanceledCallback is_canceled_cb; | |
376 test->task_id_ = test->tracker_->NewTrackedTaskId(&is_canceled_cb); | |
377 ASSERT_NE(CancelableTaskTracker::kBadTaskId, test->task_id_); | |
378 | |
379 // Post task to task thread. | |
380 test->task_thread_runner_->PostTask( | |
381 FROM_HERE, | |
382 test->IncreaseTestDataIfNotCanceledAndSignalClosure(is_canceled_cb, | |
383 event)); | |
384 is_canceled_cb.Reset(); // So the one in task thread runner is the last ref, | |
385 // and will be destroyed on task thread. | |
386 | |
387 test->tracker_->TryCancel(test->task_id_); | |
388 test->UnblockTaskThread(); | |
389 } | |
390 | |
391 TEST_F(CancelableTaskTrackerTest, CheckTrackedTaskIdOnDifferentThread) { | |
392 RunOnClientAndWait(&CheckTrackedTaskIdOnDifferentThread_Test); | |
393 EXPECT_EQ(0, test_data_); | |
394 } | 443 } |
395 | 444 |
396 } // namespace | 445 } // namespace |
OLD | NEW |