blob: ac0520a2e0ffee2db74028496ca85caeccb22d48 [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
242TEST(RepeatingTaskTest, ZeroReturnValueRepostsTheTask) {
243 NiceMock<MockClosure> closure;
244 rtc::Event done;
Tomas Gunnarsson87e7b3d2022-01-17 09:58:45 +0100245 RepeatingTaskHandle handle;
Sebastian Janssonecb68972019-01-18 10:30:54 +0100246 EXPECT_CALL(closure, Call())
247 .WillOnce(Return(TimeDelta::Zero()))
Tomas Gunnarsson87e7b3d2022-01-17 09:58:45 +0100248 .WillOnce(Invoke([&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100249 done.Set();
Tomas Gunnarsson87e7b3d2022-01-17 09:58:45 +0100250 handle.Stop();
Sebastian Janssonecb68972019-01-18 10:30:54 +0100251 return kTimeout;
252 }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100253 TaskQueueForTest task_queue("queue");
Tomas Gunnarsson87e7b3d2022-01-17 09:58:45 +0100254 handle =
255 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100256 EXPECT_TRUE(done.Wait(kTimeout.ms()));
257}
258
259TEST(RepeatingTaskTest, StartPeriodicTask) {
260 MockFunction<TimeDelta()> closure;
261 rtc::Event done;
Tomas Gunnarsson87e7b3d2022-01-17 09:58:45 +0100262 RepeatingTaskHandle handle;
Sebastian Janssonecb68972019-01-18 10:30:54 +0100263 EXPECT_CALL(closure, Call())
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100264 .WillOnce(Return(TimeDelta::Millis(20)))
265 .WillOnce(Return(TimeDelta::Millis(20)))
Tomas Gunnarsson87e7b3d2022-01-17 09:58:45 +0100266 .WillOnce(Invoke([&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100267 done.Set();
Tomas Gunnarsson87e7b3d2022-01-17 09:58:45 +0100268 handle.Stop();
Sebastian Janssonecb68972019-01-18 10:30:54 +0100269 return kTimeout;
270 }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100271 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100272 RepeatingTaskHandle::Start(task_queue.Get(), closure.AsStdFunction());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100273 EXPECT_TRUE(done.Wait(kTimeout.ms()));
274}
275
276TEST(RepeatingTaskTest, Example) {
277 class ObjectOnTaskQueue {
278 public:
279 void DoPeriodicTask() {}
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100280 TimeDelta TimeUntilNextRun() { return TimeDelta::Millis(100); }
Sebastian Janssonecb68972019-01-18 10:30:54 +0100281 void StartPeriodicTask(RepeatingTaskHandle* handle,
Danil Chapovalov4423c362019-03-06 18:41:39 +0100282 TaskQueueBase* task_queue) {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100283 *handle = RepeatingTaskHandle::Start(task_queue, [this] {
284 DoPeriodicTask();
285 return TimeUntilNextRun();
286 });
287 }
288 };
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100289 TaskQueueForTest task_queue("queue");
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200290 auto object = std::make_unique<ObjectOnTaskQueue>();
Sebastian Janssonecb68972019-01-18 10:30:54 +0100291 // Create and start the periodic task.
292 RepeatingTaskHandle handle;
Danil Chapovalov4423c362019-03-06 18:41:39 +0100293 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100294 // Restart the task
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200295 task_queue.PostTask(
296 [handle = std::move(handle)]() mutable { handle.Stop(); });
Danil Chapovalov4423c362019-03-06 18:41:39 +0100297 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200298 task_queue.PostTask(
299 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100300 struct Destructor {
301 void operator()() { object.reset(); }
302 std::unique_ptr<ObjectOnTaskQueue> object;
303 };
304 task_queue.PostTask(Destructor{std::move(object)});
305 // Do not wait for the destructor closure in order to create a race between
306 // task queue destruction and running the desctructor closure.
307}
308
Tommi532cac52020-05-18 14:53:42 +0200309TEST(RepeatingTaskTest, ClockIntegration) {
310 std::unique_ptr<QueuedTask> delayed_task;
311 uint32_t expected_ms = 0;
312 SimulatedClock clock(Timestamp::Millis(0));
313
314 NiceMock<MockTaskQueue> task_queue;
315 ON_CALL(task_queue, PostDelayedTask)
316 .WillByDefault(
317 Invoke([&delayed_task, &expected_ms](std::unique_ptr<QueuedTask> task,
318 uint32_t milliseconds) {
319 EXPECT_EQ(milliseconds, expected_ms);
320 delayed_task = std::move(task);
321 }));
322
323 expected_ms = 100;
324 RepeatingTaskHandle handle = RepeatingTaskHandle::DelayedStart(
325 &task_queue, TimeDelta::Millis(100),
326 [&clock]() {
327 EXPECT_EQ(Timestamp::Millis(100), clock.CurrentTime());
328 // Simulate work happening for 10ms.
329 clock.AdvanceTimeMilliseconds(10);
330 return TimeDelta::Millis(100);
331 },
332 &clock);
333
334 clock.AdvanceTimeMilliseconds(100);
335 QueuedTask* task_to_run = delayed_task.release();
336 expected_ms = 90;
337 EXPECT_FALSE(task_to_run->Run());
338 EXPECT_NE(nullptr, delayed_task.get());
339 handle.Stop();
340}
341
Danil Chapovalov0f9a8e32021-06-11 18:39:17 +0200342TEST(RepeatingTaskTest, CanBeStoppedAfterTaskQueueDeletedTheRepeatingTask) {
343 std::unique_ptr<QueuedTask> repeating_task;
344
345 MockTaskQueue task_queue;
346 EXPECT_CALL(task_queue, PostDelayedTask)
347 .WillOnce([&](std::unique_ptr<QueuedTask> task, uint32_t milliseconds) {
348 repeating_task = std::move(task);
349 });
350
351 RepeatingTaskHandle handle =
352 RepeatingTaskHandle::DelayedStart(&task_queue, TimeDelta::Millis(100),
353 [] { return TimeDelta::Millis(100); });
354
355 // shutdown task queue: delete all pending tasks and run 'regular' task.
356 repeating_task = nullptr;
357 handle.Stop();
358}
359
Sebastian Janssonecb68972019-01-18 10:30:54 +0100360} // namespace webrtc