OLD | NEW |
1 // Copyright 2016 The Chromium Authors. All rights reserved. | 1 // Copyright 2016 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 "base/task_scheduler/sequence.h" | 5 #include "base/task_scheduler/sequence.h" |
6 | 6 |
7 #include <utility> | 7 #include <utility> |
8 | 8 |
9 #include "base/bind.h" | 9 #include "base/bind.h" |
10 #include "base/macros.h" | 10 #include "base/macros.h" |
11 #include "base/memory/ptr_util.h" | 11 #include "base/memory/ptr_util.h" |
| 12 #include "base/test/gtest_util.h" |
12 #include "base/time/time.h" | 13 #include "base/time/time.h" |
13 #include "testing/gtest/include/gtest/gtest.h" | 14 #include "testing/gtest/include/gtest/gtest.h" |
14 | 15 |
15 namespace base { | 16 namespace base { |
16 namespace internal { | 17 namespace internal { |
17 | 18 |
18 namespace { | 19 namespace { |
19 | 20 |
20 // A class that pushes a Task to a Sequence in its destructor. | |
21 class PushTaskInDestructor { | |
22 public: | |
23 explicit PushTaskInDestructor(scoped_refptr<Sequence> sequence) | |
24 : sequence_(std::move(sequence)) {} | |
25 PushTaskInDestructor(PushTaskInDestructor&&) = default; | |
26 PushTaskInDestructor& operator=(PushTaskInDestructor&&) = default; | |
27 | |
28 ~PushTaskInDestructor() { | |
29 // |sequence_| may be nullptr in a temporary instance of this class. | |
30 if (sequence_) { | |
31 EXPECT_FALSE(sequence_->PeekTask()); | |
32 sequence_->PushTask(WrapUnique( | |
33 new Task(FROM_HERE, Closure(), TaskTraits(), TimeDelta()))); | |
34 } | |
35 } | |
36 | |
37 private: | |
38 scoped_refptr<Sequence> sequence_; | |
39 | |
40 DISALLOW_COPY_AND_ASSIGN(PushTaskInDestructor); | |
41 }; | |
42 | |
43 void DoNothing(const PushTaskInDestructor&) {} | |
44 | 21 |
45 class TaskSchedulerSequenceTest : public testing::Test { | 22 class TaskSchedulerSequenceTest : public testing::Test { |
46 public: | 23 public: |
47 TaskSchedulerSequenceTest() | 24 TaskSchedulerSequenceTest() |
48 : task_a_owned_( | 25 : task_a_owned_( |
49 new Task(FROM_HERE, | 26 new Task(FROM_HERE, |
50 Closure(), | 27 Closure(), |
51 TaskTraits().WithPriority(TaskPriority::BACKGROUND), | 28 TaskTraits().WithPriority(TaskPriority::BACKGROUND), |
52 TimeDelta())), | 29 TimeDelta())), |
53 task_b_owned_( | 30 task_b_owned_( |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
92 const Task* task_c_; | 69 const Task* task_c_; |
93 const Task* task_d_; | 70 const Task* task_d_; |
94 const Task* task_e_; | 71 const Task* task_e_; |
95 | 72 |
96 private: | 73 private: |
97 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerSequenceTest); | 74 DISALLOW_COPY_AND_ASSIGN(TaskSchedulerSequenceTest); |
98 }; | 75 }; |
99 | 76 |
100 } // namespace | 77 } // namespace |
101 | 78 |
102 TEST_F(TaskSchedulerSequenceTest, PushPopPeek) { | 79 TEST_F(TaskSchedulerSequenceTest, PushTakeRemove) { |
103 scoped_refptr<Sequence> sequence(new Sequence); | 80 scoped_refptr<Sequence> sequence(new Sequence); |
104 | 81 |
105 // Push task A in the sequence. Its sequenced time should be updated and it | 82 // Push task A in the sequence. Its sequenced time should be updated and it |
106 // should be in front of the sequence. | 83 // should be in front of the sequence. |
107 EXPECT_TRUE(sequence->PushTask(std::move(task_a_owned_))); | 84 EXPECT_TRUE(sequence->PushTask(std::move(task_a_owned_))); |
108 EXPECT_FALSE(task_a_->sequenced_time.is_null()); | 85 EXPECT_FALSE(task_a_->sequenced_time.is_null()); |
109 EXPECT_EQ(task_a_, sequence->PeekTask()); | 86 EXPECT_EQ(task_a_->traits.priority(), |
| 87 sequence->GetFrontTaskTraits().priority()); |
110 | 88 |
111 // Push task B, C and D in the sequence. Their sequenced time should be | 89 // Push task B, C and D in the sequence. Their sequenced time should be |
112 // updated and task A should always remain in front of the sequence. | 90 // updated and task A should always remain in front of the sequence. |
113 EXPECT_FALSE(sequence->PushTask(std::move(task_b_owned_))); | 91 EXPECT_FALSE(sequence->PushTask(std::move(task_b_owned_))); |
114 EXPECT_FALSE(task_b_->sequenced_time.is_null()); | 92 EXPECT_FALSE(task_b_->sequenced_time.is_null()); |
115 EXPECT_EQ(task_a_, sequence->PeekTask()); | 93 EXPECT_EQ(task_a_->traits.priority(), |
| 94 sequence->GetFrontTaskTraits().priority()); |
116 | 95 |
117 EXPECT_FALSE(sequence->PushTask(std::move(task_c_owned_))); | 96 EXPECT_FALSE(sequence->PushTask(std::move(task_c_owned_))); |
118 EXPECT_FALSE(task_c_->sequenced_time.is_null()); | 97 EXPECT_FALSE(task_c_->sequenced_time.is_null()); |
119 EXPECT_EQ(task_a_, sequence->PeekTask()); | 98 EXPECT_EQ(task_a_->traits.priority(), |
| 99 sequence->GetFrontTaskTraits().priority()); |
120 | 100 |
121 EXPECT_FALSE(sequence->PushTask(std::move(task_d_owned_))); | 101 EXPECT_FALSE(sequence->PushTask(std::move(task_d_owned_))); |
122 EXPECT_FALSE(task_d_->sequenced_time.is_null()); | 102 EXPECT_FALSE(task_d_->sequenced_time.is_null()); |
123 EXPECT_EQ(task_a_, sequence->PeekTask()); | 103 EXPECT_EQ(task_a_->traits.priority(), |
| 104 sequence->GetFrontTaskTraits().priority()); |
124 | 105 |
125 // Pop task A. Task B should now be in front. | 106 // Take ownership of the task in front of the sequence. It should be task A. |
126 EXPECT_FALSE(sequence->PopTask()); | 107 EXPECT_EQ(task_a_, sequence->TakeTask().get()); |
127 EXPECT_EQ(task_b_, sequence->PeekTask()); | |
128 | 108 |
129 // Pop task B. Task C should now be in front. | 109 // Expect to find an empty slot in front of the sequence. |
130 EXPECT_FALSE(sequence->PopTask()); | 110 EXPECT_FALSE(sequence->TakeTask()); |
131 EXPECT_EQ(task_c_, sequence->PeekTask()); | |
132 | 111 |
133 // Pop task C. Task D should now be in front. | 112 // Remove the empty slot. Task B should now be in front. |
134 EXPECT_FALSE(sequence->PopTask()); | 113 EXPECT_FALSE(sequence->RemoveFrontSlot()); |
135 EXPECT_EQ(task_d_, sequence->PeekTask()); | 114 EXPECT_EQ(task_b_, sequence->TakeTask().get()); |
| 115 EXPECT_FALSE(sequence->TakeTask()); |
| 116 |
| 117 // Remove the empty slot. Task C should now be in front. |
| 118 EXPECT_FALSE(sequence->RemoveFrontSlot()); |
| 119 EXPECT_EQ(task_c_, sequence->TakeTask().get()); |
| 120 EXPECT_FALSE(sequence->TakeTask()); |
| 121 |
| 122 // Remove the empty slot. Task D should now be in front. |
| 123 EXPECT_FALSE(sequence->RemoveFrontSlot()); |
| 124 EXPECT_EQ(task_d_, sequence->TakeTask().get()); |
| 125 EXPECT_FALSE(sequence->TakeTask()); |
136 | 126 |
137 // Push task E in the sequence. Its sequenced time should be updated and | 127 // Push task E in the sequence. Its sequenced time should be updated and |
138 // task D should remain in front. | 128 // there should still be an empty slot in front of the sequence. |
139 EXPECT_FALSE(sequence->PushTask(std::move(task_e_owned_))); | 129 EXPECT_FALSE(sequence->PushTask(std::move(task_e_owned_))); |
140 EXPECT_FALSE(task_e_->sequenced_time.is_null()); | 130 EXPECT_FALSE(task_e_->sequenced_time.is_null()); |
141 EXPECT_EQ(task_d_, sequence->PeekTask()); | 131 EXPECT_FALSE(sequence->TakeTask()); |
142 | 132 |
143 // Pop task D. Task E should now be in front. | 133 // Remove the empty slot. Task E should now be in front. |
144 EXPECT_FALSE(sequence->PopTask()); | 134 EXPECT_FALSE(sequence->RemoveFrontSlot()); |
145 EXPECT_EQ(task_e_, sequence->PeekTask()); | 135 EXPECT_EQ(task_e_, sequence->TakeTask().get()); |
| 136 EXPECT_FALSE(sequence->TakeTask()); |
146 | 137 |
147 // Pop task E. The sequence should now be empty. | 138 // Remove the empty slot. The sequence should now be empty. |
148 EXPECT_TRUE(sequence->PopTask()); | 139 EXPECT_TRUE(sequence->RemoveFrontSlot()); |
149 EXPECT_EQ(nullptr, sequence->PeekTask()); | 140 EXPECT_FALSE(sequence->TakeTask()); |
150 } | 141 } |
151 | 142 |
152 TEST_F(TaskSchedulerSequenceTest, GetSortKey) { | 143 TEST_F(TaskSchedulerSequenceTest, GetSortKey) { |
153 scoped_refptr<Sequence> sequence(new Sequence); | 144 scoped_refptr<Sequence> sequence(new Sequence); |
154 | 145 |
155 // Push task A in the sequence. The highest priority is from task A | 146 // Push task A in the sequence. The highest priority is from task A |
156 // (BACKGROUND). Task A is in front of the sequence. | 147 // (BACKGROUND). Task A is in front of the sequence. |
157 sequence->PushTask(std::move(task_a_owned_)); | 148 sequence->PushTask(std::move(task_a_owned_)); |
158 EXPECT_EQ(SequenceSortKey(TaskPriority::BACKGROUND, task_a_->sequenced_time), | 149 EXPECT_EQ(SequenceSortKey(TaskPriority::BACKGROUND, task_a_->sequenced_time), |
159 sequence->GetSortKey()); | 150 sequence->GetSortKey()); |
(...skipping 14 matching lines...) Expand all Loading... |
174 | 165 |
175 // Push task D in the sequence. The highest priority is from tasks C/D | 166 // Push task D in the sequence. The highest priority is from tasks C/D |
176 // (USER_BLOCKING). Task A is still in front of the sequence. | 167 // (USER_BLOCKING). Task A is still in front of the sequence. |
177 sequence->PushTask(std::move(task_d_owned_)); | 168 sequence->PushTask(std::move(task_d_owned_)); |
178 EXPECT_EQ( | 169 EXPECT_EQ( |
179 SequenceSortKey(TaskPriority::USER_BLOCKING, task_a_->sequenced_time), | 170 SequenceSortKey(TaskPriority::USER_BLOCKING, task_a_->sequenced_time), |
180 sequence->GetSortKey()); | 171 sequence->GetSortKey()); |
181 | 172 |
182 // Pop task A. The highest priority is still USER_BLOCKING. The task in front | 173 // Pop task A. The highest priority is still USER_BLOCKING. The task in front |
183 // of the sequence is now task B. | 174 // of the sequence is now task B. |
184 sequence->PopTask(); | 175 sequence->TakeTask(); |
| 176 sequence->RemoveFrontSlot(); |
185 EXPECT_EQ( | 177 EXPECT_EQ( |
186 SequenceSortKey(TaskPriority::USER_BLOCKING, task_b_->sequenced_time), | 178 SequenceSortKey(TaskPriority::USER_BLOCKING, task_b_->sequenced_time), |
187 sequence->GetSortKey()); | 179 sequence->GetSortKey()); |
188 | 180 |
189 // Pop task B. The highest priority is still USER_BLOCKING. The task in front | 181 // Pop task B. The highest priority is still USER_BLOCKING. The task in front |
190 // of the sequence is now task C. | 182 // of the sequence is now task C. |
191 sequence->PopTask(); | 183 sequence->TakeTask(); |
| 184 sequence->RemoveFrontSlot(); |
192 EXPECT_EQ( | 185 EXPECT_EQ( |
193 SequenceSortKey(TaskPriority::USER_BLOCKING, task_c_->sequenced_time), | 186 SequenceSortKey(TaskPriority::USER_BLOCKING, task_c_->sequenced_time), |
194 sequence->GetSortKey()); | 187 sequence->GetSortKey()); |
195 | 188 |
196 // Pop task C. The highest priority is still USER_BLOCKING. The task in front | 189 // Pop task C. The highest priority is still USER_BLOCKING. The task in front |
197 // of the sequence is now task D. | 190 // of the sequence is now task D. |
198 sequence->PopTask(); | 191 sequence->TakeTask(); |
| 192 sequence->RemoveFrontSlot(); |
199 EXPECT_EQ( | 193 EXPECT_EQ( |
200 SequenceSortKey(TaskPriority::USER_BLOCKING, task_d_->sequenced_time), | 194 SequenceSortKey(TaskPriority::USER_BLOCKING, task_d_->sequenced_time), |
201 sequence->GetSortKey()); | 195 sequence->GetSortKey()); |
202 | 196 |
203 // Push task E in the sequence. The highest priority is still USER_BLOCKING. | 197 // Push task E in the sequence. The highest priority is still USER_BLOCKING. |
204 // The task in front of the sequence is still task D. | 198 // The task in front of the sequence is still task D. |
205 sequence->PushTask(std::move(task_e_owned_)); | 199 sequence->PushTask(std::move(task_e_owned_)); |
206 EXPECT_EQ( | 200 EXPECT_EQ( |
207 SequenceSortKey(TaskPriority::USER_BLOCKING, task_d_->sequenced_time), | 201 SequenceSortKey(TaskPriority::USER_BLOCKING, task_d_->sequenced_time), |
208 sequence->GetSortKey()); | 202 sequence->GetSortKey()); |
209 | 203 |
210 // Pop task D. The highest priority is now from task E (BACKGROUND). The | 204 // Pop task D. The highest priority is now from task E (BACKGROUND). The |
211 // task in front of the sequence is now task E. | 205 // task in front of the sequence is now task E. |
212 sequence->PopTask(); | 206 sequence->TakeTask(); |
| 207 sequence->RemoveFrontSlot(); |
213 EXPECT_EQ(SequenceSortKey(TaskPriority::BACKGROUND, task_e_->sequenced_time), | 208 EXPECT_EQ(SequenceSortKey(TaskPriority::BACKGROUND, task_e_->sequenced_time), |
214 sequence->GetSortKey()); | 209 sequence->GetSortKey()); |
215 } | 210 } |
216 | 211 |
217 TEST_F(TaskSchedulerSequenceTest, CanPushTaskInTaskDestructor) { | 212 // Verify that a DCHECK fires if RemoveFrontSlot() is called on a sequence whose |
| 213 // front slot isn't empty. |
| 214 TEST_F(TaskSchedulerSequenceTest, RemoveNonEmptyFrontSlot) { |
218 scoped_refptr<Sequence> sequence(new Sequence); | 215 scoped_refptr<Sequence> sequence(new Sequence); |
219 sequence->PushTask(MakeUnique<Task>( | 216 sequence->PushTask( |
220 FROM_HERE, Bind(&DoNothing, PushTaskInDestructor(sequence)), TaskTraits(), | 217 MakeUnique<Task>(FROM_HERE, Bind(&DoNothing), TaskTraits(), TimeDelta())); |
221 TimeDelta())); | |
222 | 218 |
223 // PushTask() is invoked on |sequence| when the popped Task is destroyed. If | 219 EXPECT_DCHECK_DEATH({ sequence->RemoveFrontSlot(); }); |
224 // PopTask() destroys the Task outside the scope of its lock as expected, no | |
225 // deadlock will occur when PushTask() tries to acquire the Sequence's lock. | |
226 sequence->PopTask(); | |
227 | |
228 // Verify that |sequence| contains exactly one Task. | |
229 EXPECT_TRUE(sequence->PeekTask()); | |
230 EXPECT_TRUE(sequence->PopTask()); | |
231 } | 220 } |
232 | 221 |
233 } // namespace internal | 222 } // namespace internal |
234 } // namespace base | 223 } // namespace base |
OLD | NEW |