blob: 469ee316f3371eb367b2cffc15d6cfbeadc920fd [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
33constexpr TimeDelta kTimeout = TimeDelta::Millis<1000>();
34
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:
43 MOCK_METHOD0(Call, TimeDelta());
44 MOCK_METHOD0(Delete, void());
45};
46
47class MoveOnlyClosure {
48 public:
49 explicit MoveOnlyClosure(MockClosure* mock) : mock_(mock) {}
50 MoveOnlyClosure(const MoveOnlyClosure&) = delete;
51 MoveOnlyClosure(MoveOnlyClosure&& other) : mock_(other.mock_) {
52 other.mock_ = nullptr;
53 }
54 ~MoveOnlyClosure() {
55 if (mock_)
56 mock_->Delete();
57 }
58 TimeDelta operator()() { return mock_->Call(); }
59
60 private:
61 MockClosure* mock_;
62};
63} // namespace
64
65TEST(RepeatingTaskTest, TaskIsStoppedOnStop) {
Sebastian Janssonc46a9992019-04-04 16:49:05 +020066 const TimeDelta kShortInterval = TimeDelta::ms(50);
67 const TimeDelta kLongInterval = TimeDelta::ms(200);
Sebastian Janssonecb68972019-01-18 10:30:54 +010068 const int kShortIntervalCount = 4;
69 const int kMargin = 1;
70
Danil Chapovalov07122bc2019-03-26 14:37:01 +010071 TaskQueueForTest task_queue("TestQueue");
Sebastian Janssonecb68972019-01-18 10:30:54 +010072 std::atomic_int counter(0);
Danil Chapovalov4423c362019-03-06 18:41:39 +010073 auto handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +010074 if (++counter >= kShortIntervalCount)
75 return kLongInterval;
76 return kShortInterval;
77 });
78 // Sleep long enough to go through the initial phase.
79 Sleep(kShortInterval * (kShortIntervalCount + kMargin));
80 EXPECT_EQ(counter.load(), kShortIntervalCount);
81
Sebastian Jansson86314cf2019-09-17 20:29:59 +020082 task_queue.PostTask(
83 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +010084 // Sleep long enough that the task would run at least once more if not
85 // stopped.
86 Sleep(kLongInterval * 2);
87 EXPECT_EQ(counter.load(), kShortIntervalCount);
88}
89
90TEST(RepeatingTaskTest, CompensatesForLongRunTime) {
91 const int kTargetCount = 20;
92 const int kTargetCountMargin = 2;
93 const TimeDelta kRepeatInterval = TimeDelta::ms(2);
94 // Sleeping inside the task for longer than the repeat interval once, should
95 // be compensated for by repeating the task faster to catch up.
96 const TimeDelta kSleepDuration = TimeDelta::ms(20);
97 const int kSleepAtCount = 3;
98
99 std::atomic_int counter(0);
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100100 TaskQueueForTest task_queue("TestQueue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100101 RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100102 if (++counter == kSleepAtCount)
103 Sleep(kSleepDuration);
104 return kRepeatInterval;
105 });
106 Sleep(kRepeatInterval * kTargetCount);
107 // Execution time should not have affected the run count,
108 // but we allow some margin to reduce flakiness.
109 EXPECT_GE(counter.load(), kTargetCount - kTargetCountMargin);
110}
111
112TEST(RepeatingTaskTest, CompensatesForShortRunTime) {
113 std::atomic_int counter(0);
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100114 TaskQueueForTest task_queue("TestQueue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100115 RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100116 ++counter;
Sebastian Janssonc46a9992019-04-04 16:49:05 +0200117 // Sleeping for the 100 ms should be compensated.
118 Sleep(TimeDelta::ms(100));
119 return TimeDelta::ms(300);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100120 });
Sebastian Janssonc46a9992019-04-04 16:49:05 +0200121 Sleep(TimeDelta::ms(400));
Sebastian Janssona497d122019-02-04 16:39:28 +0100122
Sebastian Janssonecb68972019-01-18 10:30:54 +0100123 // We expect that the task have been called twice, once directly at Start and
Sebastian Janssonc46a9992019-04-04 16:49:05 +0200124 // once after 300 ms has passed.
Sebastian Janssonecb68972019-01-18 10:30:54 +0100125 EXPECT_EQ(counter.load(), 2);
126}
127
128TEST(RepeatingTaskTest, CancelDelayedTaskBeforeItRuns) {
129 rtc::Event done;
130 MockClosure mock;
131 EXPECT_CALL(mock, Call).Times(0);
132 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100133 TaskQueueForTest task_queue("queue");
Sebastian Janssonecb68972019-01-18 10:30:54 +0100134 auto handle = RepeatingTaskHandle::DelayedStart(
Danil Chapovalov4423c362019-03-06 18:41:39 +0100135 task_queue.Get(), TimeDelta::ms(100), MoveOnlyClosure(&mock));
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200136 task_queue.PostTask(
137 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100138 EXPECT_TRUE(done.Wait(kTimeout.ms()));
139}
140
141TEST(RepeatingTaskTest, CancelTaskAfterItRuns) {
142 rtc::Event done;
143 MockClosure mock;
144 EXPECT_CALL(mock, Call).WillOnce(Return(TimeDelta::ms(100)));
145 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100146 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100147 auto handle =
148 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&mock));
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200149 task_queue.PostTask(
150 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100151 EXPECT_TRUE(done.Wait(kTimeout.ms()));
152}
153
154TEST(RepeatingTaskTest, TaskCanStopItself) {
155 std::atomic_int counter(0);
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100156 TaskQueueForTest task_queue("TestQueue");
Sebastian Janssonecb68972019-01-18 10:30:54 +0100157 RepeatingTaskHandle handle;
158 task_queue.PostTask([&] {
Danil Chapovalov4423c362019-03-06 18:41:39 +0100159 handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100160 ++counter;
161 handle.Stop();
162 return TimeDelta::ms(2);
163 });
164 });
165 Sleep(TimeDelta::ms(10));
166 EXPECT_EQ(counter.load(), 1);
167}
168
169TEST(RepeatingTaskTest, ZeroReturnValueRepostsTheTask) {
170 NiceMock<MockClosure> closure;
171 rtc::Event done;
172 EXPECT_CALL(closure, Call())
173 .WillOnce(Return(TimeDelta::Zero()))
174 .WillOnce(Invoke([&done] {
175 done.Set();
176 return kTimeout;
177 }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100178 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100179 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100180 EXPECT_TRUE(done.Wait(kTimeout.ms()));
181}
182
183TEST(RepeatingTaskTest, StartPeriodicTask) {
184 MockFunction<TimeDelta()> closure;
185 rtc::Event done;
186 EXPECT_CALL(closure, Call())
187 .WillOnce(Return(TimeDelta::ms(20)))
188 .WillOnce(Return(TimeDelta::ms(20)))
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(), closure.AsStdFunction());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100195 EXPECT_TRUE(done.Wait(kTimeout.ms()));
196}
197
198TEST(RepeatingTaskTest, Example) {
199 class ObjectOnTaskQueue {
200 public:
201 void DoPeriodicTask() {}
202 TimeDelta TimeUntilNextRun() { return TimeDelta::ms(100); }
203 void StartPeriodicTask(RepeatingTaskHandle* handle,
Danil Chapovalov4423c362019-03-06 18:41:39 +0100204 TaskQueueBase* task_queue) {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100205 *handle = RepeatingTaskHandle::Start(task_queue, [this] {
206 DoPeriodicTask();
207 return TimeUntilNextRun();
208 });
209 }
210 };
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100211 TaskQueueForTest task_queue("queue");
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200212 auto object = std::make_unique<ObjectOnTaskQueue>();
Sebastian Janssonecb68972019-01-18 10:30:54 +0100213 // Create and start the periodic task.
214 RepeatingTaskHandle handle;
Danil Chapovalov4423c362019-03-06 18:41:39 +0100215 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100216 // Restart the task
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200217 task_queue.PostTask(
218 [handle = std::move(handle)]() mutable { handle.Stop(); });
Danil Chapovalov4423c362019-03-06 18:41:39 +0100219 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Jansson86314cf2019-09-17 20:29:59 +0200220 task_queue.PostTask(
221 [handle = std::move(handle)]() mutable { handle.Stop(); });
Sebastian Janssonecb68972019-01-18 10:30:54 +0100222 struct Destructor {
223 void operator()() { object.reset(); }
224 std::unique_ptr<ObjectOnTaskQueue> object;
225 };
226 task_queue.PostTask(Destructor{std::move(object)});
227 // Do not wait for the destructor closure in order to create a race between
228 // task queue destruction and running the desctructor closure.
229}
230
231} // namespace webrtc