blob: 52683e3d60bdb27f03401d534c50cca8360a7f76 [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
11#include <atomic>
12#include <chrono> // Not allowed in production per Chromium style guide.
13#include <memory>
14#include <thread> // Not allowed in production per Chromium style guide.
15
16#include "absl/memory/memory.h"
17#include "rtc_base/event.h"
18#include "rtc_base/task_utils/repeating_task.h"
19#include "test/gmock.h"
20#include "test/gtest.h"
21
22// NOTE: Since these tests rely on real time behavior, they will be flaky
23// if run on heavily loaded systems.
24namespace webrtc {
25namespace {
26using ::testing::AtLeast;
27using ::testing::Invoke;
28using ::testing::MockFunction;
29using ::testing::NiceMock;
30using ::testing::Return;
31
32constexpr TimeDelta kTimeout = TimeDelta::Millis<1000>();
33
34void Sleep(TimeDelta time_delta) {
35 // Note that Chromium style guide prohibits use of <thread> and <chrono> in
36 // production code, used here since webrtc::SleepMs may return early.
37 std::this_thread::sleep_for(std::chrono::microseconds(time_delta.us()));
38}
39
40class MockClosure {
41 public:
42 MOCK_METHOD0(Call, TimeDelta());
43 MOCK_METHOD0(Delete, void());
44};
45
46class MoveOnlyClosure {
47 public:
48 explicit MoveOnlyClosure(MockClosure* mock) : mock_(mock) {}
49 MoveOnlyClosure(const MoveOnlyClosure&) = delete;
50 MoveOnlyClosure(MoveOnlyClosure&& other) : mock_(other.mock_) {
51 other.mock_ = nullptr;
52 }
53 ~MoveOnlyClosure() {
54 if (mock_)
55 mock_->Delete();
56 }
57 TimeDelta operator()() { return mock_->Call(); }
58
59 private:
60 MockClosure* mock_;
61};
62} // namespace
63
64TEST(RepeatingTaskTest, TaskIsStoppedOnStop) {
65 const TimeDelta kShortInterval = TimeDelta::ms(5);
66 const TimeDelta kLongInterval = TimeDelta::ms(20);
67 const int kShortIntervalCount = 4;
68 const int kMargin = 1;
69
70 rtc::TaskQueue task_queue("TestQueue");
71 std::atomic_int counter(0);
72 auto handle = RepeatingTaskHandle::Start(&task_queue, [&] {
73 if (++counter >= kShortIntervalCount)
74 return kLongInterval;
75 return kShortInterval;
76 });
77 // Sleep long enough to go through the initial phase.
78 Sleep(kShortInterval * (kShortIntervalCount + kMargin));
79 EXPECT_EQ(counter.load(), kShortIntervalCount);
80
81 handle.PostStop();
82 // Sleep long enough that the task would run at least once more if not
83 // stopped.
84 Sleep(kLongInterval * 2);
85 EXPECT_EQ(counter.load(), kShortIntervalCount);
86}
87
88TEST(RepeatingTaskTest, CompensatesForLongRunTime) {
89 const int kTargetCount = 20;
90 const int kTargetCountMargin = 2;
91 const TimeDelta kRepeatInterval = TimeDelta::ms(2);
92 // Sleeping inside the task for longer than the repeat interval once, should
93 // be compensated for by repeating the task faster to catch up.
94 const TimeDelta kSleepDuration = TimeDelta::ms(20);
95 const int kSleepAtCount = 3;
96
97 std::atomic_int counter(0);
98 rtc::TaskQueue task_queue("TestQueue");
99 RepeatingTaskHandle::Start(&task_queue, [&] {
100 if (++counter == kSleepAtCount)
101 Sleep(kSleepDuration);
102 return kRepeatInterval;
103 });
104 Sleep(kRepeatInterval * kTargetCount);
105 // Execution time should not have affected the run count,
106 // but we allow some margin to reduce flakiness.
107 EXPECT_GE(counter.load(), kTargetCount - kTargetCountMargin);
108}
109
110TEST(RepeatingTaskTest, CompensatesForShortRunTime) {
111 std::atomic_int counter(0);
112 rtc::TaskQueue task_queue("TestQueue");
113 RepeatingTaskHandle::Start(&task_queue, [&] {
114 ++counter;
Sebastian Janssona497d122019-02-04 16:39:28 +0100115 // Sleeping for the 10 ms should be compensated.
116 Sleep(TimeDelta::ms(10));
117 return TimeDelta::ms(30);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100118 });
Sebastian Janssona497d122019-02-04 16:39:28 +0100119 Sleep(TimeDelta::ms(40));
120
Sebastian Janssonecb68972019-01-18 10:30:54 +0100121 // We expect that the task have been called twice, once directly at Start and
Sebastian Janssona497d122019-02-04 16:39:28 +0100122 // once after 30 ms has passed.
Sebastian Janssonecb68972019-01-18 10:30:54 +0100123 EXPECT_EQ(counter.load(), 2);
124}
125
126TEST(RepeatingTaskTest, CancelDelayedTaskBeforeItRuns) {
127 rtc::Event done;
128 MockClosure mock;
129 EXPECT_CALL(mock, Call).Times(0);
130 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
131 rtc::TaskQueue task_queue("queue");
132 auto handle = RepeatingTaskHandle::DelayedStart(
133 &task_queue, TimeDelta::ms(100), MoveOnlyClosure(&mock));
134 handle.PostStop();
135 EXPECT_TRUE(done.Wait(kTimeout.ms()));
136}
137
138TEST(RepeatingTaskTest, CancelTaskAfterItRuns) {
139 rtc::Event done;
140 MockClosure mock;
141 EXPECT_CALL(mock, Call).WillOnce(Return(TimeDelta::ms(100)));
142 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
143 rtc::TaskQueue task_queue("queue");
144 auto handle = RepeatingTaskHandle::Start(&task_queue, MoveOnlyClosure(&mock));
145 handle.PostStop();
146 EXPECT_TRUE(done.Wait(kTimeout.ms()));
147}
148
149TEST(RepeatingTaskTest, TaskCanStopItself) {
150 std::atomic_int counter(0);
151 rtc::TaskQueue task_queue("TestQueue");
152 RepeatingTaskHandle handle;
153 task_queue.PostTask([&] {
154 handle = RepeatingTaskHandle::Start(&task_queue, [&] {
155 ++counter;
156 handle.Stop();
157 return TimeDelta::ms(2);
158 });
159 });
160 Sleep(TimeDelta::ms(10));
161 EXPECT_EQ(counter.load(), 1);
162}
163
164TEST(RepeatingTaskTest, ZeroReturnValueRepostsTheTask) {
165 NiceMock<MockClosure> closure;
166 rtc::Event done;
167 EXPECT_CALL(closure, Call())
168 .WillOnce(Return(TimeDelta::Zero()))
169 .WillOnce(Invoke([&done] {
170 done.Set();
171 return kTimeout;
172 }));
173 rtc::TaskQueue task_queue("queue");
174 RepeatingTaskHandle::Start(&task_queue, MoveOnlyClosure(&closure));
175 EXPECT_TRUE(done.Wait(kTimeout.ms()));
176}
177
178TEST(RepeatingTaskTest, StartPeriodicTask) {
179 MockFunction<TimeDelta()> closure;
180 rtc::Event done;
181 EXPECT_CALL(closure, Call())
182 .WillOnce(Return(TimeDelta::ms(20)))
183 .WillOnce(Return(TimeDelta::ms(20)))
184 .WillOnce(Invoke([&done] {
185 done.Set();
186 return kTimeout;
187 }));
188 rtc::TaskQueue task_queue("queue");
189 RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction());
190 EXPECT_TRUE(done.Wait(kTimeout.ms()));
191}
192
193TEST(RepeatingTaskTest, Example) {
194 class ObjectOnTaskQueue {
195 public:
196 void DoPeriodicTask() {}
197 TimeDelta TimeUntilNextRun() { return TimeDelta::ms(100); }
198 void StartPeriodicTask(RepeatingTaskHandle* handle,
199 rtc::TaskQueue* task_queue) {
200 *handle = RepeatingTaskHandle::Start(task_queue, [this] {
201 DoPeriodicTask();
202 return TimeUntilNextRun();
203 });
204 }
205 };
206 rtc::TaskQueue task_queue("queue");
207 auto object = absl::make_unique<ObjectOnTaskQueue>();
208 // Create and start the periodic task.
209 RepeatingTaskHandle handle;
210 object->StartPeriodicTask(&handle, &task_queue);
211 // Restart the task
212 handle.PostStop();
213 object->StartPeriodicTask(&handle, &task_queue);
214 handle.PostStop();
215 struct Destructor {
216 void operator()() { object.reset(); }
217 std::unique_ptr<ObjectOnTaskQueue> object;
218 };
219 task_queue.PostTask(Destructor{std::move(object)});
220 // Do not wait for the destructor closure in order to create a race between
221 // task queue destruction and running the desctructor closure.
222}
223
224} // namespace webrtc