blob: 244bb8e403cecb09d9fd9e3b8710a4a16f3062f1 [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"
Danil Chapovalov4423c362019-03-06 18:41:39 +010018#include "rtc_base/task_queue.h"
Sebastian Janssonecb68972019-01-18 10:30:54 +010019#include "rtc_base/task_utils/repeating_task.h"
20#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) {
66 const TimeDelta kShortInterval = TimeDelta::ms(5);
67 const TimeDelta kLongInterval = TimeDelta::ms(20);
68 const int kShortIntervalCount = 4;
69 const int kMargin = 1;
70
71 rtc::TaskQueue task_queue("TestQueue");
72 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
82 handle.PostStop();
83 // Sleep long enough that the task would run at least once more if not
84 // stopped.
85 Sleep(kLongInterval * 2);
86 EXPECT_EQ(counter.load(), kShortIntervalCount);
87}
88
89TEST(RepeatingTaskTest, CompensatesForLongRunTime) {
90 const int kTargetCount = 20;
91 const int kTargetCountMargin = 2;
92 const TimeDelta kRepeatInterval = TimeDelta::ms(2);
93 // Sleeping inside the task for longer than the repeat interval once, should
94 // be compensated for by repeating the task faster to catch up.
95 const TimeDelta kSleepDuration = TimeDelta::ms(20);
96 const int kSleepAtCount = 3;
97
98 std::atomic_int counter(0);
99 rtc::TaskQueue task_queue("TestQueue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100100 RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100101 if (++counter == kSleepAtCount)
102 Sleep(kSleepDuration);
103 return kRepeatInterval;
104 });
105 Sleep(kRepeatInterval * kTargetCount);
106 // Execution time should not have affected the run count,
107 // but we allow some margin to reduce flakiness.
108 EXPECT_GE(counter.load(), kTargetCount - kTargetCountMargin);
109}
110
111TEST(RepeatingTaskTest, CompensatesForShortRunTime) {
112 std::atomic_int counter(0);
113 rtc::TaskQueue task_queue("TestQueue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100114 RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100115 ++counter;
Sebastian Janssona497d122019-02-04 16:39:28 +0100116 // Sleeping for the 10 ms should be compensated.
117 Sleep(TimeDelta::ms(10));
118 return TimeDelta::ms(30);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100119 });
Sebastian Janssona497d122019-02-04 16:39:28 +0100120 Sleep(TimeDelta::ms(40));
121
Sebastian Janssonecb68972019-01-18 10:30:54 +0100122 // We expect that the task have been called twice, once directly at Start and
Sebastian Janssona497d122019-02-04 16:39:28 +0100123 // once after 30 ms has passed.
Sebastian Janssonecb68972019-01-18 10:30:54 +0100124 EXPECT_EQ(counter.load(), 2);
125}
126
127TEST(RepeatingTaskTest, CancelDelayedTaskBeforeItRuns) {
128 rtc::Event done;
129 MockClosure mock;
130 EXPECT_CALL(mock, Call).Times(0);
131 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
132 rtc::TaskQueue task_queue("queue");
133 auto handle = RepeatingTaskHandle::DelayedStart(
Danil Chapovalov4423c362019-03-06 18:41:39 +0100134 task_queue.Get(), TimeDelta::ms(100), MoveOnlyClosure(&mock));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100135 handle.PostStop();
136 EXPECT_TRUE(done.Wait(kTimeout.ms()));
137}
138
139TEST(RepeatingTaskTest, CancelTaskAfterItRuns) {
140 rtc::Event done;
141 MockClosure mock;
142 EXPECT_CALL(mock, Call).WillOnce(Return(TimeDelta::ms(100)));
143 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
144 rtc::TaskQueue task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100145 auto handle =
146 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&mock));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100147 handle.PostStop();
148 EXPECT_TRUE(done.Wait(kTimeout.ms()));
149}
150
151TEST(RepeatingTaskTest, TaskCanStopItself) {
152 std::atomic_int counter(0);
153 rtc::TaskQueue task_queue("TestQueue");
154 RepeatingTaskHandle handle;
155 task_queue.PostTask([&] {
Danil Chapovalov4423c362019-03-06 18:41:39 +0100156 handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100157 ++counter;
158 handle.Stop();
159 return TimeDelta::ms(2);
160 });
161 });
162 Sleep(TimeDelta::ms(10));
163 EXPECT_EQ(counter.load(), 1);
164}
165
166TEST(RepeatingTaskTest, ZeroReturnValueRepostsTheTask) {
167 NiceMock<MockClosure> closure;
168 rtc::Event done;
169 EXPECT_CALL(closure, Call())
170 .WillOnce(Return(TimeDelta::Zero()))
171 .WillOnce(Invoke([&done] {
172 done.Set();
173 return kTimeout;
174 }));
175 rtc::TaskQueue task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100176 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100177 EXPECT_TRUE(done.Wait(kTimeout.ms()));
178}
179
180TEST(RepeatingTaskTest, StartPeriodicTask) {
181 MockFunction<TimeDelta()> closure;
182 rtc::Event done;
183 EXPECT_CALL(closure, Call())
184 .WillOnce(Return(TimeDelta::ms(20)))
185 .WillOnce(Return(TimeDelta::ms(20)))
186 .WillOnce(Invoke([&done] {
187 done.Set();
188 return kTimeout;
189 }));
190 rtc::TaskQueue task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100191 RepeatingTaskHandle::Start(task_queue.Get(), closure.AsStdFunction());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100192 EXPECT_TRUE(done.Wait(kTimeout.ms()));
193}
194
195TEST(RepeatingTaskTest, Example) {
196 class ObjectOnTaskQueue {
197 public:
198 void DoPeriodicTask() {}
199 TimeDelta TimeUntilNextRun() { return TimeDelta::ms(100); }
200 void StartPeriodicTask(RepeatingTaskHandle* handle,
Danil Chapovalov4423c362019-03-06 18:41:39 +0100201 TaskQueueBase* task_queue) {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100202 *handle = RepeatingTaskHandle::Start(task_queue, [this] {
203 DoPeriodicTask();
204 return TimeUntilNextRun();
205 });
206 }
207 };
208 rtc::TaskQueue task_queue("queue");
209 auto object = absl::make_unique<ObjectOnTaskQueue>();
210 // Create and start the periodic task.
211 RepeatingTaskHandle handle;
Danil Chapovalov4423c362019-03-06 18:41:39 +0100212 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100213 // Restart the task
214 handle.PostStop();
Danil Chapovalov4423c362019-03-06 18:41:39 +0100215 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100216 handle.PostStop();
217 struct Destructor {
218 void operator()() { object.reset(); }
219 std::unique_ptr<ObjectOnTaskQueue> object;
220 };
221 task_queue.PostTask(Destructor{std::move(object)});
222 // Do not wait for the destructor closure in order to create a race between
223 // task queue destruction and running the desctructor closure.
224}
225
226} // namespace webrtc