• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 "rtc_base/task_utils/repeating_task.h"
12 
13 #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 
18 #include "rtc_base/event.h"
19 #include "rtc_base/task_queue_for_test.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.
25 namespace webrtc {
26 namespace {
27 using ::testing::AtLeast;
28 using ::testing::Invoke;
29 using ::testing::MockFunction;
30 using ::testing::NiceMock;
31 using ::testing::Return;
32 
33 constexpr TimeDelta kTimeout = TimeDelta::Millis(1000);
34 
Sleep(TimeDelta time_delta)35 void 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 
41 class MockClosure {
42  public:
43   MOCK_METHOD(TimeDelta, Call, ());
44   MOCK_METHOD(void, Delete, ());
45 };
46 
47 class MockTaskQueue : public TaskQueueBase {
48  public:
MockTaskQueue()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 
62 class MoveOnlyClosure {
63  public:
MoveOnlyClosure(MockClosure * mock)64   explicit MoveOnlyClosure(MockClosure* mock) : mock_(mock) {}
65   MoveOnlyClosure(const MoveOnlyClosure&) = delete;
MoveOnlyClosure(MoveOnlyClosure && other)66   MoveOnlyClosure(MoveOnlyClosure&& other) : mock_(other.mock_) {
67     other.mock_ = nullptr;
68   }
~MoveOnlyClosure()69   ~MoveOnlyClosure() {
70     if (mock_)
71       mock_->Delete();
72   }
operator ()()73   TimeDelta operator()() { return mock_->Call(); }
74 
75  private:
76   MockClosure* mock_;
77 };
78 }  // namespace
79 
TEST(RepeatingTaskTest,TaskIsStoppedOnStop)80 TEST(RepeatingTaskTest, TaskIsStoppedOnStop) {
81   const TimeDelta kShortInterval = TimeDelta::Millis(50);
82   const TimeDelta kLongInterval = TimeDelta::Millis(200);
83   const int kShortIntervalCount = 4;
84   const int kMargin = 1;
85 
86   TaskQueueForTest task_queue("TestQueue");
87   std::atomic_int counter(0);
88   auto handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
89     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 
97   task_queue.PostTask(
98       [handle = std::move(handle)]() mutable { handle.Stop(); });
99   // 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 
TEST(RepeatingTaskTest,CompensatesForLongRunTime)105 TEST(RepeatingTaskTest, CompensatesForLongRunTime) {
106   const int kTargetCount = 20;
107   const int kTargetCountMargin = 2;
108   const TimeDelta kRepeatInterval = TimeDelta::Millis(2);
109   // Sleeping inside the task for longer than the repeat interval once, should
110   // be compensated for by repeating the task faster to catch up.
111   const TimeDelta kSleepDuration = TimeDelta::Millis(20);
112   const int kSleepAtCount = 3;
113 
114   std::atomic_int counter(0);
115   TaskQueueForTest task_queue("TestQueue");
116   RepeatingTaskHandle::Start(task_queue.Get(), [&] {
117     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 
TEST(RepeatingTaskTest,CompensatesForShortRunTime)127 TEST(RepeatingTaskTest, CompensatesForShortRunTime) {
128   std::atomic_int counter(0);
129   TaskQueueForTest task_queue("TestQueue");
130   RepeatingTaskHandle::Start(task_queue.Get(), [&] {
131     ++counter;
132     // Sleeping for the 100 ms should be compensated.
133     Sleep(TimeDelta::Millis(100));
134     return TimeDelta::Millis(300);
135   });
136   Sleep(TimeDelta::Millis(400));
137 
138   // We expect that the task have been called twice, once directly at Start and
139   // once after 300 ms has passed.
140   EXPECT_EQ(counter.load(), 2);
141 }
142 
TEST(RepeatingTaskTest,CancelDelayedTaskBeforeItRuns)143 TEST(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(); }));
148   TaskQueueForTest task_queue("queue");
149   auto handle = RepeatingTaskHandle::DelayedStart(
150       task_queue.Get(), TimeDelta::Millis(100), MoveOnlyClosure(&mock));
151   task_queue.PostTask(
152       [handle = std::move(handle)]() mutable { handle.Stop(); });
153   EXPECT_TRUE(done.Wait(kTimeout.ms()));
154 }
155 
TEST(RepeatingTaskTest,CancelTaskAfterItRuns)156 TEST(RepeatingTaskTest, CancelTaskAfterItRuns) {
157   rtc::Event done;
158   MockClosure mock;
159   EXPECT_CALL(mock, Call).WillOnce(Return(TimeDelta::Millis(100)));
160   EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
161   TaskQueueForTest task_queue("queue");
162   auto handle =
163       RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&mock));
164   task_queue.PostTask(
165       [handle = std::move(handle)]() mutable { handle.Stop(); });
166   EXPECT_TRUE(done.Wait(kTimeout.ms()));
167 }
168 
TEST(RepeatingTaskTest,TaskCanStopItself)169 TEST(RepeatingTaskTest, TaskCanStopItself) {
170   std::atomic_int counter(0);
171   TaskQueueForTest task_queue("TestQueue");
172   RepeatingTaskHandle handle;
173   task_queue.PostTask([&] {
174     handle = RepeatingTaskHandle::Start(task_queue.Get(), [&] {
175       ++counter;
176       handle.Stop();
177       return TimeDelta::Millis(2);
178     });
179   });
180   Sleep(TimeDelta::Millis(10));
181   EXPECT_EQ(counter.load(), 1);
182 }
183 
TEST(RepeatingTaskTest,ZeroReturnValueRepostsTheTask)184 TEST(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       }));
193   TaskQueueForTest task_queue("queue");
194   RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure));
195   EXPECT_TRUE(done.Wait(kTimeout.ms()));
196 }
197 
TEST(RepeatingTaskTest,StartPeriodicTask)198 TEST(RepeatingTaskTest, StartPeriodicTask) {
199   MockFunction<TimeDelta()> closure;
200   rtc::Event done;
201   EXPECT_CALL(closure, Call())
202       .WillOnce(Return(TimeDelta::Millis(20)))
203       .WillOnce(Return(TimeDelta::Millis(20)))
204       .WillOnce(Invoke([&done] {
205         done.Set();
206         return kTimeout;
207       }));
208   TaskQueueForTest task_queue("queue");
209   RepeatingTaskHandle::Start(task_queue.Get(), closure.AsStdFunction());
210   EXPECT_TRUE(done.Wait(kTimeout.ms()));
211 }
212 
TEST(RepeatingTaskTest,Example)213 TEST(RepeatingTaskTest, Example) {
214   class ObjectOnTaskQueue {
215    public:
216     void DoPeriodicTask() {}
217     TimeDelta TimeUntilNextRun() { return TimeDelta::Millis(100); }
218     void StartPeriodicTask(RepeatingTaskHandle* handle,
219                            TaskQueueBase* task_queue) {
220       *handle = RepeatingTaskHandle::Start(task_queue, [this] {
221         DoPeriodicTask();
222         return TimeUntilNextRun();
223       });
224     }
225   };
226   TaskQueueForTest task_queue("queue");
227   auto object = std::make_unique<ObjectOnTaskQueue>();
228   // Create and start the periodic task.
229   RepeatingTaskHandle handle;
230   object->StartPeriodicTask(&handle, task_queue.Get());
231   // Restart the task
232   task_queue.PostTask(
233       [handle = std::move(handle)]() mutable { handle.Stop(); });
234   object->StartPeriodicTask(&handle, task_queue.Get());
235   task_queue.PostTask(
236       [handle = std::move(handle)]() mutable { handle.Stop(); });
237   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 
TEST(RepeatingTaskTest,ClockIntegration)246 TEST(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 
279 }  // namespace webrtc
280