blob: 31cf31e03002d6fe5a4129ca9682faf8150d7b67 [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
12#include "absl/memory/memory.h"
13#include "absl/strings/string_view.h"
14#include "rtc_base/event.h"
Danil Chapovalovf504dd32019-06-05 14:16:59 +020015#include "rtc_base/ref_counter.h"
Danil Chapovalov3b548dd2019-03-01 14:58:44 +010016#include "rtc_base/task_utils/to_queued_task.h"
Steve Antonf3802842019-01-24 19:07:40 -080017#include "rtc_base/time_utils.h"
Danil Chapovalov33b716f2019-01-22 18:15:37 +010018
19namespace webrtc {
20namespace {
21
22std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(
Danil Chapovalov710f3d32019-02-06 16:00:44 +010023 const std::unique_ptr<webrtc::TaskQueueFactory>& factory,
Danil Chapovalov33b716f2019-01-22 18:15:37 +010024 absl::string_view task_queue_name,
25 TaskQueueFactory::Priority priority = TaskQueueFactory::Priority::NORMAL) {
26 return factory->CreateTaskQueue(task_queue_name, priority);
27}
28
29TEST_P(TaskQueueTest, Construct) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010030 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
31 auto queue = CreateTaskQueue(factory, "Construct");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010032 EXPECT_FALSE(queue->IsCurrent());
33}
34
35TEST_P(TaskQueueTest, PostAndCheckCurrent) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010036 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010037 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010038 auto queue = CreateTaskQueue(factory, "PostAndCheckCurrent");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010039
40 // We're not running a task, so there shouldn't be a current queue.
41 EXPECT_FALSE(queue->IsCurrent());
42 EXPECT_FALSE(TaskQueueBase::Current());
43
Danil Chapovalov3b548dd2019-03-01 14:58:44 +010044 queue->PostTask(ToQueuedTask([&event, &queue] {
Danil Chapovalov33b716f2019-01-22 18:15:37 +010045 EXPECT_TRUE(queue->IsCurrent());
46 event.Set();
47 }));
48 EXPECT_TRUE(event.Wait(1000));
49}
50
51TEST_P(TaskQueueTest, PostCustomTask) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010052 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010053 rtc::Event ran;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010054 auto queue = CreateTaskQueue(factory, "PostCustomImplementation");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010055
56 class CustomTask : public QueuedTask {
57 public:
58 explicit CustomTask(rtc::Event* ran) : ran_(ran) {}
59
60 private:
61 bool Run() override {
62 ran_->Set();
63 return false; // Do not allow the task to be deleted by the queue.
64 }
65
66 rtc::Event* const ran_;
67 } my_task(&ran);
68
69 queue->PostTask(absl::WrapUnique(&my_task));
70 EXPECT_TRUE(ran.Wait(1000));
71}
72
73TEST_P(TaskQueueTest, PostDelayedZero) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010074 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010075 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010076 auto queue = CreateTaskQueue(factory, "PostDelayedZero");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010077
Danil Chapovalov3b548dd2019-03-01 14:58:44 +010078 queue->PostDelayedTask(ToQueuedTask([&event] { event.Set(); }), 0);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010079 EXPECT_TRUE(event.Wait(1000));
80}
81
82TEST_P(TaskQueueTest, PostFromQueue) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010083 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010084 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010085 auto queue = CreateTaskQueue(factory, "PostFromQueue");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010086
Danil Chapovalov3b548dd2019-03-01 14:58:44 +010087 queue->PostTask(ToQueuedTask([&event, &queue] {
88 queue->PostTask(ToQueuedTask([&event] { event.Set(); }));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010089 }));
90 EXPECT_TRUE(event.Wait(1000));
91}
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 Chapovalov3b548dd2019-03-01 14:58:44 +0100100 queue->PostDelayedTask(ToQueuedTask([&event, &queue] {
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100101 EXPECT_TRUE(queue->IsCurrent());
102 event.Set();
103 }),
104 100);
105 EXPECT_TRUE(event.Wait(1000));
106 int64_t end = rtc::TimeMillis();
107 // These tests are a little relaxed due to how "powerful" our test bots can
108 // be. Most recently we've seen windows bots fire the callback after 94-99ms,
109 // which is why we have a little bit of leeway backwards as well.
110 EXPECT_GE(end - start, 90u);
111 EXPECT_NEAR(end - start, 190u, 100u); // Accept 90-290.
112}
113
114TEST_P(TaskQueueTest, PostMultipleDelayed) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100115 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
116 auto queue = CreateTaskQueue(factory, "PostMultipleDelayed");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100117
118 std::vector<rtc::Event> events(100);
119 for (int i = 0; i < 100; ++i) {
120 rtc::Event* event = &events[i];
Danil Chapovalov3b548dd2019-03-01 14:58:44 +0100121 queue->PostDelayedTask(ToQueuedTask([event, &queue] {
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100122 EXPECT_TRUE(queue->IsCurrent());
123 event->Set();
124 }),
125 i);
126 }
127
128 for (rtc::Event& e : events)
129 EXPECT_TRUE(e.Wait(1000));
130}
131
132TEST_P(TaskQueueTest, PostDelayedAfterDestruct) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100133 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100134 rtc::Event run;
135 rtc::Event deleted;
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100136 auto queue = CreateTaskQueue(factory, "PostDelayedAfterDestruct");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100137 queue->PostDelayedTask(
Danil Chapovalov3b548dd2019-03-01 14:58:44 +0100138 ToQueuedTask([&run] { run.Set(); }, [&deleted] { deleted.Set(); }), 100);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100139 // Destroy the queue.
140 queue = nullptr;
141 // Task might outlive the TaskQueue, but still should be deleted.
Danil Chapovalov2c6ea522019-09-16 11:37:28 +0200142 EXPECT_TRUE(deleted.Wait(1000));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100143 EXPECT_FALSE(run.Wait(0)); // and should not run.
144}
145
146TEST_P(TaskQueueTest, PostAndReuse) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100147 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100148 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100149 auto post_queue = CreateTaskQueue(factory, "PostQueue");
150 auto reply_queue = CreateTaskQueue(factory, "ReplyQueue");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100151
152 int call_count = 0;
153
154 class ReusedTask : public QueuedTask {
155 public:
156 ReusedTask(int* counter, TaskQueueBase* reply_queue, rtc::Event* event)
157 : counter_(*counter), reply_queue_(reply_queue), event_(*event) {
158 EXPECT_EQ(counter_, 0);
159 }
160
161 private:
162 bool Run() override {
163 if (++counter_ == 1) {
164 reply_queue_->PostTask(absl::WrapUnique(this));
165 // At this point, the object is owned by reply_queue_ and it's
166 // theoratically possible that the object has been deleted (e.g. if
167 // posting wasn't possible). So, don't touch any member variables here.
168
169 // Indicate to the current queue that ownership has been transferred.
170 return false;
171 } else {
172 EXPECT_EQ(counter_, 2);
173 EXPECT_TRUE(reply_queue_->IsCurrent());
174 event_.Set();
175 return true; // Indicate that the object should be deleted.
176 }
177 }
178
179 int& counter_;
180 TaskQueueBase* const reply_queue_;
181 rtc::Event& event_;
182 };
183
184 auto task =
185 absl::make_unique<ReusedTask>(&call_count, reply_queue.get(), &event);
186 post_queue->PostTask(std::move(task));
187 EXPECT_TRUE(event.Wait(1000));
188}
189
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100190TEST_P(TaskQueueTest, PostALot) {
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200191 // Waits until DecrementCount called |count| times. Thread safe.
192 class BlockingCounter {
193 public:
194 explicit BlockingCounter(int initial_count) : count_(initial_count) {}
195
196 void DecrementCount() {
197 if (count_.DecRef() == rtc::RefCountReleaseStatus::kDroppedLastRef) {
198 event_.Set();
199 }
200 }
201 bool Wait(int give_up_after_ms) { return event_.Wait(give_up_after_ms); }
202
203 private:
204 webrtc_impl::RefCounter count_;
205 rtc::Event event_;
206 };
207
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100208 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200209 static constexpr int kTaskCount = 0xffff;
210 rtc::Event posting_done;
211 BlockingCounter all_destroyed(kTaskCount);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100212
213 int tasks_executed = 0;
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200214 auto task_queue = CreateTaskQueue(factory, "PostALot");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100215
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200216 task_queue->PostTask(ToQueuedTask([&] {
217 // Post tasks from the queue to guarantee that the 1st task won't be
218 // executed before the last one is posted.
219 for (int i = 0; i < kTaskCount; ++i) {
220 task_queue->PostTask(ToQueuedTask(
221 [&] { ++tasks_executed; }, [&] { all_destroyed.DecrementCount(); }));
222 }
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100223
Danil Chapovalovf504dd32019-06-05 14:16:59 +0200224 posting_done.Set();
225 }));
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.
Danil Chapovalov95e0a602019-06-11 13:49:20 +0200235 EXPECT_TRUE(all_destroyed.Wait(60000));
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 Chapovalov3b548dd2019-03-01 14:58:44 +0100258 queue->PostTask(ToQueuedTask([&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 Chapovalov3b548dd2019-03-01 14:58:44 +0100261 queue->PostTask(ToQueuedTask([&state] { state.state = 1; }));
262 queue->PostTask(ToQueuedTask([&state, &done] {
Artem Titov01f64e02019-01-31 13:31:09 +0100263 EXPECT_EQ(state.state, 1);
264 done.Set();
265 }));
266 // Check, that state changing tasks didn't start yet.
267 EXPECT_EQ(state.state, 0);
268 }));
269 EXPECT_TRUE(done.Wait(1000));
270}
271
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100272} // namespace
273} // namespace webrtc