1 /*
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
11 #include "rtc_base/task_utils/repeating_task.h"
12
13 #include <atomic>
14 #include <memory>
15
16 #include "absl/functional/any_invocable.h"
17 #include "api/task_queue/task_queue_base.h"
18 #include "api/units/time_delta.h"
19 #include "api/units/timestamp.h"
20 #include "rtc_base/event.h"
21 #include "rtc_base/task_queue_for_test.h"
22 #include "system_wrappers/include/clock.h"
23 #include "test/gmock.h"
24 #include "test/gtest.h"
25
26 // NOTE: Since these tests rely on real time behavior, they will be flaky
27 // if run on heavily loaded systems.
28 namespace webrtc {
29 namespace {
30 using ::testing::AtLeast;
31 using ::testing::Invoke;
32 using ::testing::MockFunction;
33 using ::testing::NiceMock;
34 using ::testing::Return;
35
36 constexpr TimeDelta kTimeout = TimeDelta::Millis(1000);
37
38 class MockClosure {
39 public:
40 MOCK_METHOD(TimeDelta, Call, ());
41 MOCK_METHOD(void, Delete, ());
42 };
43
44 class MockTaskQueue : public TaskQueueBase {
45 public:
MockTaskQueue()46 MockTaskQueue() : task_queue_setter_(this) {}
47
48 MOCK_METHOD(void, Delete, (), (override));
49 MOCK_METHOD(void, PostTask, (absl::AnyInvocable<void() &&>), (override));
50 MOCK_METHOD(void,
51 PostDelayedTask,
52 (absl::AnyInvocable<void() &&>, TimeDelta),
53 (override));
54 MOCK_METHOD(void,
55 PostDelayedHighPrecisionTask,
56 (absl::AnyInvocable<void() &&>, TimeDelta),
57 (override));
58
59 private:
60 CurrentTaskQueueSetter task_queue_setter_;
61 };
62
63 class FakeTaskQueue : public TaskQueueBase {
64 public:
FakeTaskQueue(SimulatedClock * clock)65 explicit FakeTaskQueue(SimulatedClock* clock)
66 : task_queue_setter_(this), clock_(clock) {}
67
Delete()68 void Delete() override {}
69
PostTask(absl::AnyInvocable<void ()&&> task)70 void PostTask(absl::AnyInvocable<void() &&> task) override {
71 last_task_ = std::move(task);
72 last_precision_ = absl::nullopt;
73 last_delay_ = TimeDelta::Zero();
74 }
75
PostDelayedTask(absl::AnyInvocable<void ()&&> task,TimeDelta delay)76 void PostDelayedTask(absl::AnyInvocable<void() &&> task,
77 TimeDelta delay) override {
78 last_task_ = std::move(task);
79 last_precision_ = TaskQueueBase::DelayPrecision::kLow;
80 last_delay_ = delay;
81 }
82
PostDelayedHighPrecisionTask(absl::AnyInvocable<void ()&&> task,TimeDelta delay)83 void PostDelayedHighPrecisionTask(absl::AnyInvocable<void() &&> task,
84 TimeDelta delay) override {
85 last_task_ = std::move(task);
86 last_precision_ = TaskQueueBase::DelayPrecision::kHigh;
87 last_delay_ = delay;
88 }
89
AdvanceTimeAndRunLastTask()90 bool AdvanceTimeAndRunLastTask() {
91 EXPECT_TRUE(last_task_);
92 EXPECT_TRUE(last_delay_.IsFinite());
93 clock_->AdvanceTime(last_delay_);
94 last_delay_ = TimeDelta::MinusInfinity();
95 auto task = std::move(last_task_);
96 std::move(task)();
97 return last_task_ == nullptr;
98 }
99
IsTaskQueued()100 bool IsTaskQueued() { return !!last_task_; }
101
last_delay() const102 TimeDelta last_delay() const {
103 EXPECT_TRUE(last_delay_.IsFinite());
104 return last_delay_;
105 }
106
last_precision() const107 absl::optional<TaskQueueBase::DelayPrecision> last_precision() const {
108 return last_precision_;
109 }
110
111 private:
112 CurrentTaskQueueSetter task_queue_setter_;
113 SimulatedClock* clock_;
114 absl::AnyInvocable<void() &&> last_task_;
115 TimeDelta last_delay_ = TimeDelta::MinusInfinity();
116 absl::optional<TaskQueueBase::DelayPrecision> last_precision_;
117 };
118
119 // NOTE: Since this utility class holds a raw pointer to a variable that likely
120 // lives on the stack, it's important that any repeating tasks that use this
121 // class be explicitly stopped when the test criteria have been met. If the
122 // task is not stopped, an instance of this class can be deleted when the
123 // pointed-to MockClosure has been deleted and we end up trying to call a
124 // virtual method on a deleted object in the dtor.
125 class MoveOnlyClosure {
126 public:
MoveOnlyClosure(MockClosure * mock)127 explicit MoveOnlyClosure(MockClosure* mock) : mock_(mock) {}
128 MoveOnlyClosure(const MoveOnlyClosure&) = delete;
MoveOnlyClosure(MoveOnlyClosure && other)129 MoveOnlyClosure(MoveOnlyClosure&& other) : mock_(other.mock_) {
130 other.mock_ = nullptr;
131 }
~MoveOnlyClosure()132 ~MoveOnlyClosure() {
133 if (mock_)
134 mock_->Delete();
135 }
operator ()()136 TimeDelta operator()() { return mock_->Call(); }
137
138 private:
139 MockClosure* mock_;
140 };
141 } // namespace
142
TEST(RepeatingTaskTest,TaskIsStoppedOnStop)143 TEST(RepeatingTaskTest, TaskIsStoppedOnStop) {
144 const TimeDelta kShortInterval = TimeDelta::Millis(50);
145
146 SimulatedClock clock(Timestamp::Zero());
147 FakeTaskQueue task_queue(&clock);
148 std::atomic_int counter(0);
149 auto handle = RepeatingTaskHandle::Start(
150 &task_queue,
151 [&] {
152 counter++;
153 return kShortInterval;
154 },
155 TaskQueueBase::DelayPrecision::kLow, &clock);
156 EXPECT_EQ(task_queue.last_delay(), TimeDelta::Zero());
157 EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
158 EXPECT_EQ(counter.load(), 1);
159
160 // The handle reposted at the short interval.
161 EXPECT_EQ(task_queue.last_delay(), kShortInterval);
162
163 // Stop the handle. This prevernts the counter from incrementing.
164 handle.Stop();
165 EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
166 EXPECT_EQ(counter.load(), 1);
167 }
168
TEST(RepeatingTaskTest,CompensatesForLongRunTime)169 TEST(RepeatingTaskTest, CompensatesForLongRunTime) {
170 const TimeDelta kRepeatInterval = TimeDelta::Millis(2);
171 // Sleeping inside the task for longer than the repeat interval once, should
172 // be compensated for by repeating the task faster to catch up.
173 const TimeDelta kSleepDuration = TimeDelta::Millis(20);
174
175 std::atomic_int counter(0);
176 SimulatedClock clock(Timestamp::Zero());
177 FakeTaskQueue task_queue(&clock);
178 RepeatingTaskHandle::Start(
179 &task_queue,
180 [&] {
181 ++counter;
182 // Task takes longer than the repeat duration.
183 clock.AdvanceTime(kSleepDuration);
184 return kRepeatInterval;
185 },
186 TaskQueueBase::DelayPrecision::kLow, &clock);
187
188 EXPECT_EQ(task_queue.last_delay(), TimeDelta::Zero());
189 EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
190
191 // Task is posted right away since it took longer to run then the repeat
192 // interval.
193 EXPECT_EQ(task_queue.last_delay(), TimeDelta::Zero());
194 EXPECT_EQ(counter.load(), 1);
195 }
196
TEST(RepeatingTaskTest,CompensatesForShortRunTime)197 TEST(RepeatingTaskTest, CompensatesForShortRunTime) {
198 SimulatedClock clock(Timestamp::Zero());
199 FakeTaskQueue task_queue(&clock);
200 std::atomic_int counter(0);
201 RepeatingTaskHandle::Start(
202 &task_queue,
203 [&] {
204 // Simulate the task taking 100ms, which should be compensated for.
205 counter++;
206 clock.AdvanceTime(TimeDelta::Millis(100));
207 return TimeDelta::Millis(300);
208 },
209 TaskQueueBase::DelayPrecision::kLow, &clock);
210
211 // Expect instant post task.
212 EXPECT_EQ(task_queue.last_delay(), TimeDelta::Zero());
213 // Task should be retained by the handler since it is not cancelled.
214 EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
215 // New delay should be 200ms since repeat delay was 300ms but task took 100ms.
216 EXPECT_EQ(task_queue.last_delay(), TimeDelta::Millis(200));
217 }
218
TEST(RepeatingTaskTest,CancelDelayedTaskBeforeItRuns)219 TEST(RepeatingTaskTest, CancelDelayedTaskBeforeItRuns) {
220 rtc::Event done;
221 MockClosure mock;
222 EXPECT_CALL(mock, Call).Times(0);
223 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
224 TaskQueueForTest task_queue("queue");
225 auto handle = RepeatingTaskHandle::DelayedStart(
226 task_queue.Get(), TimeDelta::Millis(100), MoveOnlyClosure(&mock));
227 task_queue.PostTask(
228 [handle = std::move(handle)]() mutable { handle.Stop(); });
229 EXPECT_TRUE(done.Wait(kTimeout));
230 }
231
TEST(RepeatingTaskTest,CancelTaskAfterItRuns)232 TEST(RepeatingTaskTest, CancelTaskAfterItRuns) {
233 rtc::Event done;
234 MockClosure mock;
235 EXPECT_CALL(mock, Call).WillOnce(Return(TimeDelta::Millis(100)));
236 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
237 TaskQueueForTest task_queue("queue");
238 auto handle =
239 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&mock));
240 task_queue.PostTask(
241 [handle = std::move(handle)]() mutable { handle.Stop(); });
242 EXPECT_TRUE(done.Wait(kTimeout));
243 }
244
TEST(RepeatingTaskTest,TaskCanStopItself)245 TEST(RepeatingTaskTest, TaskCanStopItself) {
246 std::atomic_int counter(0);
247 SimulatedClock clock(Timestamp::Zero());
248 FakeTaskQueue task_queue(&clock);
249 RepeatingTaskHandle handle = RepeatingTaskHandle::Start(&task_queue, [&] {
250 ++counter;
251 handle.Stop();
252 return TimeDelta::Millis(2);
253 });
254 EXPECT_EQ(task_queue.last_delay(), TimeDelta::Zero());
255 // Task cancelled itself so wants to be released.
256 EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
257 EXPECT_EQ(counter.load(), 1);
258 }
259
TEST(RepeatingTaskTest,TaskCanStopItselfByReturningInfinity)260 TEST(RepeatingTaskTest, TaskCanStopItselfByReturningInfinity) {
261 std::atomic_int counter(0);
262 SimulatedClock clock(Timestamp::Zero());
263 FakeTaskQueue task_queue(&clock);
264 RepeatingTaskHandle handle = RepeatingTaskHandle::Start(&task_queue, [&] {
265 ++counter;
266 return TimeDelta::PlusInfinity();
267 });
268 EXPECT_EQ(task_queue.last_delay(), TimeDelta::Zero());
269 // Task cancelled itself so wants to be released.
270 EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
271 EXPECT_EQ(counter.load(), 1);
272 }
273
TEST(RepeatingTaskTest,ZeroReturnValueRepostsTheTask)274 TEST(RepeatingTaskTest, ZeroReturnValueRepostsTheTask) {
275 NiceMock<MockClosure> closure;
276 rtc::Event done;
277 EXPECT_CALL(closure, Call())
278 .WillOnce(Return(TimeDelta::Zero()))
279 .WillOnce(Invoke([&] {
280 done.Set();
281 return TimeDelta::PlusInfinity();
282 }));
283 TaskQueueForTest task_queue("queue");
284 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure));
285 EXPECT_TRUE(done.Wait(kTimeout));
286 }
287
TEST(RepeatingTaskTest,StartPeriodicTask)288 TEST(RepeatingTaskTest, StartPeriodicTask) {
289 MockFunction<TimeDelta()> closure;
290 rtc::Event done;
291 EXPECT_CALL(closure, Call())
292 .WillOnce(Return(TimeDelta::Millis(20)))
293 .WillOnce(Return(TimeDelta::Millis(20)))
294 .WillOnce(Invoke([&] {
295 done.Set();
296 return TimeDelta::PlusInfinity();
297 }));
298 TaskQueueForTest task_queue("queue");
299 RepeatingTaskHandle::Start(task_queue.Get(), closure.AsStdFunction());
300 EXPECT_TRUE(done.Wait(kTimeout));
301 }
302
TEST(RepeatingTaskTest,Example)303 TEST(RepeatingTaskTest, Example) {
304 class ObjectOnTaskQueue {
305 public:
306 void DoPeriodicTask() {}
307 TimeDelta TimeUntilNextRun() { return TimeDelta::Millis(100); }
308 void StartPeriodicTask(RepeatingTaskHandle* handle,
309 TaskQueueBase* task_queue) {
310 *handle = RepeatingTaskHandle::Start(task_queue, [this] {
311 DoPeriodicTask();
312 return TimeUntilNextRun();
313 });
314 }
315 };
316 TaskQueueForTest task_queue("queue");
317 auto object = std::make_unique<ObjectOnTaskQueue>();
318 // Create and start the periodic task.
319 RepeatingTaskHandle handle;
320 object->StartPeriodicTask(&handle, task_queue.Get());
321 // Restart the task
322 task_queue.PostTask(
323 [handle = std::move(handle)]() mutable { handle.Stop(); });
324 object->StartPeriodicTask(&handle, task_queue.Get());
325 task_queue.PostTask(
326 [handle = std::move(handle)]() mutable { handle.Stop(); });
327 struct Destructor {
328 void operator()() { object.reset(); }
329 std::unique_ptr<ObjectOnTaskQueue> object;
330 };
331 task_queue.PostTask(Destructor{std::move(object)});
332 // Do not wait for the destructor closure in order to create a race between
333 // task queue destruction and running the desctructor closure.
334 }
335
TEST(RepeatingTaskTest,ClockIntegration)336 TEST(RepeatingTaskTest, ClockIntegration) {
337 absl::AnyInvocable<void() &&> delayed_task;
338 TimeDelta expected_delay = TimeDelta::Zero();
339 SimulatedClock clock(Timestamp::Zero());
340
341 NiceMock<MockTaskQueue> task_queue;
342 ON_CALL(task_queue, PostDelayedTask)
343 .WillByDefault([&](absl::AnyInvocable<void() &&> task, TimeDelta delay) {
344 EXPECT_EQ(delay, expected_delay);
345 delayed_task = std::move(task);
346 });
347
348 expected_delay = TimeDelta::Millis(100);
349 RepeatingTaskHandle handle = RepeatingTaskHandle::DelayedStart(
350 &task_queue, TimeDelta::Millis(100),
351 [&clock]() {
352 EXPECT_EQ(Timestamp::Millis(100), clock.CurrentTime());
353 // Simulate work happening for 10ms.
354 clock.AdvanceTimeMilliseconds(10);
355 return TimeDelta::Millis(100);
356 },
357 TaskQueueBase::DelayPrecision::kLow, &clock);
358
359 clock.AdvanceTimeMilliseconds(100);
360 absl::AnyInvocable<void()&&> task_to_run = std::move(delayed_task);
361 expected_delay = TimeDelta::Millis(90);
362 std::move(task_to_run)();
363 EXPECT_NE(delayed_task, nullptr);
364 handle.Stop();
365 }
366
TEST(RepeatingTaskTest,CanBeStoppedAfterTaskQueueDeletedTheRepeatingTask)367 TEST(RepeatingTaskTest, CanBeStoppedAfterTaskQueueDeletedTheRepeatingTask) {
368 absl::AnyInvocable<void() &&> repeating_task;
369
370 MockTaskQueue task_queue;
371 EXPECT_CALL(task_queue, PostDelayedTask)
372 .WillOnce([&](absl::AnyInvocable<void() &&> task, TimeDelta delay) {
373 repeating_task = std::move(task);
374 });
375
376 RepeatingTaskHandle handle =
377 RepeatingTaskHandle::DelayedStart(&task_queue, TimeDelta::Millis(100),
378 [] { return TimeDelta::Millis(100); });
379
380 // shutdown task queue: delete all pending tasks and run 'regular' task.
381 repeating_task = nullptr;
382 handle.Stop();
383 }
384
TEST(RepeatingTaskTest,DefaultPrecisionIsLow)385 TEST(RepeatingTaskTest, DefaultPrecisionIsLow) {
386 SimulatedClock clock(Timestamp::Zero());
387 FakeTaskQueue task_queue(&clock);
388 // Closure that repeats twice.
389 MockFunction<TimeDelta()> closure;
390 EXPECT_CALL(closure, Call())
391 .WillOnce(Return(TimeDelta::Millis(1)))
392 .WillOnce(Return(TimeDelta::PlusInfinity()));
393 RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction());
394 // Initial task is a PostTask().
395 EXPECT_FALSE(task_queue.last_precision().has_value());
396 EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
397 // Repeated task is a delayed task with the default precision: low.
398 EXPECT_TRUE(task_queue.last_precision().has_value());
399 EXPECT_EQ(task_queue.last_precision().value(),
400 TaskQueueBase::DelayPrecision::kLow);
401 // No more tasks.
402 EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
403 }
404
TEST(RepeatingTaskTest,CanSpecifyToPostTasksWithLowPrecision)405 TEST(RepeatingTaskTest, CanSpecifyToPostTasksWithLowPrecision) {
406 SimulatedClock clock(Timestamp::Zero());
407 FakeTaskQueue task_queue(&clock);
408 // Closure that repeats twice.
409 MockFunction<TimeDelta()> closure;
410 EXPECT_CALL(closure, Call())
411 .WillOnce(Return(TimeDelta::Millis(1)))
412 .WillOnce(Return(TimeDelta::PlusInfinity()));
413 RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction(),
414 TaskQueueBase::DelayPrecision::kLow);
415 // Initial task is a PostTask().
416 EXPECT_FALSE(task_queue.last_precision().has_value());
417 EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
418 // Repeated task is a delayed task with the specified precision.
419 EXPECT_TRUE(task_queue.last_precision().has_value());
420 EXPECT_EQ(task_queue.last_precision().value(),
421 TaskQueueBase::DelayPrecision::kLow);
422 // No more tasks.
423 EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
424 }
425
TEST(RepeatingTaskTest,CanSpecifyToPostTasksWithHighPrecision)426 TEST(RepeatingTaskTest, CanSpecifyToPostTasksWithHighPrecision) {
427 SimulatedClock clock(Timestamp::Zero());
428 FakeTaskQueue task_queue(&clock);
429 // Closure that repeats twice.
430 MockFunction<TimeDelta()> closure;
431 EXPECT_CALL(closure, Call())
432 .WillOnce(Return(TimeDelta::Millis(1)))
433 .WillOnce(Return(TimeDelta::PlusInfinity()));
434 RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction(),
435 TaskQueueBase::DelayPrecision::kHigh);
436 // Initial task is a PostTask().
437 EXPECT_FALSE(task_queue.last_precision().has_value());
438 EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
439 // Repeated task is a delayed task with the specified precision.
440 EXPECT_TRUE(task_queue.last_precision().has_value());
441 EXPECT_EQ(task_queue.last_precision().value(),
442 TaskQueueBase::DelayPrecision::kHigh);
443 // No more tasks.
444 EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
445 }
446
447 } // namespace webrtc
448