blob: 3af337dfaf338eda1a2c9e2db927217fdfebaae7 [file] [log] [blame]
tommic06b1332016-05-14 11:31:40 -07001/*
2 * Copyright 2016 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
11#include <memory>
12#include <vector>
13
14#include "webrtc/base/bind.h"
15#include "webrtc/base/event.h"
16#include "webrtc/base/gunit.h"
17#include "webrtc/base/task_queue.h"
18#include "webrtc/base/timeutils.h"
19
20namespace rtc {
21
22namespace {
23void CheckCurrent(const char* expected_queue, Event* signal, TaskQueue* queue) {
24 EXPECT_TRUE(TaskQueue::IsCurrent(expected_queue));
25 EXPECT_TRUE(queue->IsCurrent());
26 if (signal)
27 signal->Set();
28}
29
30} // namespace
31
32TEST(TaskQueueTest, Construct) {
33 static const char kQueueName[] = "Construct";
34 TaskQueue queue(kQueueName);
35 EXPECT_FALSE(queue.IsCurrent());
36}
37
38TEST(TaskQueueTest, PostAndCheckCurrent) {
39 static const char kQueueName[] = "PostAndCheckCurrent";
40 TaskQueue queue(kQueueName);
41
42 // We're not running a task, so there shouldn't be a current queue.
43 EXPECT_FALSE(queue.IsCurrent());
44 EXPECT_FALSE(TaskQueue::Current());
45
46 Event event(false, false);
47 queue.PostTask(Bind(&CheckCurrent, kQueueName, &event, &queue));
48 EXPECT_TRUE(event.Wait(1000));
49}
50
51TEST(TaskQueueTest, PostCustomTask) {
52 static const char kQueueName[] = "PostCustomImplementation";
53 TaskQueue queue(kQueueName);
54
55 Event event(false, false);
56
57 class CustomTask : public QueuedTask {
58 public:
59 explicit CustomTask(Event* event) : event_(event) {}
60
61 private:
62 bool Run() override {
63 event_->Set();
64 return false; // Never allows the task to be deleted by the queue.
65 }
66
67 Event* const event_;
68 } my_task(&event);
69
70 // Please don't do this in production code! :)
71 queue.PostTask(std::unique_ptr<QueuedTask>(&my_task));
72 EXPECT_TRUE(event.Wait(1000));
73}
74
75TEST(TaskQueueTest, PostLambda) {
76 static const char kQueueName[] = "PostLambda";
77 TaskQueue queue(kQueueName);
78
79 Event event(false, false);
80 queue.PostTask([&event]() { event.Set(); });
81 EXPECT_TRUE(event.Wait(1000));
82}
83
84TEST(TaskQueueTest, PostFromQueue) {
85 static const char kQueueName[] = "PostFromQueue";
86 TaskQueue queue(kQueueName);
87
88 Event event(false, false);
89 queue.PostTask(
90 [&event, &queue]() { queue.PostTask([&event]() { event.Set(); }); });
91 EXPECT_TRUE(event.Wait(1000));
92}
93
tommi45d18eb2016-10-27 09:04:17 -070094// Currently flaky on Windows. See issue 6610.
95#if defined(WEBRTC_WIN)
96#define MAYBE_PostDelayed DISABLED_PostDelayed
97#else
98#define MAYBE_PostDelayed PostDelayed
99#endif
100TEST(TaskQueueTest, MAYBE_PostDelayed) {
tommic06b1332016-05-14 11:31:40 -0700101 static const char kQueueName[] = "PostDelayed";
102 TaskQueue queue(kQueueName);
103
104 Event event(false, false);
105 uint32_t start = Time();
106 queue.PostDelayedTask(Bind(&CheckCurrent, kQueueName, &event, &queue), 100);
107 EXPECT_TRUE(event.Wait(1000));
108 uint32_t end = Time();
109 EXPECT_GE(end - start, 100u);
110 EXPECT_NEAR(end - start, 200u, 100u); // Accept 100-300.
111}
112
113TEST(TaskQueueTest, PostMultipleDelayed) {
114 static const char kQueueName[] = "PostMultipleDelayed";
115 TaskQueue queue(kQueueName);
116
117 std::vector<std::unique_ptr<Event>> events;
118 for (int i = 0; i < 10; ++i) {
119 events.push_back(std::unique_ptr<Event>(new Event(false, false)));
120 queue.PostDelayedTask(
121 Bind(&CheckCurrent, kQueueName, events.back().get(), &queue), 10);
122 }
123
124 for (const auto& e : events)
125 EXPECT_TRUE(e->Wait(100));
126}
127
128TEST(TaskQueueTest, PostDelayedAfterDestruct) {
129 static const char kQueueName[] = "PostDelayedAfterDestruct";
130 Event event(false, false);
131 {
132 TaskQueue queue(kQueueName);
133 queue.PostDelayedTask(Bind(&CheckCurrent, kQueueName, &event, &queue), 100);
134 }
135 EXPECT_FALSE(event.Wait(200)); // Task should not run.
136}
137
138TEST(TaskQueueTest, PostAndReply) {
139 static const char kPostQueue[] = "PostQueue";
140 static const char kReplyQueue[] = "ReplyQueue";
141 TaskQueue post_queue(kPostQueue);
142 TaskQueue reply_queue(kReplyQueue);
143
144 Event event(false, false);
145 post_queue.PostTaskAndReply(
146 Bind(&CheckCurrent, kPostQueue, nullptr, &post_queue),
147 Bind(&CheckCurrent, kReplyQueue, &event, &reply_queue), &reply_queue);
148 EXPECT_TRUE(event.Wait(1000));
149}
150
151TEST(TaskQueueTest, PostAndReuse) {
152 static const char kPostQueue[] = "PostQueue";
153 static const char kReplyQueue[] = "ReplyQueue";
154 TaskQueue post_queue(kPostQueue);
155 TaskQueue reply_queue(kReplyQueue);
156
157 int call_count = 0;
158
159 class ReusedTask : public QueuedTask {
160 public:
161 ReusedTask(int* counter, TaskQueue* reply_queue, Event* event)
162 : counter_(counter), reply_queue_(reply_queue), event_(event) {
163 EXPECT_EQ(0, *counter_);
164 }
165
166 private:
167 bool Run() override {
168 if (++(*counter_) == 1) {
169 std::unique_ptr<QueuedTask> myself(this);
170 reply_queue_->PostTask(std::move(myself));
171 // At this point, the object is owned by reply_queue_ and it's
172 // theoratically possible that the object has been deleted (e.g. if
173 // posting wasn't possible). So, don't touch any member variables here.
174
175 // Indicate to the current queue that ownership has been transferred.
176 return false;
177 } else {
178 EXPECT_EQ(2, *counter_);
179 EXPECT_TRUE(reply_queue_->IsCurrent());
180 event_->Set();
181 return true; // Indicate that the object should be deleted.
182 }
183 }
184
185 int* const counter_;
186 TaskQueue* const reply_queue_;
187 Event* const event_;
188 };
189
190 Event event(false, false);
191 std::unique_ptr<QueuedTask> task(
192 new ReusedTask(&call_count, &reply_queue, &event));
193
194 post_queue.PostTask(std::move(task));
195 EXPECT_TRUE(event.Wait(1000));
196}
197
198TEST(TaskQueueTest, PostAndReplyLambda) {
199 static const char kPostQueue[] = "PostQueue";
200 static const char kReplyQueue[] = "ReplyQueue";
201 TaskQueue post_queue(kPostQueue);
202 TaskQueue reply_queue(kReplyQueue);
203
204 Event event(false, false);
205 bool my_flag = false;
206 post_queue.PostTaskAndReply([&my_flag]() { my_flag = true; },
207 [&event]() { event.Set(); }, &reply_queue);
208 EXPECT_TRUE(event.Wait(1000));
209 EXPECT_TRUE(my_flag);
210}
211
212void TestPostTaskAndReply(TaskQueue* work_queue,
213 const char* work_queue_name,
214 Event* event) {
215 ASSERT_FALSE(work_queue->IsCurrent());
216 work_queue->PostTaskAndReply(
217 Bind(&CheckCurrent, work_queue_name, nullptr, work_queue),
218 NewClosure([event]() { event->Set(); }));
219}
220
221// Does a PostTaskAndReply from within a task to post and reply to the current
222// queue. All in all there will be 3 tasks posted and run.
223TEST(TaskQueueTest, PostAndReply2) {
224 static const char kQueueName[] = "PostAndReply2";
225 static const char kWorkQueueName[] = "PostAndReply2_Worker";
226 TaskQueue queue(kQueueName);
227 TaskQueue work_queue(kWorkQueueName);
228
229 Event event(false, false);
230 queue.PostTask(
231 Bind(&TestPostTaskAndReply, &work_queue, kWorkQueueName, &event));
232 EXPECT_TRUE(event.Wait(1000));
233}
234
235// Tests posting more messages than a queue can queue up.
236// In situations like that, tasks will get dropped.
237TEST(TaskQueueTest, PostALot) {
238 // To destruct the event after the queue has gone out of scope.
239 Event event(false, false);
240
241 int tasks_executed = 0;
242 int tasks_cleaned_up = 0;
243 static const int kTaskCount = 0xffff;
244
245 {
246 static const char kQueueName[] = "PostALot";
247 TaskQueue queue(kQueueName);
248
249 // On linux, the limit of pending bytes in the pipe buffer is 0xffff.
250 // So here we post a total of 0xffff+1 messages, which triggers a failure
251 // case inside of the libevent queue implementation.
252
253 queue.PostTask([&event]() { event.Wait(Event::kForever); });
254 for (int i = 0; i < kTaskCount; ++i)
255 queue.PostTask(NewClosure([&tasks_executed]() { ++tasks_executed; },
256 [&tasks_cleaned_up]() { ++tasks_cleaned_up; }));
257 event.Set(); // Unblock the first task.
258 }
259
260 EXPECT_GE(tasks_cleaned_up, tasks_executed);
261 EXPECT_EQ(kTaskCount, tasks_cleaned_up);
tommic06b1332016-05-14 11:31:40 -0700262}
263
264} // namespace rtc