blob: 084f0d7e8cbb8212cfe2716532066bc95cd310c3 [file] [log] [blame]
eladalon413ee9a2017-08-22 11:02:521/*
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
Mirko Bonadei92ea95e2017-09-15 04:47:3111#include "test/single_threaded_task_queue.h"
eladalon413ee9a2017-08-22 11:02:5212
13#include <atomic>
14#include <memory>
15#include <vector>
16
Karl Wiberg918f50c2018-07-05 09:40:3317#include "absl/memory/memory.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3118#include "rtc_base/event.h"
Mirko Bonadei92ea95e2017-09-15 04:47:3119#include "test/gtest.h"
eladalon413ee9a2017-08-22 11:02:5220
21namespace webrtc {
22namespace test {
23
24namespace {
25
26using 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.
30constexpr int kMaxWaitTimeMs = 10000;
31
32TEST(SingleThreadedTaskQueueForTestingTest, SanityConstructionDestruction) {
33 SingleThreadedTaskQueueForTesting task_queue("task_queue");
34}
35
36TEST(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([&executed, &done]() {
43 executed.store(true);
44 done.Set();
45 });
46 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
47
48 EXPECT_TRUE(executed.load());
49}
50
51TEST(SingleThreadedTaskQueueForTestingTest,
52 PostMultipleTasksFromSameExternalThread) {
53 SingleThreadedTaskQueueForTesting task_queue("task_queue");
54
55 constexpr size_t kCount = 3;
56 std::atomic<bool> executed[kCount];
57 for (std::atomic<bool>& exec : executed) {
58 exec.store(false);
59 }
60
61 std::vector<std::unique_ptr<rtc::Event>> done_events;
62 for (size_t i = 0; i < kCount; i++) {
Karl Wiberg918f50c2018-07-05 09:40:3363 done_events.emplace_back(absl::make_unique<rtc::Event>(false, false));
eladalon413ee9a2017-08-22 11:02:5264 }
65
66 // To avoid the tasks which comprise the actual test from running before they
67 // have all be posted, which could result in only one task ever being in the
68 // queue at any given time, post one waiting task that would block the
69 // task-queue, and unblock only after all tasks have been posted.
70 rtc::Event rendezvous(true, false);
Yves Gerey665174f2018-06-19 13:03:0571 task_queue.PostTask(
72 [&rendezvous]() { ASSERT_TRUE(rendezvous.Wait(kMaxWaitTimeMs)); });
eladalon413ee9a2017-08-22 11:02:5273
74 // Post the tasks which comprise the test.
75 for (size_t i = 0; i < kCount; i++) {
76 task_queue.PostTask([&executed, &done_events, i]() { // |i| by value.
77 executed[i].store(true);
78 done_events[i]->Set();
79 });
80 }
81
82 rendezvous.Set(); // Release the task-queue.
83
84 // Wait until the task queue has executed all the tasks.
85 for (size_t i = 0; i < kCount; i++) {
86 ASSERT_TRUE(done_events[i]->Wait(kMaxWaitTimeMs));
87 }
88
89 for (size_t i = 0; i < kCount; i++) {
90 EXPECT_TRUE(executed[i].load());
91 }
92}
93
94TEST(SingleThreadedTaskQueueForTestingTest, PostToTaskQueueFromOwnThread) {
95 SingleThreadedTaskQueueForTesting task_queue("task_queue");
96
97 std::atomic<bool> executed(false);
98 rtc::Event done(true, false);
99
100 auto internally_posted_task = [&executed, &done]() {
101 executed.store(true);
102 done.Set();
103 };
104
105 auto externally_posted_task = [&task_queue, &internally_posted_task]() {
106 task_queue.PostTask(internally_posted_task);
107 };
108
109 task_queue.PostTask(externally_posted_task);
110
111 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
112 EXPECT_TRUE(executed.load());
113}
114
115TEST(SingleThreadedTaskQueueForTestingTest, TasksExecutedInSequence) {
116 SingleThreadedTaskQueueForTesting task_queue("task_queue");
117
118 // The first task would perform:
119 // accumulator = 10 * accumulator + i
120 // Where |i| is 1, 2 and 3 for the 1st, 2nd and 3rd tasks, respectively.
121 // The result would be 123 if and only iff the tasks were executed in order.
122 size_t accumulator = 0;
123 size_t expected_value = 0; // Updates to the correct value.
124
125 // Prevent the chain from being set in motion before we've had time to
126 // schedule it all, lest the queue only contain one task at a time.
127 rtc::Event rendezvous(true, false);
Yves Gerey665174f2018-06-19 13:03:05128 task_queue.PostTask(
129 [&rendezvous]() { ASSERT_TRUE(rendezvous.Wait(kMaxWaitTimeMs)); });
eladalon413ee9a2017-08-22 11:02:52130
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);
Yves Gerey665174f2018-06-19 13:03:05140 task_queue.PostTask([&done]() { done.Set(); });
eladalon413ee9a2017-08-22 11:02:52141
142 rendezvous.Set(); // Set the chain in motion.
143
144 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
145
146 EXPECT_EQ(accumulator, expected_value);
147}
148
149TEST(SingleThreadedTaskQueueForTestingTest, ExecutesPostedDelayedTask) {
150 SingleThreadedTaskQueueForTesting task_queue("task_queue");
151
152 std::atomic<bool> executed(false);
153 rtc::Event done(true, false);
154
155 constexpr int64_t delay_ms = 20;
156 static_assert(delay_ms < kMaxWaitTimeMs / 2, "Delay too long for tests.");
157
Yves Gerey665174f2018-06-19 13:03:05158 task_queue.PostDelayedTask(
159 [&executed, &done]() {
160 executed.store(true);
161 done.Set();
162 },
163 delay_ms);
eladalon413ee9a2017-08-22 11:02:52164 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
165
166 EXPECT_TRUE(executed.load());
167}
168
169TEST(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
Yves Gerey665174f2018-06-19 13:03:05177 task_queue.PostDelayedTask([&executed]() { executed.store(true); }, delay_ms);
eladalon413ee9a2017-08-22 11:02:52178
179 // Wait less than is enough, make sure the task was not yet executed.
180 rtc::Event not_done(true, false);
181 ASSERT_FALSE(not_done.Wait(delay_ms / 2));
182 EXPECT_FALSE(executed.load());
183}
184
185TEST(SingleThreadedTaskQueueForTestingTest,
186 TaskWithLesserDelayPostedAfterFirstDelayedTaskExectuedBeforeFirst) {
187 SingleThreadedTaskQueueForTesting task_queue("task_queue");
188
189 std::atomic<bool> earlier_executed(false);
190 constexpr int64_t earlier_delay_ms = 500;
191
192 std::atomic<bool> later_executed(false);
193 constexpr int64_t later_delay_ms = 1000;
194
195 static_assert(earlier_delay_ms + later_delay_ms < kMaxWaitTimeMs / 2,
196 "Delay too long for tests.");
197
198 rtc::Event done(true, false);
199
200 auto earlier_task = [&earlier_executed, &later_executed]() {
201 EXPECT_FALSE(later_executed.load());
202 earlier_executed.store(true);
203 };
204
205 auto later_task = [&earlier_executed, &later_executed, &done]() {
206 EXPECT_TRUE(earlier_executed.load());
207 later_executed.store(true);
208 done.Set();
209 };
210
211 task_queue.PostDelayedTask(later_task, later_delay_ms);
212 task_queue.PostDelayedTask(earlier_task, earlier_delay_ms);
213
214 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
215 ASSERT_TRUE(earlier_executed);
216 ASSERT_TRUE(later_executed);
217}
218
219TEST(SingleThreadedTaskQueueForTestingTest,
220 TaskWithGreaterDelayPostedAfterFirstDelayedTaskExectuedAfterFirst) {
221 SingleThreadedTaskQueueForTesting task_queue("task_queue");
222
223 std::atomic<bool> earlier_executed(false);
224 constexpr int64_t earlier_delay_ms = 500;
225
226 std::atomic<bool> later_executed(false);
227 constexpr int64_t later_delay_ms = 1000;
228
229 static_assert(earlier_delay_ms + later_delay_ms < kMaxWaitTimeMs / 2,
230 "Delay too long for tests.");
231
232 rtc::Event done(true, false);
233
234 auto earlier_task = [&earlier_executed, &later_executed]() {
235 EXPECT_FALSE(later_executed.load());
236 earlier_executed.store(true);
237 };
238
239 auto later_task = [&earlier_executed, &later_executed, &done]() {
240 EXPECT_TRUE(earlier_executed.load());
241 later_executed.store(true);
242 done.Set();
243 };
244
245 task_queue.PostDelayedTask(earlier_task, earlier_delay_ms);
246 task_queue.PostDelayedTask(later_task, later_delay_ms);
247
248 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
249 ASSERT_TRUE(earlier_executed);
250 ASSERT_TRUE(later_executed);
251}
252
253TEST(SingleThreadedTaskQueueForTestingTest, ExternalThreadCancelsTask) {
254 SingleThreadedTaskQueueForTesting task_queue("task_queue");
255
256 rtc::Event done(true, false);
257
258 // Prevent the to-be-cancelled task from being executed before we've had
259 // time to cancel it.
260 rtc::Event rendezvous(true, false);
Yves Gerey665174f2018-06-19 13:03:05261 task_queue.PostTask(
262 [&rendezvous]() { ASSERT_TRUE(rendezvous.Wait(kMaxWaitTimeMs)); });
eladalon413ee9a2017-08-22 11:02:52263
Yves Gerey665174f2018-06-19 13:03:05264 TaskId cancelled_task_id = task_queue.PostTask([]() { EXPECT_TRUE(false); });
265 task_queue.PostTask([&done]() { done.Set(); });
eladalon413ee9a2017-08-22 11:02:52266
267 task_queue.CancelTask(cancelled_task_id);
268
269 // Set the tasks in motion; the cancelled task does not run (otherwise the
270 // test would fail). The last task ends the test, showing that the queue
271 // progressed beyond the cancelled task.
272 rendezvous.Set();
273 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
274}
275
276// In this test, we'll set off a chain where the first task cancels the second
277// task, then a third task runs (showing that we really cancelled the task,
278// rather than just halted the task-queue).
279TEST(SingleThreadedTaskQueueForTestingTest, InternalThreadCancelsTask) {
280 SingleThreadedTaskQueueForTesting task_queue("task_queue");
281
282 rtc::Event done(true, false);
283
284 // Prevent the chain from being set-off before we've set everything up.
285 rtc::Event rendezvous(true, false);
Yves Gerey665174f2018-06-19 13:03:05286 task_queue.PostTask(
287 [&rendezvous]() { ASSERT_TRUE(rendezvous.Wait(kMaxWaitTimeMs)); });
eladalon413ee9a2017-08-22 11:02:52288
289 // This is the canceller-task. It takes cancelled_task_id by reference,
290 // because the ID will only become known after the cancelled task is
291 // scheduled.
292 TaskId cancelled_task_id;
293 auto canceller_task = [&task_queue, &cancelled_task_id]() {
294 task_queue.CancelTask(cancelled_task_id);
295 };
296 task_queue.PostTask(canceller_task);
297
298 // This task will be cancelled by the task before it.
Yves Gerey665174f2018-06-19 13:03:05299 auto cancelled_task = []() { EXPECT_TRUE(false); };
eladalon413ee9a2017-08-22 11:02:52300 cancelled_task_id = task_queue.PostTask(cancelled_task);
301
302 // When this task runs, it will allow the test to be finished.
Yves Gerey665174f2018-06-19 13:03:05303 auto completion_marker_task = [&done]() { done.Set(); };
eladalon413ee9a2017-08-22 11:02:52304 task_queue.PostTask(completion_marker_task);
305
306 rendezvous.Set(); // Set the chain in motion.
307
308 ASSERT_TRUE(done.Wait(kMaxWaitTimeMs));
309}
310
311TEST(SingleThreadedTaskQueueForTestingTest, SendTask) {
312 SingleThreadedTaskQueueForTesting task_queue("task_queue");
313
314 std::atomic<bool> executed(false);
315
316 task_queue.SendTask([&executed]() {
317 // Intentionally delay, so that if SendTask didn't block, the sender thread
318 // would have time to read |executed|.
319 rtc::Event delay(true, false);
320 ASSERT_FALSE(delay.Wait(1000));
321 executed.store(true);
322 });
323
324 EXPECT_TRUE(executed);
325}
326
327TEST(SingleThreadedTaskQueueForTestingTest,
328 DestructTaskQueueWhileTasksPending) {
329 auto task_queue =
Karl Wiberg918f50c2018-07-05 09:40:33330 absl::make_unique<SingleThreadedTaskQueueForTesting>("task_queue");
eladalon413ee9a2017-08-22 11:02:52331
332 std::atomic<size_t> counter(0);
333
334 constexpr size_t tasks = 10;
335 for (size_t i = 0; i < tasks; i++) {
336 task_queue->PostTask([&counter]() {
337 std::atomic_fetch_add(&counter, static_cast<size_t>(1));
338 rtc::Event delay(true, false);
339 ASSERT_FALSE(delay.Wait(500));
340 });
341 }
342
343 task_queue.reset();
344
345 EXPECT_LT(counter, tasks);
346}
347
348} // namespace
349} // namespace test
350} // namespace webrtc