OLD | NEW |
| (Empty) |
1 /* | |
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 #if defined(WEBRTC_WIN) | |
12 // clang-format off | |
13 #include <windows.h> // Must come first. | |
14 #include <mmsystem.h> | |
15 // clang-format on | |
16 #endif | |
17 | |
18 #include <memory> | |
19 #include <vector> | |
20 | |
21 #include "webrtc/base/bind.h" | |
22 #include "webrtc/base/event.h" | |
23 #include "webrtc/base/gunit.h" | |
24 #include "webrtc/base/task_queue.h" | |
25 #include "webrtc/base/timeutils.h" | |
26 | |
27 namespace rtc { | |
28 namespace { | |
29 // Noop on all platforms except Windows, where it turns on high precision | |
30 // multimedia timers which increases the precision of TimeMillis() while in | |
31 // scope. | |
32 class EnableHighResTimers { | |
33 public: | |
34 #if !defined(WEBRTC_WIN) | |
35 EnableHighResTimers() {} | |
36 #else | |
37 EnableHighResTimers() : enabled_(timeBeginPeriod(1) == TIMERR_NOERROR) {} | |
38 ~EnableHighResTimers() { | |
39 if (enabled_) | |
40 timeEndPeriod(1); | |
41 } | |
42 | |
43 private: | |
44 const bool enabled_; | |
45 #endif | |
46 }; | |
47 } | |
48 | |
49 namespace { | |
50 void CheckCurrent(const char* expected_queue, Event* signal, TaskQueue* queue) { | |
51 EXPECT_TRUE(TaskQueue::IsCurrent(expected_queue)); | |
52 EXPECT_TRUE(queue->IsCurrent()); | |
53 if (signal) | |
54 signal->Set(); | |
55 } | |
56 | |
57 } // namespace | |
58 | |
59 TEST(TaskQueueTest, Construct) { | |
60 static const char kQueueName[] = "Construct"; | |
61 TaskQueue queue(kQueueName); | |
62 EXPECT_FALSE(queue.IsCurrent()); | |
63 } | |
64 | |
65 TEST(TaskQueueTest, PostAndCheckCurrent) { | |
66 static const char kQueueName[] = "PostAndCheckCurrent"; | |
67 Event event(false, false); | |
68 TaskQueue queue(kQueueName); | |
69 | |
70 // We're not running a task, so there shouldn't be a current queue. | |
71 EXPECT_FALSE(queue.IsCurrent()); | |
72 EXPECT_FALSE(TaskQueue::Current()); | |
73 | |
74 queue.PostTask(Bind(&CheckCurrent, kQueueName, &event, &queue)); | |
75 EXPECT_TRUE(event.Wait(1000)); | |
76 } | |
77 | |
78 TEST(TaskQueueTest, PostCustomTask) { | |
79 static const char kQueueName[] = "PostCustomImplementation"; | |
80 Event event(false, false); | |
81 TaskQueue queue(kQueueName); | |
82 | |
83 class CustomTask : public QueuedTask { | |
84 public: | |
85 explicit CustomTask(Event* event) : event_(event) {} | |
86 | |
87 private: | |
88 bool Run() override { | |
89 event_->Set(); | |
90 return false; // Never allows the task to be deleted by the queue. | |
91 } | |
92 | |
93 Event* const event_; | |
94 } my_task(&event); | |
95 | |
96 // Please don't do this in production code! :) | |
97 queue.PostTask(std::unique_ptr<QueuedTask>(&my_task)); | |
98 EXPECT_TRUE(event.Wait(1000)); | |
99 } | |
100 | |
101 TEST(TaskQueueTest, PostLambda) { | |
102 static const char kQueueName[] = "PostLambda"; | |
103 Event event(false, false); | |
104 TaskQueue queue(kQueueName); | |
105 | |
106 queue.PostTask([&event]() { event.Set(); }); | |
107 EXPECT_TRUE(event.Wait(1000)); | |
108 } | |
109 | |
110 TEST(TaskQueueTest, PostDelayedZero) { | |
111 static const char kQueueName[] = "PostDelayedZero"; | |
112 Event event(false, false); | |
113 TaskQueue queue(kQueueName); | |
114 | |
115 queue.PostDelayedTask([&event]() { event.Set(); }, 0); | |
116 EXPECT_TRUE(event.Wait(1000)); | |
117 } | |
118 | |
119 TEST(TaskQueueTest, PostFromQueue) { | |
120 static const char kQueueName[] = "PostFromQueue"; | |
121 Event event(false, false); | |
122 TaskQueue queue(kQueueName); | |
123 | |
124 queue.PostTask( | |
125 [&event, &queue]() { queue.PostTask([&event]() { event.Set(); }); }); | |
126 EXPECT_TRUE(event.Wait(1000)); | |
127 } | |
128 | |
129 TEST(TaskQueueTest, PostDelayed) { | |
130 static const char kQueueName[] = "PostDelayed"; | |
131 Event event(false, false); | |
132 TaskQueue queue(kQueueName, TaskQueue::Priority::HIGH); | |
133 | |
134 uint32_t start = Time(); | |
135 queue.PostDelayedTask(Bind(&CheckCurrent, kQueueName, &event, &queue), 100); | |
136 EXPECT_TRUE(event.Wait(1000)); | |
137 uint32_t end = Time(); | |
138 // These tests are a little relaxed due to how "powerful" our test bots can | |
139 // be. Most recently we've seen windows bots fire the callback after 94-99ms, | |
140 // which is why we have a little bit of leeway backwards as well. | |
141 EXPECT_GE(end - start, 90u); | |
142 EXPECT_NEAR(end - start, 190u, 100u); // Accept 90-290. | |
143 } | |
144 | |
145 // This task needs to be run manually due to the slowness of some of our bots. | |
146 // TODO(tommi): Can we run this on the perf bots? | |
147 TEST(TaskQueueTest, DISABLED_PostDelayedHighRes) { | |
148 EnableHighResTimers high_res_scope; | |
149 | |
150 static const char kQueueName[] = "PostDelayedHighRes"; | |
151 Event event(false, false); | |
152 TaskQueue queue(kQueueName, TaskQueue::Priority::HIGH); | |
153 | |
154 uint32_t start = Time(); | |
155 queue.PostDelayedTask(Bind(&CheckCurrent, kQueueName, &event, &queue), 3); | |
156 EXPECT_TRUE(event.Wait(1000)); | |
157 uint32_t end = TimeMillis(); | |
158 // These tests are a little relaxed due to how "powerful" our test bots can | |
159 // be. Most recently we've seen windows bots fire the callback after 94-99ms, | |
160 // which is why we have a little bit of leeway backwards as well. | |
161 EXPECT_GE(end - start, 3u); | |
162 EXPECT_NEAR(end - start, 3, 3u); | |
163 } | |
164 | |
165 TEST(TaskQueueTest, PostMultipleDelayed) { | |
166 static const char kQueueName[] = "PostMultipleDelayed"; | |
167 TaskQueue queue(kQueueName); | |
168 | |
169 std::vector<std::unique_ptr<Event>> events; | |
170 for (int i = 0; i < 100; ++i) { | |
171 events.push_back(std::unique_ptr<Event>(new Event(false, false))); | |
172 queue.PostDelayedTask( | |
173 Bind(&CheckCurrent, kQueueName, events.back().get(), &queue), i); | |
174 } | |
175 | |
176 for (const auto& e : events) | |
177 EXPECT_TRUE(e->Wait(1000)); | |
178 } | |
179 | |
180 TEST(TaskQueueTest, PostDelayedAfterDestruct) { | |
181 static const char kQueueName[] = "PostDelayedAfterDestruct"; | |
182 Event event(false, false); | |
183 { | |
184 TaskQueue queue(kQueueName); | |
185 queue.PostDelayedTask(Bind(&CheckCurrent, kQueueName, &event, &queue), 100); | |
186 } | |
187 EXPECT_FALSE(event.Wait(200)); // Task should not run. | |
188 } | |
189 | |
190 TEST(TaskQueueTest, PostAndReply) { | |
191 static const char kPostQueue[] = "PostQueue"; | |
192 static const char kReplyQueue[] = "ReplyQueue"; | |
193 Event event(false, false); | |
194 TaskQueue post_queue(kPostQueue); | |
195 TaskQueue reply_queue(kReplyQueue); | |
196 | |
197 post_queue.PostTaskAndReply( | |
198 Bind(&CheckCurrent, kPostQueue, nullptr, &post_queue), | |
199 Bind(&CheckCurrent, kReplyQueue, &event, &reply_queue), &reply_queue); | |
200 EXPECT_TRUE(event.Wait(1000)); | |
201 } | |
202 | |
203 TEST(TaskQueueTest, PostAndReuse) { | |
204 static const char kPostQueue[] = "PostQueue"; | |
205 static const char kReplyQueue[] = "ReplyQueue"; | |
206 Event event(false, false); | |
207 TaskQueue post_queue(kPostQueue); | |
208 TaskQueue reply_queue(kReplyQueue); | |
209 | |
210 int call_count = 0; | |
211 | |
212 class ReusedTask : public QueuedTask { | |
213 public: | |
214 ReusedTask(int* counter, TaskQueue* reply_queue, Event* event) | |
215 : counter_(counter), reply_queue_(reply_queue), event_(event) { | |
216 EXPECT_EQ(0, *counter_); | |
217 } | |
218 | |
219 private: | |
220 bool Run() override { | |
221 if (++(*counter_) == 1) { | |
222 std::unique_ptr<QueuedTask> myself(this); | |
223 reply_queue_->PostTask(std::move(myself)); | |
224 // At this point, the object is owned by reply_queue_ and it's | |
225 // theoratically possible that the object has been deleted (e.g. if | |
226 // posting wasn't possible). So, don't touch any member variables here. | |
227 | |
228 // Indicate to the current queue that ownership has been transferred. | |
229 return false; | |
230 } else { | |
231 EXPECT_EQ(2, *counter_); | |
232 EXPECT_TRUE(reply_queue_->IsCurrent()); | |
233 event_->Set(); | |
234 return true; // Indicate that the object should be deleted. | |
235 } | |
236 } | |
237 | |
238 int* const counter_; | |
239 TaskQueue* const reply_queue_; | |
240 Event* const event_; | |
241 }; | |
242 | |
243 std::unique_ptr<QueuedTask> task( | |
244 new ReusedTask(&call_count, &reply_queue, &event)); | |
245 | |
246 post_queue.PostTask(std::move(task)); | |
247 EXPECT_TRUE(event.Wait(1000)); | |
248 } | |
249 | |
250 TEST(TaskQueueTest, PostAndReplyLambda) { | |
251 static const char kPostQueue[] = "PostQueue"; | |
252 static const char kReplyQueue[] = "ReplyQueue"; | |
253 Event event(false, false); | |
254 TaskQueue post_queue(kPostQueue); | |
255 TaskQueue reply_queue(kReplyQueue); | |
256 | |
257 bool my_flag = false; | |
258 post_queue.PostTaskAndReply([&my_flag]() { my_flag = true; }, | |
259 [&event]() { event.Set(); }, &reply_queue); | |
260 EXPECT_TRUE(event.Wait(1000)); | |
261 EXPECT_TRUE(my_flag); | |
262 } | |
263 | |
264 // This test covers a particular bug that we had in the libevent implementation | |
265 // where we could hit a deadlock while trying to post a reply task to a queue | |
266 // that was being deleted. The test isn't guaranteed to hit that case but it's | |
267 // written in a way that makes it likely and by running with --gtest_repeat=1000 | |
268 // the bug would occur. Alas, now it should be fixed. | |
269 TEST(TaskQueueTest, PostAndReplyDeadlock) { | |
270 Event event(false, false); | |
271 TaskQueue post_queue("PostQueue"); | |
272 TaskQueue reply_queue("ReplyQueue"); | |
273 | |
274 post_queue.PostTaskAndReply([&event]() { event.Set(); }, []() {}, | |
275 &reply_queue); | |
276 EXPECT_TRUE(event.Wait(1000)); | |
277 } | |
278 | |
279 void TestPostTaskAndReply(TaskQueue* work_queue, | |
280 const char* work_queue_name, | |
281 Event* event) { | |
282 ASSERT_FALSE(work_queue->IsCurrent()); | |
283 work_queue->PostTaskAndReply( | |
284 Bind(&CheckCurrent, work_queue_name, nullptr, work_queue), | |
285 NewClosure([event]() { event->Set(); })); | |
286 } | |
287 | |
288 // Does a PostTaskAndReply from within a task to post and reply to the current | |
289 // queue. All in all there will be 3 tasks posted and run. | |
290 TEST(TaskQueueTest, PostAndReply2) { | |
291 static const char kQueueName[] = "PostAndReply2"; | |
292 static const char kWorkQueueName[] = "PostAndReply2_Worker"; | |
293 Event event(false, false); | |
294 TaskQueue queue(kQueueName); | |
295 TaskQueue work_queue(kWorkQueueName); | |
296 | |
297 queue.PostTask( | |
298 Bind(&TestPostTaskAndReply, &work_queue, kWorkQueueName, &event)); | |
299 EXPECT_TRUE(event.Wait(1000)); | |
300 } | |
301 | |
302 // Tests posting more messages than a queue can queue up. | |
303 // In situations like that, tasks will get dropped. | |
304 TEST(TaskQueueTest, PostALot) { | |
305 // To destruct the event after the queue has gone out of scope. | |
306 Event event(false, false); | |
307 | |
308 int tasks_executed = 0; | |
309 int tasks_cleaned_up = 0; | |
310 static const int kTaskCount = 0xffff; | |
311 | |
312 { | |
313 static const char kQueueName[] = "PostALot"; | |
314 TaskQueue queue(kQueueName); | |
315 | |
316 // On linux, the limit of pending bytes in the pipe buffer is 0xffff. | |
317 // So here we post a total of 0xffff+1 messages, which triggers a failure | |
318 // case inside of the libevent queue implementation. | |
319 | |
320 queue.PostTask([&event]() { event.Wait(Event::kForever); }); | |
321 for (int i = 0; i < kTaskCount; ++i) | |
322 queue.PostTask(NewClosure([&tasks_executed]() { ++tasks_executed; }, | |
323 [&tasks_cleaned_up]() { ++tasks_cleaned_up; })); | |
324 event.Set(); // Unblock the first task. | |
325 } | |
326 | |
327 EXPECT_GE(tasks_cleaned_up, tasks_executed); | |
328 EXPECT_EQ(kTaskCount, tasks_cleaned_up); | |
329 } | |
330 | |
331 } // namespace rtc | |
OLD | NEW |