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 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577
|
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CONTENT_BROWSER_INTEREST_GROUP_AUCTION_PROCESS_MANAGER_H_
#define CONTENT_BROWSER_INTEREST_GROUP_AUCTION_PROCESS_MANAGER_H_
#include <cstddef>
#include <list>
#include <map>
#include <memory>
#include <optional>
#include <set>
#include <string>
#include "base/functional/callback.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/process/process_handle.h"
#include "base/timer/timer.h"
#include "content/common/content_export.h"
#include "content/public/browser/render_process_host_observer.h"
#include "content/services/auction_worklet/public/mojom/auction_worklet_service.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "url/origin.h"
namespace base {
class Process;
} // namespace base
namespace content {
class RenderProcessHost;
class SiteInstance;
class TrustedSignalsCacheImpl;
// Base class of per-StoragePartition manager of auction bidder and seller
// worklet processes. This provides limiting and sharing of worker processes.
//
// AuctionProcessManager managers two types of processes, idle processes, and
// non-idle processes.
//
// Idle processes are owned directly by AuctionProcessManager::idle_processes_,
// and have no associated ProcessHandle -- they have a WorkletProcess only. On
// process crash or idle timeout, they tell the AuctionProcessManager to destroy
// them.
//
// Non-idle processes have been handed out to one or more live ProcessHandles,
// and are tracked in one of the AuctionProcessManager's ProcessMap with
// raw pointers. When the last ProcessHandle releases a reference to the
// WorkletProcess, it's destroyed, and informs the AuctionProcessManager to
// remove it from the map. On process crash, it may also be removed from the
// map, to prevent reuse, even though consumers may still own references to it.
class CONTENT_EXPORT AuctionProcessManager {
public:
// The maximum number of bidder processes. Once this number is reached, no
// processes will be created for bidder worklets, though new bidder worklet
// requests can receive pre-existing processes.
static const size_t kMaxBidderProcesses;
// The maximum number of seller processes. Once this number is reached, no
// processes will be created for seller worklets, though new seller worklet
// requests can receive pre-existing processes. Distinct from
// kMaxBidderProcesses because sellers behave a bit differently - they're
// alive for the length of the auction. Also, if a putative entire shared
// process limit were consumed by seller worklets, no more auctions could run,
// since bidder worklets couldn't load to make bids.
static const size_t kMaxSellerProcesses;
// The two worklet types. Sellers and bidders never share processes, primarily
// to make accounting simpler. They also currently issue requests with
// different NIKs, so safest to keep them separate, anyways.
enum class WorkletType {
kBidder,
kSeller,
};
// Outcome of RequestWorkletService.
// These values are persisted to logs. Entries should not be renumbered and
// numeric values should never be reused.
enum class RequestWorkletServiceOutcome {
kHitProcessLimit = 0,
kUsedSharedProcess = 1,
kUsedExistingDedicatedProcess = 2,
kCreatedNewDedicatedProcess = 3,
kUsedIdleProcess = 4,
kMaxValue = kUsedIdleProcess
};
class ProcessHandle;
// Refcounted class that creates / holds Mojo Remote for an
// AuctionWorkletService. Only public so it can be used by ProcessHandle and
// by test classes.
class CONTENT_EXPORT WorkletProcess : public base::RefCounted<WorkletProcess>,
public RenderProcessHostObserver {
public:
// The Mojo pipe and related data passed in when attaching a process to a
// WorkletProcess. To make mocking easier, and given the laundry list of
// parameters, WorkletProcesses are created without a service pipe. The
// AuctionWorkletProcessManager subclass then immediately passes in a
// ProcessContext to the WorkletProcess.
struct CONTENT_EXPORT ProcessContext {
explicit ProcessContext(
mojo::PendingRemote<auction_worklet::mojom::AuctionWorkletService>
service,
RenderProcessHost* render_process_host = nullptr);
ProcessContext(ProcessContext&&);
~ProcessContext();
mojo::PendingRemote<auction_worklet::mojom::AuctionWorkletService>
service;
// May only be non-null when the WorkletProcess was created with a
// non-null ServiceInstance.
raw_ptr<RenderProcessHost> render_process_host;
};
// `is_idle` indicates whether the process will be immediately used. If not,
// a timer is started, and if it triggers before ActivateAndBindIfUnbound()
// is invoked, the AuctionProcessManager is told to delete process.
//
// `is_bound_to_origin` indicates if the process may only be used for the
// specified worklet_type and origin, or if it may be used for other values
// if needed. Only newly created idle processes may not be bound to an
// origin.
WorkletProcess(
AuctionProcessManager* auction_process_manager,
scoped_refptr<SiteInstance> site_instance,
WorkletType worklet_type,
const url::Origin& origin,
bool uses_shared_process,
bool is_idle,
bool is_bound_to_origin);
auction_worklet::mojom::AuctionWorkletService* GetService();
WorkletType worklet_type() const { return worklet_type_; }
const url::Origin& origin() const { return origin_; }
bool is_bound_to_origin() const { return is_bound_to_origin_; }
RenderProcessHost* render_process_host() const {
return render_process_host_;
}
std::optional<base::ProcessId> GetPid(
base::OnceCallback<void(base::ProcessId)> callback);
bool HasPid() const;
void OnLaunchedWithProcess(const base::Process& process);
// Sets the worklet type and origin to these values, without
// binding this process to the values.
// This function may only be called on an unbound process
// (i.e. `is_bound_to_origin` is false).
void ReassignWorkletTypeAndOrigin(WorkletType worklet_type,
const url::Origin& origin);
// Sets this process non-idle. Binds the worklet type and origin to these
// values if this process was not already bound to an origin and type. This
// function should only be called on an idle process.
void ActivateAndBindIfUnbound(WorkletType worklet_type,
const url::Origin& origin);
SiteInstance* site_instance() { return site_instance_.get(); }
// Returns a weak pointer so that tests can hold onto a pointer to the
// WorkletProcess without affecting lifetimes.
base::WeakPtr<WorkletProcess> GetWeakPtrForTesting() {
return weak_ptr_factory_.GetWeakPtr();
}
private:
friend class base::RefCounted<WorkletProcess>;
friend class DedicatedAuctionProcessManager;
friend class InRendererAuctionProcessManager;
// Used to set the Mojo service. Called immediately after construction.
void SetService(ProcessContext service_context);
// From RenderProcessHostObserver:
void RenderProcessReady(RenderProcessHost* host) override;
void RenderProcessHostDestroyed(RenderProcessHost* host) override;
void RemoveFromProcessManager(bool on_destruction);
~WorkletProcess() override;
// Must be called when setting `is_bound_to_origin_` to true, if
// SetService() has already been invoked. At that point, the origin the
// service is bound to will not change, so this method can pass an
// origin-bound TrustedSignalsCache Mojo pipe to `service_`, if the cache is
// enabled.
void OnBoundToOrigin();
raw_ptr<RenderProcessHost> render_process_host_;
// SiteInstance representing the worklet. Used only by
// InRendererAuctionProcessManager.
scoped_refptr<SiteInstance> site_instance_;
WorkletType worklet_type_;
url::Origin origin_;
const base::TimeTicks start_time_;
bool uses_shared_process_;
std::optional<base::ProcessId> pid_;
std::vector<base::OnceCallback<void(base::ProcessId)>> waiting_for_pid_;
// nulled out once OnWorkletProcessUnusable() called.
raw_ptr<AuctionProcessManager> auction_process_manager_;
mojo::Remote<auction_worklet::mojom::AuctionWorkletService> service_;
// Whether the process is idle or not. If idle, it is owned directly by the
// AuctionProcessManager. If not, it is held by one or more
// ProcessHandles as scoped_refptrs.
bool is_idle_;
// Whether the origin and worklet type are bound to this process. If this
// worklet has ever been used, or if it's a renderer process, the origin and
// type must be bound. Once bound, a WorkletProcess may never become
// unbound. ReassignWorkletTypeAndOrigin() may only be called on an unbound
// process.
bool is_bound_to_origin_;
// When a process is set idle, this timer will start to delete it after a
// fixed time to prevent holding onto unnecessary unused processes for too
// long. The timer will be cancelled if the process is set non-idle.
base::OneShotTimer remove_idle_process_from_manager_timer_;
base::WeakPtrFactory<WorkletProcess> weak_ptr_factory_{this};
};
// Class that tracks a request for an auction worklet process, and manages
// lifetime of the returned process once the request receives a process.
// Destroying the handle will abort a pending request and release any process
// it is keeping alive, so consumers should destroy these as soon as a process
// is no longer needed.
//
// A single process can be referenced by multiple handles.
class CONTENT_EXPORT ProcessHandle {
public:
ProcessHandle();
ProcessHandle(const ProcessHandle&) = delete;
ProcessHandle& operator=(const ProcessHandle&) = delete;
~ProcessHandle();
// Returns a non-null pointer once a ProcessHandle has been assigned a
// process. The pipe, however, may get broken if the process exits.
auction_worklet::mojom::AuctionWorkletService* GetService();
// Returns any RenderProcessHost being used to host this process, or
// nullptr.
RenderProcessHost* GetRenderProcessHostForTesting();
WorkletType worklet_type() const { return worklet_type_; }
const url::Origin& origin() const { return origin_; }
// Returns the underlying process assignment at this level.
// Meant for reference-equality testing.
const scoped_refptr<WorkletProcess>& worklet_process_for_testing() const {
return worklet_process_;
}
const scoped_refptr<SiteInstance>& site_instance_for_testing() const {
return site_instance_;
}
// Looks up which PID (from browser's perspective) this process is running
// in. If it's available immediately, it's returned. If not, nullopt is
// returned and |callback| will be invoked when it's available. Should not
// be called if the process hasn't been assigned yet.
std::optional<base::ProcessId> GetPid(
base::OnceCallback<void(base::ProcessId)> callback);
// Tests can call this function to configure this ProcessHandle's worklet
// process's PID to this process.
void OnBaseProcessLaunchedForTesting(const base::Process& process) const;
private:
friend class AuctionProcessManager;
friend class InRendererAuctionProcessManager;
friend class DedicatedAuctionProcessManager;
// Assigns `worklet_process` to `this`. If `callback_` is non-null, queues a
// task to invoke it asynchronously, and GetService() will return nullptr
// until its invoked, so the consumer sees a consistent picture of the
// world. Destroying the Handle will cancel the pending callback.
void AssignProcess(scoped_refptr<WorkletProcess> worklet_process);
void InvokeCallback();
base::OnceClosure callback_;
url::Origin origin_;
WorkletType worklet_type_;
// SiteInstance representing the worklet. Used only by
// InRendererAuctionProcessManager.
scoped_refptr<SiteInstance> site_instance_;
// Associated AuctionProcessManager. Set when a process is requested,
// cleared once a process is assigned (synchronously or asynchronously),
// since the AuctionProcessManager doesn't track Handles after they've been
// assigned processes - it tracks processes instead, at that point.
raw_ptr<AuctionProcessManager> manager_ = nullptr;
scoped_refptr<WorkletProcess> worklet_process_;
// Entry in the corresponding PendingRequestQueue if the handle has yet to
// be assigned a process.
std::list<raw_ptr<ProcessHandle, CtnExperimental>>::iterator
queued_request_;
base::WeakPtrFactory<ProcessHandle> weak_ptr_factory_{this};
};
AuctionProcessManager(const AuctionProcessManager&) = delete;
AuctionProcessManager& operator=(const AuctionProcessManager&) = delete;
virtual ~AuctionProcessManager();
// Requests a worklet service instance for a worklet with the specified
// properties.
//
// If a process is synchronously assigned to the ProcessHandle, returns true
// and the service pointer can immediately be retrieved from `process_handle`.
// `callback` will not be invoked. Otherwise, returns false and will invoke
// `callback` when the service pointer can be retrieved from `process_handle`.
//
// Auctions must request (and get) a service for their `kSeller` worklet
// before requesting any `kBidder` worklets to avoid deadlock.
//
// `frame_site_instance` must be the SiteInstance of the frame that requested
// the auction. It's only examined by InRendererAuctionProcessManager.
//
// Passed in ProcessHandles must be destroyed before the AuctionProcessManager
// is. ProcessHandles may not be reused.
//
// While `callback` is being invoked, it is fine to call into the
// AuctionProcessManager to request more WorkletServices, or even to delete
// the AuctionProcessManager, since nothing but the callback invocation is on
// the call stack.
[[nodiscard]] bool RequestWorkletService(
WorkletType worklet_type,
const url::Origin& origin,
scoped_refptr<SiteInstance> frame_site_instance,
ProcessHandle* process_handle,
base::OnceClosure callback);
// Start an anticipatory process for an origin if
// 1) we have not yet started one for that buyer or seller origin and
// 2) we cannot use a shared process and
// 3) we have not yet reached the quota for the number of processes.
// An anticipatory process is a process for which we do not yet need
// a worklet; however, we anticipate that we will need a
// worklet for this origin later. This process will be owned by this
// AuctionProcessManger until it is needed.
void MaybeStartAnticipatoryProcess(const url::Origin& origin,
SiteInstance* frame_site_instance,
WorkletType worklet_type);
size_t GetPendingBidderRequestsForTesting() const {
return pending_bidder_request_queue_.size();
}
size_t GetPendingSellerRequestsForTesting() const {
return pending_seller_request_queue_.size();
}
// Returns the count of non-idle bidder processes.
size_t GetBidderProcessCountForTesting() const {
return bidder_processes_.size();
}
// Returns the count of non-idle seller processes.
size_t GetSellerProcessCountForTesting() const {
return seller_processes_.size();
}
// Returns the count of idle processes, including for both bidders and
// sellers.
size_t GetIdleProcessCountForTesting() const {
return idle_processes_.size();
}
protected:
// `trusted_signals_cache` must outlive the AuctionProcessManager. Passing in
// a null cache means that there's no in-process KVv2 cache, because the
// feature is disabled.
explicit AuctionProcessManager(
TrustedSignalsCacheImpl* trusted_signals_cache);
// Launches the actual process. The process will be kept-alive and
// watched by the returned WorkletProcess.
virtual scoped_refptr<WorkletProcess> LaunchProcess(
WorkletType worklet_type,
const url::Origin& origin,
scoped_refptr<SiteInstance> site_instance,
bool is_idle) = 0;
// Hook called when a new process is assigned at the end of
// TryCreateOrGetProcessForHandle. This function is used for testing.
virtual void OnNewProcessAssigned(const ProcessHandle* process_handle) {}
// Used to compute the value of `site_instance_` field of ProcessHandle.
// A subclass can return nullptr if it is not using SiteInstance to place
// worklets in appropriate renderers, but some other mechanism implementing a
// policy that's at least as strong as site isolation would be.
virtual scoped_refptr<SiteInstance> MaybeComputeSiteInstance(
SiteInstance* frame_site_instance,
const url::Origin& worklet_origin) = 0;
// Tries to see if a shared process can be used for this, which will bypass
// the normal accounting logic and just use it. If it returns true, the
// process got assigned synchronously. There is no async case.
//
// `process_handle` will be already filled.
virtual bool TryUseSharedProcess(ProcessHandle* process_handle) = 0;
private:
// Contains ProcessHandles which have not yet been assigned processes.
// Processes requested the earliest are at the start of the list, so processes
// can be assigned in FIFO order as process slots become available. A list is
// used to allow removal of cancelled requests, or requests that are assigned
// processes out of order (which happens in the case of bidder worklets when a
// bidder further up the queue with a matching owner receives a process).
// ProcessHandles are owned by consumers, and destroyed when they no longer
// need to keep their processes alive.
using PendingRequestQueue =
std::list<raw_ptr<ProcessHandle, CtnExperimental>>;
// Contains ProcessHandles for bidder or seller requests which have not yet
// been assigned processes, indexed by origin. When the request in the
// PendingRequestQueue is assigned a process, all requests that can use the
// same process are assigned the same process. This map is used to manage that
// without searching through the entire queue.
using PendingRequestMap =
std::map<url::Origin, std::set<raw_ptr<ProcessHandle, SetExperimental>>>;
// Contains running processes. Worklet processes are refcounted, and
// automatically remove themselves from this list when destroyed.
using ProcessMap =
std::map<url::Origin, raw_ptr<WorkletProcess, CtnExperimental>>;
RequestWorkletServiceOutcome RequestWorkletServiceInternal(
WorkletType worklet_type,
const url::Origin& origin,
scoped_refptr<SiteInstance> frame_site_instance,
ProcessHandle* process_handle);
// Tries to reuse an existing process for `process_handle` or create a new
// one. `process_handle`'s WorkletType and Origin must be populated. Respects
// the bidder and seller limits.
RequestWorkletServiceOutcome TryCreateOrGetProcessForHandle(
ProcessHandle* process_handle);
// Attempts to get an idle process from `idle_processes_`
// to use with the handle.
bool TryToUseIdleProcessForHandle(ProcessHandle* process_handle);
// Invoked by ProcessHandle's destructor, if it has previously been passed to
// RequestWorkletService(). Checks if a new seller worklet can be created.
void OnProcessHandleDestroyed(ProcessHandle* process_handle);
// Removes `process_handle` from the `pending_bidder_requests_` or
// `pending_seller_requests_`, as appropriate. `process_handle` must be in one
// of those maps.
void RemovePendingProcessHandle(ProcessHandle* process_handle);
// Invoked when WorkletProcess can no longer handle new requests, either
// because it was destroyed or because the underlying process died. Updates
// the corresponding ProcessMap, and checks if a new bidder process should be
// started.
void OnWorkletProcessUnusable(WorkletProcess* worklet_process);
// Callback to call after an idle process times out so that we can
// release our hold of it.
void ReleaseIdleProcess(WorkletProcess* worklet_process);
// Helpers to access the maps of the corresponding worklet type.
PendingRequestQueue* GetPendingRequestQueue(WorkletType worklet_type);
PendingRequestMap* GetPendingRequestMap(WorkletType worklet_type);
ProcessMap* Processes(WorkletType worklet_type);
// Returns true if there's an available slot for an active process of the
// specified worklet type.
bool HasAvailableProcessSlotForActiveProcess(WorkletType worklet_type) const;
// Returns true if there's an available slot for an idle process of the
// specified worklet type.
bool HasAvailableProcessSlotForIdleProcess(
WorkletType worklet_type,
size_t num_idle_processes_of_type) const;
raw_ptr<TrustedSignalsCacheImpl> trusted_signals_cache_;
PendingRequestQueue pending_bidder_request_queue_;
PendingRequestQueue pending_seller_request_queue_;
PendingRequestMap pending_bidder_requests_;
PendingRequestMap pending_seller_requests_;
ProcessMap bidder_processes_;
ProcessMap seller_processes_;
// Idle processes sorted by creation time. These are processes that
// are not being actively used as a worklet but are on stand-by in case they
// are needed.
std::vector<scoped_refptr<WorkletProcess>> idle_processes_;
base::WeakPtrFactory<AuctionProcessManager> weak_ptr_factory_{this};
};
// An implementation of AuctionProcessManager that places worklet execution into
// dedicated utility processes, isolated by domain and role.
class CONTENT_EXPORT DedicatedAuctionProcessManager
: public AuctionProcessManager {
public:
explicit DedicatedAuctionProcessManager(
TrustedSignalsCacheImpl* trusted_signals_cache);
~DedicatedAuctionProcessManager() override;
protected:
// Virtual for testing. Takes `worklet_process` so that test classes can get
// WeakPtrs to it for creation order tracking.
virtual WorkletProcess::ProcessContext CreateProcessInternal(
WorkletProcess& worklet_process);
private:
// AuctionProcessManager implementation:
scoped_refptr<WorkletProcess> LaunchProcess(
WorkletType worklet_type,
const url::Origin& origin,
scoped_refptr<SiteInstance> site_instance,
bool is_idle) override;
scoped_refptr<SiteInstance> MaybeComputeSiteInstance(
SiteInstance* frame_site_instance,
const url::Origin& worklet_origin) override;
bool TryUseSharedProcess(ProcessHandle* process_handle) override;
};
// An alternative implementation of AuctionProcessManager that places worklet
// execution into regular renderer processes (rather than worklet-only utility
// processes) following the site isolation policy.
class CONTENT_EXPORT InRendererAuctionProcessManager
: public AuctionProcessManager {
public:
explicit InRendererAuctionProcessManager(
TrustedSignalsCacheImpl* trusted_signals_cache);
~InRendererAuctionProcessManager() override;
protected:
// Virtual for testing. Takes `worklet_process` so that test classes can get
// WeakPtrs to it for creation order tracking.
virtual WorkletProcess::ProcessContext CreateProcessInternal(
WorkletProcess& worklet_process);
private:
// AuctionProcessManager implementation:
scoped_refptr<WorkletProcess> LaunchProcess(
WorkletType worklet_type,
const url::Origin& origin,
scoped_refptr<SiteInstance> site_instance,
bool is_idle) override;
scoped_refptr<SiteInstance> MaybeComputeSiteInstance(
SiteInstance* frame_site_instance,
const url::Origin& worklet_origin) override;
bool TryUseSharedProcess(ProcessHandle* process_handle) override;
};
} // namespace content
#endif // CONTENT_BROWSER_INTEREST_GROUP_AUCTION_PROCESS_MANAGER_H_
|