File: spare_render_process_host_manager.cc

package info (click to toggle)
chromium 120.0.6099.224-1~deb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,112,112 kB
  • sloc: cpp: 32,907,025; ansic: 8,148,123; javascript: 3,679,536; python: 2,031,248; asm: 959,718; java: 804,675; xml: 617,256; sh: 111,417; objc: 100,835; perl: 88,443; cs: 53,032; makefile: 29,579; fortran: 24,137; php: 21,162; tcl: 21,147; sql: 20,809; ruby: 17,735; pascal: 12,864; yacc: 8,045; lisp: 3,388; lex: 1,323; ada: 727; awk: 329; jsp: 267; csh: 117; exp: 43; sed: 37
file content (251 lines) | stat: -rw-r--r-- 10,615 bytes parent folder | download | duplicates (2)
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
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/browser/renderer_host/spare_render_process_host_manager.h"

#include "base/check.h"
#include "base/debug/dump_without_crashing.h"
#include "base/memory/memory_pressure_monitor.h"
#include "base/no_destructor.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/site_instance_impl.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_client.h"

namespace content {

SpareRenderProcessHostManager::SpareRenderProcessHostManager() = default;
SpareRenderProcessHostManager::~SpareRenderProcessHostManager() = default;

// static
SpareRenderProcessHostManager& SpareRenderProcessHostManager::GetInstance() {
  static base::NoDestructor<SpareRenderProcessHostManager> s_instance;
  return *s_instance;
}

void SpareRenderProcessHostManager::WarmupSpareRenderProcessHost(
    BrowserContext* browser_context) {
  if (spare_render_process_host_ &&
      spare_render_process_host_->GetBrowserContext() == browser_context) {
    DCHECK_EQ(browser_context->GetDefaultStoragePartition(),
              spare_render_process_host_->GetStoragePartition());
    return;  // Nothing to warm up.
  }

  CleanupSpareRenderProcessHost();

  // Don't create a spare renderer for a BrowserContext that is in the
  // process of shutting down.
  if (browser_context->ShutdownStarted()) {
    // Create a crash dump to help us assess what scenarios trigger this
    // path to be taken.
    // TODO(acolwell): Remove this call once are confident we've eliminated
    // any problematic callers.
    base::debug::DumpWithoutCrashing();

    return;
  }

  // Don't create a spare renderer if we're using --single-process or if we've
  // got too many processes. See also ShouldTryToUseExistingProcessHost in
  // this file.
  if (RenderProcessHost::run_renderer_in_process() ||
      RenderProcessHostImpl::GetProcessCountForLimit() >=
          RenderProcessHostImpl::GetMaxRendererProcessCount()) {
    return;
  }

  // Don't create a spare renderer when the system is under load.  This is
  // currently approximated by only looking at the memory pressure.  See also
  // https://crbug.com/852905.
  auto* memory_monitor = base::MemoryPressureMonitor::Get();
  if (memory_monitor &&
      memory_monitor->GetCurrentPressureLevel() >=
          base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE) {
    return;
  }

  spare_render_process_host_ = RenderProcessHostImpl::CreateRenderProcessHost(
      browser_context, nullptr /* site_instance */);
  spare_render_process_host_->AddObserver(this);
  spare_render_process_host_->Init();

  // The spare render process isn't ready, so wait and do the "spare render
  // process changed" callback in RenderProcessReady().
}

RenderProcessHost*
SpareRenderProcessHostManager::MaybeTakeSpareRenderProcessHost(
    BrowserContext* browser_context,
    SiteInstanceImpl* site_instance) {
  // Give embedder a chance to disable using a spare RenderProcessHost for
  // certain SiteInstances.  Some navigations, such as to NTP or extensions,
  // require passing command-line flags to the renderer process at process
  // launch time, but this cannot be done for spare RenderProcessHosts, which
  // are started before it is known which navigation might use them.  So, a
  // spare RenderProcessHost should not be used in such cases.
  //
  // Note that exempting NTP and extensions from using the spare process might
  // also happen via HasProcess check below (which returns true for
  // process-per-site SiteInstances if the given process-per-site process
  // already exists).  Despite this potential overlap, it is important to do
  // both kinds of checks (to account for other non-ntp/extension
  // process-per-site scenarios + to work correctly even if
  // ShouldUseSpareRenderProcessHost starts covering non-process-per-site
  // scenarios).
  bool embedder_allows_spare_usage =
      GetContentClient()->browser()->ShouldUseSpareRenderProcessHost(
          browser_context, site_instance->GetSiteInfo().site_url());

  // The spare RenderProcessHost always launches with JIT enabled, so if JIT
  // is disabled for the site then it's not possible to use this as the JIT
  // policy will differ.
  if (GetContentClient()->browser()->IsJitDisabledForSite(
          browser_context, site_instance->GetSiteInfo().process_lock_url())) {
    embedder_allows_spare_usage = false;
  }

  // We shouldn't use the spare if:
  // 1. The SiteInstance has already got an associated process.  This is
  //    important to avoid taking and then immediately discarding the spare
  //    for process-per-site scenarios (which the HasProcess call below
  //    accounts for).  Note that HasProcess will return false and allow using
  //    the spare if the given process-per-site process hasn't been launched.
  // 2. The SiteInstance has opted out of using the spare process.
  bool site_instance_allows_spare_usage =
      !site_instance->HasProcess() &&
      site_instance->CanAssociateWithSpareProcess();

  bool hosts_pdf_content = site_instance->GetSiteInfo().is_pdf();

  // Get the StoragePartition for |site_instance|.  Note that this might be
  // different than the default StoragePartition for |browser_context|.
  StoragePartition* site_storage =
      browser_context->GetStoragePartition(site_instance);

  // Log UMA metrics.
  using SpareProcessMaybeTakeAction =
      RenderProcessHostImpl::SpareProcessMaybeTakeAction;
  SpareProcessMaybeTakeAction action =
      SpareProcessMaybeTakeAction::kNoSparePresent;
  if (!spare_render_process_host_) {
    action = SpareProcessMaybeTakeAction::kNoSparePresent;
  } else if (browser_context !=
             spare_render_process_host_->GetBrowserContext()) {
    action = SpareProcessMaybeTakeAction::kMismatchedBrowserContext;
  } else if (!spare_render_process_host_->InSameStoragePartition(
                 site_storage)) {
    action = SpareProcessMaybeTakeAction::kMismatchedStoragePartition;
  } else if (!embedder_allows_spare_usage) {
    action = SpareProcessMaybeTakeAction::kRefusedByEmbedder;
  } else if (!site_instance_allows_spare_usage) {
    action = SpareProcessMaybeTakeAction::kRefusedBySiteInstance;
  } else if (hosts_pdf_content) {
    action = SpareProcessMaybeTakeAction::kRefusedForPdfContent;
  } else {
    action = SpareProcessMaybeTakeAction::kSpareTaken;
  }
  UMA_HISTOGRAM_ENUMERATION(
      "BrowserRenderProcessHost.SpareProcessMaybeTakeAction", action);

  // Decide whether to take or drop the spare process.
  RenderProcessHost* returned_process = nullptr;
  if (spare_render_process_host_ &&
      browser_context == spare_render_process_host_->GetBrowserContext() &&
      spare_render_process_host_->InSameStoragePartition(site_storage) &&
      !site_instance->IsGuest() && embedder_allows_spare_usage &&
      site_instance_allows_spare_usage && !hosts_pdf_content) {
    CHECK(spare_render_process_host_->HostHasNotBeenUsed());

    // If the spare process ends up getting killed, the spare manager should
    // discard the spare RPH, so if one exists, it should always be live here.
    CHECK(spare_render_process_host_->IsInitializedAndNotDead());

    DCHECK_EQ(SpareProcessMaybeTakeAction::kSpareTaken, action);
    returned_process = spare_render_process_host_;
    ReleaseSpareRenderProcessHost();
  } else if (!RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) {
    // If the spare shouldn't be kept around, then discard it as soon as we
    // find that the current spare was mismatched.
    CleanupSpareRenderProcessHost();
  } else if (RenderProcessHostImpl::GetProcessCountForLimit() >=
             RenderProcessHostImpl::GetMaxRendererProcessCount()) {
    // Drop the spare if we are at a process limit and the spare wasn't taken.
    // This helps avoid process reuse.
    CleanupSpareRenderProcessHost();
  }

  return returned_process;
}

void SpareRenderProcessHostManager::PrepareForFutureRequests(
    BrowserContext* browser_context) {
  if (RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) {
    // Always keep around a spare process for the most recently requested
    // |browser_context|.
    WarmupSpareRenderProcessHost(browser_context);
  } else {
    // Discard the ignored (probably non-matching) spare so as not to waste
    // resources.
    CleanupSpareRenderProcessHost();
  }
}

void SpareRenderProcessHostManager::CleanupSpareRenderProcessHost() {
  if (spare_render_process_host_) {
    // Stop observing the process, to avoid getting notifications as a
    // consequence of the Cleanup call below - such notification could call
    // back into CleanupSpareRenderProcessHost leading to stack overflow.
    spare_render_process_host_->RemoveObserver(this);

    // Make sure the RenderProcessHost object gets destroyed.
    if (!spare_render_process_host_->AreRefCountsDisabled()) {
      spare_render_process_host_->Cleanup();
    }

    // Drop reference to the RenderProcessHost object.
    spare_render_process_host_ = nullptr;
    spare_render_process_host_changed_callback_list_.Notify(nullptr);
  }
}

base::CallbackListSubscription
SpareRenderProcessHostManager::RegisterSpareRenderProcessHostChangedCallback(
    const base::RepeatingCallback<void(RenderProcessHost*)>& cb) {
  // Do an initial notification, as the subscriber will need to know what the
  // current spare host is.
  cb.Run(spare_render_process_host_.get());
  return spare_render_process_host_changed_callback_list_.Add(cb);
}

void SpareRenderProcessHostManager::ReleaseSpareRenderProcessHost() {
  CHECK(spare_render_process_host_);

  spare_render_process_host_->RemoveObserver(this);
  spare_render_process_host_ = nullptr;
  spare_render_process_host_changed_callback_list_.Notify(nullptr);
}

void SpareRenderProcessHostManager::RenderProcessReady(
    RenderProcessHost* host) {
  CHECK_EQ(spare_render_process_host_, host);
  spare_render_process_host_changed_callback_list_.Notify(
      spare_render_process_host_);
}

void SpareRenderProcessHostManager::RenderProcessExited(
    RenderProcessHost* host,
    const ChildProcessTerminationInfo& info) {
  CHECK_EQ(spare_render_process_host_, host);
  CleanupSpareRenderProcessHost();
}

void SpareRenderProcessHostManager::RenderProcessHostDestroyed(
    RenderProcessHost* host) {
  CHECK_EQ(spare_render_process_host_, host);
  ReleaseSpareRenderProcessHost();
}

}  // namespace content