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
content / browser / interest_group / auction_process_manager.h [blame]
// 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;
// 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.
class CONTENT_EXPORT WorkletProcess : public base::RefCounted<WorkletProcess>,
public RenderProcessHostObserver {
public:
// `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,
RenderProcessHost* render_process_host,
mojo::PendingRemote<auction_worklet::mojom::AuctionWorkletService>
service,
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);
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);
private:
friend class base::RefCounted<WorkletProcess>;
friend class DedicatedAuctionProcessManager;
// From RenderProcessHostObserver:
void RenderProcessReady(RenderProcessHost* host) override;
void RenderProcessHostDestroyed(RenderProcessHost* host) override;
void RemoveFromProcessManager(bool on_destruction);
~WorkletProcess() override;
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);
private:
friend class ProcessHandleTestPeer;
friend class AuctionProcessManager;
friend class InRendererAuctionProcessManager;
friend class DedicatedAuctionProcessManager;
// Tests can call this function to configure this ProcessHandle's worklet
// process's PID to this process.
void OnBaseProcessLaunchedForTesting(const base::Process& process) const;
// 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:
AuctionProcessManager();
// 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,
const std::string& display_name,
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;
// Returns the display name to use for a process. Separate method so it can be
// used in tests.
static std::string ComputeDisplayName(WorkletType worklet_type,
const url::Origin& origin);
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;
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:
DedicatedAuctionProcessManager();
~DedicatedAuctionProcessManager() override;
private:
scoped_refptr<WorkletProcess> LaunchProcess(
WorkletType worklet_type,
const url::Origin& origin,
scoped_refptr<SiteInstance> site_instance,
const std::string& display_name,
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:
InRendererAuctionProcessManager();
~InRendererAuctionProcessManager() override;
protected:
scoped_refptr<WorkletProcess> LaunchProcess(
WorkletType worklet_type,
const url::Origin& origin,
scoped_refptr<SiteInstance> site_instance,
const std::string& display_name,
bool is_idle) override;
scoped_refptr<SiteInstance> MaybeComputeSiteInstance(
SiteInstance* frame_site_instance,
const url::Origin& worklet_origin) override;
bool TryUseSharedProcess(ProcessHandle* process_handle) override;
private:
RenderProcessHost* LaunchInSiteInstance(
SiteInstance* site_instance,
mojo::PendingReceiver<auction_worklet::mojom::AuctionWorkletService>
auction_worklet_service_receiver);
};
} // namespace content
#endif // CONTENT_BROWSER_INTEREST_GROUP_AUCTION_PROCESS_MANAGER_H_