blob: 5c20f8f260b270f874c1e2928877b9d99d4460d2 [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
Sebastian Janssonecb68972019-01-18 10:30:54 +0100104class MoveOnlyClosure {
105 public:
106 explicit MoveOnlyClosure(MockClosure* mock) : mock_(mock) {}
107 MoveOnlyClosure(const MoveOnlyClosure&) = delete;
108 MoveOnlyClosure(MoveOnlyClosure&& other) : mock_(other.mock_) {
109 other.mock_ = nullptr;
110 }
111 ~MoveOnlyClosure() {
112 if (mock_)
113 mock_->Delete();
114 }
115 TimeDelta operator()() { return mock_->Call(); }
116
117 private:
118 MockClosure* mock_;
119};
120} // namespace
121
122TEST(RepeatingTaskTest, TaskIsStoppedOnStop) {
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100123 const TimeDelta kShortInterval = TimeDelta::Millis(50);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100124
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100125 SimulatedClock clock(Timestamp::Zero());
126 FakeTaskQueue task_queue(&clock);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100127 std::atomic_int counter(0);
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100128 auto handle = RepeatingTaskHandle::Start(&task_queue, [&] {
129 counter++;
Sebastian Janssonecb68972019-01-18 10:30:54 +0100130 return kShortInterval;
131 });
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100132 EXPECT_EQ(task_queue.last_delay(), 0u);
133 EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
134 EXPECT_EQ(counter.load(), 1);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100135
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100136 // The handle reposted at the short interval.
137 EXPECT_EQ(task_queue.last_delay(), kShortInterval.ms());
138
139 // Stop the handle. This prevernts the counter from incrementing.
140 handle.Stop();
141 EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
142 EXPECT_EQ(counter.load(), 1);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100143}
144
145TEST(RepeatingTaskTest, CompensatesForLongRunTime) {
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100146 const TimeDelta kRepeatInterval = TimeDelta::Millis(2);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100147 // Sleeping inside the task for longer than the repeat interval once, should
148 // be compensated for by repeating the task faster to catch up.
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100149 const TimeDelta kSleepDuration = TimeDelta::Millis(20);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100150
151 std::atomic_int counter(0);
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100152 SimulatedClock clock(Timestamp::Zero());
153 FakeTaskQueue task_queue(&clock);
154 RepeatingTaskHandle::Start(
155 &task_queue,
156 [&] {
157 ++counter;
158 // Task takes longer than the repeat duration.
159 clock.AdvanceTime(kSleepDuration);
160 return kRepeatInterval;
161 },
162 &clock);
163
164 EXPECT_EQ(task_queue.last_delay(), 0u);
165 EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
166
167 // Task is posted right away since it took longer to run then the repeat
168 // interval.
169 EXPECT_EQ(task_queue.last_delay(), 0u);
170 EXPECT_EQ(counter.load(), 1);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100171}
172
173TEST(RepeatingTaskTest, CompensatesForShortRunTime) {
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100174 SimulatedClock clock(Timestamp::Millis(0));
175 FakeTaskQueue task_queue(&clock);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100176 std::atomic_int counter(0);
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100177 RepeatingTaskHandle::Start(
178 &task_queue,
179 [&] {
180 // Simulate the task taking 100ms, which should be compensated for.
181 counter++;
182 clock.AdvanceTime(TimeDelta::Millis(100));
183 return TimeDelta::Millis(300);
184 },
185 &clock);
Sebastian Janssona497d122019-02-04 16:39:28 +0100186
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100187 // Expect instant post task.
188 EXPECT_EQ(task_queue.last_delay(), 0u);
189 // Task should be retained by the handler since it is not cancelled.
190 EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
191 // New delay should be 200ms since repeat delay was 300ms but task took 100ms.
192 EXPECT_EQ(task_queue.last_delay(), 200u);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100193}
194
195TEST(RepeatingTaskTest, CancelDelayedTaskBeforeItRuns) {
196 rtc::Event done;
197 MockClosure mock;
198 EXPECT_CALL(mock, Call).Times(0);
199 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100200 TaskQueueForTest task_queue("queue");
Sebastian Janssonecb68972019-01-18 10:30:54 +0100201 auto handle = RepeatingTaskHandle::DelayedStart(
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100202 task_queue.Get(), TimeDelta::Millis(100), MoveOnlyClosure(&mock));
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200203 task_queue.PostTask(
204 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100205 EXPECT_TRUE(done.Wait(kTimeout.ms()));
206}
207
208TEST(RepeatingTaskTest, CancelTaskAfterItRuns) {
209 rtc::Event done;
210 MockClosure mock;
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100211 EXPECT_CALL(mock, Call).WillOnce(Return(TimeDelta::Millis(100)));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100212 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100213 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100214 auto handle =
215 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&mock));
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200216 task_queue.PostTask(
217 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100218 EXPECT_TRUE(done.Wait(kTimeout.ms()));
219}
220
221TEST(RepeatingTaskTest, TaskCanStopItself) {
222 std::atomic_int counter(0);
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100223 SimulatedClock clock(Timestamp::Zero());
224 FakeTaskQueue task_queue(&clock);
225 RepeatingTaskHandle handle = RepeatingTaskHandle::Start(&task_queue, [&] {
226 ++counter;
227 handle.Stop();
228 return TimeDelta::Millis(2);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100229 });
Evan Shrubsolef0f47432021-11-15 17:24:45 +0100230 EXPECT_EQ(task_queue.last_delay(), 0u);
231 // Task cancelled itself so wants to be released.
232 EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100233 EXPECT_EQ(counter.load(), 1);
234}
235
236TEST(RepeatingTaskTest, ZeroReturnValueRepostsTheTask) {
237 NiceMock<MockClosure> closure;
238 rtc::Event done;
239 EXPECT_CALL(closure, Call())
240 .WillOnce(Return(TimeDelta::Zero()))
241 .WillOnce(Invoke([&done] {
242 done.Set();
243 return kTimeout;
244 }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100245 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100246 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100247 EXPECT_TRUE(done.Wait(kTimeout.ms()));
248}
249
250TEST(RepeatingTaskTest, StartPeriodicTask) {
251 MockFunction<TimeDelta()> closure;
252 rtc::Event done;
253 EXPECT_CALL(closure, Call())
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100254 .WillOnce(Return(TimeDelta::Millis(20)))
255 .WillOnce(Return(TimeDelta::Millis(20)))
Sebastian Janssonecb68972019-01-18 10:30:54 +0100256 .WillOnce(Invoke([&done] {
257 done.Set();
258 return kTimeout;
259 }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100260 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100261 RepeatingTaskHandle::Start(task_queue.Get(), closure.AsStdFunction());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100262 EXPECT_TRUE(done.Wait(kTimeout.ms()));
263}
264
265TEST(RepeatingTaskTest, Example) {
266 class ObjectOnTaskQueue {
267 public:
268 void DoPeriodicTask() {}
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100269 TimeDelta TimeUntilNextRun() { return TimeDelta::Millis(100); }
Sebastian Janssonecb68972019-01-18 10:30:54 +0100270 void StartPeriodicTask(RepeatingTaskHandle* handle,
Danil Chapovalov4423c362019-03-06 18:41:39 +0100271 TaskQueueBase* task_queue) {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100272 *handle = RepeatingTaskHandle::Start(task_queue, [this] {
273 DoPeriodicTask();
274 return TimeUntilNextRun();
275 });
276 }
277 };
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100278 TaskQueueForTest task_queue("queue");
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200279 auto object = std::make_unique<ObjectOnTaskQueue>();
Sebastian Janssonecb68972019-01-18 10:30:54 +0100280 // Create and start the periodic task.
281 RepeatingTaskHandle handle;
Danil Chapovalov4423c362019-03-06 18:41:39 +0100282 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100283 // Restart the task
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200284 task_queue.PostTask(
285 [handle = std::move(handle)]() mutable { handle.Stop(); });
Danil Chapovalov4423c362019-03-06 18:41:39 +0100286 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200287 task_queue.PostTask(
288 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100289 struct Destructor {
290 void operator()() { object.reset(); }
291 std::unique_ptr<ObjectOnTaskQueue> object;
292 };
293 task_queue.PostTask(Destructor{std::move(object)});
294 // Do not wait for the destructor closure in order to create a race between
295 // task queue destruction and running the desctructor closure.
296}
297
Tommi532cac52020-05-18 14:53:42 +0200298TEST(RepeatingTaskTest, ClockIntegration) {
299 std::unique_ptr<QueuedTask> delayed_task;
300 uint32_t expected_ms = 0;
301 SimulatedClock clock(Timestamp::Millis(0));
302
303 NiceMock<MockTaskQueue> task_queue;
304 ON_CALL(task_queue, PostDelayedTask)
305 .WillByDefault(
306 Invoke([&delayed_task, &expected_ms](std::unique_ptr<QueuedTask> task,
307 uint32_t milliseconds) {
308 EXPECT_EQ(milliseconds, expected_ms);
309 delayed_task = std::move(task);
310 }));
311
312 expected_ms = 100;
313 RepeatingTaskHandle handle = RepeatingTaskHandle::DelayedStart(
314 &task_queue, TimeDelta::Millis(100),
315 [&clock]() {
316 EXPECT_EQ(Timestamp::Millis(100), clock.CurrentTime());
317 // Simulate work happening for 10ms.
318 clock.AdvanceTimeMilliseconds(10);
319 return TimeDelta::Millis(100);
320 },
321 &clock);
322
323 clock.AdvanceTimeMilliseconds(100);
324 QueuedTask* task_to_run = delayed_task.release();
325 expected_ms = 90;
326 EXPECT_FALSE(task_to_run->Run());
327 EXPECT_NE(nullptr, delayed_task.get());
328 handle.Stop();
329}
330
Danil Chapovalov0f9a8e32021-06-11 18:39:17 +0200331TEST(RepeatingTaskTest, CanBeStoppedAfterTaskQueueDeletedTheRepeatingTask) {
332 std::unique_ptr<QueuedTask> repeating_task;
333
334 MockTaskQueue task_queue;
335 EXPECT_CALL(task_queue, PostDelayedTask)
336 .WillOnce([&](std::unique_ptr<QueuedTask> task, uint32_t milliseconds) {
337 repeating_task = std::move(task);
338 });
339
340 RepeatingTaskHandle handle =
341 RepeatingTaskHandle::DelayedStart(&task_queue, TimeDelta::Millis(100),
342 [] { return TimeDelta::Millis(100); });
343
344 // shutdown task queue: delete all pending tasks and run 'regular' task.
345 repeating_task = nullptr;
346 handle.Stop();
347}
348
Sebastian Janssonecb68972019-01-18 10:30:54 +0100349} // namespace webrtc