Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(20)

Side by Side Diff: chrome/browser/after_startup_task_utils.cc

Issue 949293002: Implement a poor man's PostAfterStartupTask() function. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/after_startup_task_utils.h"
6
7 #include "base/lazy_instance.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/metrics/histogram_macros.h"
10 #include "base/process/process_info.h"
11 #include "base/rand_util.h"
12 #include "base/synchronization/cancellation_flag.h"
13 #include "base/task_runner.h"
14 #include "base/tracked_objects.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_iterator.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/browser/render_frame_host.h"
20 #include "content/public/browser/web_contents.h"
21 #include "content/public/browser/web_contents_observer.h"
22
23 using content::BrowserThread;
24 using content::WebContents;
25 using content::WebContentsObserver;
26 using StartupCompleteFlag = base::CancellationFlag;
Nico 2015/04/09 20:10:15 This is used in a single place. Why is this useful
michaeln 2015/04/09 21:48:39 This should help tip readers off to the fact that
27
28 namespace {
29
30 struct AfterStartupTask {
31 AfterStartupTask(const tracked_objects::Location& from_here,
32 const scoped_refptr<base::TaskRunner>& task_runner,
33 const base::Closure& task)
34 : from_here(from_here), task_runner(task_runner), task(task) {}
35 ~AfterStartupTask() {}
36
37 const tracked_objects::Location from_here;
38 const scoped_refptr<base::TaskRunner> task_runner;
39 const base::Closure task;
40 };
41
42 // The flag may be read on any thread, but must only be set on the UI thread.
43 base::LazyInstance<StartupCompleteFlag>::Leaky g_startup_complete_flag;
44
45 // The queue may only be accessed on the UI thread.
46 base::LazyInstance<std::deque<AfterStartupTask*>>::Leaky g_after_startup_tasks;
47
48 bool IsBrowserStartupComplete() {
49 // Be sure to allocate the flag on the main thread.
Nico 2015/04/09 20:10:15 (i found this comment confusing. i get it now, but
michaeln 2015/04/09 21:48:39 Done how about 'initialize the lazyinstance on th
50 if (g_startup_complete_flag == nullptr)
51 return false;
52 return g_startup_complete_flag.Get().IsSet();
53 }
54
55 void RunTask(scoped_ptr<AfterStartupTask> queued_task) {
56 // We're careful to delete the caller's |task| on the target runner's thread.
57 DCHECK(queued_task->task_runner->RunsTasksOnCurrentThread());
58 queued_task->task.Run();
59 }
60
61 void ScheduleTask(scoped_ptr<AfterStartupTask> queued_task) {
62 // Spread their execution over a brief time.
63 const int kMinDelay = 0;
64 const int kMaxDelay = 10;
Nico 2015/04/09 20:10:15 suffix name with unit (kMinDelaySec, kMaxDelaySec)
michaeln 2015/04/09 21:48:39 Done.
65 scoped_refptr<base::TaskRunner> target_runner = queued_task->task_runner;
66 tracked_objects::Location from_here = queued_task->from_here;
67 target_runner->PostDelayedTask(
68 from_here, base::Bind(&RunTask, base::Passed(queued_task.Pass())),
69 base::TimeDelta::FromSeconds(base::RandInt(kMinDelay, kMaxDelay)));
70 }
71
72 void QueueTask(scoped_ptr<AfterStartupTask> queued_task) {
73 if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) {
74 BrowserThread::PostTask(
75 BrowserThread::UI, FROM_HERE,
76 base::Bind(QueueTask, base::Passed(queued_task.Pass())));
77 return;
78 }
79
80 // The flag may have been set while the task to invoke this method
81 // on the UI thread was inflight.
82 if (IsBrowserStartupComplete()) {
83 ScheduleTask(queued_task.Pass());
84 return;
85 }
86 g_after_startup_tasks.Get().push_back(queued_task.release());
87 }
88
89 void SetBrowserStartupIsComplete() {
90 DCHECK_CURRENTLY_ON(BrowserThread::UI);
91 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
92 // CurrentProcessInfo::CreationTime() is not available on all platforms.
93 const base::Time process_creation_time =
94 base::CurrentProcessInfo::CreationTime();
95 if (!process_creation_time.is_null()) {
96 UMA_HISTOGRAM_LONG_TIMES("AfterStartupTasks.DelayedUntilTime",
97 base::Time::Now() - process_creation_time);
98 }
99 #endif // defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
100 UMA_HISTOGRAM_COUNTS_10000("AfterStartupTasks.Count",
101 g_after_startup_tasks.Get().size());
102 g_startup_complete_flag.Get().Set();
103 for (AfterStartupTask* queued_task : g_after_startup_tasks.Get())
104 ScheduleTask(make_scoped_ptr(queued_task));
105 g_after_startup_tasks.Get().clear();
106
107 // The shrink_to_fit() method is not available for all of our build targets.
108 std::deque<AfterStartupTask*>(g_after_startup_tasks.Get())
109 .swap(g_after_startup_tasks.Get());
110 }
111
112 // Observes the first visible page load and sets the startup complete
113 // flag accordingly.
114 class StartupObserver : public WebContentsObserver, public base::NonThreadSafe {
115 public:
116 StartupObserver() : weak_factory_(this) {}
117 ~StartupObserver() override { DCHECK(IsBrowserStartupComplete()); }
118
119 void Start();
120
121 private:
122 void OnStartupComplete() {
123 DCHECK(CalledOnValidThread());
124 SetBrowserStartupIsComplete();
125 delete this;
126 }
127
128 void OnFailsafeTimeout() { OnStartupComplete(); }
129
130 // WebContentsObserver overrides
131 void DidFinishLoad(content::RenderFrameHost* render_frame_host,
132 const GURL& validated_url) override {
133 if (!render_frame_host->GetParent())
134 OnStartupComplete();
135 }
136
137 void DidFailLoad(content::RenderFrameHost* render_frame_host,
138 const GURL& validated_url,
139 int error_code,
140 const base::string16& error_description) override {
141 if (!render_frame_host->GetParent())
142 OnStartupComplete();
143 }
144
145 void WebContentsDestroyed() override { OnStartupComplete(); }
146
147 base::WeakPtrFactory<StartupObserver> weak_factory_;
148
149 DISALLOW_COPY_AND_ASSIGN(StartupObserver);
150 };
151
152 void StartupObserver::Start() {
153 // Signal completion quickly when there is no first page to load.
154 const int kShortDelaySecs = 3;
155 base::TimeDelta delay = base::TimeDelta::FromSeconds(kShortDelaySecs);
156
157 #if !defined(OS_ANDROID)
158 WebContents* contents = nullptr;
159 for (chrome::BrowserIterator iter; !iter.done(); iter.Next()) {
160 contents = (*iter)->tab_strip_model()->GetActiveWebContents();
161 if (contents && contents->GetMainFrame() &&
162 contents->GetMainFrame()->GetVisibilityState() ==
163 blink::WebPageVisibilityStateVisible) {
164 break;
165 }
166 }
167
168 if (contents) {
169 // Give the page time to finish loading.
170 const int kLongerDelayMins = 3;
171 Observe(contents);
172 delay = base::TimeDelta::FromMinutes(kLongerDelayMins);
173 }
174 #endif // !defined(OS_ANDROID)
Nico 2015/04/09 20:10:15 shouldn't we default to more than 3 unconditional
michaeln 2015/04/09 21:48:39 i think android should probably monitor the initia
175
176 BrowserThread::PostDelayedTask(BrowserThread::UI, FROM_HERE,
177 base::Bind(&StartupObserver::OnFailsafeTimeout,
178 weak_factory_.GetWeakPtr()),
179 delay);
180 }
181
182 } // namespace
183
184 void AfterStartupTaskUtils::StartMonitoringStartup() {
185 // The observer is self-deleting.
186 (new StartupObserver)->Start();
187 }
188
189 void AfterStartupTaskUtils::PostTask(
190 const tracked_objects::Location& from_here,
191 const scoped_refptr<base::TaskRunner>& task_runner,
192 const base::Closure& task) {
193 if (IsBrowserStartupComplete()) {
194 task_runner->PostTask(from_here, task);
195 return;
196 }
197
198 scoped_ptr<AfterStartupTask> queued_task(
199 new AfterStartupTask(from_here, task_runner, task));
200 QueueTask(queued_task.Pass());
201 }
202
203 void AfterStartupTaskUtils::SetBrowserStartupIsComplete() {
204 ::SetBrowserStartupIsComplete();
205 }
206
207 bool AfterStartupTaskUtils::IsBrowserStartupComplete() {
208 return ::IsBrowserStartupComplete();
209 }
210
211 void AfterStartupTaskUtils::UnsafeResetForTesting() {
212 DCHECK(g_after_startup_tasks.Get().empty());
213 if (!IsBrowserStartupComplete())
214 return;
215 g_startup_complete_flag.Get().UnsafeResetForTesting();
216 DCHECK(!IsBrowserStartupComplete());
217 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698