0

FLEDGE: Add flag to run in-render worklet service off main thread

...since that's very congested. Also add a metric to monitor the
impact on download queuing delays.

Bug: 374034252
Change-Id: I3d73340b07448283c31b6602782bc6a3042ac6d6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5936777
Reviewed-by: mmenke <mmenke@chromium.org>
Commit-Queue: Maks Orlovich <morlovich@chromium.org>
Reviewed-by: Orr Bernstein <orrb@google.com>
Reviewed-by: Dave Tapuska <dtapuska@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1370798}
This commit is contained in:
Maks Orlovich
2024-10-18 20:07:13 +00:00
committed by Chromium LUCI CQ
parent 2825f94fa9
commit be1dfd15d3
8 changed files with 161 additions and 5 deletions
content
testing/variations
tools/metrics/histograms/metadata/others

@ -26380,6 +26380,54 @@ IN_PROC_BROWSER_TEST_F(
.has_value());
}
#if BUILDFLAG(IS_ANDROID)
class InterestGroupUseMainThreadInRendererTest
: public InterestGroupBrowserTest {
public:
InterestGroupUseMainThreadInRendererTest() {
feature_list_.InitAndDisableFeature(
features::kFledgeAndroidWorkletOffMainThread);
}
protected:
base::test::ScopedFeatureList feature_list_;
};
// This just makes sure that turning off kFledgeAndroidWorkletOffMainThread
// (which is on in field trial config) doesn't break things.
IN_PROC_BROWSER_TEST_F(InterestGroupUseMainThreadInRendererTest,
BasicOperation) {
GURL test_url =
embedded_https_test_server().GetURL("a.test", "/page_with_iframe.html");
ASSERT_TRUE(NavigateToURL(shell(), test_url));
url::Origin test_origin = url::Origin::Create(test_url);
GURL ad_url =
embedded_https_test_server().GetURL("c.test", "/echo?render_cars");
EXPECT_EQ(kSuccess,
JoinInterestGroupAndVerify(
blink::TestInterestGroupBuilder(
/*owner=*/test_origin,
/*name=*/"cars")
.SetBiddingUrl(embedded_https_test_server().GetURL(
"a.test", "/interest_group/bidding_logic.js"))
.SetAds(/*ads=*/{{{ad_url, /*metadata=*/std::nullopt}}})
.Build()));
const char kConfigTemplate[] = R"({
seller: $1,
decisionLogicURL: $2,
interestGroupBuyers: [$1],
})";
RunAuctionAndWaitForURLAndNavigateIframe(
JsReplace(kConfigTemplate, test_origin,
embedded_https_test_server().GetURL(
"a.test", "/interest_group/decision_logic.js")),
/*expected_url=*/ad_url);
}
#endif
} // namespace
} // namespace content

@ -228,6 +228,13 @@ BASE_FEATURE(kFledgeBidderWorkletThreadPool,
"FledgeBidderWorkletThreadPool",
base::FEATURE_ENABLED_BY_DEFAULT);
#if BUILDFLAG(IS_ANDROID)
// Makes FLEDGE worklets on Android not use the main thread for their mojo.
BASE_FEATURE(kFledgeAndroidWorkletOffMainThread,
"FledgeAndroidWorkletOffMainThread",
base::FEATURE_DISABLED_BY_DEFAULT);
#endif
// The scaling factor for calculating the number of bidder worklet threads based
// on the number of Interest Groups.
// Formula: #threads = 1 + scaling_factor * log10(#IGs)

@ -58,6 +58,10 @@ CONTENT_EXPORT extern const base::FeatureParam<int>
CONTENT_EXPORT BASE_DECLARE_FEATURE(kFledgeBidderWorkletThreadPool);
CONTENT_EXPORT extern const base::FeatureParam<double>
kFledgeBidderWorkletThreadPoolSizeLogarithmicScalingFactor;
#if BUILDFLAG(IS_ANDROID)
CONTENT_EXPORT BASE_DECLARE_FEATURE(kFledgeAndroidWorkletOffMainThread);
#endif
#if BUILDFLAG(IS_ANDROID)
CONTENT_EXPORT BASE_DECLARE_FEATURE(
kFocusRenderWidgetHostViewAndroidOnActionDown);

@ -11,8 +11,11 @@
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/ref_counted.h"
#include "base/no_destructor.h"
#include "base/sequence_checker.h"
#include "base/synchronization/waitable_event.h"
#include "base/task/thread_pool.h"
#include "build/build_config.h"
#include "content/common/features.h"
#include "content/services/auction_worklet/auction_v8_helper.h"
#include "content/services/auction_worklet/bidder_worklet.h"
@ -30,6 +33,18 @@ namespace {
static size_t g_next_seller_worklet_thread_index = 0;
#if BUILDFLAG(IS_ANDROID)
scoped_refptr<base::SequencedTaskRunner> AuctionWorkletMojoRunner() {
// It's important we always use the same runner here since it needs to
// talk to AuctionV8HelperHolder.
static base::NoDestructor<scoped_refptr<base::SequencedTaskRunner>>
auction_worklet_mojo_runner(
scoped_refptr(base::ThreadPool::CreateSequencedTaskRunner(
{base::TaskPriority::USER_VISIBLE})));
return *auction_worklet_mojo_runner;
}
#endif
} // namespace
// V8HelperHolder exists to make sure we don't end up creating a fresh V8 thread
@ -199,15 +214,24 @@ std::vector<AuctionWorkletServiceImpl::V8HelperHolder*>*
std::vector<AuctionWorkletServiceImpl::V8HelperHolder*>*
AuctionWorkletServiceImpl::V8HelperHolder::g_seller_instances = nullptr;
#if BUILDFLAG(IS_ANDROID)
// static
void AuctionWorkletServiceImpl::CreateForRenderer(
mojo::PendingReceiver<mojom::AuctionWorkletService> receiver) {
mojo::MakeSelfOwnedReceiver(
base::WrapUnique(new AuctionWorkletServiceImpl(
ProcessModel::kShared,
mojo::PendingReceiver<mojom::AuctionWorkletService>())),
std::move(receiver));
if (base::FeatureList::IsEnabled(
features::kFledgeAndroidWorkletOffMainThread)) {
auto task_runner = AuctionWorkletMojoRunner();
task_runner->PostTask(
FROM_HERE,
base::BindOnce(
&AuctionWorkletServiceImpl::CreateForRendererOnThisThread,
task_runner, std::move(receiver)));
} else {
CreateForRendererOnThisThread(/*task_runner=*/nullptr, std::move(receiver));
}
}
#endif
// static
std::unique_ptr<AuctionWorkletServiceImpl>
@ -234,6 +258,7 @@ AuctionWorkletServiceImpl::~AuctionWorkletServiceImpl() = default;
std::vector<scoped_refptr<AuctionV8Helper>>
AuctionWorkletServiceImpl::AuctionV8HelpersForTesting() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<scoped_refptr<AuctionV8Helper>> result;
for (const auto& v8_helper_holder : auction_bidder_v8_helper_holders_) {
result.push_back(v8_helper_holder->V8Helper());
@ -246,6 +271,7 @@ AuctionWorkletServiceImpl::AuctionV8HelpersForTesting() {
void AuctionWorkletServiceImpl::SetTrustedSignalsCache(
mojo::PendingRemote<mojom::TrustedSignalsCache> trusted_signals_cache) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!trusted_signals_kvv2_manager_);
DCHECK(!pending_trusted_signals_cache_);
pending_trusted_signals_cache_ = std::move(trusted_signals_cache);
@ -268,6 +294,7 @@ void AuctionWorkletServiceImpl::LoadBidderWorklet(
mojom::AuctionWorkletPermissionsPolicyStatePtr permissions_policy_state,
std::optional<uint16_t> experiment_group_id,
mojom::TrustedSignalsPublicKeyPtr public_key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
// If needed, expand the thread pool to match the number of threads requested.
for (size_t i = auction_bidder_v8_helper_holders_.size();
i < shared_storage_hosts.size(); ++i) {
@ -313,6 +340,7 @@ void AuctionWorkletServiceImpl::LoadSellerWorklet(
mojom::AuctionWorkletPermissionsPolicyStatePtr permissions_policy_state,
std::optional<uint16_t> experiment_group_id,
mojom::TrustedSignalsPublicKeyPtr public_key) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
std::vector<scoped_refptr<AuctionV8Helper>> v8_helpers;
for (size_t i = 0; i < auction_seller_v8_helper_holders_.size(); ++i) {
v8_helpers.push_back(auction_seller_v8_helper_holders_[i]->V8Helper());
@ -341,6 +369,7 @@ void AuctionWorkletServiceImpl::LoadSellerWorklet(
}
size_t AuctionWorkletServiceImpl::GetNextSellerWorkletThreadIndex() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
size_t result = g_next_seller_worklet_thread_index++;
g_next_seller_worklet_thread_index %=
auction_seller_v8_helper_holders_.size();
@ -350,6 +379,7 @@ size_t AuctionWorkletServiceImpl::GetNextSellerWorkletThreadIndex() {
void AuctionWorkletServiceImpl::DisconnectSellerWorklet(
mojo::ReceiverId receiver_id,
const std::string& reason) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
seller_worklets_.RemoveWithReason(receiver_id, /*custom_reason_code=*/0,
reason);
}
@ -357,12 +387,30 @@ void AuctionWorkletServiceImpl::DisconnectSellerWorklet(
void AuctionWorkletServiceImpl::DisconnectBidderWorklet(
mojo::ReceiverId receiver_id,
const std::string& reason) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
bidder_worklets_.RemoveWithReason(receiver_id, /*custom_reason_code=*/0,
reason);
}
#if BUILDFLAG(IS_ANDROID)
// static
void AuctionWorkletServiceImpl::CreateForRendererOnThisThread(
scoped_refptr<base::SequencedTaskRunner> task_runner,
mojo::PendingReceiver<mojom::AuctionWorkletService> receiver) {
if (task_runner) {
DCHECK(task_runner->RunsTasksInCurrentSequence());
}
mojo::MakeSelfOwnedReceiver(
base::WrapUnique(new AuctionWorkletServiceImpl(
ProcessModel::kShared,
mojo::PendingReceiver<mojom::AuctionWorkletService>())),
std::move(receiver));
}
#endif
TrustedSignalsKVv2Manager*
AuctionWorkletServiceImpl::GetTrustedSignalsKVv2Manager() {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
if (!trusted_signals_kvv2_manager_ && pending_trusted_signals_cache_) {
// Check for bidder V8 context first, since sellers are automatically
// populated, and want use V8 contexts that will be used anyways, instead of

@ -6,6 +6,9 @@
#define CONTENT_SERVICES_AUCTION_WORKLET_AUCTION_WORKLET_SERVICE_IMPL_H_
#include "base/memory/scoped_refptr.h"
#include "base/sequence_checker.h"
#include "base/task/sequenced_task_runner.h"
#include "build/build_config.h"
#include "content/common/content_export.h"
#include "content/services/auction_worklet/auction_v8_helper.h"
#include "content/services/auction_worklet/public/mojom/auction_network_events_handler.mojom.h"
@ -44,10 +47,12 @@ class CONTENT_EXPORT AuctionWorkletServiceImpl
delete;
~AuctionWorkletServiceImpl() override;
#if BUILDFLAG(IS_ANDROID)
// Factory method intended for use when running in the renderer.
// Creates an instance owned by (and bound to) `receiver`.
static void CreateForRenderer(
mojo::PendingReceiver<mojom::AuctionWorkletService> receiver);
#endif
// Factory method intended for use when running as a service.
// Will be bound to `receiver` but owned by the return value
@ -114,6 +119,12 @@ class CONTENT_EXPORT AuctionWorkletServiceImpl
void DisconnectBidderWorklet(mojo::ReceiverId receiver_id,
const std::string& reason);
#if BUILDFLAG(IS_ANDROID)
static void CreateForRendererOnThisThread(
scoped_refptr<base::SequencedTaskRunner> task_runner,
mojo::PendingReceiver<mojom::AuctionWorkletService> receiver);
#endif
// Returns `trusted_signals_manager_kvv2_`, populating it if needed. If
// SetTrustedSignalsCache() was never called, returns nullptr. Must be called
// only after the relevant V8HelperHolders have been created. Uses
@ -152,6 +163,8 @@ class CONTENT_EXPORT AuctionWorkletServiceImpl
mojo::UniqueReceiverSet<mojom::BidderWorklet> bidder_worklets_;
mojo::UniqueReceiverSet<mojom::SellerWorklet> seller_worklets_;
SEQUENCE_CHECKER(sequence_checker_);
};
} // namespace auction_worklet

@ -12,6 +12,7 @@
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/trace_event.h"
@ -470,6 +471,10 @@ void AuctionDownloader::TraceResult(bool failure,
base::TimeTicks completion_time,
int64_t encoded_data_length,
int64_t decoded_body_length) {
if (!completion_time.is_null()) {
base::UmaHistogramTimes("Ads.InterestGroup.Auction.DownloadThreadDelay",
base::TimeTicks::Now() - completion_time);
}
TRACE_EVENT_INSTANT1(
"devtools.timeline", "ResourceFinish", TRACE_EVENT_SCOPE_THREAD, "data",
[&](perfetto::TracedValue dest) {

@ -18574,6 +18574,21 @@
]
}
],
"ProtectedAudienceAndroidWorkletOffMainThread": [
{
"platforms": [
"android"
],
"experiments": [
{
"name": "Enabled",
"enable_features": [
"FledgeAndroidWorkletOffMainThread"
]
}
]
}
],
"ProtectedAudienceDealsSupport": [
{
"platforms": [

@ -742,6 +742,22 @@ chromium-metrics-reviews@google.com.
</summary>
</histogram>
<histogram name="Ads.InterestGroup.Auction.DownloadThreadDelay" units="ms"
expires_after="2025-03-02">
<owner>morlovich@google.com</owner>
<owner>privacy-sandbox-dev@chromium.org</owner>
<summary>
Records the delay between when a worklet-initiated download completed in the
network service, and when the processing of the response to it began in the
process running the worklet.
Reported after every successful download.
See https://github.com/WICG/turtledove/blob/main/FLEDGE.md for the latest
version of the FLEDGE explainer.
</summary>
</histogram>
<histogram name="Ads.InterestGroup.Auction.FinalReporterState"
enum="InterestGroupAuctionReporterState" expires_after="2025-03-02">
<owner>mmenke@chromium.org</owner>