[go: nahoru, domu]

blob: 031c8cdba06d4d26cb1299f0fa1b5d66b6f72aac [file] [log] [blame]
// 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 "base/check.h"
#include "base/task/sequenced_task_runner.h"
#include "third_party/webrtc_overrides/task_queue_factory.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::unique_ptr<base::AutoLock> auto_active_lock;
if (!is_inactivated_by_callback) {
auto_active_lock = std::make_unique<base::AutoLock>(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