blob: 2fb15d1e5a30b0d6019eae363b8873177f4ca4af [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>
14#include <chrono> // Not allowed in production per Chromium style guide.
15#include <memory>
16#include <thread> // Not allowed in production per Chromium style guide.
17
Sebastian Janssonecb68972019-01-18 10:30:54 +010018#include "rtc_base/event.h"
Danil Chapovalov07122bc2019-03-26 14:37:01 +010019#include "rtc_base/task_queue_for_test.h"
Sebastian Janssonecb68972019-01-18 10:30:54 +010020#include "test/gmock.h"
21#include "test/gtest.h"
22
23// NOTE: Since these tests rely on real time behavior, they will be flaky
24// if run on heavily loaded systems.
25namespace webrtc {
26namespace {
27using ::testing::AtLeast;
28using ::testing::Invoke;
29using ::testing::MockFunction;
30using ::testing::NiceMock;
31using ::testing::Return;
32
Danil Chapovalov0c626af2020-02-10 11:16:00 +010033constexpr TimeDelta kTimeout = TimeDelta::Millis(1000);
Sebastian Janssonecb68972019-01-18 10:30:54 +010034
35void Sleep(TimeDelta time_delta) {
36 // Note that Chromium style guide prohibits use of <thread> and <chrono> in
37 // production code, used here since webrtc::SleepMs may return early.
38 std::this_thread::sleep_for(std::chrono::microseconds(time_delta.us()));
39}
40
41class MockClosure {
42 public:
Danil Chapovalov42748d82020-05-14 20:42:41 +020043 MOCK_METHOD(TimeDelta, Call, ());
44 MOCK_METHOD(void, Delete, ());
Sebastian Janssonecb68972019-01-18 10:30:54 +010045};
46
Tommi532cac52020-05-18 14:53:42 +020047class MockTaskQueue : public TaskQueueBase {
48 public:
49 MockTaskQueue() : task_queue_setter_(this) {}
50
51 MOCK_METHOD(void, Delete, (), (override));
52 MOCK_METHOD(void, PostTask, (std::unique_ptr<QueuedTask> task), (override));
53 MOCK_METHOD(void,
54 PostDelayedTask,
55 (std::unique_ptr<QueuedTask> task, uint32_t milliseconds),
56 (override));
57
58 private:
59 CurrentTaskQueueSetter task_queue_setter_;
60};
61
Sebastian Janssonecb68972019-01-18 10:30:54 +010062class MoveOnlyClosure {
63 public:
64 explicit MoveOnlyClosure(MockClosure* mock) : mock_(mock) {}
65 MoveOnlyClosure(const MoveOnlyClosure&) = delete;
66 MoveOnlyClosure(MoveOnlyClosure&& other) : mock_(other.mock_) {
67 other.mock_ = nullptr;
68 }
69 ~MoveOnlyClosure() {
70 if (mock_)
71 mock_->Delete();
72 }
73 TimeDelta operator()() { return mock_->Call(); }
74
75 private:
76 MockClosure* mock_;
77};
78} // namespace
79
80TEST(RepeatingTaskTest, TaskIsStoppedOnStop) {
Danil Chapovalov0c626af2020-02-10 11:16:00 +010081 const TimeDelta kShortInterval = TimeDelta::Millis(50);
82 const TimeDelta kLongInterval = TimeDelta::Millis(200);
Sebastian Janssonecb68972019-01-18 10:30:54 +010083 const int kShortIntervalCount = 4;
84 const int kMargin = 1;
85
Danil Chapovalov07122bc2019-03-26 14:37:01 +010086 TaskQueueForTest task_queue("TestQueue");
Sebastian Janssonecb68972019-01-18 10:30:54 +010087 std::atomic_int counter(0);
Danil Chapovalov4423c362019-03-06 18:41:39 +010088 auto handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +010089 if (++counter >= kShortIntervalCount)
90 return kLongInterval;
91 return kShortInterval;
92 });
93 // Sleep long enough to go through the initial phase.
94 Sleep(kShortInterval * (kShortIntervalCount + kMargin));
95 EXPECT_EQ(counter.load(), kShortIntervalCount);
96
Sebastian Jansson86314cf2019-09-17 20:29:59 +020097 task_queue.PostTask(
98 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +010099 // Sleep long enough that the task would run at least once more if not
100 // stopped.
101 Sleep(kLongInterval * 2);
102 EXPECT_EQ(counter.load(), kShortIntervalCount);
103}
104
105TEST(RepeatingTaskTest, CompensatesForLongRunTime) {
106 const int kTargetCount = 20;
107 const int kTargetCountMargin = 2;
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100108 const TimeDelta kRepeatInterval = TimeDelta::Millis(2);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100109 // Sleeping inside the task for longer than the repeat interval once, should
110 // be compensated for by repeating the task faster to catch up.
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100111 const TimeDelta kSleepDuration = TimeDelta::Millis(20);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100112 const int kSleepAtCount = 3;
113
114 std::atomic_int counter(0);
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100115 TaskQueueForTest task_queue("TestQueue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100116 RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100117 if (++counter == kSleepAtCount)
118 Sleep(kSleepDuration);
119 return kRepeatInterval;
120 });
121 Sleep(kRepeatInterval * kTargetCount);
122 // Execution time should not have affected the run count,
123 // but we allow some margin to reduce flakiness.
124 EXPECT_GE(counter.load(), kTargetCount - kTargetCountMargin);
125}
126
127TEST(RepeatingTaskTest, CompensatesForShortRunTime) {
128 std::atomic_int counter(0);
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100129 TaskQueueForTest task_queue("TestQueue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100130 RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100131 ++counter;
Sebastian Janssonc46a9992019-04-04 16:49:05 +0200132 // Sleeping for the 100 ms should be compensated.
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100133 Sleep(TimeDelta::Millis(100));
134 return TimeDelta::Millis(300);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100135 });
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100136 Sleep(TimeDelta::Millis(400));
Sebastian Janssona497d122019-02-04 16:39:28 +0100137
Sebastian Janssonecb68972019-01-18 10:30:54 +0100138 // We expect that the task have been called twice, once directly at Start and
Sebastian Janssonc46a9992019-04-04 16:49:05 +0200139 // once after 300 ms has passed.
Sebastian Janssonecb68972019-01-18 10:30:54 +0100140 EXPECT_EQ(counter.load(), 2);
141}
142
143TEST(RepeatingTaskTest, CancelDelayedTaskBeforeItRuns) {
144 rtc::Event done;
145 MockClosure mock;
146 EXPECT_CALL(mock, Call).Times(0);
147 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100148 TaskQueueForTest task_queue("queue");
Sebastian Janssonecb68972019-01-18 10:30:54 +0100149 auto handle = RepeatingTaskHandle::DelayedStart(
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100150 task_queue.Get(), TimeDelta::Millis(100), MoveOnlyClosure(&mock));
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200151 task_queue.PostTask(
152 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100153 EXPECT_TRUE(done.Wait(kTimeout.ms()));
154}
155
156TEST(RepeatingTaskTest, CancelTaskAfterItRuns) {
157 rtc::Event done;
158 MockClosure mock;
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100159 EXPECT_CALL(mock, Call).WillOnce(Return(TimeDelta::Millis(100)));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100160 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100161 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100162 auto handle =
163 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&mock));
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200164 task_queue.PostTask(
165 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100166 EXPECT_TRUE(done.Wait(kTimeout.ms()));
167}
168
169TEST(RepeatingTaskTest, TaskCanStopItself) {
170 std::atomic_int counter(0);
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100171 TaskQueueForTest task_queue("TestQueue");
Sebastian Janssonecb68972019-01-18 10:30:54 +0100172 RepeatingTaskHandle handle;
173 task_queue.PostTask([&] {
Danil Chapovalov4423c362019-03-06 18:41:39 +0100174 handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100175 ++counter;
176 handle.Stop();
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100177 return TimeDelta::Millis(2);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100178 });
179 });
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100180 Sleep(TimeDelta::Millis(10));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100181 EXPECT_EQ(counter.load(), 1);
182}
183
184TEST(RepeatingTaskTest, ZeroReturnValueRepostsTheTask) {
185 NiceMock<MockClosure> closure;
186 rtc::Event done;
187 EXPECT_CALL(closure, Call())
188 .WillOnce(Return(TimeDelta::Zero()))
189 .WillOnce(Invoke([&done] {
190 done.Set();
191 return kTimeout;
192 }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100193 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100194 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100195 EXPECT_TRUE(done.Wait(kTimeout.ms()));
196}
197
198TEST(RepeatingTaskTest, StartPeriodicTask) {
199 MockFunction<TimeDelta()> closure;
200 rtc::Event done;
201 EXPECT_CALL(closure, Call())
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100202 .WillOnce(Return(TimeDelta::Millis(20)))
203 .WillOnce(Return(TimeDelta::Millis(20)))
Sebastian Janssonecb68972019-01-18 10:30:54 +0100204 .WillOnce(Invoke([&done] {
205 done.Set();
206 return kTimeout;
207 }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100208 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100209 RepeatingTaskHandle::Start(task_queue.Get(), closure.AsStdFunction());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100210 EXPECT_TRUE(done.Wait(kTimeout.ms()));
211}
212
213TEST(RepeatingTaskTest, Example) {
214 class ObjectOnTaskQueue {
215 public:
216 void DoPeriodicTask() {}
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100217 TimeDelta TimeUntilNextRun() { return TimeDelta::Millis(100); }
Sebastian Janssonecb68972019-01-18 10:30:54 +0100218 void StartPeriodicTask(RepeatingTaskHandle* handle,
Danil Chapovalov4423c362019-03-06 18:41:39 +0100219 TaskQueueBase* task_queue) {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100220 *handle = RepeatingTaskHandle::Start(task_queue, [this] {
221 DoPeriodicTask();
222 return TimeUntilNextRun();
223 });
224 }
225 };
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100226 TaskQueueForTest task_queue("queue");
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200227 auto object = std::make_unique<ObjectOnTaskQueue>();
Sebastian Janssonecb68972019-01-18 10:30:54 +0100228 // Create and start the periodic task.
229 RepeatingTaskHandle handle;
Danil Chapovalov4423c362019-03-06 18:41:39 +0100230 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100231 // Restart the task
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200232 task_queue.PostTask(
233 [handle = std::move(handle)]() mutable { handle.Stop(); });
Danil Chapovalov4423c362019-03-06 18:41:39 +0100234 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200235 task_queue.PostTask(
236 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100237 struct Destructor {
238 void operator()() { object.reset(); }
239 std::unique_ptr<ObjectOnTaskQueue> object;
240 };
241 task_queue.PostTask(Destructor{std::move(object)});
242 // Do not wait for the destructor closure in order to create a race between
243 // task queue destruction and running the desctructor closure.
244}
245
Tommi532cac52020-05-18 14:53:42 +0200246TEST(RepeatingTaskTest, ClockIntegration) {
247 std::unique_ptr<QueuedTask> delayed_task;
248 uint32_t expected_ms = 0;
249 SimulatedClock clock(Timestamp::Millis(0));
250
251 NiceMock<MockTaskQueue> task_queue;
252 ON_CALL(task_queue, PostDelayedTask)
253 .WillByDefault(
254 Invoke([&delayed_task, &expected_ms](std::unique_ptr<QueuedTask> task,
255 uint32_t milliseconds) {
256 EXPECT_EQ(milliseconds, expected_ms);
257 delayed_task = std::move(task);
258 }));
259
260 expected_ms = 100;
261 RepeatingTaskHandle handle = RepeatingTaskHandle::DelayedStart(
262 &task_queue, TimeDelta::Millis(100),
263 [&clock]() {
264 EXPECT_EQ(Timestamp::Millis(100), clock.CurrentTime());
265 // Simulate work happening for 10ms.
266 clock.AdvanceTimeMilliseconds(10);
267 return TimeDelta::Millis(100);
268 },
269 &clock);
270
271 clock.AdvanceTimeMilliseconds(100);
272 QueuedTask* task_to_run = delayed_task.release();
273 expected_ms = 90;
274 EXPECT_FALSE(task_to_run->Run());
275 EXPECT_NE(nullptr, delayed_task.get());
276 handle.Stop();
277}
278
Sebastian Janssonecb68972019-01-18 10:30:54 +0100279} // namespace webrtc