blob: 8d02ed6c9e755c235861758d4d24461d11656721 [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 Chapovalov3b548dd2019-03-01 14:58:44 +010015#include "rtc_base/task_utils/to_queued_task.h"
Steve Antonf3802842019-01-24 19:07:40 -080016#include "rtc_base/time_utils.h"
Danil Chapovalov33b716f2019-01-22 18:15:37 +010017
18namespace webrtc {
19namespace {
20
21std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue(
Danil Chapovalov710f3d32019-02-06 16:00:44 +010022 const std::unique_ptr<webrtc::TaskQueueFactory>& factory,
Danil Chapovalov33b716f2019-01-22 18:15:37 +010023 absl::string_view task_queue_name,
24 TaskQueueFactory::Priority priority = TaskQueueFactory::Priority::NORMAL) {
25 return factory->CreateTaskQueue(task_queue_name, priority);
26}
27
28TEST_P(TaskQueueTest, Construct) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010029 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
30 auto queue = CreateTaskQueue(factory, "Construct");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010031 EXPECT_FALSE(queue->IsCurrent());
32}
33
34TEST_P(TaskQueueTest, PostAndCheckCurrent) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010035 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010036 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010037 auto queue = CreateTaskQueue(factory, "PostAndCheckCurrent");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010038
39 // We're not running a task, so there shouldn't be a current queue.
40 EXPECT_FALSE(queue->IsCurrent());
41 EXPECT_FALSE(TaskQueueBase::Current());
42
Danil Chapovalov3b548dd2019-03-01 14:58:44 +010043 queue->PostTask(ToQueuedTask([&event, &queue] {
Danil Chapovalov33b716f2019-01-22 18:15:37 +010044 EXPECT_TRUE(queue->IsCurrent());
45 event.Set();
46 }));
47 EXPECT_TRUE(event.Wait(1000));
48}
49
50TEST_P(TaskQueueTest, PostCustomTask) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010051 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010052 rtc::Event ran;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010053 auto queue = CreateTaskQueue(factory, "PostCustomImplementation");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010054
55 class CustomTask : public QueuedTask {
56 public:
57 explicit CustomTask(rtc::Event* ran) : ran_(ran) {}
58
59 private:
60 bool Run() override {
61 ran_->Set();
62 return false; // Do not allow the task to be deleted by the queue.
63 }
64
65 rtc::Event* const ran_;
66 } my_task(&ran);
67
68 queue->PostTask(absl::WrapUnique(&my_task));
69 EXPECT_TRUE(ran.Wait(1000));
70}
71
72TEST_P(TaskQueueTest, PostDelayedZero) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010073 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010074 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010075 auto queue = CreateTaskQueue(factory, "PostDelayedZero");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010076
Danil Chapovalov3b548dd2019-03-01 14:58:44 +010077 queue->PostDelayedTask(ToQueuedTask([&event] { event.Set(); }), 0);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010078 EXPECT_TRUE(event.Wait(1000));
79}
80
81TEST_P(TaskQueueTest, PostFromQueue) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010082 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010083 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010084 auto queue = CreateTaskQueue(factory, "PostFromQueue");
Danil Chapovalov33b716f2019-01-22 18:15:37 +010085
Danil Chapovalov3b548dd2019-03-01 14:58:44 +010086 queue->PostTask(ToQueuedTask([&event, &queue] {
87 queue->PostTask(ToQueuedTask([&event] { event.Set(); }));
Danil Chapovalov33b716f2019-01-22 18:15:37 +010088 }));
89 EXPECT_TRUE(event.Wait(1000));
90}
91
92TEST_P(TaskQueueTest, PostDelayed) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +010093 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +010094 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +010095 auto queue =
96 CreateTaskQueue(factory, "PostDelayed", TaskQueueFactory::Priority::HIGH);
Danil Chapovalov33b716f2019-01-22 18:15:37 +010097
98 int64_t start = rtc::TimeMillis();
Danil Chapovalov3b548dd2019-03-01 14:58:44 +010099 queue->PostDelayedTask(ToQueuedTask([&event, &queue] {
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100100 EXPECT_TRUE(queue->IsCurrent());
101 event.Set();
102 }),
103 100);
104 EXPECT_TRUE(event.Wait(1000));
105 int64_t end = rtc::TimeMillis();
106 // These tests are a little relaxed due to how "powerful" our test bots can
107 // be. Most recently we've seen windows bots fire the callback after 94-99ms,
108 // which is why we have a little bit of leeway backwards as well.
109 EXPECT_GE(end - start, 90u);
110 EXPECT_NEAR(end - start, 190u, 100u); // Accept 90-290.
111}
112
113TEST_P(TaskQueueTest, PostMultipleDelayed) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100114 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
115 auto queue = CreateTaskQueue(factory, "PostMultipleDelayed");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100116
117 std::vector<rtc::Event> events(100);
118 for (int i = 0; i < 100; ++i) {
119 rtc::Event* event = &events[i];
Danil Chapovalov3b548dd2019-03-01 14:58:44 +0100120 queue->PostDelayedTask(ToQueuedTask([event, &queue] {
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100121 EXPECT_TRUE(queue->IsCurrent());
122 event->Set();
123 }),
124 i);
125 }
126
127 for (rtc::Event& e : events)
128 EXPECT_TRUE(e.Wait(1000));
129}
130
131TEST_P(TaskQueueTest, PostDelayedAfterDestruct) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100132 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100133 rtc::Event run;
134 rtc::Event deleted;
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100135 auto queue = CreateTaskQueue(factory, "PostDelayedAfterDestruct");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100136 queue->PostDelayedTask(
Danil Chapovalov3b548dd2019-03-01 14:58:44 +0100137 ToQueuedTask([&run] { run.Set(); }, [&deleted] { deleted.Set(); }), 100);
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100138 // Destroy the queue.
139 queue = nullptr;
140 // Task might outlive the TaskQueue, but still should be deleted.
141 EXPECT_TRUE(deleted.Wait(200));
142 EXPECT_FALSE(run.Wait(0)); // and should not run.
143}
144
145TEST_P(TaskQueueTest, PostAndReuse) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100146 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100147 rtc::Event event;
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100148 auto post_queue = CreateTaskQueue(factory, "PostQueue");
149 auto reply_queue = CreateTaskQueue(factory, "ReplyQueue");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100150
151 int call_count = 0;
152
153 class ReusedTask : public QueuedTask {
154 public:
155 ReusedTask(int* counter, TaskQueueBase* reply_queue, rtc::Event* event)
156 : counter_(*counter), reply_queue_(reply_queue), event_(*event) {
157 EXPECT_EQ(counter_, 0);
158 }
159
160 private:
161 bool Run() override {
162 if (++counter_ == 1) {
163 reply_queue_->PostTask(absl::WrapUnique(this));
164 // At this point, the object is owned by reply_queue_ and it's
165 // theoratically possible that the object has been deleted (e.g. if
166 // posting wasn't possible). So, don't touch any member variables here.
167
168 // Indicate to the current queue that ownership has been transferred.
169 return false;
170 } else {
171 EXPECT_EQ(counter_, 2);
172 EXPECT_TRUE(reply_queue_->IsCurrent());
173 event_.Set();
174 return true; // Indicate that the object should be deleted.
175 }
176 }
177
178 int& counter_;
179 TaskQueueBase* const reply_queue_;
180 rtc::Event& event_;
181 };
182
183 auto task =
184 absl::make_unique<ReusedTask>(&call_count, reply_queue.get(), &event);
185 post_queue->PostTask(std::move(task));
186 EXPECT_TRUE(event.Wait(1000));
187}
188
189// Tests posting more messages than a queue can queue up.
190// In situations like that, tasks will get dropped.
191TEST_P(TaskQueueTest, PostALot) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100192 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100193 // To destruct the event after the queue has gone out of scope.
194 rtc::Event event;
195
196 int tasks_executed = 0;
197 int tasks_cleaned_up = 0;
198 static const int kTaskCount = 0xffff;
199
200 {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100201 auto queue = CreateTaskQueue(factory, "PostALot");
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100202
203 // On linux, the limit of pending bytes in the pipe buffer is 0xffff.
204 // So here we post a total of 0xffff+1 messages, which triggers a failure
205 // case inside of the libevent queue implementation.
206
François Doray8ea977d2019-03-22 13:01:54 -0400207 queue->PostTask(ToQueuedTask([&event] {
208 rtc::ScopedAllowBaseSyncPrimitivesForTesting allow_base_sync_primitives;
209 event.Wait(rtc::Event::kForever);
210 }));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100211 for (int i = 0; i < kTaskCount; ++i)
212 queue->PostTask(
Danil Chapovalov3b548dd2019-03-01 14:58:44 +0100213 ToQueuedTask([&tasks_executed] { ++tasks_executed; },
214 [&tasks_cleaned_up] { ++tasks_cleaned_up; }));
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100215 event.Set(); // Unblock the first task.
216 }
217
218 EXPECT_GE(tasks_cleaned_up, tasks_executed);
219 EXPECT_EQ(tasks_cleaned_up, kTaskCount);
220}
221
Artem Titov01f64e02019-01-31 13:31:09 +0100222// Test posting two tasks that have shared state not protected by a
223// lock. The TaskQueue should guarantee memory read-write order and
224// FIFO task execution order, so the second task should always see the
225// changes that were made by the first task.
226//
227// If the TaskQueue doesn't properly synchronize the execution of
228// tasks, there will be a data race, which is undefined behavior. The
229// EXPECT calls may randomly catch this, but to make the most of this
230// unit test, run it under TSan or some other tool that is able to
231// directly detect data races.
232TEST_P(TaskQueueTest, PostTwoWithSharedUnprotectedState) {
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100233 std::unique_ptr<webrtc::TaskQueueFactory> factory = GetParam()();
Artem Titov01f64e02019-01-31 13:31:09 +0100234 struct SharedState {
235 // First task will set this value to 1 and second will assert it.
236 int state = 0;
237 } state;
238
Danil Chapovalov710f3d32019-02-06 16:00:44 +0100239 auto queue = CreateTaskQueue(factory, "PostTwoWithSharedUnprotectedState");
Artem Titov01f64e02019-01-31 13:31:09 +0100240 rtc::Event done;
Danil Chapovalov3b548dd2019-03-01 14:58:44 +0100241 queue->PostTask(ToQueuedTask([&state, &queue, &done] {
Artem Titov01f64e02019-01-31 13:31:09 +0100242 // Post tasks from queue to guarantee, that 1st task won't be
243 // executed before the second one will be posted.
Danil Chapovalov3b548dd2019-03-01 14:58:44 +0100244 queue->PostTask(ToQueuedTask([&state] { state.state = 1; }));
245 queue->PostTask(ToQueuedTask([&state, &done] {
Artem Titov01f64e02019-01-31 13:31:09 +0100246 EXPECT_EQ(state.state, 1);
247 done.Set();
248 }));
249 // Check, that state changing tasks didn't start yet.
250 EXPECT_EQ(state.state, 0);
251 }));
252 EXPECT_TRUE(done.Wait(1000));
253}
254
Danil Chapovalov33b716f2019-01-22 18:15:37 +0100255} // namespace
256} // namespace webrtc