blob: 9825635a73f2ff9200473ce54437a90795c7fa1f [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
tommi0b942152017-03-10 09:33:53 -080011#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
Yves Gerey3e707812018-11-28 16:47:49 +010018#include <stdint.h>
tommic06b1332016-05-14 11:31:40 -070019#include <memory>
Yves Gerey3e707812018-11-28 16:47:49 +010020#include <utility>
tommic06b1332016-05-14 11:31:40 -070021#include <vector>
22
Yves Gerey3e707812018-11-28 16:47:49 +010023#include "absl/memory/memory.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020024#include "rtc_base/bind.h"
25#include "rtc_base/event.h"
Yves Gerey3e707812018-11-28 16:47:49 +010026#include "rtc_base/task_queue.h"
Tommi68561562018-02-13 19:47:50 +010027#include "rtc_base/task_queue_for_test.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020028#include "rtc_base/timeutils.h"
Yves Gerey3e707812018-11-28 16:47:49 +010029#include "test/gtest.h"
tommic06b1332016-05-14 11:31:40 -070030
Tommi68561562018-02-13 19:47:50 +010031using rtc::test::TaskQueueForTest;
32
tommic06b1332016-05-14 11:31:40 -070033namespace rtc {
Tommi68561562018-02-13 19:47:50 +010034
tommi0b942152017-03-10 09:33:53 -080035namespace {
36// Noop on all platforms except Windows, where it turns on high precision
37// multimedia timers which increases the precision of TimeMillis() while in
38// scope.
39class EnableHighResTimers {
40 public:
41#if !defined(WEBRTC_WIN)
42 EnableHighResTimers() {}
43#else
44 EnableHighResTimers() : enabled_(timeBeginPeriod(1) == TIMERR_NOERROR) {}
45 ~EnableHighResTimers() {
46 if (enabled_)
47 timeEndPeriod(1);
48 }
49
50 private:
51 const bool enabled_;
52#endif
53};
tommic06b1332016-05-14 11:31:40 -070054
nisse2c7b7a62017-09-04 05:18:21 -070055void CheckCurrent(Event* signal, TaskQueue* queue) {
tommic06b1332016-05-14 11:31:40 -070056 EXPECT_TRUE(queue->IsCurrent());
57 if (signal)
58 signal->Set();
59}
60
61} // namespace
62
63TEST(TaskQueueTest, Construct) {
64 static const char kQueueName[] = "Construct";
65 TaskQueue queue(kQueueName);
66 EXPECT_FALSE(queue.IsCurrent());
67}
68
69TEST(TaskQueueTest, PostAndCheckCurrent) {
70 static const char kQueueName[] = "PostAndCheckCurrent";
Niels Möllerc572ff32018-11-07 08:43:50 +010071 Event event;
tommic06b1332016-05-14 11:31:40 -070072 TaskQueue queue(kQueueName);
73
74 // We're not running a task, so there shouldn't be a current queue.
75 EXPECT_FALSE(queue.IsCurrent());
76 EXPECT_FALSE(TaskQueue::Current());
77
nisse2c7b7a62017-09-04 05:18:21 -070078 queue.PostTask(Bind(&CheckCurrent, &event, &queue));
tommic06b1332016-05-14 11:31:40 -070079 EXPECT_TRUE(event.Wait(1000));
80}
81
82TEST(TaskQueueTest, PostCustomTask) {
83 static const char kQueueName[] = "PostCustomImplementation";
Tommi68561562018-02-13 19:47:50 +010084 TaskQueueForTest queue(kQueueName);
tommic06b1332016-05-14 11:31:40 -070085
86 class CustomTask : public QueuedTask {
87 public:
Tommi68561562018-02-13 19:47:50 +010088 CustomTask() {}
89 bool ran() const { return ran_; }
tommic06b1332016-05-14 11:31:40 -070090
91 private:
92 bool Run() override {
Tommi68561562018-02-13 19:47:50 +010093 ran_ = true;
94 return false; // Never allow the task to be deleted by the queue.
tommic06b1332016-05-14 11:31:40 -070095 }
96
Tommi68561562018-02-13 19:47:50 +010097 bool ran_ = false;
98 } my_task;
tommic06b1332016-05-14 11:31:40 -070099
Tommi68561562018-02-13 19:47:50 +0100100 queue.SendTask(&my_task);
101 EXPECT_TRUE(my_task.ran());
tommic06b1332016-05-14 11:31:40 -0700102}
103
104TEST(TaskQueueTest, PostLambda) {
Tommi68561562018-02-13 19:47:50 +0100105 TaskQueueForTest queue("PostLambda");
106 bool ran = false;
107 queue.SendTask([&ran]() { ran = true; });
108 EXPECT_TRUE(ran);
tommic06b1332016-05-14 11:31:40 -0700109}
110
tommiede07592017-02-27 07:16:10 -0800111TEST(TaskQueueTest, PostDelayedZero) {
112 static const char kQueueName[] = "PostDelayedZero";
Niels Möllerc572ff32018-11-07 08:43:50 +0100113 Event event;
tommiede07592017-02-27 07:16:10 -0800114 TaskQueue queue(kQueueName);
115
116 queue.PostDelayedTask([&event]() { event.Set(); }, 0);
117 EXPECT_TRUE(event.Wait(1000));
118}
119
tommic06b1332016-05-14 11:31:40 -0700120TEST(TaskQueueTest, PostFromQueue) {
121 static const char kQueueName[] = "PostFromQueue";
Niels Möllerc572ff32018-11-07 08:43:50 +0100122 Event event;
tommic06b1332016-05-14 11:31:40 -0700123 TaskQueue queue(kQueueName);
124
tommic06b1332016-05-14 11:31:40 -0700125 queue.PostTask(
126 [&event, &queue]() { queue.PostTask([&event]() { event.Set(); }); });
127 EXPECT_TRUE(event.Wait(1000));
128}
129
tommic5b435d2016-10-31 02:17:11 -0700130TEST(TaskQueueTest, PostDelayed) {
tommic06b1332016-05-14 11:31:40 -0700131 static const char kQueueName[] = "PostDelayed";
Niels Möllerc572ff32018-11-07 08:43:50 +0100132 Event event;
tommi5bdee472017-03-03 05:20:12 -0800133 TaskQueue queue(kQueueName, TaskQueue::Priority::HIGH);
tommic06b1332016-05-14 11:31:40 -0700134
tommic06b1332016-05-14 11:31:40 -0700135 uint32_t start = Time();
nisse2c7b7a62017-09-04 05:18:21 -0700136 queue.PostDelayedTask(Bind(&CheckCurrent, &event, &queue), 100);
tommic06b1332016-05-14 11:31:40 -0700137 EXPECT_TRUE(event.Wait(1000));
138 uint32_t end = Time();
tommic5b435d2016-10-31 02:17:11 -0700139 // These tests are a little relaxed due to how "powerful" our test bots can
tommi67fcad82016-11-16 10:50:24 -0800140 // be. Most recently we've seen windows bots fire the callback after 94-99ms,
tommic5b435d2016-10-31 02:17:11 -0700141 // which is why we have a little bit of leeway backwards as well.
tommi67fcad82016-11-16 10:50:24 -0800142 EXPECT_GE(end - start, 90u);
143 EXPECT_NEAR(end - start, 190u, 100u); // Accept 90-290.
tommic06b1332016-05-14 11:31:40 -0700144}
145
tommi0b942152017-03-10 09:33:53 -0800146// This task needs to be run manually due to the slowness of some of our bots.
147// TODO(tommi): Can we run this on the perf bots?
148TEST(TaskQueueTest, DISABLED_PostDelayedHighRes) {
149 EnableHighResTimers high_res_scope;
150
151 static const char kQueueName[] = "PostDelayedHighRes";
Niels Möllerc572ff32018-11-07 08:43:50 +0100152 Event event;
tommi0b942152017-03-10 09:33:53 -0800153 TaskQueue queue(kQueueName, TaskQueue::Priority::HIGH);
154
155 uint32_t start = Time();
nisse2c7b7a62017-09-04 05:18:21 -0700156 queue.PostDelayedTask(Bind(&CheckCurrent, &event, &queue), 3);
tommi0b942152017-03-10 09:33:53 -0800157 EXPECT_TRUE(event.Wait(1000));
158 uint32_t end = TimeMillis();
159 // These tests are a little relaxed due to how "powerful" our test bots can
160 // be. Most recently we've seen windows bots fire the callback after 94-99ms,
161 // which is why we have a little bit of leeway backwards as well.
162 EXPECT_GE(end - start, 3u);
163 EXPECT_NEAR(end - start, 3, 3u);
164}
165
tommic06b1332016-05-14 11:31:40 -0700166TEST(TaskQueueTest, PostMultipleDelayed) {
167 static const char kQueueName[] = "PostMultipleDelayed";
168 TaskQueue queue(kQueueName);
169
170 std::vector<std::unique_ptr<Event>> events;
tommif9d91542017-02-17 02:47:11 -0800171 for (int i = 0; i < 100; ++i) {
Niels Möllerc572ff32018-11-07 08:43:50 +0100172 events.push_back(absl::make_unique<Event>());
Yves Gerey665174f2018-06-19 15:03:05 +0200173 queue.PostDelayedTask(Bind(&CheckCurrent, events.back().get(), &queue), i);
tommic06b1332016-05-14 11:31:40 -0700174 }
175
176 for (const auto& e : events)
tommif9d91542017-02-17 02:47:11 -0800177 EXPECT_TRUE(e->Wait(1000));
tommic06b1332016-05-14 11:31:40 -0700178}
179
180TEST(TaskQueueTest, PostDelayedAfterDestruct) {
181 static const char kQueueName[] = "PostDelayedAfterDestruct";
Niels Möllerc572ff32018-11-07 08:43:50 +0100182 Event run;
183 Event deleted;
tommic06b1332016-05-14 11:31:40 -0700184 {
185 TaskQueue queue(kQueueName);
Danil Chapovalova36631c2018-09-24 20:27:22 +0200186 queue.PostDelayedTask(
187 rtc::NewClosure([&run] { run.Set(); }, [&deleted] { deleted.Set(); }),
188 100);
tommic06b1332016-05-14 11:31:40 -0700189 }
Danil Chapovalova36631c2018-09-24 20:27:22 +0200190 // Task might outlive the TaskQueue, but still should be deleted.
191 EXPECT_TRUE(deleted.Wait(200));
192 EXPECT_FALSE(run.Wait(0)); // and should not run.
tommic06b1332016-05-14 11:31:40 -0700193}
194
195TEST(TaskQueueTest, PostAndReply) {
196 static const char kPostQueue[] = "PostQueue";
197 static const char kReplyQueue[] = "ReplyQueue";
Niels Möllerc572ff32018-11-07 08:43:50 +0100198 Event event;
tommic06b1332016-05-14 11:31:40 -0700199 TaskQueue post_queue(kPostQueue);
200 TaskQueue reply_queue(kReplyQueue);
201
Yves Gerey665174f2018-06-19 15:03:05 +0200202 post_queue.PostTaskAndReply(Bind(&CheckCurrent, nullptr, &post_queue),
203 Bind(&CheckCurrent, &event, &reply_queue),
204 &reply_queue);
tommic06b1332016-05-14 11:31:40 -0700205 EXPECT_TRUE(event.Wait(1000));
206}
207
208TEST(TaskQueueTest, PostAndReuse) {
209 static const char kPostQueue[] = "PostQueue";
210 static const char kReplyQueue[] = "ReplyQueue";
Niels Möllerc572ff32018-11-07 08:43:50 +0100211 Event event;
tommic06b1332016-05-14 11:31:40 -0700212 TaskQueue post_queue(kPostQueue);
213 TaskQueue reply_queue(kReplyQueue);
214
215 int call_count = 0;
216
217 class ReusedTask : public QueuedTask {
218 public:
219 ReusedTask(int* counter, TaskQueue* reply_queue, Event* event)
220 : counter_(counter), reply_queue_(reply_queue), event_(event) {
221 EXPECT_EQ(0, *counter_);
222 }
223
224 private:
225 bool Run() override {
226 if (++(*counter_) == 1) {
227 std::unique_ptr<QueuedTask> myself(this);
228 reply_queue_->PostTask(std::move(myself));
229 // At this point, the object is owned by reply_queue_ and it's
230 // theoratically possible that the object has been deleted (e.g. if
231 // posting wasn't possible). So, don't touch any member variables here.
232
233 // Indicate to the current queue that ownership has been transferred.
234 return false;
235 } else {
236 EXPECT_EQ(2, *counter_);
237 EXPECT_TRUE(reply_queue_->IsCurrent());
238 event_->Set();
239 return true; // Indicate that the object should be deleted.
240 }
241 }
242
243 int* const counter_;
244 TaskQueue* const reply_queue_;
245 Event* const event_;
246 };
247
Danil Chapovalov6f09ae22017-10-12 14:39:25 +0200248 std::unique_ptr<ReusedTask> task(
tommic06b1332016-05-14 11:31:40 -0700249 new ReusedTask(&call_count, &reply_queue, &event));
250
251 post_queue.PostTask(std::move(task));
252 EXPECT_TRUE(event.Wait(1000));
253}
254
255TEST(TaskQueueTest, PostAndReplyLambda) {
256 static const char kPostQueue[] = "PostQueue";
257 static const char kReplyQueue[] = "ReplyQueue";
Niels Möllerc572ff32018-11-07 08:43:50 +0100258 Event event;
tommic06b1332016-05-14 11:31:40 -0700259 TaskQueue post_queue(kPostQueue);
260 TaskQueue reply_queue(kReplyQueue);
261
tommic06b1332016-05-14 11:31:40 -0700262 bool my_flag = false;
263 post_queue.PostTaskAndReply([&my_flag]() { my_flag = true; },
264 [&event]() { event.Set(); }, &reply_queue);
265 EXPECT_TRUE(event.Wait(1000));
266 EXPECT_TRUE(my_flag);
267}
268
Danil Chapovalov6f09ae22017-10-12 14:39:25 +0200269TEST(TaskQueueTest, PostCopyableClosure) {
270 struct CopyableClosure {
271 CopyableClosure(int* num_copies, int* num_moves, Event* event)
272 : num_copies(num_copies), num_moves(num_moves), event(event) {}
273 CopyableClosure(const CopyableClosure& other)
274 : num_copies(other.num_copies),
275 num_moves(other.num_moves),
276 event(other.event) {
277 ++*num_copies;
278 }
279 CopyableClosure(CopyableClosure&& other)
280 : num_copies(other.num_copies),
281 num_moves(other.num_moves),
282 event(other.event) {
283 ++*num_moves;
284 }
285 void operator()() { event->Set(); }
286
287 int* num_copies;
288 int* num_moves;
289 Event* event;
290 };
291
292 int num_copies = 0;
293 int num_moves = 0;
Niels Möllerc572ff32018-11-07 08:43:50 +0100294 Event event;
Danil Chapovalov6f09ae22017-10-12 14:39:25 +0200295
296 static const char kPostQueue[] = "PostCopyableClosure";
297 TaskQueue post_queue(kPostQueue);
298 {
299 CopyableClosure closure(&num_copies, &num_moves, &event);
300 post_queue.PostTask(closure);
301 // Destroy closure to check with msan and tsan posted task has own copy.
302 }
303
304 EXPECT_TRUE(event.Wait(1000));
305 EXPECT_EQ(num_copies, 1);
306 EXPECT_EQ(num_moves, 0);
307}
308
309TEST(TaskQueueTest, PostMoveOnlyClosure) {
310 struct SomeState {
311 explicit SomeState(Event* event) : event(event) {}
312 ~SomeState() { event->Set(); }
313 Event* event;
314 };
315 struct MoveOnlyClosure {
316 MoveOnlyClosure(int* num_moves, std::unique_ptr<SomeState> state)
317 : num_moves(num_moves), state(std::move(state)) {}
318 MoveOnlyClosure(const MoveOnlyClosure&) = delete;
319 MoveOnlyClosure(MoveOnlyClosure&& other)
320 : num_moves(other.num_moves), state(std::move(other.state)) {
321 ++*num_moves;
322 }
323 void operator()() { state.reset(); }
324
325 int* num_moves;
326 std::unique_ptr<SomeState> state;
327 };
328
329 int num_moves = 0;
Niels Möllerc572ff32018-11-07 08:43:50 +0100330 Event event;
Danil Chapovalov6f09ae22017-10-12 14:39:25 +0200331 std::unique_ptr<SomeState> state(new SomeState(&event));
332
333 static const char kPostQueue[] = "PostMoveOnlyClosure";
334 TaskQueue post_queue(kPostQueue);
335 post_queue.PostTask(MoveOnlyClosure(&num_moves, std::move(state)));
336
337 EXPECT_TRUE(event.Wait(1000));
338 EXPECT_EQ(num_moves, 1);
339}
340
341TEST(TaskQueueTest, PostMoveOnlyCleanup) {
342 struct SomeState {
343 explicit SomeState(Event* event) : event(event) {}
344 ~SomeState() { event->Set(); }
345 Event* event;
346 };
347 struct MoveOnlyClosure {
348 void operator()() { state.reset(); }
349
350 std::unique_ptr<SomeState> state;
351 };
352
Niels Möllerc572ff32018-11-07 08:43:50 +0100353 Event event_run;
354 Event event_cleanup;
Danil Chapovalov6f09ae22017-10-12 14:39:25 +0200355 std::unique_ptr<SomeState> state_run(new SomeState(&event_run));
356 std::unique_ptr<SomeState> state_cleanup(new SomeState(&event_cleanup));
357
358 static const char kPostQueue[] = "PostMoveOnlyCleanup";
359 TaskQueue post_queue(kPostQueue);
360 post_queue.PostTask(NewClosure(MoveOnlyClosure{std::move(state_run)},
361 MoveOnlyClosure{std::move(state_cleanup)}));
362
363 EXPECT_TRUE(event_cleanup.Wait(1000));
364 // Expect run closure to complete before cleanup closure.
365 EXPECT_TRUE(event_run.Wait(0));
366}
367
tommi8c80c6e2017-02-23 00:34:52 -0800368// This test covers a particular bug that we had in the libevent implementation
369// where we could hit a deadlock while trying to post a reply task to a queue
370// that was being deleted. The test isn't guaranteed to hit that case but it's
371// written in a way that makes it likely and by running with --gtest_repeat=1000
372// the bug would occur. Alas, now it should be fixed.
373TEST(TaskQueueTest, PostAndReplyDeadlock) {
Niels Möllerc572ff32018-11-07 08:43:50 +0100374 Event event;
tommi8c80c6e2017-02-23 00:34:52 -0800375 TaskQueue post_queue("PostQueue");
376 TaskQueue reply_queue("ReplyQueue");
377
378 post_queue.PostTaskAndReply([&event]() { event.Set(); }, []() {},
379 &reply_queue);
380 EXPECT_TRUE(event.Wait(1000));
381}
382
Danil Chapovalov29038882018-09-07 16:28:20 +0200383// http://bugs.webrtc.org/9728
384#if defined(WEBRTC_WIN)
385#define MAYBE_DeleteTaskQueueAfterPostAndReply \
386 DISABLED_DeleteTaskQueueAfterPostAndReply
387#else
388#define MAYBE_DeleteTaskQueueAfterPostAndReply DeleteTaskQueueAfterPostAndReply
389#endif
390TEST(TaskQueueTest, MAYBE_DeleteTaskQueueAfterPostAndReply) {
Niels Möllerc572ff32018-11-07 08:43:50 +0100391 Event task_deleted;
392 Event reply_deleted;
Danil Chapovalov29038882018-09-07 16:28:20 +0200393 auto* task_queue = new TaskQueue("Queue");
394
395 task_queue->PostTaskAndReply(
396 /*task=*/rtc::NewClosure(
397 /*closure=*/[] {},
398 /*cleanup=*/[&task_deleted] { task_deleted.Set(); }),
399 /*reply=*/rtc::NewClosure(
400 /*closure=*/[] {},
401 /*cleanup=*/[&reply_deleted] { reply_deleted.Set(); }));
402
403 delete task_queue;
404
405 EXPECT_TRUE(task_deleted.Wait(1000));
406 EXPECT_TRUE(reply_deleted.Wait(1000));
407}
408
Yves Gerey665174f2018-06-19 15:03:05 +0200409void TestPostTaskAndReply(TaskQueue* work_queue, Event* event) {
tommic06b1332016-05-14 11:31:40 -0700410 ASSERT_FALSE(work_queue->IsCurrent());
Yves Gerey665174f2018-06-19 15:03:05 +0200411 work_queue->PostTaskAndReply(Bind(&CheckCurrent, nullptr, work_queue),
412 NewClosure([event]() { event->Set(); }));
tommic06b1332016-05-14 11:31:40 -0700413}
414
415// Does a PostTaskAndReply from within a task to post and reply to the current
416// queue. All in all there will be 3 tasks posted and run.
417TEST(TaskQueueTest, PostAndReply2) {
418 static const char kQueueName[] = "PostAndReply2";
419 static const char kWorkQueueName[] = "PostAndReply2_Worker";
Niels Möllerc572ff32018-11-07 08:43:50 +0100420 Event event;
tommic06b1332016-05-14 11:31:40 -0700421 TaskQueue queue(kQueueName);
422 TaskQueue work_queue(kWorkQueueName);
423
Yves Gerey665174f2018-06-19 15:03:05 +0200424 queue.PostTask(Bind(&TestPostTaskAndReply, &work_queue, &event));
tommic06b1332016-05-14 11:31:40 -0700425 EXPECT_TRUE(event.Wait(1000));
426}
427
428// Tests posting more messages than a queue can queue up.
429// In situations like that, tasks will get dropped.
430TEST(TaskQueueTest, PostALot) {
431 // To destruct the event after the queue has gone out of scope.
Niels Möllerc572ff32018-11-07 08:43:50 +0100432 Event event;
tommic06b1332016-05-14 11:31:40 -0700433
434 int tasks_executed = 0;
435 int tasks_cleaned_up = 0;
436 static const int kTaskCount = 0xffff;
437
438 {
439 static const char kQueueName[] = "PostALot";
440 TaskQueue queue(kQueueName);
441
442 // On linux, the limit of pending bytes in the pipe buffer is 0xffff.
443 // So here we post a total of 0xffff+1 messages, which triggers a failure
444 // case inside of the libevent queue implementation.
445
446 queue.PostTask([&event]() { event.Wait(Event::kForever); });
447 for (int i = 0; i < kTaskCount; ++i)
448 queue.PostTask(NewClosure([&tasks_executed]() { ++tasks_executed; },
449 [&tasks_cleaned_up]() { ++tasks_cleaned_up; }));
450 event.Set(); // Unblock the first task.
451 }
452
453 EXPECT_GE(tasks_cleaned_up, tasks_executed);
454 EXPECT_EQ(kTaskCount, tasks_cleaned_up);
tommic06b1332016-05-14 11:31:40 -0700455}
456
457} // namespace rtc