blob: 7849f4273d3a40a0766b80966909a40abc10fef2 [file] [log] [blame]
Danil Chapovalov33b716f2019-01-22 18:15:37 +01001/*
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
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020012#include <memory>
13
14#include "absl/cleanup/cleanup.h"
Danil Chapovalov33b716f2019-01-22 18:15:37 +010015#include "absl/strings/string_view.h"
Markus Handell82da9322022-12-16 15:50:24 +010016#include "api/task_queue/task_queue_base.h"
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020017#include "api/units/time_delta.h"
Danil Chapovalov33b716f2019-01-22 18:15:37 +010018#include "rtc_base/event.h"
Danil Chapovalovf504dd32019-06-05 14:16:59 +020019#include "rtc_base/ref_counter.h"
Steve Antonf3802842019-01-24 19:07:40 -080020#include "rtc_base/time_utils.h"
Danil Chapovalov33b716f2019-01-22 18:15:37 +010021
22namespace webrtc {
23namespace {
24
Markus Handell82da9322022-12-16 15:50:24 +010025// Avoids a dependency to system_wrappers.
26void SleepFor(TimeDelta duration) {
27 rtc::ScopedAllowBaseSyncPrimitivesForTesting allow;
28 rtc::Event event;
29 event.Wait(duration);
30}
31
Danil Chapovalov33b716f2019-01-22 18:15:37 +010032std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(
Danil Chapovalov710f3d32019-02-06 16:00:44 +010033 const std::unique_ptr<webrtc::TaskQueueFactory>& factory,
Danil Chapovalov33b716f2019-01-22 18:15:37 +010034 absl::string_view task_queue_name,
35 TaskQueueFactory::Priority priority = TaskQueueFactory::Priority::NORMAL) {
36 return factory->CreateTaskQueue(task_queue_name, priority);
37}
38
39TEST_P(TaskQueueTest, Construct) {
Ali Tofigh4b681942022-08-23 12:57:16 +020040 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov710f3d32019-02-06 16:00:44 +010041 auto queue = CreateTaskQueue(factory, "Construct");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010042 EXPECT_FALSE(queue->IsCurrent());
43}
44
45TEST_P(TaskQueueTest, PostAndCheckCurrent) {
Ali Tofigh4b681942022-08-23 12:57:16 +020046 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010047 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010048 auto queue = CreateTaskQueue(factory, "PostAndCheckCurrent");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010049
Artem Titov0e61fdd2021-07-25 21:50:14 +020050 // We're not running a task, so `queue` shouldn't be current.
Tommi6866dc72020-05-15 10:11:56 +020051 // Note that because rtc::Thread also supports the TQ interface and
52 // TestMainImpl::Init wraps the main test thread (bugs.webrtc.org/9714), that
53 // means that TaskQueueBase::Current() will still return a valid value.
Danil Chapovalov33b716f2019-01-22 18:15:37 +010054 EXPECT_FALSE(queue->IsCurrent());
Danil Chapovalov33b716f2019-01-22 18:15:37 +010055
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020056 queue->PostTask([&event, &queue] {
Danil Chapovalov33b716f2019-01-22 18:15:37 +010057 EXPECT_TRUE(queue->IsCurrent());
58 event.Set();
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020059 });
Markus Handell2cfc1af2022-08-19 08:16:48 +000060 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010061}
62
63TEST_P(TaskQueueTest, PostCustomTask) {
Ali Tofigh4b681942022-08-23 12:57:16 +020064 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010065 rtc::Event ran;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010066 auto queue = CreateTaskQueue(factory, "PostCustomImplementation");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010067
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020068 class CustomTask {
Danil Chapovalov33b716f2019-01-22 18:15:37 +010069 public:
70 explicit CustomTask(rtc::Event* ran) : ran_(ran) {}
71
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020072 void operator()() { ran_->Set(); }
Danil Chapovalov33b716f2019-01-22 18:15:37 +010073
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020074 private:
Danil Chapovalov33b716f2019-01-22 18:15:37 +010075 rtc::Event* const ran_;
76 } my_task(&ran);
77
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020078 queue->PostTask(my_task);
Markus Handell2cfc1af2022-08-19 08:16:48 +000079 EXPECT_TRUE(ran.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010080}
81
82TEST_P(TaskQueueTest, PostDelayedZero) {
Ali Tofigh4b681942022-08-23 12:57:16 +020083 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010084 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010085 auto queue = CreateTaskQueue(factory, "PostDelayedZero");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010086
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020087 queue->PostDelayedTask([&event] { event.Set(); }, TimeDelta::Zero());
Markus Handell2cfc1af2022-08-19 08:16:48 +000088 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010089}
90
91TEST_P(TaskQueueTest, PostFromQueue) {
Ali Tofigh4b681942022-08-23 12:57:16 +020092 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010093 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010094 auto queue = CreateTaskQueue(factory, "PostFromQueue");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010095
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020096 queue->PostTask(
97 [&event, &queue] { queue->PostTask([&event] { event.Set(); }); });
Markus Handell2cfc1af2022-08-19 08:16:48 +000098 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010099}
100
101TEST_P(TaskQueueTest, PostDelayed) {
Ali Tofigh4b681942022-08-23 12:57:16 +0200102 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100103 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100104 auto queue =
105 CreateTaskQueue(factory, "PostDelayed", TaskQueueFactory::Priority::HIGH);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100106
107 int64_t start = rtc::TimeMillis();
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200108 queue->PostDelayedTask(
109 [&event, &queue] {
110 EXPECT_TRUE(queue->IsCurrent());
111 event.Set();
112 },
113 TimeDelta::Millis(100));
Markus Handell2cfc1af2022-08-19 08:16:48 +0000114 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100115 int64_t end = rtc::TimeMillis();
116 // These tests are a little relaxed due to how "powerful" our test bots can
117 // be. Most recently we've seen windows bots fire the callback after 94-99ms,
118 // which is why we have a little bit of leeway backwards as well.
119 EXPECT_GE(end - start, 90u);
120 EXPECT_NEAR(end - start, 190u, 100u); // Accept 90-290.
121}
122
123TEST_P(TaskQueueTest, PostMultipleDelayed) {
Ali Tofigh4b681942022-08-23 12:57:16 +0200124 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100125 auto queue = CreateTaskQueue(factory, "PostMultipleDelayed");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100126
127 std::vector<rtc::Event> events(100);
128 for (int i = 0; i < 100; ++i) {
129 rtc::Event* event = &events[i];
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200130 queue->PostDelayedTask(
131 [event, &queue] {
132 EXPECT_TRUE(queue->IsCurrent());
133 event->Set();
134 },
135 TimeDelta::Millis(i));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100136 }
137
138 for (rtc::Event& e : events)
Markus Handell2cfc1af2022-08-19 08:16:48 +0000139 EXPECT_TRUE(e.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100140}
141
142TEST_P(TaskQueueTest, PostDelayedAfterDestruct) {
Ali Tofigh4b681942022-08-23 12:57:16 +0200143 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100144 rtc::Event run;
145 rtc::Event deleted;
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100146 auto queue = CreateTaskQueue(factory, "PostDelayedAfterDestruct");
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200147 absl::Cleanup cleanup = [&deleted] { deleted.Set(); };
148 queue->PostDelayedTask([&run, cleanup = std::move(cleanup)] { run.Set(); },
149 TimeDelta::Millis(100));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100150 // Destroy the queue.
151 queue = nullptr;
152 // Task might outlive the TaskQueue, but still should be deleted.
Markus Handell2cfc1af2022-08-19 08:16:48 +0000153 EXPECT_TRUE(deleted.Wait(TimeDelta::Seconds(1)));
154 EXPECT_FALSE(run.Wait(TimeDelta::Zero())); // and should not run.
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100155}
156
Markus Handell82da9322022-12-16 15:50:24 +0100157TEST_P(TaskQueueTest, PostDelayedHighPrecisionAfterDestruct) {
158 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
159 rtc::Event run;
160 rtc::Event deleted;
161 auto queue =
162 CreateTaskQueue(factory, "PostDelayedHighPrecisionAfterDestruct");
163 absl::Cleanup cleanup = [&deleted] { deleted.Set(); };
164 queue->PostDelayedHighPrecisionTask(
165 [&run, cleanup = std::move(cleanup)] { run.Set(); },
166 TimeDelta::Millis(100));
167 // Destroy the queue.
168 queue = nullptr;
169 // Task might outlive the TaskQueue, but still should be deleted.
170 EXPECT_TRUE(deleted.Wait(TimeDelta::Seconds(1)));
171 EXPECT_FALSE(run.Wait(TimeDelta::Zero())); // and should not run.
172}
173
174TEST_P(TaskQueueTest, PostedUnexecutedClosureDestroyedOnTaskQueue) {
175 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
176 auto queue =
177 CreateTaskQueue(factory, "PostedUnexecutedClosureDestroyedOnTaskQueue");
178 TaskQueueBase* queue_ptr = queue.get();
179 queue->PostTask([] { SleepFor(TimeDelta::Millis(100)); });
180 // Give the task queue a chance to start executing the first lambda.
181 SleepFor(TimeDelta::Millis(10));
182 // Then ensure the next lambda (which is likely not executing yet) is
183 // destroyed in the task queue context when the queue is deleted.
184 auto cleanup = absl::Cleanup(
185 [queue_ptr] { EXPECT_EQ(queue_ptr, TaskQueueBase::Current()); });
186 queue->PostTask([cleanup = std::move(cleanup)] {});
187 queue = nullptr;
188}
189
190TEST_P(TaskQueueTest, PostedExecutedClosureDestroyedOnTaskQueue) {
191 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
192 auto queue =
193 CreateTaskQueue(factory, "PostedExecutedClosureDestroyedOnTaskQueue");
194 TaskQueueBase* queue_ptr = queue.get();
195 // Ensure an executed lambda is destroyed on the task queue.
196 rtc::Event finished;
197 queue->PostTask([cleanup = absl::Cleanup([queue_ptr, &finished] {
198 EXPECT_EQ(queue_ptr, TaskQueueBase::Current());
199 finished.Set();
200 })] {});
201 finished.Wait(rtc::Event::kForever);
202}
203
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100204TEST_P(TaskQueueTest, PostAndReuse) {
Ali Tofigh4b681942022-08-23 12:57:16 +0200205 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100206 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100207 auto post_queue = CreateTaskQueue(factory, "PostQueue");
208 auto reply_queue = CreateTaskQueue(factory, "ReplyQueue");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100209
210 int call_count = 0;
211
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200212 class ReusedTask {
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100213 public:
214 ReusedTask(int* counter, TaskQueueBase* reply_queue, rtc::Event* event)
215 : counter_(*counter), reply_queue_(reply_queue), event_(*event) {
216 EXPECT_EQ(counter_, 0);
217 }
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200218 ReusedTask(ReusedTask&&) = default;
219 ReusedTask& operator=(ReusedTask&&) = delete;
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100220
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200221 void operator()() && {
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100222 if (++counter_ == 1) {
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200223 reply_queue_->PostTask(std::move(*this));
224 // At this point, the object is in the moved-from state.
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100225 } else {
226 EXPECT_EQ(counter_, 2);
227 EXPECT_TRUE(reply_queue_->IsCurrent());
228 event_.Set();
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100229 }
230 }
231
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200232 private:
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100233 int& counter_;
234 TaskQueueBase* const reply_queue_;
235 rtc::Event& event_;
236 };
237
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200238 ReusedTask task(&call_count, reply_queue.get(), &event);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100239 post_queue->PostTask(std::move(task));
Markus Handell2cfc1af2022-08-19 08:16:48 +0000240 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100241}
242
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100243TEST_P(TaskQueueTest, PostALot) {
Artem Titov0e61fdd2021-07-25 21:50:14 +0200244 // Waits until DecrementCount called `count` times. Thread safe.
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200245 class BlockingCounter {
246 public:
247 explicit BlockingCounter(int initial_count) : count_(initial_count) {}
248
249 void DecrementCount() {
250 if (count_.DecRef() == rtc::RefCountReleaseStatus::kDroppedLastRef) {
251 event_.Set();
252 }
253 }
Markus Handell2cfc1af2022-08-19 08:16:48 +0000254 bool Wait(TimeDelta give_up_after) { return event_.Wait(give_up_after); }
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200255
256 private:
257 webrtc_impl::RefCounter count_;
258 rtc::Event event_;
259 };
260
Ali Tofigh4b681942022-08-23 12:57:16 +0200261 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200262 static constexpr int kTaskCount = 0xffff;
263 rtc::Event posting_done;
264 BlockingCounter all_destroyed(kTaskCount);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100265
266 int tasks_executed = 0;
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200267 auto task_queue = CreateTaskQueue(factory, "PostALot");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100268
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200269 task_queue->PostTask([&] {
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200270 // Post tasks from the queue to guarantee that the 1st task won't be
271 // executed before the last one is posted.
272 for (int i = 0; i < kTaskCount; ++i) {
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200273 absl::Cleanup cleanup = [&] { all_destroyed.DecrementCount(); };
274 task_queue->PostTask([&tasks_executed, cleanup = std::move(cleanup)] {
275 ++tasks_executed;
276 });
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200277 }
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100278
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200279 posting_done.Set();
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200280 });
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100281
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200282 // Before destroying the task queue wait until all child tasks are posted.
Danil Chapovalov95e0a602019-06-11 13:49:20 +0200283 posting_done.Wait(rtc::Event::kForever);
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200284 // Destroy the task queue.
285 task_queue = nullptr;
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100286
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200287 // Expect all tasks are destroyed eventually. In some task queue
288 // implementations that might happen on a different thread after task queue is
289 // destroyed.
Markus Handell2cfc1af2022-08-19 08:16:48 +0000290 EXPECT_TRUE(all_destroyed.Wait(TimeDelta::Minutes(1)));
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200291 EXPECT_LE(tasks_executed, kTaskCount);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100292}
293
Artem Titov01f64e02019-01-31 13:31:09 +0100294// Test posting two tasks that have shared state not protected by a
295// lock. The TaskQueue should guarantee memory read-write order and
296// FIFO task execution order, so the second task should always see the
297// changes that were made by the first task.
298//
299// If the TaskQueue doesn't properly synchronize the execution of
300// tasks, there will be a data race, which is undefined behavior. The
301// EXPECT calls may randomly catch this, but to make the most of this
302// unit test, run it under TSan or some other tool that is able to
303// directly detect data races.
304TEST_P(TaskQueueTest, PostTwoWithSharedUnprotectedState) {
Ali Tofigh4b681942022-08-23 12:57:16 +0200305 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Artem Titov01f64e02019-01-31 13:31:09 +0100306 struct SharedState {
307 // First task will set this value to 1 and second will assert it.
308 int state = 0;
309 } state;
310
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100311 auto queue = CreateTaskQueue(factory, "PostTwoWithSharedUnprotectedState");
Artem Titov01f64e02019-01-31 13:31:09 +0100312 rtc::Event done;
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200313 queue->PostTask([&state, &queue, &done] {
Artem Titov01f64e02019-01-31 13:31:09 +0100314 // Post tasks from queue to guarantee, that 1st task won't be
315 // executed before the second one will be posted.
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200316 queue->PostTask([&state] { state.state = 1; });
317 queue->PostTask([&state, &done] {
Artem Titov01f64e02019-01-31 13:31:09 +0100318 EXPECT_EQ(state.state, 1);
319 done.Set();
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200320 });
Artem Titov01f64e02019-01-31 13:31:09 +0100321 // Check, that state changing tasks didn't start yet.
322 EXPECT_EQ(state.state, 0);
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200323 });
Markus Handell2cfc1af2022-08-19 08:16:48 +0000324 EXPECT_TRUE(done.Wait(TimeDelta::Seconds(1)));
Artem Titov01f64e02019-01-31 13:31:09 +0100325}
326
Danil Chapovalov5ad16a52020-06-09 14:44:31 +0200327// TaskQueueTest is a set of tests for any implementation of the TaskQueueBase.
328// Tests are instantiated next to the concrete implementation(s).
329// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#creating-value-parameterized-abstract-tests
330GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TaskQueueTest);
331
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100332} // namespace
333} // namespace webrtc