blob: 2dc8367d0667316d7a077072b7670c489b3ea07c [file] [log] [blame]
// Copyright 2016 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/scheduler/main_thread/auto_advancing_virtual_time_domain.h"
#include "base/atomicops.h"
#include "base/time/time_override.h"
#include "build/build_config.h"
#include "third_party/blink/renderer/platform/scheduler/common/scheduler_helper.h"
// windows.h #defines MemoryBarrier on x64. So we copy this bit
// from base/atomicops.h to be independent of the include order in this file.
#if defined(OS_WIN) && defined(ARCH_CPU_64_BITS)
#undef MemoryBarrier
#endif
namespace blink {
namespace scheduler {
AutoAdvancingVirtualTimeDomain::AutoAdvancingVirtualTimeDomain(
base::Time initial_time,
base::TimeTicks initial_time_ticks,
SchedulerHelper* helper,
BaseTimeOverridePolicy policy)
: task_starvation_count_(0),
max_task_starvation_count_(0),
can_advance_virtual_time_(true),
observer_(nullptr),
helper_(helper),
now_ticks_(initial_time_ticks),
initial_time_ticks_(initial_time_ticks),
initial_time_(initial_time),
previous_time_(initial_time) {
DCHECK_EQ(AutoAdvancingVirtualTimeDomain::g_time_domain_, nullptr);
AutoAdvancingVirtualTimeDomain::g_time_domain_ = this;
// GetVirtualTime / GetVirtualTimeTicks access g_time_domain_.
base::subtle::MemoryBarrier();
if (policy == BaseTimeOverridePolicy::OVERRIDE) {
time_overrides_ = std::make_unique<base::subtle::ScopedTimeClockOverrides>(
&AutoAdvancingVirtualTimeDomain::GetVirtualTime,
&AutoAdvancingVirtualTimeDomain::GetVirtualTimeTicks, nullptr);
}
helper_->AddTaskObserver(this);
}
AutoAdvancingVirtualTimeDomain::~AutoAdvancingVirtualTimeDomain() {
helper_->RemoveTaskObserver(this);
time_overrides_.reset();
// GetVirtualTime / GetVirtualTimeTicks (the functions we may have
// temporariliy installed in the constructor) access g_time_domain_.
base::subtle::MemoryBarrier();
DCHECK_EQ(AutoAdvancingVirtualTimeDomain::g_time_domain_, this);
AutoAdvancingVirtualTimeDomain::g_time_domain_ = nullptr;
}
base::sequence_manager::LazyNow AutoAdvancingVirtualTimeDomain::CreateLazyNow()
const {
base::AutoLock lock(now_ticks_lock_);
return base::sequence_manager::LazyNow(now_ticks_);
}
base::TimeTicks AutoAdvancingVirtualTimeDomain::Now() const {
base::AutoLock lock(now_ticks_lock_);
return now_ticks_;
}
base::Optional<base::TimeDelta>
AutoAdvancingVirtualTimeDomain::DelayTillNextTask(
base::sequence_manager::LazyNow* lazy_now) {
base::Optional<base::TimeTicks> run_time = NextScheduledRunTime();
if (!run_time)
return base::nullopt;
// We may have advanced virtual time past the next task when a
// WebScopedVirtualTimePauser unpauses.
if (run_time <= Now())
return base::TimeDelta();
// Rely on MaybeFastForwardToNextTask to be called to advance
// virtual time.
return base::nullopt;
}
bool AutoAdvancingVirtualTimeDomain::MaybeFastForwardToNextTask() {
if (!can_advance_virtual_time_)
return false;
base::Optional<base::TimeTicks> run_time = NextScheduledRunTime();
if (!run_time)
return false;
if (MaybeAdvanceVirtualTime(*run_time)) {
task_starvation_count_ = 0;
return true;
}
return false;
}
void AutoAdvancingVirtualTimeDomain::SetNextDelayedDoWork(
base::sequence_manager::LazyNow* lazy_now,
base::TimeTicks run_time) {
// Ignore cancelation since no delayed work is actually being posted.
if (run_time == base::TimeTicks::Max())
return;
// Avoid posting pointless DoWorks, i.e. if the time domain has more then one
// scheduled wake up then we don't need to do anything.
if (can_advance_virtual_time_ && NumberOfScheduledWakeUps() == 1u)
RequestDoWork();
}
void AutoAdvancingVirtualTimeDomain::SetObserver(Observer* observer) {
observer_ = observer;
}
void AutoAdvancingVirtualTimeDomain::SetCanAdvanceVirtualTime(
bool can_advance_virtual_time) {
can_advance_virtual_time_ = can_advance_virtual_time;
if (can_advance_virtual_time_)
RequestDoWork();
}
void AutoAdvancingVirtualTimeDomain::SetMaxVirtualTimeTaskStarvationCount(
int max_task_starvation_count) {
max_task_starvation_count_ = max_task_starvation_count;
if (max_task_starvation_count_ == 0)
task_starvation_count_ = 0;
}
void AutoAdvancingVirtualTimeDomain::SetVirtualTimeFence(
base::TimeTicks virtual_time_fence) {
virtual_time_fence_ = virtual_time_fence;
if (!requested_next_virtual_time_.is_null())
MaybeAdvanceVirtualTime(requested_next_virtual_time_);
}
bool AutoAdvancingVirtualTimeDomain::MaybeAdvanceVirtualTime(
base::TimeTicks new_virtual_time) {
// If set, don't advance past the end of |virtual_time_fence_|.
if (!virtual_time_fence_.is_null() &&
new_virtual_time > virtual_time_fence_) {
requested_next_virtual_time_ = new_virtual_time;
new_virtual_time = virtual_time_fence_;
} else {
requested_next_virtual_time_ = base::TimeTicks();
}
if (new_virtual_time <= Now())
return false;
{
base::AutoLock lock(now_ticks_lock_);
now_ticks_ = new_virtual_time;
}
if (observer_)
observer_->OnVirtualTimeAdvanced();
return true;
}
const char* AutoAdvancingVirtualTimeDomain::GetName() const {
return "AutoAdvancingVirtualTimeDomain";
}
void AutoAdvancingVirtualTimeDomain::WillProcessTask(
const base::PendingTask& pending_task) {}
void AutoAdvancingVirtualTimeDomain::DidProcessTask(
const base::PendingTask& pending_task) {
if (max_task_starvation_count_ == 0 ||
++task_starvation_count_ < max_task_starvation_count_) {
return;
}
// Delayed tasks are being excessively starved, so allow virtual time to
// advance.
base::Optional<base::TimeTicks> run_time = NextScheduledRunTime();
if (run_time && MaybeAdvanceVirtualTime(*run_time))
task_starvation_count_ = 0;
}
base::Time AutoAdvancingVirtualTimeDomain::Date() const {
base::TimeDelta offset = Now() - initial_time_ticks_;
return initial_time_ + offset;
}
AutoAdvancingVirtualTimeDomain* AutoAdvancingVirtualTimeDomain::g_time_domain_ =
nullptr;
// static
base::TimeTicks AutoAdvancingVirtualTimeDomain::GetVirtualTimeTicks() {
DCHECK(AutoAdvancingVirtualTimeDomain::g_time_domain_);
return AutoAdvancingVirtualTimeDomain::g_time_domain_->Now();
}
// static
base::Time AutoAdvancingVirtualTimeDomain::GetVirtualTime() {
DCHECK(AutoAdvancingVirtualTimeDomain::g_time_domain_);
return AutoAdvancingVirtualTimeDomain::g_time_domain_->Date();
}
AutoAdvancingVirtualTimeDomain::Observer::Observer() = default;
AutoAdvancingVirtualTimeDomain::Observer::~Observer() = default;
} // namespace scheduler
} // namespace blink