From 27e8a095bf2d542c60a1402b87aaeccebbb28ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Bostr=C3=B6m?= Date: Mon, 24 Jan 2022 17:12:35 +0100 Subject: [PATCH] Add ability to specify delayed task precision in RepeatingTaskHandle. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See go/postdelayedtask-precision-in-webrtc for context of which use cases are considered "high" or "low". Most use cases are "low" which is the default, but this CL allows opting in to "high". Will be used by FrameBuffer2. Bug: webrtc:13604 Change-Id: Iebf6eea44779873e78746da749a39e1101b92819 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/248861 Reviewed-by: Tomas Gunnarsson Commit-Queue: Henrik Boström Cr-Commit-Position: refs/heads/main@{#35776} --- api/task_queue/task_queue_base.h | 25 ++++++ rtc_base/task_utils/repeating_task.cc | 5 +- rtc_base/task_utils/repeating_task.h | 20 +++-- .../task_utils/repeating_task_unittest.cc | 85 ++++++++++++++++++- 4 files changed, 125 insertions(+), 10 deletions(-) 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