Commit a9533519 authored by Erik Chen's avatar Erik Chen Committed by Commit Bot

Implement responsiveness::MessageLoopObserver.

This CL adds the class MessageLoopObserver, which forwards events from the UI
and IO thread message loop task runners to the responsiveness Watcher and
Calculator.

Bug: 859155
Change-Id: Ie492f1fbc26940007cb303bdc5cdbdb59d887c19
Reviewed-on: https://chromium-review.googlesource.com/1149042
Commit-Queue: Erik Chen <erikchen@chromium.org>
Reviewed-by: default avatarAvi Drissman <avi@chromium.org>
Reviewed-by: default avatarGabriel Charette <gab@chromium.org>
Reviewed-by: default avatarTimothy Dresser <tdresser@chromium.org>
Cr-Commit-Position: refs/heads/master@{#579083}
parent ba3108de
...@@ -273,6 +273,11 @@ void MessageLoop::RemoveTaskObserver(TaskObserver* task_observer) { ...@@ -273,6 +273,11 @@ void MessageLoop::RemoveTaskObserver(TaskObserver* task_observer) {
task_observers_.RemoveObserver(task_observer); task_observers_.RemoveObserver(task_observer);
} }
void MessageLoop::SetAddQueueTimeToTasks(bool enable) {
DCHECK_CALLED_ON_VALID_THREAD(bound_thread_checker_);
underlying_task_runner_->SetAddQueueTimeToTasks(enable);
}
bool MessageLoop::IsIdleForTesting() { bool MessageLoop::IsIdleForTesting() {
// Have unprocessed tasks? (this reloads the work queue if necessary) // Have unprocessed tasks? (this reloads the work queue if necessary)
if (sequenced_task_source_->HasTasks()) if (sequenced_task_source_->HasTasks())
......
...@@ -182,6 +182,10 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate, ...@@ -182,6 +182,10 @@ class BASE_EXPORT MessageLoop : public MessagePump::Delegate,
void AddTaskObserver(TaskObserver* task_observer); void AddTaskObserver(TaskObserver* task_observer);
void RemoveTaskObserver(TaskObserver* task_observer); void RemoveTaskObserver(TaskObserver* task_observer);
// When this functionality is enabled, the queue time will be recorded for
// posted tasks.
void SetAddQueueTimeToTasks(bool enable);
// Returns true if the message loop is idle (ignoring delayed tasks). This is // Returns true if the message loop is idle (ignoring delayed tasks). This is
// the same condition which triggers DoWork() to return false: i.e. // the same condition which triggers DoWork() to return false: i.e.
// out of tasks which can be processed at the current run-level -- there might // out of tasks which can be processed at the current run-level -- there might
......
...@@ -74,6 +74,11 @@ void MessageLoopCurrent::RemoveTaskObserver(TaskObserver* task_observer) { ...@@ -74,6 +74,11 @@ void MessageLoopCurrent::RemoveTaskObserver(TaskObserver* task_observer) {
current_->RemoveTaskObserver(task_observer); current_->RemoveTaskObserver(task_observer);
} }
void MessageLoopCurrent::SetAddQueueTimeToTasks(bool enable) {
DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
current_->SetAddQueueTimeToTasks(enable);
}
void MessageLoopCurrent::SetNestableTasksAllowed(bool allowed) { void MessageLoopCurrent::SetNestableTasksAllowed(bool allowed) {
DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_); DCHECK_CALLED_ON_VALID_THREAD(current_->bound_thread_checker_);
if (allowed) { if (allowed) {
......
...@@ -120,6 +120,10 @@ class BASE_EXPORT MessageLoopCurrent { ...@@ -120,6 +120,10 @@ class BASE_EXPORT MessageLoopCurrent {
void AddTaskObserver(TaskObserver* task_observer); void AddTaskObserver(TaskObserver* task_observer);
void RemoveTaskObserver(TaskObserver* task_observer); void RemoveTaskObserver(TaskObserver* task_observer);
// When this functionality is enabled, the queue time will be recorded for
// posted tasks.
void SetAddQueueTimeToTasks(bool enable);
// Enables or disables the recursive task processing. This happens in the case // Enables or disables the recursive task processing. This happens in the case
// of recursive message loops. Some unwanted message loops may occur when // of recursive message loops. Some unwanted message loops may occur when
// using common controls or printer functions. By default, recursive task // using common controls or printer functions. By default, recursive task
......
...@@ -107,6 +107,11 @@ void MessageLoopTaskRunner::InjectTask(OnceClosure task) { ...@@ -107,6 +107,11 @@ void MessageLoopTaskRunner::InjectTask(OnceClosure task) {
DCHECK(success) << "Injected a task in a dead task runner."; DCHECK(success) << "Injected a task in a dead task runner.";
} }
void MessageLoopTaskRunner::SetAddQueueTimeToTasks(bool enable) {
DCHECK_NE(add_queue_time_to_tasks_, enable);
add_queue_time_to_tasks_ = enable;
}
MessageLoopTaskRunner::~MessageLoopTaskRunner() = default; MessageLoopTaskRunner::~MessageLoopTaskRunner() = default;
bool MessageLoopTaskRunner::AddToIncomingQueue(const Location& from_here, bool MessageLoopTaskRunner::AddToIncomingQueue(const Location& from_here,
...@@ -122,6 +127,13 @@ bool MessageLoopTaskRunner::AddToIncomingQueue(const Location& from_here, ...@@ -122,6 +127,13 @@ bool MessageLoopTaskRunner::AddToIncomingQueue(const Location& from_here,
PendingTask pending_task(from_here, std::move(task), PendingTask pending_task(from_here, std::move(task),
CalculateDelayedRuntime(from_here, delay), nestable); CalculateDelayedRuntime(from_here, delay), nestable);
if (add_queue_time_to_tasks_) {
if (pending_task.delayed_run_time.is_null()) {
pending_task.queue_time = base::TimeTicks::Now();
} else {
pending_task.queue_time = pending_task.delayed_run_time - delay;
}
}
#if defined(OS_WIN) #if defined(OS_WIN)
// We consider the task needs a high resolution timer if the delay is // We consider the task needs a high resolution timer if the delay is
......
...@@ -57,6 +57,10 @@ class BASE_EXPORT MessageLoopTaskRunner : public SingleThreadTaskRunner, ...@@ -57,6 +57,10 @@ class BASE_EXPORT MessageLoopTaskRunner : public SingleThreadTaskRunner,
bool HasTasks() override; bool HasTasks() override;
void InjectTask(OnceClosure task) override; void InjectTask(OnceClosure task) override;
// When this functionality is enabled, AddToIncomingQueue() will also add the
// queue time to the task.
void SetAddQueueTimeToTasks(bool enable);
private: private:
friend class RefCountedThreadSafe<MessageLoopTaskRunner>; friend class RefCountedThreadSafe<MessageLoopTaskRunner>;
~MessageLoopTaskRunner() override; ~MessageLoopTaskRunner() override;
...@@ -100,6 +104,9 @@ class BASE_EXPORT MessageLoopTaskRunner : public SingleThreadTaskRunner, ...@@ -100,6 +104,9 @@ class BASE_EXPORT MessageLoopTaskRunner : public SingleThreadTaskRunner,
// The next sequence number to use for delayed tasks. // The next sequence number to use for delayed tasks.
int next_sequence_num_ = 0; int next_sequence_num_ = 0;
// Whether to add the queue time to tasks.
bool add_queue_time_to_tasks_ = false;
DISALLOW_COPY_AND_ASSIGN(MessageLoopTaskRunner); DISALLOW_COPY_AND_ASSIGN(MessageLoopTaskRunner);
}; };
......
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/containers/queue.h" #include "base/containers/queue.h"
#include "base/location.h" #include "base/location.h"
#include "base/optional.h"
#include "base/time/time.h" #include "base/time/time.h"
namespace base { namespace base {
...@@ -44,6 +45,10 @@ struct BASE_EXPORT PendingTask { ...@@ -44,6 +45,10 @@ struct BASE_EXPORT PendingTask {
// The time when the task should be run. // The time when the task should be run.
base::TimeTicks delayed_run_time; base::TimeTicks delayed_run_time;
// The time at which the task was queued. Only set if the task was posted to a
// MessageLoop with SetAddQueueTimeToTasks(true).
Optional<TimeTicks> queue_time;
// Chain of up-to-four symbols of the parent tasks which led to this one being // Chain of up-to-four symbols of the parent tasks which led to this one being
// posted. // posted.
std::array<const void*, 4> task_backtrace = {}; std::array<const void*, 4> task_backtrace = {};
......
...@@ -1518,6 +1518,8 @@ jumbo_source_set("browser") { ...@@ -1518,6 +1518,8 @@ jumbo_source_set("browser") {
"sandbox_parameters_mac.mm", "sandbox_parameters_mac.mm",
"scheduler/responsiveness/calculator.cc", "scheduler/responsiveness/calculator.cc",
"scheduler/responsiveness/calculator.h", "scheduler/responsiveness/calculator.h",
"scheduler/responsiveness/message_loop_observer.cc",
"scheduler/responsiveness/message_loop_observer.h",
"scheduler/responsiveness/watcher.cc", "scheduler/responsiveness/watcher.cc",
"scheduler/responsiveness/watcher.h", "scheduler/responsiveness/watcher.h",
"scoped_active_url.cc", "scoped_active_url.cc",
......
// Copyright 2018 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 "content/browser/scheduler/responsiveness/message_loop_observer.h"
namespace responsiveness {
MessageLoopObserver::MessageLoopObserver(TaskCallback will_run_task_callback,
TaskCallback did_run_task_callback)
: will_run_task_callback_(will_run_task_callback),
did_run_task_callback_(did_run_task_callback) {
base::MessageLoopCurrent::Get()->SetAddQueueTimeToTasks(true);
base::MessageLoopCurrent::Get()->AddTaskObserver(this);
}
MessageLoopObserver::~MessageLoopObserver() {
base::MessageLoopCurrent::Get()->RemoveTaskObserver(this);
base::MessageLoopCurrent::Get()->SetAddQueueTimeToTasks(false);
}
void MessageLoopObserver::WillProcessTask(
const base::PendingTask& pending_task) {
will_run_task_callback_.Run(&pending_task);
}
void MessageLoopObserver::DidProcessTask(
const base::PendingTask& pending_task) {
did_run_task_callback_.Run(&pending_task);
}
} // namespace responsiveness
// Copyright 2018 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.
#ifndef CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_MESSAGE_LOOP_OBSERVER_H_
#define CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_MESSAGE_LOOP_OBSERVER_H_
#include "base/macros.h"
#include "base/message_loop/message_loop_current.h"
#include "content/common/content_export.h"
namespace base {
struct PendingTask;
} // namespace base
namespace responsiveness {
// This object is not thread safe. It must be constructed and destroyed on the
// same thread. The callbacks will occur synchronously from WillProcessTask()
// and DidProcessTask().
class CONTENT_EXPORT MessageLoopObserver
: base::MessageLoopCurrent::TaskObserver {
public:
using TaskCallback =
base::RepeatingCallback<void(const base::PendingTask* task)>;
// The constructor will register the object as an observer of the current
// MessageLoop. The destructor will unregister the object.
MessageLoopObserver(TaskCallback will_run_task_callback,
TaskCallback did_run_task_callback);
~MessageLoopObserver() override;
private:
void WillProcessTask(const base::PendingTask& pending_task) override;
void DidProcessTask(const base::PendingTask& pending_task) override;
TaskCallback will_run_task_callback_;
TaskCallback did_run_task_callback_;
DISALLOW_COPY_AND_ASSIGN(MessageLoopObserver);
};
} // namespace responsiveness
#endif // CONTENT_BROWSER_SCHEDULER_RESPONSIVENESS_MESSAGE_LOOP_OBSERVER_H_
...@@ -6,11 +6,12 @@ ...@@ -6,11 +6,12 @@
#include "base/pending_task.h" #include "base/pending_task.h"
#include "content/browser/scheduler/responsiveness/calculator.h" #include "content/browser/scheduler/responsiveness/calculator.h"
#include "content/browser/scheduler/responsiveness/message_loop_observer.h"
#include "content/public/browser/browser_thread.h" #include "content/public/browser/browser_thread.h"
namespace responsiveness { namespace responsiveness {
Watcher::Metadata::Metadata(void* identifier) : identifier(identifier) {} Watcher::Metadata::Metadata(const void* identifier) : identifier(identifier) {}
Watcher::Watcher() { Watcher::Watcher() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
...@@ -27,9 +28,12 @@ void Watcher::SetUp() { ...@@ -27,9 +28,12 @@ void Watcher::SetUp() {
calculator_ = MakeCalculator(); calculator_ = MakeCalculator();
RegisterMessageLoopObserverUI();
content::BrowserThread::PostTask( content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE, content::BrowserThread::IO, FROM_HERE,
base::BindOnce(&Watcher::SetUpOnIOThread, this, calculator_.get())); base::BindOnce(&Watcher::SetUpOnIOThread, base::Unretained(this),
calculator_.get()));
} }
void Watcher::Destroy() { void Watcher::Destroy() {
...@@ -38,9 +42,11 @@ void Watcher::Destroy() { ...@@ -38,9 +42,11 @@ void Watcher::Destroy() {
DCHECK(!destroy_was_called_); DCHECK(!destroy_was_called_);
destroy_was_called_ = true; destroy_was_called_ = true;
message_loop_observer_ui_.reset();
content::BrowserThread::PostTask( content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE, content::BrowserThread::IO, FROM_HERE,
base::BindOnce(&Watcher::TearDownOnIOThread, this)); base::BindOnce(&Watcher::TearDownOnIOThread, base::Unretained(this)));
} }
std::unique_ptr<Calculator> Watcher::MakeCalculator() { std::unique_ptr<Calculator> Watcher::MakeCalculator() {
...@@ -52,23 +58,42 @@ Watcher::~Watcher() { ...@@ -52,23 +58,42 @@ Watcher::~Watcher() {
DCHECK(destroy_was_called_); DCHECK(destroy_was_called_);
} }
void Watcher::RegisterMessageLoopObserverUI() {
// We must use base::Unretained(this) to prevent ownership cycle.
MessageLoopObserver::TaskCallback will_run_callback = base::BindRepeating(
&Watcher::WillRunTaskOnUIThread, base::Unretained(this));
MessageLoopObserver::TaskCallback did_run_callback = base::BindRepeating(
&Watcher::DidRunTaskOnUIThread, base::Unretained(this));
message_loop_observer_ui_.reset(new MessageLoopObserver(
std::move(will_run_callback), std::move(did_run_callback)));
}
void Watcher::RegisterMessageLoopObserverIO() {
// We must use base::Unretained(this) to prevent ownership cycle.
MessageLoopObserver::TaskCallback will_run_callback = base::BindRepeating(
&Watcher::WillRunTaskOnIOThread, base::Unretained(this));
MessageLoopObserver::TaskCallback did_run_callback = base::BindRepeating(
&Watcher::DidRunTaskOnIOThread, base::Unretained(this));
message_loop_observer_io_.reset(new MessageLoopObserver(
std::move(will_run_callback), std::move(did_run_callback)));
}
void Watcher::SetUpOnIOThread(Calculator* calculator) { void Watcher::SetUpOnIOThread(Calculator* calculator) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO); DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
// TODO(erikchen): Add MessageLoopObserver to IO thread. RegisterMessageLoopObserverIO();
calculator_io_ = calculator; calculator_io_ = calculator;
} }
void Watcher::TearDownOnIOThread() { void Watcher::TearDownOnIOThread() {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO); DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
// TODO(erikchen): Remove MessageLoopObserver from IO thread. message_loop_observer_io_.reset();
calculator_io_ = nullptr; calculator_io_ = nullptr;
content::BrowserThread::PostTask( content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE, content::BrowserThread::UI, FROM_HERE,
base::BindOnce(&Watcher::TearDownOnUIThread, this)); base::BindOnce(&Watcher::TearDownOnUIThread, base::Unretained(this)));
} }
void Watcher::TearDownOnUIThread() { void Watcher::TearDownOnUIThread() {
...@@ -78,13 +103,13 @@ void Watcher::TearDownOnUIThread() { ...@@ -78,13 +103,13 @@ void Watcher::TearDownOnUIThread() {
Release(); Release();
} }
void Watcher::WillRunTaskOnUIThread(base::PendingTask* task) { void Watcher::WillRunTaskOnUIThread(const base::PendingTask* task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
WillRunTask(task, &currently_running_metadata_ui_); WillRunTask(task, &currently_running_metadata_ui_);
} }
void Watcher::DidRunTaskOnUIThread(base::PendingTask* task) { void Watcher::DidRunTaskOnUIThread(const base::PendingTask* task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI); DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
// It's safe to use base::Unretained since the callback will be synchronously // It's safe to use base::Unretained since the callback will be synchronously
...@@ -97,13 +122,13 @@ void Watcher::DidRunTaskOnUIThread(base::PendingTask* task) { ...@@ -97,13 +122,13 @@ void Watcher::DidRunTaskOnUIThread(base::PendingTask* task) {
&mismatched_task_identifiers_ui_, std::move(callback)); &mismatched_task_identifiers_ui_, std::move(callback));
} }
void Watcher::WillRunTaskOnIOThread(base::PendingTask* task) { void Watcher::WillRunTaskOnIOThread(const base::PendingTask* task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO); DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
WillRunTask(task, &currently_running_metadata_io_); WillRunTask(task, &currently_running_metadata_io_);
} }
void Watcher::DidRunTaskOnIOThread(base::PendingTask* task) { void Watcher::DidRunTaskOnIOThread(const base::PendingTask* task) {
DCHECK_CURRENTLY_ON(content::BrowserThread::IO); DCHECK_CURRENTLY_ON(content::BrowserThread::IO);
// It's safe to use base::Unretained since the callback will be synchronously // It's safe to use base::Unretained since the callback will be synchronously
...@@ -115,7 +140,7 @@ void Watcher::DidRunTaskOnIOThread(base::PendingTask* task) { ...@@ -115,7 +140,7 @@ void Watcher::DidRunTaskOnIOThread(base::PendingTask* task) {
&mismatched_task_identifiers_io_, std::move(callback)); &mismatched_task_identifiers_io_, std::move(callback));
} }
void Watcher::WillRunTask(base::PendingTask* task, void Watcher::WillRunTask(const base::PendingTask* task,
std::stack<Metadata>* currently_running_metadata) { std::stack<Metadata>* currently_running_metadata) {
// Reentrancy should be rare. // Reentrancy should be rare.
if (UNLIKELY(!currently_running_metadata->empty())) { if (UNLIKELY(!currently_running_metadata->empty())) {
...@@ -131,7 +156,7 @@ void Watcher::WillRunTask(base::PendingTask* task, ...@@ -131,7 +156,7 @@ void Watcher::WillRunTask(base::PendingTask* task,
} }
} }
void Watcher::DidRunTask(base::PendingTask* task, void Watcher::DidRunTask(const base::PendingTask* task,
std::stack<Metadata>* currently_running_metadata, std::stack<Metadata>* currently_running_metadata,
int* mismatched_task_identifiers, int* mismatched_task_identifiers,
TaskOrEventFinishedCallback callback) { TaskOrEventFinishedCallback callback) {
...@@ -139,7 +164,8 @@ void Watcher::DidRunTask(base::PendingTask* task, ...@@ -139,7 +164,8 @@ void Watcher::DidRunTask(base::PendingTask* task,
// the identifier should differ is when Watcher is first constructed. The // the identifier should differ is when Watcher is first constructed. The
// TaskRunner Observers are added while a task is being run, which means that // TaskRunner Observers are added while a task is being run, which means that
// there was no corresponding WillRunTask. // there was no corresponding WillRunTask.
if (UNLIKELY(task != currently_running_metadata->top().identifier)) { if (UNLIKELY(currently_running_metadata->empty() ||
(task != currently_running_metadata->top().identifier))) {
*mismatched_task_identifiers += 1; *mismatched_task_identifiers += 1;
DCHECK_LE(*mismatched_task_identifiers, 1); DCHECK_LE(*mismatched_task_identifiers, 1);
return; return;
...@@ -158,9 +184,14 @@ void Watcher::DidRunTask(base::PendingTask* task, ...@@ -158,9 +184,14 @@ void Watcher::DidRunTask(base::PendingTask* task,
// For delayed tasks, measure the duration of the task itself, rather than the // For delayed tasks, measure the duration of the task itself, rather than the
// duration from schedule time to finish time. // duration from schedule time to finish time.
base::TimeTicks schedule_time; base::TimeTicks schedule_time;
if (task->delayed_run_time.is_null()) { if (delayed_task_start.is_null()) {
// TODO(erikchen): Implement DelayedTask::queue_time. // Tasks which were posted before the MessageLoopObserver was created will
// schedule_time = task->queue_time; // not have a queue_time, and should be ignored. This doesn't affect delayed
// tasks.
if (UNLIKELY(!task->queue_time))
return;
schedule_time = task->queue_time.value();
} else { } else {
schedule_time = delayed_task_start; schedule_time = delayed_task_start;
} }
......
...@@ -21,6 +21,7 @@ struct PendingTask; ...@@ -21,6 +21,7 @@ struct PendingTask;
namespace responsiveness { namespace responsiveness {
class Calculator; class Calculator;
class MessageLoopObserver;
// This class watches events and tasks processed on the UI and IO threads of the // This class watches events and tasks processed on the UI and IO threads of the
// browser process. It forwards stats on execution latency to Calculator, which // browser process. It forwards stats on execution latency to Calculator, which
...@@ -51,6 +52,8 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> { ...@@ -51,6 +52,8 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> {
// Exposed for tests. // Exposed for tests.
virtual std::unique_ptr<Calculator> MakeCalculator(); virtual std::unique_ptr<Calculator> MakeCalculator();
virtual ~Watcher(); virtual ~Watcher();
virtual void RegisterMessageLoopObserverUI();
virtual void RegisterMessageLoopObserverIO();
private: private:
friend class base::RefCounted<Watcher>; friend class base::RefCounted<Watcher>;
...@@ -60,10 +63,10 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> { ...@@ -60,10 +63,10 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> {
// Metadata for currently running tasks and events is needed to track whether // Metadata for currently running tasks and events is needed to track whether
// or not they caused reentrancy. // or not they caused reentrancy.
struct Metadata { struct Metadata {
explicit Metadata(void* identifier); explicit Metadata(const void* identifier);
// An opaque identifier for the task or event. // An opaque identifier for the task or event.
void* identifier = nullptr; const void* identifier = nullptr;
// Whether the task or event has caused reentrancy. // Whether the task or event has caused reentrancy.
bool caused_reentrancy = false; bool caused_reentrancy = false;
...@@ -80,32 +83,31 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> { ...@@ -80,32 +83,31 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> {
void TearDownOnIOThread(); void TearDownOnIOThread();
void TearDownOnUIThread(); void TearDownOnUIThread();
// TODO(erikchen): Implement MessageLoopObserver.
// These methods are called by the MessageLoopObserver of the UI thread to // These methods are called by the MessageLoopObserver of the UI thread to
// allow Watcher to collect metadata about the tasks being run. // allow Watcher to collect metadata about the tasks being run.
void WillRunTaskOnUIThread(base::PendingTask* task); void WillRunTaskOnUIThread(const base::PendingTask* task);
void DidRunTaskOnUIThread(base::PendingTask* task); void DidRunTaskOnUIThread(const base::PendingTask* task);
// TODO(erikchen): Implement MessageLoopObserver.
// These methods are called by the MessageLoopObserver of the IO thread to // These methods are called by the MessageLoopObserver of the IO thread to
// allow Watcher to collect metadata about the tasks being run. // allow Watcher to collect metadata about the tasks being run.
void WillRunTaskOnIOThread(base::PendingTask* task); void WillRunTaskOnIOThread(const base::PendingTask* task);
void DidRunTaskOnIOThread(base::PendingTask* task); void DidRunTaskOnIOThread(const base::PendingTask* task);
// Common implementations for the thread-specific methods. // Common implementations for the thread-specific methods.
void WillRunTask(base::PendingTask* task, void WillRunTask(const base::PendingTask* task,
std::stack<Metadata>* currently_running_metadata); std::stack<Metadata>* currently_running_metadata);
// |callback| will either be synchronously invoked, or else never invoked. // |callback| will either be synchronously invoked, or else never invoked.
using TaskOrEventFinishedCallback = using TaskOrEventFinishedCallback =
base::OnceCallback<void(base::TimeTicks, base::TimeTicks)>; base::OnceCallback<void(base::TimeTicks, base::TimeTicks)>;
void DidRunTask(base::PendingTask* task, void DidRunTask(const base::PendingTask* task,
std::stack<Metadata>* currently_running_metadata, std::stack<Metadata>* currently_running_metadata,
int* mismatched_task_identifiers, int* mismatched_task_identifiers,
TaskOrEventFinishedCallback callback); TaskOrEventFinishedCallback callback);
// The following members are all affine to the UI thread. // The following members are all affine to the UI thread.
std::unique_ptr<Calculator> calculator_; std::unique_ptr<Calculator> calculator_;
std::unique_ptr<MessageLoopObserver> message_loop_observer_ui_;
// Metadata for currently running tasks and events on the UI thread. // Metadata for currently running tasks and events on the UI thread.
std::stack<Metadata> currently_running_metadata_ui_; std::stack<Metadata> currently_running_metadata_ui_;
...@@ -118,6 +120,7 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> { ...@@ -118,6 +120,7 @@ class CONTENT_EXPORT Watcher : public base::RefCounted<Watcher> {
// The following members are all affine to the IO thread. // The following members are all affine to the IO thread.
std::stack<Metadata> currently_running_metadata_io_; std::stack<Metadata> currently_running_metadata_io_;
int mismatched_task_identifiers_io_ = 0; int mismatched_task_identifiers_io_ = 0;
std::unique_ptr<MessageLoopObserver> message_loop_observer_io_;
// The implementation of this class guarantees that |calculator_io_| will be // The implementation of this class guarantees that |calculator_io_| will be
// non-nullptr and point to a valid object any time it is used on the IO // non-nullptr and point to a valid object any time it is used on the IO
......
...@@ -6,7 +6,10 @@ ...@@ -6,7 +6,10 @@
#include "base/location.h" #include "base/location.h"
#include "base/pending_task.h" #include "base/pending_task.h"
#include "base/run_loop.h"
#include "base/synchronization/lock.h"
#include "content/browser/scheduler/responsiveness/calculator.h" #include "content/browser/scheduler/responsiveness/calculator.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/test/test_browser_thread_bundle.h" #include "content/public/test/test_browser_thread_bundle.h"
#include "testing/gtest/include/gtest/gtest.h" #include "testing/gtest/include/gtest/gtest.h"
...@@ -18,20 +21,30 @@ class FakeCalculator : public Calculator { ...@@ -18,20 +21,30 @@ class FakeCalculator : public Calculator {
public: public:
void TaskOrEventFinishedOnUIThread(base::TimeTicks schedule_time, void TaskOrEventFinishedOnUIThread(base::TimeTicks schedule_time,
base::TimeTicks finish_time) override { base::TimeTicks finish_time) override {
++tasks_on_ui_thread_; queue_times_ui_.push_back(schedule_time);
} }
void TaskOrEventFinishedOnIOThread(base::TimeTicks schedule_time, void TaskOrEventFinishedOnIOThread(base::TimeTicks schedule_time,
base::TimeTicks finish_time) override { base::TimeTicks finish_time) override {
++tasks_on_io_thread_; base::AutoLock l(io_thread_lock_);
queue_times_io_.push_back(schedule_time);
} }
int NumTasksOnUIThread() { return tasks_on_ui_thread_; } int NumTasksOnUIThread() { return static_cast<int>(queue_times_ui_.size()); }
int NumTasksOnIOThread() { return tasks_on_io_thread_; } std::vector<base::TimeTicks>& QueueTimesUIThread() { return queue_times_ui_; }
int NumTasksOnIOThread() {
base::AutoLock l(io_thread_lock_);
return static_cast<int>(queue_times_io_.size());
}
std::vector<base::TimeTicks>& QueueTimesIOThread() {
base::AutoLock l(io_thread_lock_);
return queue_times_io_;
}
private: private:
int tasks_on_ui_thread_ = 0; std::vector<base::TimeTicks> queue_times_ui_;
int tasks_on_io_thread_ = 0; base::Lock io_thread_lock_;
std::vector<base::TimeTicks> queue_times_io_;
}; };
class FakeWatcher : public Watcher { class FakeWatcher : public Watcher {
...@@ -43,14 +56,32 @@ class FakeWatcher : public Watcher { ...@@ -43,14 +56,32 @@ class FakeWatcher : public Watcher {
return calculator; return calculator;
} }
FakeWatcher() : Watcher() {} void RegisterMessageLoopObserverUI() override {
if (register_message_loop_observer_)
Watcher::RegisterMessageLoopObserverUI();
}
void RegisterMessageLoopObserverIO() override {
if (register_message_loop_observer_)
Watcher::RegisterMessageLoopObserverIO();
}
FakeWatcher(bool register_message_loop_observer)
: Watcher(),
register_message_loop_observer_(register_message_loop_observer) {}
int NumTasksOnUIThread() { return calculator_->NumTasksOnUIThread(); } int NumTasksOnUIThread() { return calculator_->NumTasksOnUIThread(); }
std::vector<base::TimeTicks>& QueueTimesUIThread() {
return calculator_->QueueTimesUIThread();
}
std::vector<base::TimeTicks>& QueueTimesIOThread() {
return calculator_->QueueTimesIOThread();
}
int NumTasksOnIOThread() { return calculator_->NumTasksOnIOThread(); } int NumTasksOnIOThread() { return calculator_->NumTasksOnIOThread(); }
private: private:
~FakeWatcher() override{}; ~FakeWatcher() override{};
FakeCalculator* calculator_ = nullptr; FakeCalculator* calculator_ = nullptr;
bool register_message_loop_observer_ = false;
}; };
} // namespace } // namespace
...@@ -60,7 +91,8 @@ class ResponsivenessWatcherTest : public testing::Test { ...@@ -60,7 +91,8 @@ class ResponsivenessWatcherTest : public testing::Test {
void SetUp() override { void SetUp() override {
// Watcher's constructor posts a task to IO thread, which in the unit test // Watcher's constructor posts a task to IO thread, which in the unit test
// is also this thread. Regardless, we need to let those tasks finish. // is also this thread. Regardless, we need to let those tasks finish.
watcher_ = scoped_refptr<FakeWatcher>(new FakeWatcher); watcher_ = scoped_refptr<FakeWatcher>(
new FakeWatcher(/*register_message_loop_observer=*/false));
watcher_->SetUp(); watcher_->SetUp();
test_browser_thread_bundle_.RunUntilIdle(); test_browser_thread_bundle_.RunUntilIdle();
} }
...@@ -82,6 +114,7 @@ class ResponsivenessWatcherTest : public testing::Test { ...@@ -82,6 +114,7 @@ class ResponsivenessWatcherTest : public testing::Test {
TEST_F(ResponsivenessWatcherTest, TaskForwarding) { TEST_F(ResponsivenessWatcherTest, TaskForwarding) {
for (int i = 0; i < 3; ++i) { for (int i = 0; i < 3; ++i) {
base::PendingTask task(FROM_HERE, base::OnceClosure()); base::PendingTask task(FROM_HERE, base::OnceClosure());
task.queue_time = base::TimeTicks::Now();
watcher_->WillRunTaskOnUIThread(&task); watcher_->WillRunTaskOnUIThread(&task);
watcher_->DidRunTaskOnUIThread(&task); watcher_->DidRunTaskOnUIThread(&task);
} }
...@@ -90,6 +123,7 @@ TEST_F(ResponsivenessWatcherTest, TaskForwarding) { ...@@ -90,6 +123,7 @@ TEST_F(ResponsivenessWatcherTest, TaskForwarding) {
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
base::PendingTask task(FROM_HERE, base::OnceClosure()); base::PendingTask task(FROM_HERE, base::OnceClosure());
task.queue_time = base::TimeTicks::Now();
watcher_->WillRunTaskOnIOThread(&task); watcher_->WillRunTaskOnIOThread(&task);
watcher_->DidRunTaskOnIOThread(&task); watcher_->DidRunTaskOnIOThread(&task);
} }
...@@ -99,11 +133,14 @@ TEST_F(ResponsivenessWatcherTest, TaskForwarding) { ...@@ -99,11 +133,14 @@ TEST_F(ResponsivenessWatcherTest, TaskForwarding) {
// Test that nested tasks are not forwarded to the calculator. // Test that nested tasks are not forwarded to the calculator.
TEST_F(ResponsivenessWatcherTest, TaskNesting) { TEST_F(ResponsivenessWatcherTest, TaskNesting) {
// TODO(erikchen): Check that the right task is forwarded to the calculator. base::TimeTicks now = base::TimeTicks::Now();
// Requires implementation of PendingTask::queue_time.
base::PendingTask task1(FROM_HERE, base::OnceClosure()); base::PendingTask task1(FROM_HERE, base::OnceClosure());
task1.queue_time = now + base::TimeDelta::FromMilliseconds(1);
base::PendingTask task2(FROM_HERE, base::OnceClosure()); base::PendingTask task2(FROM_HERE, base::OnceClosure());
task2.queue_time = now + base::TimeDelta::FromMilliseconds(2);
base::PendingTask task3(FROM_HERE, base::OnceClosure()); base::PendingTask task3(FROM_HERE, base::OnceClosure());
task3.queue_time = now + base::TimeDelta::FromMilliseconds(3);
watcher_->WillRunTaskOnUIThread(&task1); watcher_->WillRunTaskOnUIThread(&task1);
watcher_->WillRunTaskOnUIThread(&task2); watcher_->WillRunTaskOnUIThread(&task2);
...@@ -112,8 +149,74 @@ TEST_F(ResponsivenessWatcherTest, TaskNesting) { ...@@ -112,8 +149,74 @@ TEST_F(ResponsivenessWatcherTest, TaskNesting) {
watcher_->DidRunTaskOnUIThread(&task2); watcher_->DidRunTaskOnUIThread(&task2);
watcher_->DidRunTaskOnUIThread(&task1); watcher_->DidRunTaskOnUIThread(&task1);
EXPECT_EQ(1, watcher_->NumTasksOnUIThread()); ASSERT_EQ(1, watcher_->NumTasksOnUIThread());
// The innermost task should be the one that is passed through, as it didn't
// cause reentrancy.
EXPECT_EQ(now + base::TimeDelta::FromMilliseconds(3),
watcher_->QueueTimesUIThread()[0]);
EXPECT_EQ(0, watcher_->NumTasksOnIOThread()); EXPECT_EQ(0, watcher_->NumTasksOnIOThread());
} }
class ResponsivenessWatcherRealIOThreadTest : public testing::Test {
public:
ResponsivenessWatcherRealIOThreadTest()
: test_browser_thread_bundle_(
content::TestBrowserThreadBundle::REAL_IO_THREAD) {}
void SetUp() override {
// Watcher's constructor posts a task to IO thread. We need to let those
// tasks finish.
watcher_ = scoped_refptr<FakeWatcher>(
new FakeWatcher(/*register_message_loop_observer=*/true));
watcher_->SetUp();
test_browser_thread_bundle_.RunIOThreadUntilIdle();
}
void TearDown() override {
watcher_->Destroy();
watcher_.reset();
// Destroy a task onto the IO thread, which posts back to the UI thread
// to complete destruction.
test_browser_thread_bundle_.RunIOThreadUntilIdle();
test_browser_thread_bundle_.RunUntilIdle();
}
protected:
// This member sets up BrowserThread::IO and BrowserThread::UI. It must be the
// first member, as other members may depend on these abstractions.
content::TestBrowserThreadBundle test_browser_thread_bundle_;
scoped_refptr<FakeWatcher> watcher_;
};
TEST_F(ResponsivenessWatcherRealIOThreadTest, MessageLoopObserver) {
// Post a do-nothing task onto the UI thread.
content::BrowserThread::PostTask(content::BrowserThread::UI, FROM_HERE,
base::BindOnce([]() {}));
// Post a do-nothing task onto the IO thread.
content::BrowserThread::PostTask(content::BrowserThread::IO, FROM_HERE,
base::BindOnce([]() {}));
// Post a task onto the IO thread that hops back to the UI thread. This
// guarantees that both of the do-nothing tasks have already been processed.
base::RunLoop run_loop;
content::BrowserThread::PostTask(
content::BrowserThread::IO, FROM_HERE,
base::BindOnce(
[](base::OnceClosure quit_closure) {
content::BrowserThread::PostTask(
content::BrowserThread::UI, FROM_HERE, std::move(quit_closure));
},
run_loop.QuitClosure()));
run_loop.Run();
ASSERT_GE(watcher_->NumTasksOnUIThread(), 1);
EXPECT_FALSE(watcher_->QueueTimesUIThread()[0].is_null());
ASSERT_GE(watcher_->NumTasksOnIOThread(), 1);
EXPECT_FALSE(watcher_->QueueTimesIOThread()[0].is_null());
}
} // namespace responsiveness } // namespace responsiveness
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment