xref: /aosp_15_r20/external/webrtc/rtc_base/task_utils/repeating_task_unittest.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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 <memory>
15 
16 #include "absl/functional/any_invocable.h"
17 #include "api/task_queue/task_queue_base.h"
18 #include "api/units/time_delta.h"
19 #include "api/units/timestamp.h"
20 #include "rtc_base/event.h"
21 #include "rtc_base/task_queue_for_test.h"
22 #include "system_wrappers/include/clock.h"
23 #include "test/gmock.h"
24 #include "test/gtest.h"
25 
26 // NOTE: Since these tests rely on real time behavior, they will be flaky
27 // if run on heavily loaded systems.
28 namespace webrtc {
29 namespace {
30 using ::testing::AtLeast;
31 using ::testing::Invoke;
32 using ::testing::MockFunction;
33 using ::testing::NiceMock;
34 using ::testing::Return;
35 
36 constexpr TimeDelta kTimeout = TimeDelta::Millis(1000);
37 
38 class MockClosure {
39  public:
40   MOCK_METHOD(TimeDelta, Call, ());
41   MOCK_METHOD(void, Delete, ());
42 };
43 
44 class MockTaskQueue : public TaskQueueBase {
45  public:
MockTaskQueue()46   MockTaskQueue() : task_queue_setter_(this) {}
47 
48   MOCK_METHOD(void, Delete, (), (override));
49   MOCK_METHOD(void, PostTask, (absl::AnyInvocable<void() &&>), (override));
50   MOCK_METHOD(void,
51               PostDelayedTask,
52               (absl::AnyInvocable<void() &&>, TimeDelta),
53               (override));
54   MOCK_METHOD(void,
55               PostDelayedHighPrecisionTask,
56               (absl::AnyInvocable<void() &&>, TimeDelta),
57               (override));
58 
59  private:
60   CurrentTaskQueueSetter task_queue_setter_;
61 };
62 
63 class FakeTaskQueue : public TaskQueueBase {
64  public:
FakeTaskQueue(SimulatedClock * clock)65   explicit FakeTaskQueue(SimulatedClock* clock)
66       : task_queue_setter_(this), clock_(clock) {}
67 
Delete()68   void Delete() override {}
69 
PostTask(absl::AnyInvocable<void ()&&> task)70   void PostTask(absl::AnyInvocable<void() &&> task) override {
71     last_task_ = std::move(task);
72     last_precision_ = absl::nullopt;
73     last_delay_ = TimeDelta::Zero();
74   }
75 
PostDelayedTask(absl::AnyInvocable<void ()&&> task,TimeDelta delay)76   void PostDelayedTask(absl::AnyInvocable<void() &&> task,
77                        TimeDelta delay) override {
78     last_task_ = std::move(task);
79     last_precision_ = TaskQueueBase::DelayPrecision::kLow;
80     last_delay_ = delay;
81   }
82 
PostDelayedHighPrecisionTask(absl::AnyInvocable<void ()&&> task,TimeDelta delay)83   void PostDelayedHighPrecisionTask(absl::AnyInvocable<void() &&> task,
84                                     TimeDelta delay) override {
85     last_task_ = std::move(task);
86     last_precision_ = TaskQueueBase::DelayPrecision::kHigh;
87     last_delay_ = delay;
88   }
89 
AdvanceTimeAndRunLastTask()90   bool AdvanceTimeAndRunLastTask() {
91     EXPECT_TRUE(last_task_);
92     EXPECT_TRUE(last_delay_.IsFinite());
93     clock_->AdvanceTime(last_delay_);
94     last_delay_ = TimeDelta::MinusInfinity();
95     auto task = std::move(last_task_);
96     std::move(task)();
97     return last_task_ == nullptr;
98   }
99 
IsTaskQueued()100   bool IsTaskQueued() { return !!last_task_; }
101 
last_delay() const102   TimeDelta last_delay() const {
103     EXPECT_TRUE(last_delay_.IsFinite());
104     return last_delay_;
105   }
106 
last_precision() const107   absl::optional<TaskQueueBase::DelayPrecision> last_precision() const {
108     return last_precision_;
109   }
110 
111  private:
112   CurrentTaskQueueSetter task_queue_setter_;
113   SimulatedClock* clock_;
114   absl::AnyInvocable<void() &&> last_task_;
115   TimeDelta last_delay_ = TimeDelta::MinusInfinity();
116   absl::optional<TaskQueueBase::DelayPrecision> last_precision_;
117 };
118 
119 // NOTE: Since this utility class holds a raw pointer to a variable that likely
120 // lives on the stack, it's important that any repeating tasks that use this
121 // class be explicitly stopped when the test criteria have been met. If the
122 // task is not stopped, an instance of this class can be deleted when the
123 // pointed-to MockClosure has been deleted and we end up trying to call a
124 // virtual method on a deleted object in the dtor.
125 class MoveOnlyClosure {
126  public:
MoveOnlyClosure(MockClosure * mock)127   explicit MoveOnlyClosure(MockClosure* mock) : mock_(mock) {}
128   MoveOnlyClosure(const MoveOnlyClosure&) = delete;
MoveOnlyClosure(MoveOnlyClosure && other)129   MoveOnlyClosure(MoveOnlyClosure&& other) : mock_(other.mock_) {
130     other.mock_ = nullptr;
131   }
~MoveOnlyClosure()132   ~MoveOnlyClosure() {
133     if (mock_)
134       mock_->Delete();
135   }
operator ()()136   TimeDelta operator()() { return mock_->Call(); }
137 
138  private:
139   MockClosure* mock_;
140 };
141 }  // namespace
142 
TEST(RepeatingTaskTest,TaskIsStoppedOnStop)143 TEST(RepeatingTaskTest, TaskIsStoppedOnStop) {
144   const TimeDelta kShortInterval = TimeDelta::Millis(50);
145 
146   SimulatedClock clock(Timestamp::Zero());
147   FakeTaskQueue task_queue(&clock);
148   std::atomic_int counter(0);
149   auto handle = RepeatingTaskHandle::Start(
150       &task_queue,
151       [&] {
152         counter++;
153         return kShortInterval;
154       },
155       TaskQueueBase::DelayPrecision::kLow, &clock);
156   EXPECT_EQ(task_queue.last_delay(), TimeDelta::Zero());
157   EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
158   EXPECT_EQ(counter.load(), 1);
159 
160   // The handle reposted at the short interval.
161   EXPECT_EQ(task_queue.last_delay(), kShortInterval);
162 
163   // Stop the handle. This prevernts the counter from incrementing.
164   handle.Stop();
165   EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
166   EXPECT_EQ(counter.load(), 1);
167 }
168 
TEST(RepeatingTaskTest,CompensatesForLongRunTime)169 TEST(RepeatingTaskTest, CompensatesForLongRunTime) {
170   const TimeDelta kRepeatInterval = TimeDelta::Millis(2);
171   // Sleeping inside the task for longer than the repeat interval once, should
172   // be compensated for by repeating the task faster to catch up.
173   const TimeDelta kSleepDuration = TimeDelta::Millis(20);
174 
175   std::atomic_int counter(0);
176   SimulatedClock clock(Timestamp::Zero());
177   FakeTaskQueue task_queue(&clock);
178   RepeatingTaskHandle::Start(
179       &task_queue,
180       [&] {
181         ++counter;
182         // Task takes longer than the repeat duration.
183         clock.AdvanceTime(kSleepDuration);
184         return kRepeatInterval;
185       },
186       TaskQueueBase::DelayPrecision::kLow, &clock);
187 
188   EXPECT_EQ(task_queue.last_delay(), TimeDelta::Zero());
189   EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
190 
191   // Task is posted right away since it took longer to run then the repeat
192   // interval.
193   EXPECT_EQ(task_queue.last_delay(), TimeDelta::Zero());
194   EXPECT_EQ(counter.load(), 1);
195 }
196 
TEST(RepeatingTaskTest,CompensatesForShortRunTime)197 TEST(RepeatingTaskTest, CompensatesForShortRunTime) {
198   SimulatedClock clock(Timestamp::Zero());
199   FakeTaskQueue task_queue(&clock);
200   std::atomic_int counter(0);
201   RepeatingTaskHandle::Start(
202       &task_queue,
203       [&] {
204         // Simulate the task taking 100ms, which should be compensated for.
205         counter++;
206         clock.AdvanceTime(TimeDelta::Millis(100));
207         return TimeDelta::Millis(300);
208       },
209       TaskQueueBase::DelayPrecision::kLow, &clock);
210 
211   // Expect instant post task.
212   EXPECT_EQ(task_queue.last_delay(), TimeDelta::Zero());
213   // Task should be retained by the handler since it is not cancelled.
214   EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
215   // New delay should be 200ms since repeat delay was 300ms but task took 100ms.
216   EXPECT_EQ(task_queue.last_delay(), TimeDelta::Millis(200));
217 }
218 
TEST(RepeatingTaskTest,CancelDelayedTaskBeforeItRuns)219 TEST(RepeatingTaskTest, CancelDelayedTaskBeforeItRuns) {
220   rtc::Event done;
221   MockClosure mock;
222   EXPECT_CALL(mock, Call).Times(0);
223   EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
224   TaskQueueForTest task_queue("queue");
225   auto handle = RepeatingTaskHandle::DelayedStart(
226       task_queue.Get(), TimeDelta::Millis(100), MoveOnlyClosure(&mock));
227   task_queue.PostTask(
228       [handle = std::move(handle)]() mutable { handle.Stop(); });
229   EXPECT_TRUE(done.Wait(kTimeout));
230 }
231 
TEST(RepeatingTaskTest,CancelTaskAfterItRuns)232 TEST(RepeatingTaskTest, CancelTaskAfterItRuns) {
233   rtc::Event done;
234   MockClosure mock;
235   EXPECT_CALL(mock, Call).WillOnce(Return(TimeDelta::Millis(100)));
236   EXPECT_CALL(mock, Delete).WillOnce(Invoke([&done] { done.Set(); }));
237   TaskQueueForTest task_queue("queue");
238   auto handle =
239       RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&mock));
240   task_queue.PostTask(
241       [handle = std::move(handle)]() mutable { handle.Stop(); });
242   EXPECT_TRUE(done.Wait(kTimeout));
243 }
244 
TEST(RepeatingTaskTest,TaskCanStopItself)245 TEST(RepeatingTaskTest, TaskCanStopItself) {
246   std::atomic_int counter(0);
247   SimulatedClock clock(Timestamp::Zero());
248   FakeTaskQueue task_queue(&clock);
249   RepeatingTaskHandle handle = RepeatingTaskHandle::Start(&task_queue, [&] {
250     ++counter;
251     handle.Stop();
252     return TimeDelta::Millis(2);
253   });
254   EXPECT_EQ(task_queue.last_delay(), TimeDelta::Zero());
255   // Task cancelled itself so wants to be released.
256   EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
257   EXPECT_EQ(counter.load(), 1);
258 }
259 
TEST(RepeatingTaskTest,TaskCanStopItselfByReturningInfinity)260 TEST(RepeatingTaskTest, TaskCanStopItselfByReturningInfinity) {
261   std::atomic_int counter(0);
262   SimulatedClock clock(Timestamp::Zero());
263   FakeTaskQueue task_queue(&clock);
264   RepeatingTaskHandle handle = RepeatingTaskHandle::Start(&task_queue, [&] {
265     ++counter;
266     return TimeDelta::PlusInfinity();
267   });
268   EXPECT_EQ(task_queue.last_delay(), TimeDelta::Zero());
269   // Task cancelled itself so wants to be released.
270   EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
271   EXPECT_EQ(counter.load(), 1);
272 }
273 
TEST(RepeatingTaskTest,ZeroReturnValueRepostsTheTask)274 TEST(RepeatingTaskTest, ZeroReturnValueRepostsTheTask) {
275   NiceMock<MockClosure> closure;
276   rtc::Event done;
277   EXPECT_CALL(closure, Call())
278       .WillOnce(Return(TimeDelta::Zero()))
279       .WillOnce(Invoke([&] {
280         done.Set();
281         return TimeDelta::PlusInfinity();
282       }));
283   TaskQueueForTest task_queue("queue");
284   RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure));
285   EXPECT_TRUE(done.Wait(kTimeout));
286 }
287 
TEST(RepeatingTaskTest,StartPeriodicTask)288 TEST(RepeatingTaskTest, StartPeriodicTask) {
289   MockFunction<TimeDelta()> closure;
290   rtc::Event done;
291   EXPECT_CALL(closure, Call())
292       .WillOnce(Return(TimeDelta::Millis(20)))
293       .WillOnce(Return(TimeDelta::Millis(20)))
294       .WillOnce(Invoke([&] {
295         done.Set();
296         return TimeDelta::PlusInfinity();
297       }));
298   TaskQueueForTest task_queue("queue");
299   RepeatingTaskHandle::Start(task_queue.Get(), closure.AsStdFunction());
300   EXPECT_TRUE(done.Wait(kTimeout));
301 }
302 
TEST(RepeatingTaskTest,Example)303 TEST(RepeatingTaskTest, Example) {
304   class ObjectOnTaskQueue {
305    public:
306     void DoPeriodicTask() {}
307     TimeDelta TimeUntilNextRun() { return TimeDelta::Millis(100); }
308     void StartPeriodicTask(RepeatingTaskHandle* handle,
309                            TaskQueueBase* task_queue) {
310       *handle = RepeatingTaskHandle::Start(task_queue, [this] {
311         DoPeriodicTask();
312         return TimeUntilNextRun();
313       });
314     }
315   };
316   TaskQueueForTest task_queue("queue");
317   auto object = std::make_unique<ObjectOnTaskQueue>();
318   // Create and start the periodic task.
319   RepeatingTaskHandle handle;
320   object->StartPeriodicTask(&handle, task_queue.Get());
321   // Restart the task
322   task_queue.PostTask(
323       [handle = std::move(handle)]() mutable { handle.Stop(); });
324   object->StartPeriodicTask(&handle, task_queue.Get());
325   task_queue.PostTask(
326       [handle = std::move(handle)]() mutable { handle.Stop(); });
327   struct Destructor {
328     void operator()() { object.reset(); }
329     std::unique_ptr<ObjectOnTaskQueue> object;
330   };
331   task_queue.PostTask(Destructor{std::move(object)});
332   // Do not wait for the destructor closure in order to create a race between
333   // task queue destruction and running the desctructor closure.
334 }
335 
TEST(RepeatingTaskTest,ClockIntegration)336 TEST(RepeatingTaskTest, ClockIntegration) {
337   absl::AnyInvocable<void() &&> delayed_task;
338   TimeDelta expected_delay = TimeDelta::Zero();
339   SimulatedClock clock(Timestamp::Zero());
340 
341   NiceMock<MockTaskQueue> task_queue;
342   ON_CALL(task_queue, PostDelayedTask)
343       .WillByDefault([&](absl::AnyInvocable<void() &&> task, TimeDelta delay) {
344         EXPECT_EQ(delay, expected_delay);
345         delayed_task = std::move(task);
346       });
347 
348   expected_delay = TimeDelta::Millis(100);
349   RepeatingTaskHandle handle = RepeatingTaskHandle::DelayedStart(
350       &task_queue, TimeDelta::Millis(100),
351       [&clock]() {
352         EXPECT_EQ(Timestamp::Millis(100), clock.CurrentTime());
353         // Simulate work happening for 10ms.
354         clock.AdvanceTimeMilliseconds(10);
355         return TimeDelta::Millis(100);
356       },
357       TaskQueueBase::DelayPrecision::kLow, &clock);
358 
359   clock.AdvanceTimeMilliseconds(100);
360   absl::AnyInvocable<void()&&> task_to_run = std::move(delayed_task);
361   expected_delay = TimeDelta::Millis(90);
362   std::move(task_to_run)();
363   EXPECT_NE(delayed_task, nullptr);
364   handle.Stop();
365 }
366 
TEST(RepeatingTaskTest,CanBeStoppedAfterTaskQueueDeletedTheRepeatingTask)367 TEST(RepeatingTaskTest, CanBeStoppedAfterTaskQueueDeletedTheRepeatingTask) {
368   absl::AnyInvocable<void() &&> repeating_task;
369 
370   MockTaskQueue task_queue;
371   EXPECT_CALL(task_queue, PostDelayedTask)
372       .WillOnce([&](absl::AnyInvocable<void() &&> task, TimeDelta delay) {
373         repeating_task = std::move(task);
374       });
375 
376   RepeatingTaskHandle handle =
377       RepeatingTaskHandle::DelayedStart(&task_queue, TimeDelta::Millis(100),
378                                         [] { return TimeDelta::Millis(100); });
379 
380   // shutdown task queue: delete all pending tasks and run 'regular' task.
381   repeating_task = nullptr;
382   handle.Stop();
383 }
384 
TEST(RepeatingTaskTest,DefaultPrecisionIsLow)385 TEST(RepeatingTaskTest, DefaultPrecisionIsLow) {
386   SimulatedClock clock(Timestamp::Zero());
387   FakeTaskQueue task_queue(&clock);
388   // Closure that repeats twice.
389   MockFunction<TimeDelta()> closure;
390   EXPECT_CALL(closure, Call())
391       .WillOnce(Return(TimeDelta::Millis(1)))
392       .WillOnce(Return(TimeDelta::PlusInfinity()));
393   RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction());
394   // Initial task is a PostTask().
395   EXPECT_FALSE(task_queue.last_precision().has_value());
396   EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
397   // Repeated task is a delayed task with the default precision: low.
398   EXPECT_TRUE(task_queue.last_precision().has_value());
399   EXPECT_EQ(task_queue.last_precision().value(),
400             TaskQueueBase::DelayPrecision::kLow);
401   // No more tasks.
402   EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
403 }
404 
TEST(RepeatingTaskTest,CanSpecifyToPostTasksWithLowPrecision)405 TEST(RepeatingTaskTest, CanSpecifyToPostTasksWithLowPrecision) {
406   SimulatedClock clock(Timestamp::Zero());
407   FakeTaskQueue task_queue(&clock);
408   // Closure that repeats twice.
409   MockFunction<TimeDelta()> closure;
410   EXPECT_CALL(closure, Call())
411       .WillOnce(Return(TimeDelta::Millis(1)))
412       .WillOnce(Return(TimeDelta::PlusInfinity()));
413   RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction(),
414                              TaskQueueBase::DelayPrecision::kLow);
415   // Initial task is a PostTask().
416   EXPECT_FALSE(task_queue.last_precision().has_value());
417   EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
418   // Repeated task is a delayed task with the specified precision.
419   EXPECT_TRUE(task_queue.last_precision().has_value());
420   EXPECT_EQ(task_queue.last_precision().value(),
421             TaskQueueBase::DelayPrecision::kLow);
422   // No more tasks.
423   EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
424 }
425 
TEST(RepeatingTaskTest,CanSpecifyToPostTasksWithHighPrecision)426 TEST(RepeatingTaskTest, CanSpecifyToPostTasksWithHighPrecision) {
427   SimulatedClock clock(Timestamp::Zero());
428   FakeTaskQueue task_queue(&clock);
429   // Closure that repeats twice.
430   MockFunction<TimeDelta()> closure;
431   EXPECT_CALL(closure, Call())
432       .WillOnce(Return(TimeDelta::Millis(1)))
433       .WillOnce(Return(TimeDelta::PlusInfinity()));
434   RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction(),
435                              TaskQueueBase::DelayPrecision::kHigh);
436   // Initial task is a PostTask().
437   EXPECT_FALSE(task_queue.last_precision().has_value());
438   EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask());
439   // Repeated task is a delayed task with the specified precision.
440   EXPECT_TRUE(task_queue.last_precision().has_value());
441   EXPECT_EQ(task_queue.last_precision().value(),
442             TaskQueueBase::DelayPrecision::kHigh);
443   // No more tasks.
444   EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask());
445 }
446 
447 }  // namespace webrtc
448