File: after_startup_task_utils.cc

package info (click to toggle)
chromium 138.0.7204.157-1
  • links: PTS, VCS
  • area: main
  • in suites: sid, trixie
  • size: 6,071,864 kB
  • sloc: cpp: 34,936,859; ansic: 7,176,967; javascript: 4,110,704; python: 1,419,953; asm: 946,768; xml: 739,967; pascal: 187,324; sh: 89,623; perl: 88,663; objc: 79,944; sql: 50,304; cs: 41,786; fortran: 24,137; makefile: 21,806; php: 13,980; tcl: 13,166; yacc: 8,925; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (257 lines) | stat: -rw-r--r-- 8,742 bytes parent folder | download | duplicates (4)
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());
}