blob: 0f6b1d013fae970b24c2ba48f87ee87c85b25e92 [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"
Ali Tofigh4b681942022-08-23 12:57:16 +020016#include "api/task_queue/default_task_queue_factory.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
25std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(
Danil Chapovalov710f3d32019-02-06 16:00:44 +010026 const std::unique_ptr<webrtc::TaskQueueFactory>& factory,
Danil Chapovalov33b716f2019-01-22 18:15:37 +010027 absl::string_view task_queue_name,
28 TaskQueueFactory::Priority priority = TaskQueueFactory::Priority::NORMAL) {
29 return factory->CreateTaskQueue(task_queue_name, priority);
30}
31
32TEST_P(TaskQueueTest, Construct) {
Ali Tofigh4b681942022-08-23 12:57:16 +020033 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov710f3d32019-02-06 16:00:44 +010034 auto queue = CreateTaskQueue(factory, "Construct");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010035 EXPECT_FALSE(queue->IsCurrent());
36}
37
38TEST_P(TaskQueueTest, PostAndCheckCurrent) {
Ali Tofigh4b681942022-08-23 12:57:16 +020039 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010040 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010041 auto queue = CreateTaskQueue(factory, "PostAndCheckCurrent");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010042
Artem Titov0e61fdd2021-07-25 21:50:14 +020043 // We're not running a task, so `queue` shouldn't be current.
Tommi6866dc72020-05-15 10:11:56 +020044 // 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.
Danil Chapovalov33b716f2019-01-22 18:15:37 +010047 EXPECT_FALSE(queue->IsCurrent());
Danil Chapovalov33b716f2019-01-22 18:15:37 +010048
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020049 queue->PostTask([&event, &queue] {
Danil Chapovalov33b716f2019-01-22 18:15:37 +010050 EXPECT_TRUE(queue->IsCurrent());
51 event.Set();
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020052 });
Markus Handell2cfc1af2022-08-19 08:16:48 +000053 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010054}
55
56TEST_P(TaskQueueTest, PostCustomTask) {
Ali Tofigh4b681942022-08-23 12:57:16 +020057 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010058 rtc::Event ran;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010059 auto queue = CreateTaskQueue(factory, "PostCustomImplementation");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010060
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020061 class CustomTask {
Danil Chapovalov33b716f2019-01-22 18:15:37 +010062 public:
63 explicit CustomTask(rtc::Event* ran) : ran_(ran) {}
64
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020065 void operator()() { ran_->Set(); }
Danil Chapovalov33b716f2019-01-22 18:15:37 +010066
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020067 private:
Danil Chapovalov33b716f2019-01-22 18:15:37 +010068 rtc::Event* const ran_;
69 } my_task(&ran);
70
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020071 queue->PostTask(my_task);
Markus Handell2cfc1af2022-08-19 08:16:48 +000072 EXPECT_TRUE(ran.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010073}
74
75TEST_P(TaskQueueTest, PostDelayedZero) {
Ali Tofigh4b681942022-08-23 12:57:16 +020076 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010077 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010078 auto queue = CreateTaskQueue(factory, "PostDelayedZero");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010079
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020080 queue->PostDelayedTask([&event] { event.Set(); }, TimeDelta::Zero());
Markus Handell2cfc1af2022-08-19 08:16:48 +000081 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010082}
83
84TEST_P(TaskQueueTest, PostFromQueue) {
Ali Tofigh4b681942022-08-23 12:57:16 +020085 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010086 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010087 auto queue = CreateTaskQueue(factory, "PostFromQueue");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010088
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020089 queue->PostTask(
90 [&event, &queue] { queue->PostTask([&event] { event.Set(); }); });
Markus Handell2cfc1af2022-08-19 08:16:48 +000091 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010092}
93
94TEST_P(TaskQueueTest, PostDelayed) {
Ali Tofigh4b681942022-08-23 12:57:16 +020095 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010096 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010097 auto queue =
98 CreateTaskQueue(factory, "PostDelayed", TaskQueueFactory::Priority::HIGH);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010099
100 int64_t start = rtc::TimeMillis();
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200101 queue->PostDelayedTask(
102 [&event, &queue] {
103 EXPECT_TRUE(queue->IsCurrent());
104 event.Set();
105 },
106 TimeDelta::Millis(100));
Markus Handell2cfc1af2022-08-19 08:16:48 +0000107 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100108 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
116TEST_P(TaskQueueTest, PostMultipleDelayed) {
Ali Tofigh4b681942022-08-23 12:57:16 +0200117 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100118 auto queue = CreateTaskQueue(factory, "PostMultipleDelayed");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100119
120 std::vector<rtc::Event> events(100);
121 for (int i = 0; i < 100; ++i) {
122 rtc::Event* event = &events[i];
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200123 queue->PostDelayedTask(
124 [event, &queue] {
125 EXPECT_TRUE(queue->IsCurrent());
126 event->Set();
127 },
128 TimeDelta::Millis(i));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100129 }
130
131 for (rtc::Event& e : events)
Markus Handell2cfc1af2022-08-19 08:16:48 +0000132 EXPECT_TRUE(e.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100133}
134
135TEST_P(TaskQueueTest, PostDelayedAfterDestruct) {
Ali Tofigh4b681942022-08-23 12:57:16 +0200136 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100137 rtc::Event run;
138 rtc::Event deleted;
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100139 auto queue = CreateTaskQueue(factory, "PostDelayedAfterDestruct");
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200140 absl::Cleanup cleanup = [&deleted] { deleted.Set(); };
141 queue->PostDelayedTask([&run, cleanup = std::move(cleanup)] { run.Set(); },
142 TimeDelta::Millis(100));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100143 // Destroy the queue.
144 queue = nullptr;
145 // Task might outlive the TaskQueue, but still should be deleted.
Markus Handell2cfc1af2022-08-19 08:16:48 +0000146 EXPECT_TRUE(deleted.Wait(TimeDelta::Seconds(1)));
147 EXPECT_FALSE(run.Wait(TimeDelta::Zero())); // and should not run.
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100148}
149
150TEST_P(TaskQueueTest, PostAndReuse) {
Ali Tofigh4b681942022-08-23 12:57:16 +0200151 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100152 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100153 auto post_queue = CreateTaskQueue(factory, "PostQueue");
154 auto reply_queue = CreateTaskQueue(factory, "ReplyQueue");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100155
156 int call_count = 0;
157
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200158 class ReusedTask {
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100159 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 }
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200164 ReusedTask(ReusedTask&&) = default;
165 ReusedTask& operator=(ReusedTask&&) = delete;
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100166
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200167 void operator()() && {
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100168 if (++counter_ == 1) {
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200169 reply_queue_->PostTask(std::move(*this));
170 // At this point, the object is in the moved-from state.
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100171 } else {
172 EXPECT_EQ(counter_, 2);
173 EXPECT_TRUE(reply_queue_->IsCurrent());
174 event_.Set();
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100175 }
176 }
177
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200178 private:
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100179 int& counter_;
180 TaskQueueBase* const reply_queue_;
181 rtc::Event& event_;
182 };
183
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200184 ReusedTask task(&call_count, reply_queue.get(), &event);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100185 post_queue->PostTask(std::move(task));
Markus Handell2cfc1af2022-08-19 08:16:48 +0000186 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100187}
188
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100189TEST_P(TaskQueueTest, PostALot) {
Artem Titov0e61fdd2021-07-25 21:50:14 +0200190 // Waits until DecrementCount called `count` times. Thread safe.
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200191 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 }
Markus Handell2cfc1af2022-08-19 08:16:48 +0000200 bool Wait(TimeDelta give_up_after) { return event_.Wait(give_up_after); }
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200201
202 private:
203 webrtc_impl::RefCounter count_;
204 rtc::Event event_;
205 };
206
Ali Tofigh4b681942022-08-23 12:57:16 +0200207 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200208 static constexpr int kTaskCount = 0xffff;
209 rtc::Event posting_done;
210 BlockingCounter all_destroyed(kTaskCount);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100211
212 int tasks_executed = 0;
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200213 auto task_queue = CreateTaskQueue(factory, "PostALot");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100214
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200215 task_queue->PostTask([&] {
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200216 // 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) {
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200219 absl::Cleanup cleanup = [&] { all_destroyed.DecrementCount(); };
220 task_queue->PostTask([&tasks_executed, cleanup = std::move(cleanup)] {
221 ++tasks_executed;
222 });
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200223 }
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100224
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200225 posting_done.Set();
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200226 });
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100227
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200228 // Before destroying the task queue wait until all child tasks are posted.
Danil Chapovalov95e0a602019-06-11 13:49:20 +0200229 posting_done.Wait(rtc::Event::kForever);
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200230 // Destroy the task queue.
231 task_queue = nullptr;
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100232
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200233 // 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.
Markus Handell2cfc1af2022-08-19 08:16:48 +0000236 EXPECT_TRUE(all_destroyed.Wait(TimeDelta::Minutes(1)));
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200237 EXPECT_LE(tasks_executed, kTaskCount);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100238}
239
Artem Titov01f64e02019-01-31 13:31:09 +0100240// 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.
250TEST_P(TaskQueueTest, PostTwoWithSharedUnprotectedState) {
Ali Tofigh4b681942022-08-23 12:57:16 +0200251 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()(nullptr);
Artem Titov01f64e02019-01-31 13:31:09 +0100252 struct SharedState {
253 // First task will set this value to 1 and second will assert it.
254 int state = 0;
255 } state;
256
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100257 auto queue = CreateTaskQueue(factory, "PostTwoWithSharedUnprotectedState");
Artem Titov01f64e02019-01-31 13:31:09 +0100258 rtc::Event done;
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200259 queue->PostTask([&state, &queue, &done] {
Artem Titov01f64e02019-01-31 13:31:09 +0100260 // Post tasks from queue to guarantee, that 1st task won't be
261 // executed before the second one will be posted.
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200262 queue->PostTask([&state] { state.state = 1; });
263 queue->PostTask([&state, &done] {
Artem Titov01f64e02019-01-31 13:31:09 +0100264 EXPECT_EQ(state.state, 1);
265 done.Set();
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200266 });
Artem Titov01f64e02019-01-31 13:31:09 +0100267 // Check, that state changing tasks didn't start yet.
268 EXPECT_EQ(state.state, 0);
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200269 });
Markus Handell2cfc1af2022-08-19 08:16:48 +0000270 EXPECT_TRUE(done.Wait(TimeDelta::Seconds(1)));
Artem Titov01f64e02019-01-31 13:31:09 +0100271}
272
Danil Chapovalov5ad16a52020-06-09 14:44:31 +0200273// 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
276GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TaskQueueTest);
277
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100278} // namespace
279} // namespace webrtc