diff --git a/rtc_base/task_utils/repeating_task.cc b/rtc_base/task_utils/repeating_task.cc index 1f3eb1d064..b9bfdd87a4 100644 --- a/rtc_base/task_utils/repeating_task.cc +++ b/rtc_base/task_utils/repeating_task.cc @@ -38,13 +38,14 @@ bool RepeatingTaskBase::Run() { return true; TimeDelta delay = RunClosure(); + RTC_DCHECK_GE(delay, TimeDelta::Zero()); - // The closure might have stopped this task, in which case we return true to - // destruct this object. - if (!alive_flag_->alive()) + // A delay of +infinity means that the task should not be run again. + // Alternatively, the closure might have stopped this task. In either which + // case we return true to destruct this object. + if (delay.IsPlusInfinity() || !alive_flag_->alive()) return true; - RTC_DCHECK(delay.IsFinite()); TimeDelta lost_time = clock_->CurrentTime() - next_run_time_; next_run_time_ += delay; delay -= lost_time; diff --git a/rtc_base/task_utils/repeating_task.h b/rtc_base/task_utils/repeating_task.h index 91a40e0714..4c9983c349 100644 --- a/rtc_base/task_utils/repeating_task.h +++ b/rtc_base/task_utils/repeating_task.h @@ -52,7 +52,10 @@ class RepeatingTaskBase : public QueuedTask { RTC_GUARDED_BY(task_queue_); }; -// The template closure pattern is based on rtc::ClosureTask. +// The template closure pattern is based on rtc::ClosureTask. The provided +// closure should have a TimeDelta return value, specifing the desired +// non-negative interval to next repetition, or TimeDelta::PlusInfinity to +// indicate that the task should be deleted and not called again. template class RepeatingTaskImpl final : public RepeatingTaskBase { public: diff --git a/rtc_base/task_utils/repeating_task_unittest.cc b/rtc_base/task_utils/repeating_task_unittest.cc index ac0520a2e0..1d26ee62dc 100644 --- a/rtc_base/task_utils/repeating_task_unittest.cc +++ b/rtc_base/task_utils/repeating_task_unittest.cc @@ -239,34 +239,43 @@ TEST(RepeatingTaskTest, TaskCanStopItself) { EXPECT_EQ(counter.load(), 1); } +TEST(RepeatingTaskTest, TaskCanStopItselfByReturningInfinity) { + std::atomic_int counter(0); + SimulatedClock clock(Timestamp::Zero()); + FakeTaskQueue task_queue(&clock); + RepeatingTaskHandle handle = RepeatingTaskHandle::Start(&task_queue, [&] { + ++counter; + return TimeDelta::PlusInfinity(); + }); + EXPECT_EQ(task_queue.last_delay(), 0u); + // Task cancelled itself so wants to be released. + EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask()); + EXPECT_EQ(counter.load(), 1); +} + TEST(RepeatingTaskTest, ZeroReturnValueRepostsTheTask) { NiceMock closure; rtc::Event done; - RepeatingTaskHandle handle; EXPECT_CALL(closure, Call()) .WillOnce(Return(TimeDelta::Zero())) .WillOnce(Invoke([&] { done.Set(); - handle.Stop(); - return kTimeout; + return TimeDelta::PlusInfinity(); })); TaskQueueForTest task_queue("queue"); - handle = - RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure)); + RepeatingTaskHandle::Start(task_queue.Get(), MoveOnlyClosure(&closure)); EXPECT_TRUE(done.Wait(kTimeout.ms())); } TEST(RepeatingTaskTest, StartPeriodicTask) { MockFunction closure; rtc::Event done; - RepeatingTaskHandle handle; EXPECT_CALL(closure, Call()) .WillOnce(Return(TimeDelta::Millis(20))) .WillOnce(Return(TimeDelta::Millis(20))) .WillOnce(Invoke([&] { done.Set(); - handle.Stop(); - return kTimeout; + return TimeDelta::PlusInfinity(); })); TaskQueueForTest task_queue("queue"); RepeatingTaskHandle::Start(task_queue.Get(), closure.AsStdFunction());