blob: 74261dff245bbaa9b4af1ce69b7651a8ffa33dd8 [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 Chapovalov07122bc2019-03-26 14:37:01 +010018#include "rtc_base/task_queue_for_test.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};
Sebastian Jansson46b4a0f2019-03-26 15:24:23 +010063
64// Helper closure class to stop repeating task on a task queue. This is
65// equivalent to [handle{move(handle)}] { handle.Stop(); } in c++14.
66class TaskHandleStopper {
67 public:
68 explicit TaskHandleStopper(RepeatingTaskHandle handle)
69 : handle_(std::move(handle)) {}
70 void operator()() { handle_.Stop(); }
71
72 private:
73 RepeatingTaskHandle handle_;
74};
Sebastian Janssonecb68972019-01-18 10:30:54 +010075} // namespace
76
77TEST(RepeatingTaskTest, TaskIsStoppedOnStop) {
78 const TimeDelta kShortInterval = TimeDelta::ms(5);
79 const TimeDelta kLongInterval = TimeDelta::ms(20);
80 const int kShortIntervalCount = 4;
81 const int kMargin = 1;
82
Danil Chapovalov07122bc2019-03-26 14:37:01 +010083 TaskQueueForTest task_queue("TestQueue");
Sebastian Janssonecb68972019-01-18 10:30:54 +010084 std::atomic_int counter(0);
Danil Chapovalov4423c362019-03-06 18:41:39 +010085 auto handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +010086 if (++counter >= kShortIntervalCount)
87 return kLongInterval;
88 return kShortInterval;
89 });
90 // Sleep long enough to go through the initial phase.
91 Sleep(kShortInterval * (kShortIntervalCount + kMargin));
92 EXPECT_EQ(counter.load(), kShortIntervalCount);
93
Sebastian Jansson46b4a0f2019-03-26 15:24:23 +010094 task_queue.PostTask(TaskHandleStopper(std::move(handle)));
Sebastian Janssonecb68972019-01-18 10:30:54 +010095 // Sleep long enough that the task would run at least once more if not
96 // stopped.
97 Sleep(kLongInterval * 2);
98 EXPECT_EQ(counter.load(), kShortIntervalCount);
99}
100
101TEST(RepeatingTaskTest, CompensatesForLongRunTime) {
102 const int kTargetCount = 20;
103 const int kTargetCountMargin = 2;
104 const TimeDelta kRepeatInterval = TimeDelta::ms(2);
105 // Sleeping inside the task for longer than the repeat interval once, should
106 // be compensated for by repeating the task faster to catch up.
107 const TimeDelta kSleepDuration = TimeDelta::ms(20);
108 const int kSleepAtCount = 3;
109
110 std::atomic_int counter(0);
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100111 TaskQueueForTest task_queue("TestQueue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100112 RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100113 if (++counter == kSleepAtCount)
114 Sleep(kSleepDuration);
115 return kRepeatInterval;
116 });
117 Sleep(kRepeatInterval * kTargetCount);
118 // Execution time should not have affected the run count,
119 // but we allow some margin to reduce flakiness.
120 EXPECT_GE(counter.load(), kTargetCount - kTargetCountMargin);
121}
122
123TEST(RepeatingTaskTest, CompensatesForShortRunTime) {
124 std::atomic_int counter(0);
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100125 TaskQueueForTest task_queue("TestQueue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100126 RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100127 ++counter;
Sebastian Janssona497d122019-02-04 16:39:28 +0100128 // Sleeping for the 10 ms should be compensated.
129 Sleep(TimeDelta::ms(10));
130 return TimeDelta::ms(30);
Sebastian Janssonecb68972019-01-18 10:30:54 +0100131 });
Sebastian Janssona497d122019-02-04 16:39:28 +0100132 Sleep(TimeDelta::ms(40));
133
Sebastian Janssonecb68972019-01-18 10:30:54 +0100134 // We expect that the task have been called twice, once directly at Start and
Sebastian Janssona497d122019-02-04 16:39:28 +0100135 // once after 30 ms has passed.
Sebastian Janssonecb68972019-01-18 10:30:54 +0100136 EXPECT_EQ(counter.load(), 2);
137}
138
139TEST(RepeatingTaskTest, CancelDelayedTaskBeforeItRuns) {
140 rtc::Event done;
141 MockClosure mock;
142 EXPECT_CALL(mock, Call).Times(0);
143 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100144 TaskQueueForTest task_queue("queue");
Sebastian Janssonecb68972019-01-18 10:30:54 +0100145 auto handle = RepeatingTaskHandle::DelayedStart(
Danil Chapovalov4423c362019-03-06 18:41:39 +0100146 task_queue.Get(), TimeDelta::ms(100), MoveOnlyClosure(&mock));
Sebastian Jansson46b4a0f2019-03-26 15:24:23 +0100147 task_queue.PostTask(TaskHandleStopper(std::move(handle)));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100148 EXPECT_TRUE(done.Wait(kTimeout.ms()));
149}
150
151TEST(RepeatingTaskTest, CancelTaskAfterItRuns) {
152 rtc::Event done;
153 MockClosure mock;
154 EXPECT_CALL(mock, Call).WillOnce(Return(TimeDelta::ms(100)));
155 EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100156 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100157 auto handle =
158 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&mock));
Sebastian Jansson46b4a0f2019-03-26 15:24:23 +0100159 task_queue.PostTask(TaskHandleStopper(std::move(handle)));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100160 EXPECT_TRUE(done.Wait(kTimeout.ms()));
161}
162
163TEST(RepeatingTaskTest, TaskCanStopItself) {
164 std::atomic_int counter(0);
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100165 TaskQueueForTest task_queue("TestQueue");
Sebastian Janssonecb68972019-01-18 10:30:54 +0100166 RepeatingTaskHandle handle;
167 task_queue.PostTask([&] {
Danil Chapovalov4423c362019-03-06 18:41:39 +0100168 handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100169 ++counter;
170 handle.Stop();
171 return TimeDelta::ms(2);
172 });
173 });
174 Sleep(TimeDelta::ms(10));
175 EXPECT_EQ(counter.load(), 1);
176}
177
178TEST(RepeatingTaskTest, ZeroReturnValueRepostsTheTask) {
179 NiceMock<MockClosure> closure;
180 rtc::Event done;
181 EXPECT_CALL(closure, Call())
182 .WillOnce(Return(TimeDelta::Zero()))
183 .WillOnce(Invoke([&done] {
184 done.Set();
185 return kTimeout;
186 }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100187 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100188 RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100189 EXPECT_TRUE(done.Wait(kTimeout.ms()));
190}
191
192TEST(RepeatingTaskTest, StartPeriodicTask) {
193 MockFunction<TimeDelta()> closure;
194 rtc::Event done;
195 EXPECT_CALL(closure, Call())
196 .WillOnce(Return(TimeDelta::ms(20)))
197 .WillOnce(Return(TimeDelta::ms(20)))
198 .WillOnce(Invoke([&done] {
199 done.Set();
200 return kTimeout;
201 }));
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100202 TaskQueueForTest task_queue("queue");
Danil Chapovalov4423c362019-03-06 18:41:39 +0100203 RepeatingTaskHandle::Start(task_queue.Get(), closure.AsStdFunction());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100204 EXPECT_TRUE(done.Wait(kTimeout.ms()));
205}
206
207TEST(RepeatingTaskTest, Example) {
208 class ObjectOnTaskQueue {
209 public:
210 void DoPeriodicTask() {}
211 TimeDelta TimeUntilNextRun() { return TimeDelta::ms(100); }
212 void StartPeriodicTask(RepeatingTaskHandle* handle,
Danil Chapovalov4423c362019-03-06 18:41:39 +0100213 TaskQueueBase* task_queue) {
Sebastian Janssonecb68972019-01-18 10:30:54 +0100214 *handle = RepeatingTaskHandle::Start(task_queue, [this] {
215 DoPeriodicTask();
216 return TimeUntilNextRun();
217 });
218 }
219 };
Danil Chapovalov07122bc2019-03-26 14:37:01 +0100220 TaskQueueForTest task_queue("queue");
Sebastian Janssonecb68972019-01-18 10:30:54 +0100221 auto object = absl::make_unique<ObjectOnTaskQueue>();
222 // Create and start the periodic task.
223 RepeatingTaskHandle handle;
Danil Chapovalov4423c362019-03-06 18:41:39 +0100224 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Janssonecb68972019-01-18 10:30:54 +0100225 // Restart the task
Sebastian Jansson46b4a0f2019-03-26 15:24:23 +0100226 task_queue.PostTask(TaskHandleStopper(std::move(handle)));
Danil Chapovalov4423c362019-03-06 18:41:39 +0100227 object->StartPeriodicTask(&handle, task_queue.Get());
Sebastian Jansson46b4a0f2019-03-26 15:24:23 +0100228 task_queue.PostTask(TaskHandleStopper(std::move(handle)));
Sebastian Janssonecb68972019-01-18 10:30:54 +0100229 struct Destructor {
230 void operator()() { object.reset(); }
231 std::unique_ptr<ObjectOnTaskQueue> object;
232 };
233 task_queue.PostTask(Destructor{std::move(object)});
234 // Do not wait for the destructor closure in order to create a race between
235 // task queue destruction and running the desctructor closure.
236}
237
238} // namespace webrtc