blob: 69532d61f42b05541b6ac00de62322e174fbfe67 [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"
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020016#include "api/units/time_delta.h"
Danil Chapovalov33b716f2019-01-22 18:15:37 +010017#include "rtc_base/event.h"
Danil Chapovalovf504dd32019-06-05 14:16:59 +020018#include "rtc_base/ref_counter.h"
Steve Antonf3802842019-01-24 19:07:40 -080019#include "rtc_base/time_utils.h"
Danil Chapovalov33b716f2019-01-22 18:15:37 +010020
21namespace webrtc {
22namespace {
23
24std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(
Danil Chapovalov710f3d32019-02-06 16:00:44 +010025 const std::unique_ptr<webrtc::TaskQueueFactory>& factory,
Danil Chapovalov33b716f2019-01-22 18:15:37 +010026 absl::string_view task_queue_name,
27 TaskQueueFactory::Priority priority = TaskQueueFactory::Priority::NORMAL) {
28 return factory->CreateTaskQueue(task_queue_name, priority);
29}
30
31TEST_P(TaskQueueTest, Construct) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010032 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
33 auto queue = CreateTaskQueue(factory, "Construct");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010034 EXPECT_FALSE(queue->IsCurrent());
35}
36
37TEST_P(TaskQueueTest, PostAndCheckCurrent) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010038 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010039 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010040 auto queue = CreateTaskQueue(factory, "PostAndCheckCurrent");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010041
Artem Titov0e61fdd2021-07-25 21:50:14 +020042 // We're not running a task, so `queue` shouldn't be current.
Tommi6866dc72020-05-15 10:11:56 +020043 // Note that because rtc::Thread also supports the TQ interface and
44 // TestMainImpl::Init wraps the main test thread (bugs.webrtc.org/9714), that
45 // means that TaskQueueBase::Current() will still return a valid value.
Danil Chapovalov33b716f2019-01-22 18:15:37 +010046 EXPECT_FALSE(queue->IsCurrent());
Danil Chapovalov33b716f2019-01-22 18:15:37 +010047
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020048 queue->PostTask([&event, &queue] {
Danil Chapovalov33b716f2019-01-22 18:15:37 +010049 EXPECT_TRUE(queue->IsCurrent());
50 event.Set();
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020051 });
Markus Handell2cfc1af2022-08-19 08:16:48 +000052 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010053}
54
55TEST_P(TaskQueueTest, PostCustomTask) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010056 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010057 rtc::Event ran;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010058 auto queue = CreateTaskQueue(factory, "PostCustomImplementation");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010059
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020060 class CustomTask {
Danil Chapovalov33b716f2019-01-22 18:15:37 +010061 public:
62 explicit CustomTask(rtc::Event* ran) : ran_(ran) {}
63
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020064 void operator()() { ran_->Set(); }
Danil Chapovalov33b716f2019-01-22 18:15:37 +010065
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020066 private:
Danil Chapovalov33b716f2019-01-22 18:15:37 +010067 rtc::Event* const ran_;
68 } my_task(&ran);
69
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020070 queue->PostTask(my_task);
Markus Handell2cfc1af2022-08-19 08:16:48 +000071 EXPECT_TRUE(ran.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010072}
73
74TEST_P(TaskQueueTest, PostDelayedZero) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010075 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010076 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010077 auto queue = CreateTaskQueue(factory, "PostDelayedZero");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010078
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020079 queue->PostDelayedTask([&event] { event.Set(); }, TimeDelta::Zero());
Markus Handell2cfc1af2022-08-19 08:16:48 +000080 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010081}
82
83TEST_P(TaskQueueTest, PostFromQueue) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010084 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010085 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010086 auto queue = CreateTaskQueue(factory, "PostFromQueue");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010087
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +020088 queue->PostTask(
89 [&event, &queue] { queue->PostTask([&event] { event.Set(); }); });
Markus Handell2cfc1af2022-08-19 08:16:48 +000090 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010091}
92
93TEST_P(TaskQueueTest, PostDelayed) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010094 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010095 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010096 auto queue =
97 CreateTaskQueue(factory, "PostDelayed", TaskQueueFactory::Priority::HIGH);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010098
99 int64_t start = rtc::TimeMillis();
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200100 queue->PostDelayedTask(
101 [&event, &queue] {
102 EXPECT_TRUE(queue->IsCurrent());
103 event.Set();
104 },
105 TimeDelta::Millis(100));
Markus Handell2cfc1af2022-08-19 08:16:48 +0000106 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100107 int64_t end = rtc::TimeMillis();
108 // These tests are a little relaxed due to how "powerful" our test bots can
109 // be. Most recently we've seen windows bots fire the callback after 94-99ms,
110 // which is why we have a little bit of leeway backwards as well.
111 EXPECT_GE(end - start, 90u);
112 EXPECT_NEAR(end - start, 190u, 100u); // Accept 90-290.
113}
114
115TEST_P(TaskQueueTest, PostMultipleDelayed) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100116 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
117 auto queue = CreateTaskQueue(factory, "PostMultipleDelayed");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100118
119 std::vector<rtc::Event> events(100);
120 for (int i = 0; i < 100; ++i) {
121 rtc::Event* event = &events[i];
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200122 queue->PostDelayedTask(
123 [event, &queue] {
124 EXPECT_TRUE(queue->IsCurrent());
125 event->Set();
126 },
127 TimeDelta::Millis(i));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100128 }
129
130 for (rtc::Event& e : events)
Markus Handell2cfc1af2022-08-19 08:16:48 +0000131 EXPECT_TRUE(e.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100132}
133
134TEST_P(TaskQueueTest, PostDelayedAfterDestruct) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100135 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100136 rtc::Event run;
137 rtc::Event deleted;
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100138 auto queue = CreateTaskQueue(factory, "PostDelayedAfterDestruct");
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200139 absl::Cleanup cleanup = [&deleted] { deleted.Set(); };
140 queue->PostDelayedTask([&run, cleanup = std::move(cleanup)] { run.Set(); },
141 TimeDelta::Millis(100));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100142 // Destroy the queue.
143 queue = nullptr;
144 // Task might outlive the TaskQueue, but still should be deleted.
Markus Handell2cfc1af2022-08-19 08:16:48 +0000145 EXPECT_TRUE(deleted.Wait(TimeDelta::Seconds(1)));
146 EXPECT_FALSE(run.Wait(TimeDelta::Zero())); // and should not run.
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100147}
148
149TEST_P(TaskQueueTest, PostAndReuse) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100150 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100151 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100152 auto post_queue = CreateTaskQueue(factory, "PostQueue");
153 auto reply_queue = CreateTaskQueue(factory, "ReplyQueue");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100154
155 int call_count = 0;
156
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200157 class ReusedTask {
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100158 public:
159 ReusedTask(int* counter, TaskQueueBase* reply_queue, rtc::Event* event)
160 : counter_(*counter), reply_queue_(reply_queue), event_(*event) {
161 EXPECT_EQ(counter_, 0);
162 }
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200163 ReusedTask(ReusedTask&&) = default;
164 ReusedTask& operator=(ReusedTask&&) = delete;
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100165
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200166 void operator()() && {
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100167 if (++counter_ == 1) {
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200168 reply_queue_->PostTask(std::move(*this));
169 // At this point, the object is in the moved-from state.
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100170 } else {
171 EXPECT_EQ(counter_, 2);
172 EXPECT_TRUE(reply_queue_->IsCurrent());
173 event_.Set();
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100174 }
175 }
176
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200177 private:
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100178 int& counter_;
179 TaskQueueBase* const reply_queue_;
180 rtc::Event& event_;
181 };
182
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200183 ReusedTask task(&call_count, reply_queue.get(), &event);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100184 post_queue->PostTask(std::move(task));
Markus Handell2cfc1af2022-08-19 08:16:48 +0000185 EXPECT_TRUE(event.Wait(TimeDelta::Seconds(1)));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100186}
187
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100188TEST_P(TaskQueueTest, PostALot) {
Artem Titov0e61fdd2021-07-25 21:50:14 +0200189 // Waits until DecrementCount called `count` times. Thread safe.
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200190 class BlockingCounter {
191 public:
192 explicit BlockingCounter(int initial_count) : count_(initial_count) {}
193
194 void DecrementCount() {
195 if (count_.DecRef() == rtc::RefCountReleaseStatus::kDroppedLastRef) {
196 event_.Set();
197 }
198 }
Markus Handell2cfc1af2022-08-19 08:16:48 +0000199 bool Wait(TimeDelta give_up_after) { return event_.Wait(give_up_after); }
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200200
201 private:
202 webrtc_impl::RefCounter count_;
203 rtc::Event event_;
204 };
205
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100206 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200207 static constexpr int kTaskCount = 0xffff;
208 rtc::Event posting_done;
209 BlockingCounter all_destroyed(kTaskCount);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100210
211 int tasks_executed = 0;
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200212 auto task_queue = CreateTaskQueue(factory, "PostALot");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100213
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200214 task_queue->PostTask([&] {
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200215 // Post tasks from the queue to guarantee that the 1st task won't be
216 // executed before the last one is posted.
217 for (int i = 0; i < kTaskCount; ++i) {
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200218 absl::Cleanup cleanup = [&] { all_destroyed.DecrementCount(); };
219 task_queue->PostTask([&tasks_executed, cleanup = std::move(cleanup)] {
220 ++tasks_executed;
221 });
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200222 }
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100223
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200224 posting_done.Set();
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200225 });
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100226
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200227 // Before destroying the task queue wait until all child tasks are posted.
Danil Chapovalov95e0a602019-06-11 13:49:20 +0200228 posting_done.Wait(rtc::Event::kForever);
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200229 // Destroy the task queue.
230 task_queue = nullptr;
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100231
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200232 // Expect all tasks are destroyed eventually. In some task queue
233 // implementations that might happen on a different thread after task queue is
234 // destroyed.
Markus Handell2cfc1af2022-08-19 08:16:48 +0000235 EXPECT_TRUE(all_destroyed.Wait(TimeDelta::Minutes(1)));
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200236 EXPECT_LE(tasks_executed, kTaskCount);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100237}
238
Artem Titov01f64e02019-01-31 13:31:09 +0100239// Test posting two tasks that have shared state not protected by a
240// lock. The TaskQueue should guarantee memory read-write order and
241// FIFO task execution order, so the second task should always see the
242// changes that were made by the first task.
243//
244// If the TaskQueue doesn't properly synchronize the execution of
245// tasks, there will be a data race, which is undefined behavior. The
246// EXPECT calls may randomly catch this, but to make the most of this
247// unit test, run it under TSan or some other tool that is able to
248// directly detect data races.
249TEST_P(TaskQueueTest, PostTwoWithSharedUnprotectedState) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100250 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Artem Titov01f64e02019-01-31 13:31:09 +0100251 struct SharedState {
252 // First task will set this value to 1 and second will assert it.
253 int state = 0;
254 } state;
255
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100256 auto queue = CreateTaskQueue(factory, "PostTwoWithSharedUnprotectedState");
Artem Titov01f64e02019-01-31 13:31:09 +0100257 rtc::Event done;
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200258 queue->PostTask([&state, &queue, &done] {
Artem Titov01f64e02019-01-31 13:31:09 +0100259 // Post tasks from queue to guarantee, that 1st task won't be
260 // executed before the second one will be posted.
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200261 queue->PostTask([&state] { state.state = 1; });
262 queue->PostTask([&state, &done] {
Artem Titov01f64e02019-01-31 13:31:09 +0100263 EXPECT_EQ(state.state, 1);
264 done.Set();
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200265 });
Artem Titov01f64e02019-01-31 13:31:09 +0100266 // Check, that state changing tasks didn't start yet.
267 EXPECT_EQ(state.state, 0);
Danil Chapovalov8feb6fd2022-07-05 11:01:27 +0200268 });
Markus Handell2cfc1af2022-08-19 08:16:48 +0000269 EXPECT_TRUE(done.Wait(TimeDelta::Seconds(1)));
Artem Titov01f64e02019-01-31 13:31:09 +0100270}
271
Danil Chapovalov5ad16a52020-06-09 14:44:31 +0200272// TaskQueueTest is a set of tests for any implementation of the TaskQueueBase.
273// Tests are instantiated next to the concrete implementation(s).
274// https://github.com/google/googletest/blob/master/googletest/docs/advanced.md#creating-value-parameterized-abstract-tests
275GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TaskQueueTest);
276
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100277} // namespace
278} // namespace webrtc