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 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
|
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/after_startup_task_utils.h"
#include "base/containers/circular_deque.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram_macros.h"
#include "base/no_destructor.h"
#include "base/process/process.h"
#include "base/synchronization/atomic_flag.h"
#include "base/task/sequenced_task_runner.h"
#include "base/trace_event/trace_event.h"
#include "build/build_config.h"
#include "chrome/browser/ui/browser_finder.h"
#include "components/performance_manager/public/graph/graph.h"
#include "components/performance_manager/public/graph/page_node.h"
#include "components/performance_manager/public/performance_manager.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/ui/ash/login/login_display_host.h"
#endif
using content::BrowserThread;
namespace {
using performance_manager::PerformanceManager;
struct AfterStartupTask {
AfterStartupTask(const base::Location& from_here,
const scoped_refptr<base::SequencedTaskRunner>& task_runner,
base::OnceClosure task)
: from_here(from_here), task_runner(task_runner), task(std::move(task)) {}
~AfterStartupTask() = default;
const base::Location from_here;
const scoped_refptr<base::SequencedTaskRunner> task_runner;
base::OnceClosure task;
};
// The flag may be read on any thread, but must only be set on the UI thread.
base::AtomicFlag& GetStartupCompleteFlag() {
static base::NoDestructor<base::AtomicFlag> startup_complete_flag;
return *startup_complete_flag;
}
// The queue may only be accessed on the UI thread.
base::circular_deque<AfterStartupTask*>& GetAfterStartupTasks() {
static base::NoDestructor<base::circular_deque<AfterStartupTask*>>
after_startup_tasks;
return *after_startup_tasks;
}
bool IsBrowserStartupComplete() {
return GetStartupCompleteFlag().IsSet();
}
void RunTask(std::unique_ptr<AfterStartupTask> queued_task) {
// We're careful to delete the caller's |task| on the target runner's thread.
DCHECK(queued_task->task_runner->RunsTasksInCurrentSequence());
std::move(queued_task->task).Run();
}
void ScheduleTask(std::unique_ptr<AfterStartupTask> queued_task) {
scoped_refptr<base::SequencedTaskRunner> target_runner =
queued_task->task_runner;
base::Location from_here = queued_task->from_here;
target_runner->PostTask(from_here,
base::BindOnce(&RunTask, std::move(queued_task)));
}
void QueueTask(std::unique_ptr<AfterStartupTask> queued_task) {
DCHECK(queued_task);
// Use CHECK instead of DCHECK to crash earlier. See http://crbug.com/711167
// for details.
CHECK(queued_task->task);
if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
// Posted with USER_VISIBLE priority to avoid this becoming an after startup
// task itself.
content::GetUIThreadTaskRunner({base::TaskPriority::USER_VISIBLE})
->PostTask(FROM_HERE,
base::BindOnce(QueueTask, std::move(queued_task)));
return;
}
// The flag may have been set while the task to invoke this method
// on the UI thread was inflight.
if (IsBrowserStartupComplete()) {
ScheduleTask(std::move(queued_task));
return;
}
GetAfterStartupTasks().push_back(queued_task.release());
}
void SetBrowserStartupIsComplete() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (IsBrowserStartupComplete())
return;
size_t browser_count = 0;
#if !BUILDFLAG(IS_ANDROID)
browser_count = chrome::GetTotalBrowserCount();
#endif // !BUILDFLAG(IS_ANDROID)
TRACE_EVENT_INSTANT1("startup", "Startup.StartupComplete",
TRACE_EVENT_SCOPE_GLOBAL, "BrowserCount", browser_count);
GetStartupCompleteFlag().Set();
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) || \
BUILDFLAG(IS_CHROMEOS)
// Process::Current().CreationTime() is not available on all platforms.
const base::Time process_creation_time =
base::Process::Current().CreationTime();
if (!process_creation_time.is_null()) {
UMA_HISTOGRAM_LONG_TIMES("Startup.AfterStartupTaskDelayedUntilTime",
base::Time::Now() - process_creation_time);
}
#endif // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN) || BUILDFLAG(IS_LINUX) ||
// BUILDFLAG(IS_CHROMEOS)
UMA_HISTOGRAM_COUNTS_10000("Startup.AfterStartupTaskCount",
GetAfterStartupTasks().size());
for (AfterStartupTask* queued_task : GetAfterStartupTasks()) {
ScheduleTask(base::WrapUnique(queued_task));
}
GetAfterStartupTasks().clear();
GetAfterStartupTasks().shrink_to_fit();
}
// Observes the first visible page load and sets the startup complete
// flag accordingly. Ownership is passed to the Performance Manager
// after creation.
class StartupObserver : public performance_manager::GraphOwned,
public performance_manager::PageNodeObserver {
public:
StartupObserver(const StartupObserver&) = delete;
StartupObserver& operator=(const StartupObserver&) = delete;
~StartupObserver() override = default;
static void Start();
private:
using LoadingState = performance_manager::PageNode::LoadingState;
StartupObserver() = default;
void OnStartupComplete() {
CHECK(PerformanceManager::IsAvailable());
SetBrowserStartupIsComplete();
// This will result in delete getting called.
TakeFromGraph();
}
// GraphOwned overrides
void OnPassedToGraph(performance_manager::Graph* graph) override {
graph->AddPageNodeObserver(this);
}
void OnTakenFromGraph(performance_manager::Graph* graph) override {
graph->RemovePageNodeObserver(this);
}
// PageNodeObserver overrides
void OnLoadingStateChanged(const performance_manager::PageNode* page_node,
LoadingState previous_state) override {
// Only interested in visible PageNodes
if (page_node->IsVisible()) {
if (page_node->GetLoadingState() == LoadingState::kLoadedIdle ||
page_node->GetLoadingState() == LoadingState::kLoadingTimedOut) {
OnStartupComplete();
}
}
}
void TakeFromGraph() {
// Remove this object from the performance manager. This will
// cause the object to be deleted.
CHECK(PerformanceManager::IsAvailable());
PerformanceManager::GetGraph()->TakeFromGraph(this);
}
};
// static
void StartupObserver::Start() {
CHECK(PerformanceManager::IsAvailable());
// Pass a new StartupObserver to the performance manager so we can get
// notified when loading completes. The performance manager takes ownership.
PerformanceManager::GetGraph()->PassToGraph(
base::WrapUnique(new StartupObserver()));
}
} // namespace
void AfterStartupTaskUtils::StartMonitoringStartup() {
#if BUILDFLAG(IS_CHROMEOS)
// If we are on a login screen which does not expect WebUI to be loaded,
// Browser won't be created at startup.
if (ash::LoginDisplayHost::default_host() &&
!ash::LoginDisplayHost::default_host()->IsWebUIStarted()) {
content::GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(&SetBrowserStartupIsComplete));
return;
}
#endif
// For Android, startup completion is signaled via
// AfterStartupTaskUtils.java. We do not use the StartupObserver.
#if !BUILDFLAG(IS_ANDROID)
StartupObserver::Start();
#endif // !BUILDFLAG(IS_ANDROID)
// Add failsafe timeout
content::GetUIThreadTaskRunner({})->PostDelayedTask(
FROM_HERE, base::BindOnce(&SetBrowserStartupIsComplete),
base::Minutes(3));
}
void AfterStartupTaskUtils::PostTask(
const base::Location& from_here,
const scoped_refptr<base::SequencedTaskRunner>& destination_runner,
base::OnceClosure task) {
if (IsBrowserStartupComplete()) {
destination_runner->PostTask(from_here, std::move(task));
return;
}
std::unique_ptr<AfterStartupTask> queued_task(
new AfterStartupTask(from_here, destination_runner, std::move(task)));
QueueTask(std::move(queued_task));
}
void AfterStartupTaskUtils::SetBrowserStartupIsCompleteForTesting() {
::SetBrowserStartupIsComplete();
}
void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
::SetBrowserStartupIsComplete();
}
bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
return ::IsBrowserStartupComplete();
}
void AfterStartupTaskUtils::UnsafeResetForTesting() {
DCHECK(GetAfterStartupTasks().empty());
if (!IsBrowserStartupComplete())
return;
GetStartupCompleteFlag().UnsafeResetForTesting(); // IN-TEST
DCHECK(!IsBrowserStartupComplete());
}
|