OLD | NEW |
---|---|
(Empty) | |
1 /* | |
2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved. | |
3 * | |
4 * Use of this source code is governed by a BSD-style license | |
5 * that can be found in the LICENSE file in the root of the source | |
6 * tree. An additional intellectual property rights grant can be found | |
7 * in the file PATENTS. All contributing project authors may | |
8 * be found in the AUTHORS file in the root of the source tree. | |
9 */ | |
10 | |
11 #include "webrtc/test/single_threaded_task_queue.h" | |
12 | |
13 #include <atomic> | |
14 #include <memory> | |
15 #include <vector> | |
16 | |
17 #include "webrtc/rtc_base/event.h" | |
18 #include "webrtc/rtc_base/ptr_util.h" | |
19 #include "webrtc/test/gtest.h" | |
20 | |
21 namespace webrtc { | |
22 namespace test { | |
23 | |
24 namespace { | |
25 | |
26 using TaskId = SingleThreadedTaskQueueForTesting::TaskId; | |
27 | |
28 // Test should not rely on the object under test not being faulty. If the task | |
29 // queue ever blocks forever, we want the tests to fail, rather than hang. | |
30 constexpr int kMaxWaitTimeMs = 10000; | |
31 | |
32 TEST(SingleThreadedTaskQueueForTestingTest, SanityConstructionDestruction) { | |
33 SingleThreadedTaskQueueForTesting task_queue("task_queue"); | |
34 } | |
35 | |
36 TEST(SingleThreadedTaskQueueForTestingTest, ExecutesPostedTasks) { | |
37 SingleThreadedTaskQueueForTesting task_queue("task_queue"); | |
38 | |
39 std::atomic<bool> executed(false); | |
40 rtc::Event done(true, false); | |
41 | |
42 task_queue.PostTask([&]() { | |
nisse-webrtc
2017/08/18 11:08:29
Prefer explicit capture list, for this and other l
eladalon
2017/08/18 12:12:38
Done.
| |
43 executed.store(true); | |
44 done.Set(); | |
45 }); | |
46 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs)); | |
47 | |
48 EXPECT_TRUE(executed.load()); | |
49 } | |
50 | |
51 TEST(SingleThreadedTaskQueueForTestingTest, | |
52 PostMultipleTasksFromSameExternalThread) { | |
53 SingleThreadedTaskQueueForTesting task_queue("task_queue"); | |
54 | |
55 std::vector<std::unique_ptr<rtc::Event>> done_events; | |
56 for (size_t i = 0; i < 3; i++) { | |
57 done_events.emplace_back(rtc::MakeUnique<rtc::Event>(false, false)); | |
58 } | |
59 std::atomic<bool> executed[done_events.size()]; | |
60 for (std::atomic<bool>& exec : executed) { | |
61 exec.store(false); | |
62 } | |
63 | |
64 // To avoid the tasks which comprise the actual test from running before they | |
65 // have all be posted, which could result in only one task ever being in the | |
66 // queue at any given time, post one waiting task that would block the | |
67 // task-queue, and unblock only after all tasks have been posted. | |
68 rtc::Event rendezvous(true, false); | |
69 task_queue.PostTask([&]() { | |
70 ASSERT_TRUE(rendezvous.Wait(kMaxWaitTimeMs)); | |
71 }); | |
72 | |
73 // Post the tasks which comprise the test. | |
74 for (size_t i = 0; i < done_events.size(); i++) { | |
75 task_queue.PostTask([&executed, &done_events, i]() { // |i| by value. | |
76 executed[i].store(true); | |
77 done_events[i]->Set(); | |
78 }); | |
79 } | |
80 | |
81 rendezvous.Set(); // Release the task-queue. | |
82 | |
83 // Wait until the task queue has executed all the tasks. | |
84 for (size_t i = 0; i < done_events.size(); i++) { | |
85 ASSERT_TRUE(done_events[i]->Wait(kMaxWaitTimeMs)); | |
86 } | |
87 | |
88 for (size_t i = 0; i < done_events.size(); i++) { | |
89 EXPECT_TRUE(executed[i].load()); | |
90 } | |
91 } | |
92 | |
93 TEST(SingleThreadedTaskQueueForTestingTest, PostToTaskQueueFromOwnThread) { | |
94 SingleThreadedTaskQueueForTesting task_queue("task_queue"); | |
95 | |
96 std::atomic<bool> executed(false); | |
97 rtc::Event done(true, false); | |
98 | |
99 auto internally_posted_task = [&executed, &done]() { | |
100 executed.store(true); | |
101 done.Set(); | |
102 }; | |
103 | |
104 auto externally_posted_task = [&task_queue, &internally_posted_task]() { | |
105 task_queue.PostTask(internally_posted_task); | |
106 }; | |
107 | |
108 task_queue.PostTask(externally_posted_task); | |
109 | |
110 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs)); | |
111 EXPECT_TRUE(executed.load()); | |
112 } | |
113 | |
114 TEST(SingleThreadedTaskQueueForTestingTest, TasksExecutedInSequence) { | |
115 SingleThreadedTaskQueueForTesting task_queue("task_queue"); | |
116 | |
117 // The first task would perform: | |
118 // accumulator = 10 * accumulator + i | |
119 // Where |i| is 1, 2 and 3 for the 1st, 2nd and 3rd tasks, respectively. | |
120 // The result would be 123 if and only iff the tasks were executed in order. | |
121 size_t accumulator = 0; | |
122 size_t expected_value = 0; // Updates to the correct value. | |
123 | |
124 // Prevent the chain from being set in motion before we've had time to | |
125 // schedule it all, lest the queue only contain one task at a time. | |
126 rtc::Event rendezvous(true, false); | |
127 task_queue.PostTask([&rendezvous]() { | |
128 ASSERT_TRUE(rendezvous.Wait(kMaxWaitTimeMs)); | |
129 }); | |
130 | |
131 for (size_t i = 0; i < 3; i++) { | |
132 task_queue.PostTask([&accumulator, i]() { // |i| passed by value. | |
133 accumulator = 10 * accumulator + i; | |
134 }); | |
135 expected_value = 10 * expected_value + i; | |
136 } | |
137 | |
138 // The test will wait for the task-queue to finish. | |
139 rtc::Event done(true, false); | |
140 task_queue.PostTask([&done]() { | |
141 done.Set(); | |
142 }); | |
143 | |
144 rendezvous.Set(); // Set the chain in motion. | |
145 | |
146 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs)); | |
147 | |
148 EXPECT_EQ(accumulator, expected_value); | |
149 } | |
150 | |
151 TEST(SingleThreadedTaskQueueForTestingTest, ExecutesPostedDelayedTask) { | |
152 SingleThreadedTaskQueueForTesting task_queue("task_queue"); | |
153 | |
154 std::atomic<bool> executed(false); | |
155 rtc::Event done(true, false); | |
156 | |
157 constexpr int64_t delay_ms = 20; | |
158 static_assert(delay_ms < kMaxWaitTimeMs / 2, "Delay too long for tests."); | |
159 | |
160 task_queue.PostDelayedTask([&]() { | |
161 executed.store(true); | |
162 done.Set(); | |
163 }, delay_ms); | |
164 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs)); | |
165 | |
166 EXPECT_TRUE(executed.load()); | |
167 } | |
168 | |
169 TEST(SingleThreadedTaskQueueForTestingTest, DoesNotExecuteDelayedTaskTooSoon) { | |
170 SingleThreadedTaskQueueForTesting task_queue("task_queue"); | |
171 | |
172 std::atomic<bool> executed(false); | |
173 | |
174 constexpr int64_t delay_ms = 2000; | |
175 static_assert(delay_ms < kMaxWaitTimeMs / 2, "Delay too long for tests."); | |
176 | |
177 task_queue.PostDelayedTask([&]() { | |
178 executed.store(true); | |
179 }, delay_ms); | |
180 | |
181 // Wait less than is enough, make sure the task was not yet executed. | |
nisse-webrtc
2017/08/18 11:08:29
Tests like this easily become flaky. Ideally, use
eladalon
2017/08/18 12:12:38
I had a TODO for using a fake-clock here, but I re
| |
182 rtc::Event not_done(true, false); | |
183 ASSERT_FALSE(not_done.Wait(delay_ms / 2)); | |
184 EXPECT_FALSE(executed.load()); | |
185 } | |
186 | |
187 TEST(SingleThreadedTaskQueueForTestingTest, | |
188 TaskWithLesserDelayPostedAfterFirstDelayedTaskExectuedBeforeFirst) { | |
nisse-webrtc
2017/08/18 11:08:29
This is also potentially flaky.
eladalon
2017/08/18 12:12:38
Same answer.
| |
189 SingleThreadedTaskQueueForTesting task_queue("task_queue"); | |
190 | |
191 std::atomic<bool> earlier_executed(false); | |
192 constexpr int64_t earlier_delay_ms = 500; | |
193 | |
194 std::atomic<bool> later_executed(false); | |
195 constexpr int64_t later_delay_ms = 1000; | |
196 | |
197 static_assert(earlier_delay_ms + later_delay_ms < kMaxWaitTimeMs / 2, | |
198 "Delay too long for tests."); | |
199 | |
200 rtc::Event done(true, false); | |
201 | |
202 auto earlier_task = [&]() { | |
203 EXPECT_FALSE(later_executed.load()); | |
204 earlier_executed.store(true); | |
205 }; | |
206 | |
207 auto later_task = [&]() { | |
208 EXPECT_TRUE(earlier_executed.load()); | |
209 later_executed.store(true); | |
210 done.Set(); | |
211 }; | |
212 | |
213 task_queue.PostDelayedTask(later_task, later_delay_ms); | |
214 task_queue.PostDelayedTask(earlier_task, earlier_delay_ms); | |
215 | |
216 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs)); | |
217 ASSERT_TRUE(earlier_executed); | |
218 ASSERT_TRUE(later_executed); | |
219 } | |
220 | |
221 TEST(SingleThreadedTaskQueueForTestingTest, | |
222 TaskWithGreaterDelayPostedAfterFirstDelayedTaskExectuedAfterFirst) { | |
223 SingleThreadedTaskQueueForTesting task_queue("task_queue"); | |
224 | |
225 std::atomic<bool> earlier_executed(false); | |
226 constexpr int64_t earlier_delay_ms = 500; | |
227 | |
228 std::atomic<bool> later_executed(false); | |
229 constexpr int64_t later_delay_ms = 1000; | |
230 | |
231 static_assert(earlier_delay_ms + later_delay_ms < kMaxWaitTimeMs / 2, | |
232 "Delay too long for tests."); | |
233 | |
234 rtc::Event done(true, false); | |
235 | |
236 auto earlier_task = [&]() { | |
237 EXPECT_FALSE(later_executed.load()); | |
238 earlier_executed.store(true); | |
239 }; | |
240 | |
241 auto later_task = [&]() { | |
242 EXPECT_TRUE(earlier_executed.load()); | |
243 later_executed.store(true); | |
244 done.Set(); | |
245 }; | |
246 | |
247 task_queue.PostDelayedTask(earlier_task, earlier_delay_ms); | |
248 task_queue.PostDelayedTask(later_task, later_delay_ms); | |
249 | |
250 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs)); | |
251 ASSERT_TRUE(earlier_executed); | |
252 ASSERT_TRUE(later_executed); | |
253 } | |
254 | |
255 TEST(SingleThreadedTaskQueueForTestingTest, ExternalThreadCancelsTask) { | |
256 SingleThreadedTaskQueueForTesting task_queue("task_queue"); | |
257 | |
258 rtc::Event done(true, false); | |
259 | |
260 // Prevent the to-be-cancelled task from being executed before we've had | |
261 // time to cancel it. | |
262 rtc::Event rendezvous(true, false); | |
263 task_queue.PostTask([&]() { | |
264 ASSERT_TRUE(rendezvous.Wait(kMaxWaitTimeMs)); | |
265 }); | |
266 | |
267 TaskId cancelled_task_id = task_queue.PostTask([]() { | |
268 EXPECT_TRUE(false); | |
269 }); | |
270 task_queue.PostTask([&done]() { | |
271 done.Set(); | |
272 }); | |
273 | |
274 task_queue.CancelTask(cancelled_task_id); | |
275 | |
276 // Set the tasks in motion; the cancelled task does not run (otherwise the | |
277 // test would fail). The last task ends the test, showing that the queue | |
278 // progressed beyond the cancelled task. | |
279 rendezvous.Set(); | |
280 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs)); | |
281 } | |
282 | |
283 // In this test, we'll set off a chain where the first task cancels the second | |
284 // task, then a third task runs (showing that we really cancelled the task, | |
285 // rather than just halted the task-queue). | |
286 TEST(SingleThreadedTaskQueueForTestingTest, InternalThreadCancelsTask) { | |
287 SingleThreadedTaskQueueForTesting task_queue("task_queue"); | |
288 | |
289 rtc::Event done(true, false); | |
290 | |
291 // Prevent the chain from being set-off before we've set everything up. | |
292 rtc::Event rendezvous(true, false); | |
293 task_queue.PostTask([&]() { | |
294 ASSERT_TRUE(rendezvous.Wait(kMaxWaitTimeMs)); | |
295 }); | |
296 | |
297 // This is the canceller-task. It takes cancelled_task_id by reference, | |
298 // because the ID will only become known after the cancelled task is | |
299 // scheduled. | |
300 TaskId cancelled_task_id; | |
301 auto canceller_task = [&task_queue, &cancelled_task_id]() { | |
302 task_queue.CancelTask(cancelled_task_id); | |
303 }; | |
304 task_queue.PostTask(canceller_task); | |
305 | |
306 // This task will be cancelled by the task before it. | |
307 auto cancelled_task = []() { | |
308 EXPECT_TRUE(false); | |
309 }; | |
310 cancelled_task_id = task_queue.PostTask(cancelled_task); | |
311 | |
312 // When this task runs, it will allow the test to be finished. | |
313 auto completion_marker_task = [&done]() { | |
314 done.Set(); | |
315 }; | |
316 task_queue.PostTask(completion_marker_task); | |
317 | |
318 rendezvous.Set(); // Set the chain in motion. | |
319 | |
320 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs)); | |
321 } | |
322 | |
323 TEST(SingleThreadedTaskQueueForTestingTest, SendTask) { | |
324 SingleThreadedTaskQueueForTesting task_queue("task_queue"); | |
325 | |
326 std::atomic<bool> executed(false); | |
327 | |
328 task_queue.SendTask([&executed]() { | |
329 // Intentionally delay, so that if SendTask didn't block, the sender thread | |
330 // would have time to read |executed|. | |
331 rtc::Event delay(true, false); | |
332 ASSERT_FALSE(delay.Wait(1000)); | |
nisse-webrtc
2017/08/18 11:08:29
When delay is all you want, prefer the sleep funct
eladalon
2017/08/18 12:12:38
Thread::SleepMs() is only used in six places in ou
| |
333 executed.store(true); | |
334 }); | |
335 | |
336 EXPECT_TRUE(executed); | |
337 } | |
338 | |
339 TEST(SingleThreadedTaskQueueForTestingTest, | |
340 DestructTaskQueueWhileTasksPending) { | |
341 auto task_queue = | |
342 rtc::MakeUnique<SingleThreadedTaskQueueForTesting>("task_queue"); | |
343 | |
344 std::atomic<size_t> counter(0); | |
345 | |
346 constexpr size_t tasks = 10; | |
347 for (size_t i = 0; i < tasks; i++) { | |
348 task_queue->PostTask([&counter]() { | |
349 std::atomic_fetch_add(&counter, static_cast<size_t>(1)); | |
350 rtc::Event delay(true, false); | |
351 ASSERT_FALSE(delay.Wait(500)); | |
352 }); | |
353 } | |
354 | |
355 task_queue.reset(); | |
356 | |
357 EXPECT_LT(counter, tasks); | |
358 } | |
359 | |
360 } // namespace | |
361 } // namespace test | |
362 } // namespace webrtc | |
OLD | NEW |