1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181
|
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/webrtc_overrides/low_precision_timer.h"
#include <optional>
#include <utility>
#include "base/check.h"
#include "base/check_op.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/memory/scoped_refptr.h"
#include "base/synchronization/lock.h"
#include "base/task/delay_policy.h"
#include "base/task/sequenced_task_runner.h"
#include "third_party/webrtc_overrides/timer_based_tick_provider.h"
namespace blink {
LowPrecisionTimer::SchedulableCallback::SchedulableCallback(
scoped_refptr<base::SequencedTaskRunner> task_runner,
base::RepeatingCallback<void()> callback,
base::TimeDelta repeated_delay)
: task_runner_(std::move(task_runner)),
callback_(std::move(callback)),
repeated_delay_(std::move(repeated_delay)) {}
LowPrecisionTimer::SchedulableCallback::~SchedulableCallback() {
DCHECK(!is_active_);
}
void LowPrecisionTimer::SchedulableCallback::Schedule(
base::TimeTicks scheduled_time) {
base::AutoLock auto_scheduled_time_lock(scheduled_time_lock_);
DCHECK_EQ(scheduled_time_, base::TimeTicks::Max())
<< "The callback has already been scheduled.";
scheduled_time_ = scheduled_time;
// Snap target time to metronome tick!
base::TimeTicks target_time = TimerBasedTickProvider::TimeSnappedToNextTick(
scheduled_time_, TimerBasedTickProvider::kDefaultPeriod);
task_runner_->PostDelayedTaskAt(
base::subtle::PostDelayedTaskPassKey(), FROM_HERE,
base::BindOnce(&LowPrecisionTimer::SchedulableCallback::MaybeRun, this),
target_time, base::subtle::DelayPolicy::kPrecise);
}
bool LowPrecisionTimer::SchedulableCallback::IsScheduled() {
base::AutoLock auto_scheduled_time_lock(scheduled_time_lock_);
return scheduled_time_ != base::TimeTicks::Max();
}
base::TimeTicks LowPrecisionTimer::SchedulableCallback::Inactivate() {
// If we're inside the task runner and the task is currently running, that
// means Inactivate() was called from inside the callback, and |active_lock_|
// is already aquired on the current task runner. Acquiring it again would
// cause deadlock.
bool is_inactivated_by_callback =
task_runner_->RunsTasksInCurrentSequence() && is_currently_running_;
std::optional<base::AutoLock> auto_active_lock;
if (!is_inactivated_by_callback) {
auto_active_lock.emplace(active_lock_);
}
is_active_ = false;
repeated_delay_ = base::TimeDelta(); // Prevent automatic re-schedule.
base::AutoLock auto_scheduled_time_lock(scheduled_time_lock_);
return scheduled_time_;
}
void LowPrecisionTimer::SchedulableCallback::MaybeRun() {
DCHECK(task_runner_->RunsTasksInCurrentSequence());
// Run unless we've been cancelled.
base::AutoLock auto_active_lock(active_lock_);
if (!is_active_) {
return;
}
{
// Reset scheduled time as to allow re-scheduling. Release the lock to allow
// the callback to re-schedule without deadlock.
base::AutoLock auto_scheduled_time_lock(scheduled_time_lock_);
scheduled_time_ = base::TimeTicks::Max();
}
is_currently_running_ = true;
callback_.Run();
is_currently_running_ = false;
if (!repeated_delay_.is_zero()) {
Schedule(base::TimeTicks::Now() + repeated_delay_);
}
}
LowPrecisionTimer::LowPrecisionTimer(
scoped_refptr<base::SequencedTaskRunner> task_runner,
base::RepeatingCallback<void()> callback)
: callback_(std::move(callback)), task_runner_(std::move(task_runner)) {}
LowPrecisionTimer::~LowPrecisionTimer() {
DCHECK(is_shutdown_);
DCHECK(!schedulable_callback_);
}
void LowPrecisionTimer::Shutdown() {
base::AutoLock auto_lock(lock_);
if (is_shutdown_) {
// Already shut down.
return;
}
if (schedulable_callback_) {
schedulable_callback_->Inactivate();
schedulable_callback_ = nullptr;
}
is_shutdown_ = true;
}
void LowPrecisionTimer::StartOneShot(base::TimeDelta delay) {
DCHECK_GE(delay, base::TimeDelta());
base::AutoLock auto_lock(lock_);
DCHECK(!is_shutdown_);
repeated_delay_ = base::TimeDelta(); // Not repeating.
ScheduleCallback(base::TimeTicks::Now() + delay);
}
void LowPrecisionTimer::StartRepeating(base::TimeDelta delay) {
DCHECK_GE(delay, base::TimeDelta());
base::AutoLock auto_lock(lock_);
DCHECK(!is_shutdown_);
repeated_delay_ = delay;
ScheduleCallback(base::TimeTicks::Now() + delay);
}
bool LowPrecisionTimer::IsActive() {
base::AutoLock auto_lock(lock_);
if (!schedulable_callback_) {
return false;
}
if (!repeated_delay_.is_zero()) {
return true;
}
return schedulable_callback_->IsScheduled();
}
void LowPrecisionTimer::Stop() {
base::AutoLock auto_lock(lock_);
if (!schedulable_callback_)
return;
repeated_delay_ = base::TimeDelta(); // Not repeating.
schedulable_callback_->Inactivate();
schedulable_callback_ = nullptr;
}
// EXCLUSIVE_LOCKS_REQUIRED(lock_)
void LowPrecisionTimer::ScheduleCallback(base::TimeTicks scheduled_time) {
if (!schedulable_callback_) {
schedulable_callback_ = base::MakeRefCounted<SchedulableCallback>(
task_runner_, callback_, repeated_delay_);
}
schedulable_callback_->Schedule(scheduled_time);
}
// EXCLUSIVE_LOCKS_REQUIRED(lock_)
void LowPrecisionTimer::RescheduleCallback() {
if (!schedulable_callback_)
return;
base::TimeTicks cancelled_scheduled_time =
schedulable_callback_->Inactivate();
schedulable_callback_ = nullptr;
if (cancelled_scheduled_time == base::TimeTicks::Max())
return; // We don't have a scheduled time.
ScheduleCallback(cancelled_scheduled_time);
}
void LowPrecisionTimer::MoveToNewTaskRunner(
scoped_refptr<base::SequencedTaskRunner> task_runner) {
base::AutoLock auto_lock(lock_);
DCHECK(task_runner);
task_runner_ = std::move(task_runner);
RescheduleCallback();
}
} // namespace blink
|