#include "content/browser/scheduler/responsiveness/watcher.h"
#include <variant>
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/pending_task.h"
#include "base/power_monitor/power_monitor.h"
#include "build/build_config.h"
#include "content/browser/scheduler/responsiveness/calculator.h"
#include "content/browser/scheduler/responsiveness/message_loop_observer.h"
#include "content/browser/scheduler/responsiveness/native_event_observer.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
namespace content {
namespace responsiveness {
Watcher::Metadata::Metadata(const void* identifier,
bool was_blocked_or_low_priority,
base::TimeTicks execution_start_time)
: identifier(identifier),
was_blocked_or_low_priority(was_blocked_or_low_priority),
execution_start_time(execution_start_time) {}
std::unique_ptr<Calculator> Watcher::CreateCalculator() {
return std::make_unique<Calculator>(
GetContentClient()->browser()->CreateResponsivenessCalculatorDelegate());
}
std::unique_ptr<MetricSource> Watcher::CreateMetricSource() {
return std::make_unique<MetricSource>(this);
}
void Watcher::WillRunTaskOnUIThread(const base::PendingTask* task,
bool was_blocked_or_low_priority) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
WillRunTask(task, was_blocked_or_low_priority,
¤tly_running_metadata_ui_);
}
void Watcher::DidRunTaskOnUIThread(const base::PendingTask* task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
auto lambda = [this](base::TimeTicks queue_time,
base::TimeTicks execution_start_time,
base::TimeTicks execution_finish_time) {
calculator_->TaskOrEventFinishedOnUIThread(queue_time, execution_start_time,
execution_finish_time);
};
DidRunTask(task, ¤tly_running_metadata_ui_,
&mismatched_task_identifiers_ui_, lambda);
}
void Watcher::WillRunTaskOnIOThread(const base::PendingTask* task,
bool was_blocked_or_low_priority) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
WillRunTask(task, was_blocked_or_low_priority,
¤tly_running_metadata_io_);
}
void Watcher::DidRunTaskOnIOThread(const base::PendingTask* task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
auto lambda = [this](base::TimeTicks queue_time,
base::TimeTicks execution_start_time,
base::TimeTicks execution_finish_time) {
calculator_io_->TaskOrEventFinishedOnIOThread(
queue_time, execution_start_time, execution_finish_time);
};
DidRunTask(task, ¤tly_running_metadata_io_,
&mismatched_task_identifiers_io_, lambda);
}
void Watcher::WillRunTask(const base::PendingTask* task,
bool was_blocked_or_low_priority,
std::vector<Metadata>* currently_running_metadata) {
if (!currently_running_metadata->empty()) [[unlikely]] {
currently_running_metadata->back().caused_reentrancy = true;
}
const base::TimeTicks execution_start_time = base::TimeTicks::Now();
currently_running_metadata->emplace_back(task, was_blocked_or_low_priority,
execution_start_time);
}
void Watcher::DidRunTask(const base::PendingTask* task,
std::vector<Metadata>* currently_running_metadata,
int* mismatched_task_identifiers,
TaskOrEventFinishedCallback callback) {
if (currently_running_metadata->empty() ||
(task != currently_running_metadata->back().identifier)) [[unlikely]] {
*mismatched_task_identifiers += 1;
return currently_running_metadata->clear();
}
const Metadata metadata = currently_running_metadata->back();
currently_running_metadata->pop_back();
if (metadata.caused_reentrancy) [[unlikely]] {
return;
}
if (task->queue_time.is_null() && task->delayed_run_time.is_null())
[[unlikely]] {
return;
}
const bool is_delayed_task = !task->delayed_run_time.is_null();
const base::TimeTicks queue_time =
is_delayed_task || metadata.was_blocked_or_low_priority
? metadata.execution_start_time
: task->queue_time;
const base::TimeTicks execution_finish_time = base::TimeTicks::Now();
DCHECK(!queue_time.is_null());
DCHECK(!metadata.execution_start_time.is_null());
DCHECK(!execution_finish_time.is_null());
DCHECK_LE(queue_time, metadata.execution_start_time);
DCHECK_LE(metadata.execution_start_time, execution_finish_time);
callback(queue_time, metadata.execution_start_time, execution_finish_time);
}
void Watcher::WillRunEventOnUIThread(const void* opaque_identifier) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!currently_running_metadata_ui_.empty()) [[unlikely]] {
currently_running_metadata_ui_.back().caused_reentrancy = true;
}
const base::TimeTicks execution_start_time = base::TimeTicks::Now();
currently_running_metadata_ui_.emplace_back(
opaque_identifier, false,
execution_start_time);
}
void Watcher::DidRunEventOnUIThread(const void* opaque_identifier) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (currently_running_metadata_ui_.empty() ||
(opaque_identifier != currently_running_metadata_ui_.back().identifier))
[[unlikely]] {
mismatched_event_identifiers_ui_ += 1;
return currently_running_metadata_ui_.clear();
}
const bool caused_reentrancy =
currently_running_metadata_ui_.back().caused_reentrancy;
const base::TimeTicks execution_start_time =
currently_running_metadata_ui_.back().execution_start_time;
currently_running_metadata_ui_.pop_back();
if (caused_reentrancy) [[unlikely]] {
return;
}
const base::TimeTicks queue_time = execution_start_time;
const base::TimeTicks execution_finish_time = base::TimeTicks::Now();
calculator_->TaskOrEventFinishedOnUIThread(queue_time, execution_start_time,
execution_finish_time);
}
Watcher::Watcher() = default;
Watcher::~Watcher() = default;
void Watcher::SetUp() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
calculator_ = CreateCalculator();
currently_running_metadata_ui_.reserve(5);
metric_source_ = CreateMetricSource();
metric_source_->SetUp();
}
void Watcher::Destroy() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
base::ScopedClosureRunner on_destroy_complete(base::BindOnce(
&Watcher::FinishDestroyMetricSource, base::RetainedRef(this)));
metric_source_->Destroy(std::move(on_destroy_complete));
}
void Watcher::OnFirstIdle() {
calculator_->OnFirstIdle();
}
void Watcher::SetUpOnIOThread() {
currently_running_metadata_io_.reserve(5);
DCHECK(calculator_.get());
calculator_io_ = calculator_.get();
}
void Watcher::TearDownOnUIThread() {}
void Watcher::FinishDestroyMetricSource() {
metric_source_ = nullptr;
}
void Watcher::TearDownOnIOThread() {
calculator_io_ = nullptr;
}
}
}