blob: 1d26ee62dcf4c591c736efd1d364ff3ab4d831c8 [file] [log] [blame]
Sebastian Janssonecb68972019-01-18 10:30:54 +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
Jonas Olssona4d87372019-07-05 19:08:33 +020011#include "rtc_base/task_utils/repeating_task.h"
12
Sebastian Janssonecb68972019-01-18 10:30:54 +010013#include <atomic>
Sebastian Janssonecb68972019-01-18 10:30:54 +010014#include <memory>
Sebastian Janssonecb68972019-01-18 10:30:54 +010015
Evan Shrubsolef0f47432021-11-15 17:24:45 +010016#include "api/task_queue/queued_task.h"
17#include "api/task_queue/task_queue_base.h"
18#include "api/units/timestamp.h"
Sebastian Janssonecb68972019-01-18 10:30:54 +010019#include "rtc_base/event.h"
Danil Chapovalov07122bc2019-03-26 14:37:01 +010020#include "rtc_base/task_queue_for_test.h"
Evan Shrubsolef0f47432021-11-15 17:24:45 +010021#include "rtc_base/task_utils/to_queued_task.h"
22#include "system_wrappers/include/clock.h"
Sebastian Janssonecb68972019-01-18 10:30:54 +010023#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.
28namespace webrtc {
29namespace {
30using ::testing::AtLeast;
31using ::testing::Invoke;
32using ::testing::MockFunction;
33using ::testing::NiceMock;
34using ::testing::Return;
35
Danil Chapovalov0c626af2020-02-10 11:16:00 +010036constexpr TimeDelta kTimeout = TimeDelta::Millis(1000);
Sebastian Janssonecb68972019-01-18 10:30:54 +010037
Sebastian Janssonecb68972019-01-18 10:30:54 +010038class MockClosure {
39 public:
Danil Chapovalov42748d82020-05-14 20:42:41 +020040 MOCK_METHOD(TimeDelta, Call, ());
41 MOCK_METHOD(void, Delete, ());
Sebastian Janssonecb68972019-01-18 10:30:54 +010042};
43
Tommi532cac52020-05-18 14:53:42 +020044class MockTaskQueue : public TaskQueueBase {
45 public:
46 MockTaskQueue() : task_queue_setter_(this) {}
47
48 MOCK_METHOD(void, Delete, (), (override));
49 MOCK_METHOD(void, PostTask, (std::unique_ptr<QueuedTask> task), (override));
50 MOCK_METHOD(void,
51 PostDelayedTask,
52 (std::unique_ptr<QueuedTask> task, uint32_t milliseconds),
53 (override));
54
55 private:
56 CurrentTaskQueueSetter task_queue_setter_;
57};
58
Evan Shrubsolef0f47432021-11-15 17:24:45 +010059class FakeTaskQueue : public TaskQueueBase {
60 public:
61 explicit FakeTaskQueue(SimulatedClock* clock)
62 : task_queue_setter_(this), clock_(clock) {}
63
64 void Delete() override {}
65
66 void PostTask(std::unique_ptr<QueuedTask> task) override {
67 PostDelayedTask(std::move(task), 0);
68 }
69
70 void PostDelayedTask(std::unique_ptr<QueuedTask> task,
71 uint32_t milliseconds) override {
72 last_task_ = std::move(task);
73 last_delay_ = milliseconds;
74 }
75
76 bool AdvanceTimeAndRunLastTask() {
77 EXPECT_TRUE(last_task_);
78 EXPECT_TRUE(last_delay_);
79 clock_->AdvanceTimeMilliseconds(last_delay_.value_or(0));
80 last_delay_.reset();
81 auto task = std::move(last_task_);
82 bool delete_task = task->Run();
83 if (!delete_task) {
84 // If the task should not be deleted then just release it.
85 task.release();
86 }
87 return delete_task;
88 }
89
90 bool IsTaskQueued() { return !!last_task_; }
91
92 uint32_t last_delay() const {
93 EXPECT_TRUE(last_delay_.has_value());
94 return last_delay_.value_or(-1);
95 }
96
97 private:
98 CurrentTaskQueueSetter task_queue_setter_;
99 SimulatedClock* clock_;
100 std::unique_ptr<QueuedTask> last_task_;
101 absl::optional<uint32_t> last_delay_;
102};
103
Tomas Gunnarsson87e7b3d2022-01-17 09:58:45 +0100104// NOTE: Since this utility class holds a raw pointer to a variable that likely
105// lives on the stack, it's important that any repeating tasks that use this
106// class be explicitly stopped when the test criteria have been met. If the
107// task is not stopped, an instance of this class can be deleted when the
108// pointed-to MockClosure has been deleted and we end up trying to call a
109// virtual method on a deleted object in the dtor.
Sebastian Janssonecb68972019-01-18 10:30:54 +0100110class MoveOnlyClosure {
111 public:
112 explicit MoveOnlyClosure(MockClosure* mock) : mock_(mock) {}
113 MoveOnlyClosure(const MoveOnlyClosure&) = delete;
114 MoveOnlyClosure(MoveOnlyClosure&& other) : mock_(other.mock_) {
115 other.mock_ = nullptr;
116 }
117 ~MoveOnlyClosure() {
118 if (mock_)
119 mock_->Delete();
120 }
121 TimeDelta operator()() { return mock_->Call(); }
122
123 private:
124 MockClosure* mock_;
125};
126} // namespace
127
128TEST(RepeatingTaskTest, TaskIsStoppedOnStop) {
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100129 const TimeDelta kShortInterval = TimeDelta::Millis(50);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100130
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100131 SimulatedClock clock(Timestamp::Zero());
132 FakeTaskQueue task_queue(&clock);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100133 std::atomic_int counter(0);
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100134 auto handle = RepeatingTaskHandle::Start(&task_queue, [&] {
135 counter++;
Sebastian Janssonecb68972019-01-18 10:30:54 +0100136 return kShortInterval;
137 });
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100138 EXPECT_EQ(task_queue.last_delay(), 0u);
139 EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
140 EXPECT_EQ(counter.load(), 1);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100141
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100142 // The handle reposted at the short interval.
143 EXPECT_EQ(task_queue.last_delay(), kShortInterval.ms());
144
145 // Stop the handle. This prevernts the counter from incrementing.
146 handle.Stop();
147 EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
148 EXPECT_EQ(counter.load(), 1);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100149}
150
151TEST(RepeatingTaskTest, CompensatesForLongRunTime) {
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100152 const TimeDelta kRepeatInterval = TimeDelta::Millis(2);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100153 // Sleeping inside the task for longer than the repeat interval once, should
154 // be compensated for by repeating the task faster to catch up.
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100155 const TimeDelta kSleepDuration = TimeDelta::Millis(20);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100156
157 std::atomic_int counter(0);
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100158 SimulatedClock clock(Timestamp::Zero());
159 FakeTaskQueue task_queue(&clock);
160 RepeatingTaskHandle::Start(
161 &task_queue,
162 [&] {
163 ++counter;
164 // Task takes longer than the repeat duration.
165 clock.AdvanceTime(kSleepDuration);
166 return kRepeatInterval;
167 },
168 &clock);
169
170 EXPECT_EQ(task_queue.last_delay(), 0u);
171 EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
172
173 // Task is posted right away since it took longer to run then the repeat
174 // interval.
175 EXPECT_EQ(task_queue.last_delay(), 0u);
176 EXPECT_EQ(counter.load(), 1);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100177}
178
179TEST(RepeatingTaskTest, CompensatesForShortRunTime) {
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100180 SimulatedClock clock(Timestamp::Millis(0));
181 FakeTaskQueue task_queue(&clock);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100182 std::atomic_int counter(0);
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100183 RepeatingTaskHandle::Start(
184 &task_queue,
185 [&] {
186 // Simulate the task taking 100ms, which should be compensated for.
187 counter++;
188 clock.AdvanceTime(TimeDelta::Millis(100));
189 return TimeDelta::Millis(300);
190 },
191 &clock);
Sebastian Janssona497d122019-02-04 16:39:28 +0100192
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100193 // Expect instant post task.
194 EXPECT_EQ(task_queue.last_delay(), 0u);
195 // Task should be retained by the handler since it is not cancelled.
196 EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
197 // New delay should be 200ms since repeat delay was 300ms but task took 100ms.
198 EXPECT_EQ(task_queue.last_delay(), 200u);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100199}
200
201TEST(RepeatingTaskTest, CancelDelayedTaskBeforeItRuns) {
202 rtc::Event done;
203 MockClosure mock;
204 EXPECT_CALL(mock, Call).Times(0);
205 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100206 TaskQueueForTest task_queue("queue");
Sebastian Janssonecb68972019-01-18 10:30:54 +0100207 auto handle = RepeatingTaskHandle::DelayedStart(
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100208 task_queue.Get(), TimeDelta::Millis(100), MoveOnlyClosure(&mock));
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200209 task_queue.PostTask(
210 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100211 EXPECT_TRUE(done.Wait(kTimeout.ms()));
212}
213
214TEST(RepeatingTaskTest, CancelTaskAfterItRuns) {
215 rtc::Event done;
216 MockClosure mock;
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100217 EXPECT_CALL(mock, Call).WillOnce(Return(TimeDelta::Millis(100)));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100218 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100219 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100220 auto handle =
221 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&mock));
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200222 task_queue.PostTask(
223 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100224 EXPECT_TRUE(done.Wait(kTimeout.ms()));
225}
226
227TEST(RepeatingTaskTest, TaskCanStopItself) {
228 std::atomic_int counter(0);
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100229 SimulatedClock clock(Timestamp::Zero());
230 FakeTaskQueue task_queue(&clock);
231 RepeatingTaskHandle handle = RepeatingTaskHandle::Start(&task_queue, [&] {
232 ++counter;
233 handle.Stop();
234 return TimeDelta::Millis(2);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100235 });
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100236 EXPECT_EQ(task_queue.last_delay(), 0u);
237 // Task cancelled itself so wants to be released.
238 EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100239 EXPECT_EQ(counter.load(), 1);
240}
241
Niels Möller902b5542022-01-17 15:20:24 +0100242TEST(RepeatingTaskTest, TaskCanStopItselfByReturningInfinity) {
243 std::atomic_int counter(0);
244 SimulatedClock clock(Timestamp::Zero());
245 FakeTaskQueue task_queue(&clock);
246 RepeatingTaskHandle handle = RepeatingTaskHandle::Start(&task_queue, [&] {
247 ++counter;
248 return TimeDelta::PlusInfinity();
249 });
250 EXPECT_EQ(task_queue.last_delay(), 0u);
251 // Task cancelled itself so wants to be released.
252 EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
253 EXPECT_EQ(counter.load(), 1);
254}
255
Sebastian Janssonecb68972019-01-18 10:30:54 +0100256TEST(RepeatingTaskTest, ZeroReturnValueRepostsTheTask) {
257 NiceMock<MockClosure> closure;
258 rtc::Event done;
259 EXPECT_CALL(closure, Call())
260 .WillOnce(Return(TimeDelta::Zero()))
Tomas Gunnarsson87e7b3d2022-01-17 09:58:45 +0100261 .WillOnce(Invoke([&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100262 done.Set();
Niels Möller902b5542022-01-17 15:20:24 +0100263 return TimeDelta::PlusInfinity();
Sebastian Janssonecb68972019-01-18 10:30:54 +0100264 }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100265 TaskQueueForTest task_queue("queue");
Niels Möller902b5542022-01-17 15:20:24 +0100266 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100267 EXPECT_TRUE(done.Wait(kTimeout.ms()));
268}
269
270TEST(RepeatingTaskTest, StartPeriodicTask) {
271 MockFunction<TimeDelta()> closure;
272 rtc::Event done;
273 EXPECT_CALL(closure, Call())
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100274 .WillOnce(Return(TimeDelta::Millis(20)))
275 .WillOnce(Return(TimeDelta::Millis(20)))
Tomas Gunnarsson87e7b3d2022-01-17 09:58:45 +0100276 .WillOnce(Invoke([&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100277 done.Set();
Niels Möller902b5542022-01-17 15:20:24 +0100278 return TimeDelta::PlusInfinity();
Sebastian Janssonecb68972019-01-18 10:30:54 +0100279 }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100280 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100281 RepeatingTaskHandle::Start(task_queue.Get(), closure.AsStdFunction());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100282 EXPECT_TRUE(done.Wait(kTimeout.ms()));
283}
284
285TEST(RepeatingTaskTest, Example) {
286 class ObjectOnTaskQueue {
287 public:
288 void DoPeriodicTask() {}
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100289 TimeDelta TimeUntilNextRun() { return TimeDelta::Millis(100); }
Sebastian Janssonecb68972019-01-18 10:30:54 +0100290 void StartPeriodicTask(RepeatingTaskHandle* handle,
Danil Chapovalov4423c362019-03-06 18:41:39 +0100291 TaskQueueBase* task_queue) {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100292 *handle = RepeatingTaskHandle::Start(task_queue, [this] {
293 DoPeriodicTask();
294 return TimeUntilNextRun();
295 });
296 }
297 };
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100298 TaskQueueForTest task_queue("queue");
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200299 auto object = std::make_unique<ObjectOnTaskQueue>();
Sebastian Janssonecb68972019-01-18 10:30:54 +0100300 // Create and start the periodic task.
301 RepeatingTaskHandle handle;
Danil Chapovalov4423c362019-03-06 18:41:39 +0100302 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100303 // Restart the task
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200304 task_queue.PostTask(
305 [handle = std::move(handle)]() mutable { handle.Stop(); });
Danil Chapovalov4423c362019-03-06 18:41:39 +0100306 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200307 task_queue.PostTask(
308 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100309 struct Destructor {
310 void operator()() { object.reset(); }
311 std::unique_ptr<ObjectOnTaskQueue> object;
312 };
313 task_queue.PostTask(Destructor{std::move(object)});
314 // Do not wait for the destructor closure in order to create a race between
315 // task queue destruction and running the desctructor closure.
316}
317
Tommi532cac52020-05-18 14:53:42 +0200318TEST(RepeatingTaskTest, ClockIntegration) {
319 std::unique_ptr<QueuedTask> delayed_task;
320 uint32_t expected_ms = 0;
321 SimulatedClock clock(Timestamp::Millis(0));
322
323 NiceMock<MockTaskQueue> task_queue;
324 ON_CALL(task_queue, PostDelayedTask)
325 .WillByDefault(
326 Invoke([&delayed_task, &expected_ms](std::unique_ptr<QueuedTask> task,
327 uint32_t milliseconds) {
328 EXPECT_EQ(milliseconds, expected_ms);
329 delayed_task = std::move(task);
330 }));
331
332 expected_ms = 100;
333 RepeatingTaskHandle handle = RepeatingTaskHandle::DelayedStart(
334 &task_queue, TimeDelta::Millis(100),
335 [&clock]() {
336 EXPECT_EQ(Timestamp::Millis(100), clock.CurrentTime());
337 // Simulate work happening for 10ms.
338 clock.AdvanceTimeMilliseconds(10);
339 return TimeDelta::Millis(100);
340 },
341 &clock);
342
343 clock.AdvanceTimeMilliseconds(100);
344 QueuedTask* task_to_run = delayed_task.release();
345 expected_ms = 90;
346 EXPECT_FALSE(task_to_run->Run());
347 EXPECT_NE(nullptr, delayed_task.get());
348 handle.Stop();
349}
350
Danil Chapovalov0f9a8e32021-06-11 18:39:17 +0200351TEST(RepeatingTaskTest, CanBeStoppedAfterTaskQueueDeletedTheRepeatingTask) {
352 std::unique_ptr<QueuedTask> repeating_task;
353
354 MockTaskQueue task_queue;
355 EXPECT_CALL(task_queue, PostDelayedTask)
356 .WillOnce([&](std::unique_ptr<QueuedTask> task, uint32_t milliseconds) {
357 repeating_task = std::move(task);
358 });
359
360 RepeatingTaskHandle handle =
361 RepeatingTaskHandle::DelayedStart(&task_queue, TimeDelta::Millis(100),
362 [] { return TimeDelta::Millis(100); });
363
364 // shutdown task queue: delete all pending tasks and run 'regular' task.
365 repeating_task = nullptr;
366 handle.Stop();
367}
368
Sebastian Janssonecb68972019-01-18 10:30:54 +0100369} // namespace webrtc