Let a RepeatingTask stop itself by returning a delay of PlusInfinity.

Bug: none
Change-Id: I5bf87e236019d156ffe85c5b91ce09f5f4042937
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/247160
Reviewed-by: Evan Shrubsole <eshr@webrtc.org>
Reviewed-by: Tomas Gunnarsson <tommi@webrtc.org>
Commit-Queue: Niels Moller <nisse@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#35710}
diff --git a/rtc_base/task_utils/repeating_task.cc b/rtc_base/task_utils/repeating_task.cc
index 1f3eb1d..b9bfdd8 100644
--- a/rtc_base/task_utils/repeating_task.cc
+++ b/rtc_base/task_utils/repeating_task.cc
@@ -38,13 +38,14 @@
     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 91a40e0..4c9983c 100644
--- a/rtc_base/task_utils/repeating_task.h
+++ b/rtc_base/task_utils/repeating_task.h
@@ -52,7 +52,10 @@
       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 Closure>
 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 ac0520a..1d26ee6 100644
--- a/rtc_base/task_utils/repeating_task_unittest.cc
+++ b/rtc_base/task_utils/repeating_task_unittest.cc
@@ -239,34 +239,43 @@
   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<MockClosure> 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<TimeDelta()> 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());