diff --git a/api/task_queue/task_queue_base.h b/api/task_queue/task_queue_base.h index df422dcdc6..b7c92f8647 100644 --- a/api/task_queue/task_queue_base.h +++ b/api/task_queue/task_queue_base.h @@ -25,6 +25,16 @@ namespace webrtc { // known task queue, use IsCurrent(). class RTC_LOCKABLE RTC_EXPORT TaskQueueBase { public: + enum class DelayPrecision { + // This may include up to a 17 ms leeway in addition to OS timer precision. + // See PostDelayedTask() for more information. + kLow, + // This does not have the additional delay that kLow has, but it is still + // limited by OS timer precision. See PostDelayedHighPrecisionTask() for + // more information. + kHigh, + }; + // Starts destruction of the task queue. // On return ensures no task are running and no new tasks are able to start // on the task queue. @@ -98,6 +108,21 @@ class RTC_LOCKABLE RTC_EXPORT TaskQueueBase { PostDelayedTask(std::move(task), milliseconds); } + // As specified by |precision|, calls either PostDelayedTask() or + // PostDelayedHighPrecisionTask(). + void PostDelayedTaskWithPrecision(DelayPrecision precision, + std::unique_ptr task, + uint32_t milliseconds) { + switch (precision) { + case DelayPrecision::kLow: + PostDelayedTask(std::move(task), milliseconds); + break; + case DelayPrecision::kHigh: + PostDelayedHighPrecisionTask(std::move(task), milliseconds); + break; + } + } + // Returns the task queue that is running the current thread. // Returns nullptr if this thread is not associated with any task queue. // May be called on any thread or task queue, including this task queue. diff --git a/rtc_base/task_utils/repeating_task.cc b/rtc_base/task_utils/repeating_task.cc index b9bfdd87a4..c07df4567c 100644 --- a/rtc_base/task_utils/repeating_task.cc +++ b/rtc_base/task_utils/repeating_task.cc @@ -21,10 +21,12 @@ namespace webrtc_repeating_task_impl { RepeatingTaskBase::RepeatingTaskBase( TaskQueueBase* task_queue, + TaskQueueBase::DelayPrecision precision, TimeDelta first_delay, Clock* clock, rtc::scoped_refptr alive_flag) : task_queue_(task_queue), + precision_(precision), clock_(clock), next_run_time_(clock_->CurrentTime() + first_delay), alive_flag_(std::move(alive_flag)) {} @@ -51,7 +53,8 @@ bool RepeatingTaskBase::Run() { delay -= lost_time; delay = std::max(delay, TimeDelta::Zero()); - task_queue_->PostDelayedTask(absl::WrapUnique(this), delay.ms()); + task_queue_->PostDelayedTaskWithPrecision(precision_, absl::WrapUnique(this), + delay.ms()); // Return false to tell the TaskQueue to not destruct this object since we // have taken ownership with absl::WrapUnique. diff --git a/rtc_base/task_utils/repeating_task.h b/rtc_base/task_utils/repeating_task.h index 4c9983c349..20f28d54f4 100644 --- a/rtc_base/task_utils/repeating_task.h +++ b/rtc_base/task_utils/repeating_task.h @@ -34,6 +34,7 @@ void RepeatingTaskImplDTraceProbeRun(); class RepeatingTaskBase : public QueuedTask { public: RepeatingTaskBase(TaskQueueBase* task_queue, + TaskQueueBase::DelayPrecision precision, TimeDelta first_delay, Clock* clock, rtc::scoped_refptr alive_flag); @@ -45,6 +46,7 @@ class RepeatingTaskBase : public QueuedTask { bool Run() final; TaskQueueBase* const task_queue_; + const TaskQueueBase::DelayPrecision precision_; Clock* const clock_; // This is always finite. Timestamp next_run_time_ RTC_GUARDED_BY(task_queue_); @@ -60,11 +62,13 @@ template class RepeatingTaskImpl final : public RepeatingTaskBase { public: RepeatingTaskImpl(TaskQueueBase* task_queue, + TaskQueueBase::DelayPrecision precision, TimeDelta first_delay, Closure&& closure, Clock* clock, rtc::scoped_refptr alive_flag) : RepeatingTaskBase(task_queue, + precision, first_delay, clock, std::move(alive_flag)), @@ -106,17 +110,20 @@ class RepeatingTaskHandle { // owned by the TaskQueue and will live until it has been stopped or the // TaskQueue deletes it. It's perfectly fine to destroy the handle while the // task is running, since the repeated task is owned by the TaskQueue. + // The tasks are scheduled onto the task queue using the specified precision. template static RepeatingTaskHandle Start(TaskQueueBase* task_queue, Closure&& closure, + TaskQueueBase::DelayPrecision precision = + TaskQueueBase::DelayPrecision::kLow, Clock* clock = Clock::GetRealTimeClock()) { auto alive_flag = PendingTaskSafetyFlag::CreateDetached(); webrtc_repeating_task_impl::RepeatingTaskHandleDTraceProbeStart(); task_queue->PostTask( std::make_unique< webrtc_repeating_task_impl::RepeatingTaskImpl>( - task_queue, TimeDelta::Zero(), std::forward(closure), - clock, alive_flag)); + task_queue, precision, TimeDelta::Zero(), + std::forward(closure), clock, alive_flag)); return RepeatingTaskHandle(std::move(alive_flag)); } @@ -127,14 +134,17 @@ class RepeatingTaskHandle { TaskQueueBase* task_queue, TimeDelta first_delay, Closure&& closure, + TaskQueueBase::DelayPrecision precision = + TaskQueueBase::DelayPrecision::kLow, Clock* clock = Clock::GetRealTimeClock()) { auto alive_flag = PendingTaskSafetyFlag::CreateDetached(); webrtc_repeating_task_impl::RepeatingTaskHandleDTraceProbeDelayedStart(); - task_queue->PostDelayedTask( + task_queue->PostDelayedTaskWithPrecision( + precision, std::make_unique< webrtc_repeating_task_impl::RepeatingTaskImpl>( - task_queue, first_delay, std::forward(closure), clock, - alive_flag), + task_queue, precision, first_delay, std::forward(closure), + clock, alive_flag), first_delay.ms()); return RepeatingTaskHandle(std::move(alive_flag)); } diff --git a/rtc_base/task_utils/repeating_task_unittest.cc b/rtc_base/task_utils/repeating_task_unittest.cc index 1d26ee62dc..d6ab920e50 100644 --- a/rtc_base/task_utils/repeating_task_unittest.cc +++ b/rtc_base/task_utils/repeating_task_unittest.cc @@ -64,12 +64,22 @@ class FakeTaskQueue : public TaskQueueBase { void Delete() override {} void PostTask(std::unique_ptr task) override { - PostDelayedTask(std::move(task), 0); + last_task_ = std::move(task); + last_precision_ = absl::nullopt; + last_delay_ = 0; } void PostDelayedTask(std::unique_ptr task, uint32_t milliseconds) override { last_task_ = std::move(task); + last_precision_ = TaskQueueBase::DelayPrecision::kLow; + last_delay_ = milliseconds; + } + + void PostDelayedHighPrecisionTask(std::unique_ptr task, + uint32_t milliseconds) override { + last_task_ = std::move(task); + last_precision_ = TaskQueueBase::DelayPrecision::kHigh; last_delay_ = milliseconds; } @@ -94,11 +104,16 @@ class FakeTaskQueue : public TaskQueueBase { return last_delay_.value_or(-1); } + absl::optional last_precision() const { + return last_precision_; + } + private: CurrentTaskQueueSetter task_queue_setter_; SimulatedClock* clock_; std::unique_ptr last_task_; absl::optional last_delay_; + absl::optional last_precision_; }; // NOTE: Since this utility class holds a raw pointer to a variable that likely @@ -165,7 +180,7 @@ TEST(RepeatingTaskTest, CompensatesForLongRunTime) { clock.AdvanceTime(kSleepDuration); return kRepeatInterval; }, - &clock); + TaskQueueBase::DelayPrecision::kLow, &clock); EXPECT_EQ(task_queue.last_delay(), 0u); EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask()); @@ -188,7 +203,7 @@ TEST(RepeatingTaskTest, CompensatesForShortRunTime) { clock.AdvanceTime(TimeDelta::Millis(100)); return TimeDelta::Millis(300); }, - &clock); + TaskQueueBase::DelayPrecision::kLow, &clock); // Expect instant post task. EXPECT_EQ(task_queue.last_delay(), 0u); @@ -338,7 +353,7 @@ TEST(RepeatingTaskTest, ClockIntegration) { clock.AdvanceTimeMilliseconds(10); return TimeDelta::Millis(100); }, - &clock); + TaskQueueBase::DelayPrecision::kLow, &clock); clock.AdvanceTimeMilliseconds(100); QueuedTask* task_to_run = delayed_task.release(); @@ -366,4 +381,66 @@ TEST(RepeatingTaskTest, CanBeStoppedAfterTaskQueueDeletedTheRepeatingTask) { handle.Stop(); } +TEST(RepeatingTaskTest, DefaultPrecisionIsLow) { + SimulatedClock clock(Timestamp::Zero()); + FakeTaskQueue task_queue(&clock); + // Closure that repeats twice. + MockFunction closure; + EXPECT_CALL(closure, Call()) + .WillOnce(Return(TimeDelta::Millis(1))) + .WillOnce(Return(TimeDelta::PlusInfinity())); + RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction()); + // Initial task is a PostTask(). + EXPECT_FALSE(task_queue.last_precision().has_value()); + EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask()); + // Repeated task is a delayed task with the default precision: low. + EXPECT_TRUE(task_queue.last_precision().has_value()); + EXPECT_EQ(task_queue.last_precision().value(), + TaskQueueBase::DelayPrecision::kLow); + // No more tasks. + EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask()); +} + +TEST(RepeatingTaskTest, CanSpecifyToPostTasksWithLowPrecision) { + SimulatedClock clock(Timestamp::Zero()); + FakeTaskQueue task_queue(&clock); + // Closure that repeats twice. + MockFunction closure; + EXPECT_CALL(closure, Call()) + .WillOnce(Return(TimeDelta::Millis(1))) + .WillOnce(Return(TimeDelta::PlusInfinity())); + RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction(), + TaskQueueBase::DelayPrecision::kLow); + // Initial task is a PostTask(). + EXPECT_FALSE(task_queue.last_precision().has_value()); + EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask()); + // Repeated task is a delayed task with the specified precision. + EXPECT_TRUE(task_queue.last_precision().has_value()); + EXPECT_EQ(task_queue.last_precision().value(), + TaskQueueBase::DelayPrecision::kLow); + // No more tasks. + EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask()); +} + +TEST(RepeatingTaskTest, CanSpecifyToPostTasksWithHighPrecision) { + SimulatedClock clock(Timestamp::Zero()); + FakeTaskQueue task_queue(&clock); + // Closure that repeats twice. + MockFunction closure; + EXPECT_CALL(closure, Call()) + .WillOnce(Return(TimeDelta::Millis(1))) + .WillOnce(Return(TimeDelta::PlusInfinity())); + RepeatingTaskHandle::Start(&task_queue, closure.AsStdFunction(), + TaskQueueBase::DelayPrecision::kHigh); + // Initial task is a PostTask(). + EXPECT_FALSE(task_queue.last_precision().has_value()); + EXPECT_FALSE(task_queue.AdvanceTimeAndRunLastTask()); + // Repeated task is a delayed task with the specified precision. + EXPECT_TRUE(task_queue.last_precision().has_value()); + EXPECT_EQ(task_queue.last_precision().value(), + TaskQueueBase::DelayPrecision::kHigh); + // No more tasks. + EXPECT_TRUE(task_queue.AdvanceTimeAndRunLastTask()); +} + } // namespace webrtc