1 /*
2 * Copyright 2019 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 #include "api/task_queue/task_queue_test.h"
11
12 #include <memory>
13
14 #include "absl/cleanup/cleanup.h"
15 #include "absl/strings/string_view.h"
16 #include "api/task_queue/default_task_queue_factory.h"
17 #include "api/units/time_delta.h"
18 #include "rtc_base/event.h"
19 #include "rtc_base/ref_counter.h"
20 #include "rtc_base/time_utils.h"
21
22 namespace webrtc {
23 namespace {
24
CreateTaskQueue(const std::unique_ptr<webrtc::TaskQueueFactory> & factory,absl::string_view task_queue_name,TaskQueueFactory::Priority priority=TaskQueueFactory::Priority::NORMAL)25 std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(
26 const std::unique_ptr<webrtc::TaskQueueFactory>& factory,
27 absl::string_view task_queue_name,
28 TaskQueueFactory::Priority priority = TaskQueueFactory::Priority::NORMAL) {
29 return factory->CreateTaskQueue(task_queue_name, priority);
30 }
31
TEST_P(TaskQueueTest,Construct)32 TEST_P(TaskQueueTest, Construct) {
33 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
34 auto queue = CreateTaskQueue(factory, "Construct");
35 EXPECT_FALSE(queue->IsCurrent());
36 }
37
TEST_P(TaskQueueTest,PostAndCheckCurrent)38 TEST_P(TaskQueueTest, PostAndCheckCurrent) {
39 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
40 rtc::Event event;
41 auto queue = CreateTaskQueue(factory, "PostAndCheckCurrent");
42
43 // We're not running a task, so `queue` shouldn't be current.
44 // Note that because rtc::Thread also supports the TQ interface and
45 // TestMainImpl::Init wraps the main test thread (bugs.webrtc.org/9714), that
46 // means that TaskQueueBase::Current() will still return a valid value.
47 EXPECT_FALSE(queue->IsCurrent());
48
49 queue->PostTask([&event, &queue] {
50 EXPECT_TRUE(queue->IsCurrent());
51 event.Set();
52 });
53 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
54 }
55
TEST_P(TaskQueueTest,PostCustomTask)56 TEST_P(TaskQueueTest, PostCustomTask) {
57 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
58 rtc::Event ran;
59 auto queue = CreateTaskQueue(factory, "PostCustomImplementation");
60
61 class CustomTask {
62 public:
63 explicit CustomTask(rtc::Event* ran) : ran_(ran) {}
64
65 void operator()() { ran_->Set(); }
66
67 private:
68 rtc::Event* const ran_;
69 } my_task(&ran);
70
71 queue->PostTask(my_task);
72 EXPECT_TRUE(ran.Wait(TimeDelta::Seconds(1)));
73 }
74
TEST_P(TaskQueueTest,PostDelayedZero)75 TEST_P(TaskQueueTest, PostDelayedZero) {
76 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
77 rtc::Event event;
78 auto queue = CreateTaskQueue(factory, "PostDelayedZero");
79
80 queue->PostDelayedTask([&event] { event.Set(); }, TimeDelta::Zero());
81 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
82 }
83
TEST_P(TaskQueueTest,PostFromQueue)84 TEST_P(TaskQueueTest, PostFromQueue) {
85 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
86 rtc::Event event;
87 auto queue = CreateTaskQueue(factory, "PostFromQueue");
88
89 queue->PostTask(
90 [&event, &queue] { queue->PostTask([&event] { event.Set(); }); });
91 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
92 }
93
TEST_P(TaskQueueTest,PostDelayed)94 TEST_P(TaskQueueTest, PostDelayed) {
95 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
96 rtc::Event event;
97 auto queue =
98 CreateTaskQueue(factory, "PostDelayed", TaskQueueFactory::Priority::HIGH);
99
100 int64_t start = rtc::TimeMillis();
101 queue->PostDelayedTask(
102 [&event, &queue] {
103 EXPECT_TRUE(queue->IsCurrent());
104 event.Set();
105 },
106 TimeDelta::Millis(100));
107 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
108 int64_t end = rtc::TimeMillis();
109 // These tests are a little relaxed due to how "powerful" our test bots can
110 // be. Most recently we've seen windows bots fire the callback after 94-99ms,
111 // which is why we have a little bit of leeway backwards as well.
112 EXPECT_GE(end - start, 90u);
113 EXPECT_NEAR(end - start, 190u, 100u); // Accept 90-290.
114 }
115
TEST_P(TaskQueueTest,PostMultipleDelayed)116 TEST_P(TaskQueueTest, PostMultipleDelayed) {
117 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
118 auto queue = CreateTaskQueue(factory, "PostMultipleDelayed");
119
120 std::vector<rtc::Event> events(100);
121 for (int i = 0; i < 100; ++i) {
122 rtc::Event* event = &events[i];
123 queue->PostDelayedTask(
124 [event, &queue] {
125 EXPECT_TRUE(queue->IsCurrent());
126 event->Set();
127 },
128 TimeDelta::Millis(i));
129 }
130
131 for (rtc::Event& e : events)
132 EXPECT_TRUE(e.Wait(TimeDelta::Seconds(1)));
133 }
134
TEST_P(TaskQueueTest,PostDelayedAfterDestruct)135 TEST_P(TaskQueueTest, PostDelayedAfterDestruct) {
136 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
137 rtc::Event run;
138 rtc::Event deleted;
139 auto queue = CreateTaskQueue(factory, "PostDelayedAfterDestruct");
140 absl::Cleanup cleanup = [&deleted] { deleted.Set(); };
141 queue->PostDelayedTask([&run, cleanup = std::move(cleanup)] { run.Set(); },
142 TimeDelta::Millis(100));
143 // Destroy the queue.
144 queue = nullptr;
145 // Task might outlive the TaskQueue, but still should be deleted.
146 EXPECT_TRUE(deleted.Wait(TimeDelta::Seconds(1)));
147 EXPECT_FALSE(run.Wait(TimeDelta::Zero())); // and should not run.
148 }
149
TEST_P(TaskQueueTest,PostAndReuse)150 TEST_P(TaskQueueTest, PostAndReuse) {
151 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
152 rtc::Event event;
153 auto post_queue = CreateTaskQueue(factory, "PostQueue");
154 auto reply_queue = CreateTaskQueue(factory, "ReplyQueue");
155
156 int call_count = 0;
157
158 class ReusedTask {
159 public:
160 ReusedTask(int* counter, TaskQueueBase* reply_queue, rtc::Event* event)
161 : counter_(*counter), reply_queue_(reply_queue), event_(*event) {
162 EXPECT_EQ(counter_, 0);
163 }
164 ReusedTask(ReusedTask&&) = default;
165 ReusedTask& operator=(ReusedTask&&) = delete;
166
167 void operator()() && {
168 if (++counter_ == 1) {
169 reply_queue_->PostTask(std::move(*this));
170 // At this point, the object is in the moved-from state.
171 } else {
172 EXPECT_EQ(counter_, 2);
173 EXPECT_TRUE(reply_queue_->IsCurrent());
174 event_.Set();
175 }
176 }
177
178 private:
179 int& counter_;
180 TaskQueueBase* const reply_queue_;
181 rtc::Event& event_;
182 };
183
184 ReusedTask task(&call_count, reply_queue.get(), &event);
185 post_queue->PostTask(std::move(task));
186 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
187 }
188
TEST_P(TaskQueueTest,PostALot)189 TEST_P(TaskQueueTest, PostALot) {
190 // Waits until DecrementCount called `count` times. Thread safe.
191 class BlockingCounter {
192 public:
193 explicit BlockingCounter(int initial_count) : count_(initial_count) {}
194
195 void DecrementCount() {
196 if (count_.DecRef() == rtc::RefCountReleaseStatus::kDroppedLastRef) {
197 event_.Set();
198 }
199 }
200 bool Wait(TimeDelta give_up_after) { return event_.Wait(give_up_after); }
201
202 private:
203 webrtc_impl::RefCounter count_;
204 rtc::Event event_;
205 };
206
207 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
208 static constexpr int kTaskCount = 0xffff;
209 rtc::Event posting_done;
210 BlockingCounter all_destroyed(kTaskCount);
211
212 int tasks_executed = 0;
213 auto task_queue = CreateTaskQueue(factory, "PostALot");
214
215 task_queue->PostTask([&] {
216 // Post tasks from the queue to guarantee that the 1st task won't be
217 // executed before the last one is posted.
218 for (int i = 0; i < kTaskCount; ++i) {
219 absl::Cleanup cleanup = [&] { all_destroyed.DecrementCount(); };
220 task_queue->PostTask([&tasks_executed, cleanup = std::move(cleanup)] {
221 ++tasks_executed;
222 });
223 }
224
225 posting_done.Set();
226 });
227
228 // Before destroying the task queue wait until all child tasks are posted.
229 posting_done.Wait(rtc::Event::kForever);
230 // Destroy the task queue.
231 task_queue = nullptr;
232
233 // Expect all tasks are destroyed eventually. In some task queue
234 // implementations that might happen on a different thread after task queue is
235 // destroyed.
236 EXPECT_TRUE(all_destroyed.Wait(TimeDelta::Minutes(1)));
237 EXPECT_LE(tasks_executed, kTaskCount);
238 }
239
240 // Test posting two tasks that have shared state not protected by a
241 // lock. The TaskQueue should guarantee memory read-write order and
242 // FIFO task execution order, so the second task should always see the
243 // changes that were made by the first task.
244 //
245 // If the TaskQueue doesn't properly synchronize the execution of
246 // tasks, there will be a data race, which is undefined behavior. The
247 // EXPECT calls may randomly catch this, but to make the most of this
248 // unit test, run it under TSan or some other tool that is able to
249 // directly detect data races.
TEST_P(TaskQueueTest,PostTwoWithSharedUnprotectedState)250 TEST_P(TaskQueueTest, PostTwoWithSharedUnprotectedState) {
251 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
252 struct SharedState {
253 // First task will set this value to 1 and second will assert it.
254 int state = 0;
255 } state;
256
257 auto queue = CreateTaskQueue(factory, "PostTwoWithSharedUnprotectedState");
258 rtc::Event done;
259 queue->PostTask([&state, &queue, &done] {
260 // Post tasks from queue to guarantee, that 1st task won't be
261 // executed before the second one will be posted.
262 queue->PostTask([&state] { state.state = 1; });
263 queue->PostTask([&state, &done] {
264 EXPECT_EQ(state.state, 1);
265 done.Set();
266 });
267 // Check, that state changing tasks didn't start yet.
268 EXPECT_EQ(state.state, 0);
269 });
270 EXPECT_TRUE(done.Wait(TimeDelta::Seconds(1)));
271 }
272
273 // TaskQueueTest is a set of tests for any implementation of the TaskQueueBase.
274 // Tests are instantiated next to the concrete implementation(s).
275 // https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#creating-value-parameterized-abstract-tests
276 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TaskQueueTest);
277
278 } // namespace
279 } // namespace webrtc
280