
This CL adds the fix to the issue that the ServiceWorker startup goes through the main thread via the initial IPC. Currently the initial ServiceWorker startup partially work on the renderer main thread. This is because we need `cors_exempt_header_list` to start the ServiceWorker, and the list is passed to the renderer process at the process initialization. However, the ServiceWorker thread could try to access `cors_exempt_headers` before its set on the main thread. This CL tries to avoid the main thread by passing `cors_exempt_header_list` directly from the browser process to ServiceWorker in `EmbeddedWorkerInstance::Start()`. By doing this, the ServiceWorker startup doesn't have to run on the main thread. To ensure this fix works, this CL adds UMA to count the length of `cors_exempt_header_list`. Assuming the length is always same. This fix is implemented behind the flag. This doesn't change the default behavior. Bug: 40753993 Change-Id: I2704644a183d53114eb407a404a7dc898b2a01e6 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5568543 Reviewed-by: Takashi Toyoshima <toyoshim@chromium.org> Commit-Queue: Shunya Shishido <sisidovski@chromium.org> Reviewed-by: Rakina Zata Amni <rakina@chromium.org> Cr-Commit-Position: refs/heads/main@{#1307863}
235 lines
9.1 KiB
C++
235 lines
9.1 KiB
C++
// Copyright 2019 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/renderer/browser_exposed_renderer_interfaces.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <memory>
|
|
#include <utility>
|
|
|
|
#include "base/feature_list.h"
|
|
#include "base/functional/bind.h"
|
|
#include "base/memory/scoped_refptr.h"
|
|
#include "base/memory/weak_ptr.h"
|
|
#include "base/task/single_thread_task_runner.h"
|
|
#include "base/task/task_runner.h"
|
|
#include "base/task/thread_pool.h"
|
|
#include "base/time/time.h"
|
|
#include "base/trace_event/trace_event.h"
|
|
#include "build/build_config.h"
|
|
#include "content/common/features.h"
|
|
#include "content/common/frame.mojom.h"
|
|
#include "content/public/common/content_client.h"
|
|
#include "content/public/common/resource_usage_reporter.mojom.h"
|
|
#include "content/public/common/resource_usage_reporter_type_converters.h"
|
|
#include "content/public/renderer/content_renderer_client.h"
|
|
#include "content/renderer/render_thread_impl.h"
|
|
#include "content/renderer/service_worker/embedded_worker_instance_client_impl.h"
|
|
#include "content/renderer/worker/shared_worker_factory_impl.h"
|
|
#include "content/services/auction_worklet/auction_worklet_service_impl.h"
|
|
#include "mojo/public/cpp/bindings/binder_map.h"
|
|
#include "mojo/public/cpp/bindings/pending_receiver.h"
|
|
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
|
|
#include "v8/include/v8-isolate.h"
|
|
#include "v8/include/v8-statistics.h"
|
|
|
|
namespace content {
|
|
|
|
namespace {
|
|
|
|
constexpr int kWaitForWorkersStatsTimeoutMS = 20;
|
|
|
|
class ResourceUsageReporterImpl : public content::mojom::ResourceUsageReporter {
|
|
public:
|
|
explicit ResourceUsageReporterImpl(base::WeakPtr<RenderThread> thread)
|
|
: thread_(std::move(thread)) {}
|
|
ResourceUsageReporterImpl(const ResourceUsageReporterImpl&) = delete;
|
|
~ResourceUsageReporterImpl() override = default;
|
|
|
|
ResourceUsageReporterImpl& operator=(const ResourceUsageReporterImpl&) =
|
|
delete;
|
|
|
|
private:
|
|
static void CollectOnWorkerThread(
|
|
const scoped_refptr<base::TaskRunner>& master,
|
|
base::WeakPtr<ResourceUsageReporterImpl> impl) {
|
|
size_t total_bytes = 0;
|
|
size_t used_bytes = 0;
|
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
|
if (isolate) {
|
|
v8::HeapStatistics heap_stats;
|
|
isolate->GetHeapStatistics(&heap_stats);
|
|
total_bytes = heap_stats.total_heap_size();
|
|
used_bytes = heap_stats.used_heap_size();
|
|
}
|
|
master->PostTask(FROM_HERE,
|
|
base::BindOnce(&ResourceUsageReporterImpl::ReceiveStats,
|
|
impl, total_bytes, used_bytes));
|
|
}
|
|
|
|
void ReceiveStats(size_t total_bytes, size_t used_bytes) {
|
|
usage_data_->v8_bytes_allocated += total_bytes;
|
|
usage_data_->v8_bytes_used += used_bytes;
|
|
workers_to_go_--;
|
|
if (!workers_to_go_)
|
|
SendResults();
|
|
}
|
|
|
|
void SendResults() {
|
|
if (!callback_.is_null())
|
|
std::move(callback_).Run(std::move(usage_data_));
|
|
callback_.Reset();
|
|
weak_factory_.InvalidateWeakPtrs();
|
|
workers_to_go_ = 0;
|
|
}
|
|
|
|
void GetUsageData(GetUsageDataCallback callback) override {
|
|
DCHECK(callback_.is_null());
|
|
weak_factory_.InvalidateWeakPtrs();
|
|
usage_data_ = mojom::ResourceUsageData::New();
|
|
usage_data_->reports_v8_stats = true;
|
|
callback_ = std::move(callback);
|
|
|
|
// Since it is not safe to call any Blink or V8 functions until Blink has
|
|
// been initialized (which also initializes V8), early out and send 0 back
|
|
// for all resources.
|
|
if (!thread_) {
|
|
SendResults();
|
|
return;
|
|
}
|
|
|
|
blink::WebCacheResourceTypeStats stats;
|
|
blink::WebCache::GetResourceTypeStats(&stats);
|
|
usage_data_->web_cache_stats = mojom::ResourceTypeStats::From(stats);
|
|
|
|
v8::Isolate* isolate = v8::Isolate::GetCurrent();
|
|
if (isolate) {
|
|
v8::HeapStatistics heap_stats;
|
|
isolate->GetHeapStatistics(&heap_stats);
|
|
usage_data_->v8_bytes_allocated = heap_stats.total_heap_size();
|
|
usage_data_->v8_bytes_used = heap_stats.used_heap_size();
|
|
}
|
|
base::RepeatingClosure collect =
|
|
base::BindRepeating(&ResourceUsageReporterImpl::CollectOnWorkerThread,
|
|
base::SingleThreadTaskRunner::GetCurrentDefault(),
|
|
weak_factory_.GetWeakPtr());
|
|
workers_to_go_ =
|
|
RenderThread::Get()->PostTaskToAllWebWorkers(std::move(collect));
|
|
if (workers_to_go_) {
|
|
// The guard task to send out partial stats
|
|
// in case some workers are not responsive.
|
|
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
|
|
FROM_HERE,
|
|
base::BindOnce(&ResourceUsageReporterImpl::SendResults,
|
|
weak_factory_.GetWeakPtr()),
|
|
base::Milliseconds(kWaitForWorkersStatsTimeoutMS));
|
|
} else {
|
|
// No worker threads so just send out the main thread data right away.
|
|
SendResults();
|
|
}
|
|
}
|
|
|
|
const base::WeakPtr<RenderThread> thread_;
|
|
mojom::ResourceUsageDataPtr usage_data_;
|
|
GetUsageDataCallback callback_;
|
|
int workers_to_go_ = 0;
|
|
|
|
base::WeakPtrFactory<ResourceUsageReporterImpl> weak_factory_{this};
|
|
};
|
|
|
|
void CreateResourceUsageReporter(
|
|
base::WeakPtr<RenderThreadImpl> render_thread,
|
|
mojo::PendingReceiver<mojom::ResourceUsageReporter> receiver) {
|
|
mojo::MakeSelfOwnedReceiver(
|
|
std::make_unique<ResourceUsageReporterImpl>(std::move(render_thread)),
|
|
std::move(receiver));
|
|
}
|
|
|
|
void CreateEmbeddedWorkerWithRenderMainThread(
|
|
scoped_refptr<base::SingleThreadTaskRunner> initiator_task_runner,
|
|
base::WeakPtr<RenderThreadImpl> render_thread,
|
|
mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient>
|
|
receiver) {
|
|
TRACE_EVENT0("ServiceWorker", "CreateEmbeddedWorkerWithRenderMainThread");
|
|
initiator_task_runner->PostTask(
|
|
FROM_HERE, base::BindOnce(&EmbeddedWorkerInstanceClientImpl::Create,
|
|
initiator_task_runner,
|
|
render_thread->cors_exempt_header_list(),
|
|
std::move(receiver)));
|
|
}
|
|
|
|
void CreateEmbeddedWorker(
|
|
scoped_refptr<base::SingleThreadTaskRunner> initiator_task_runner,
|
|
mojo::PendingReceiver<blink::mojom::EmbeddedWorkerInstanceClient>
|
|
receiver) {
|
|
TRACE_EVENT0("ServiceWorker", "CreateEmbeddedWorker");
|
|
// An empty fake list is passed to
|
|
// `EmbeddedWorkerInstanceClientImpl::Create()`. That will be overridden by
|
|
// the actual cors exempt header list in
|
|
// `EmbeddedWorkerInstanceClientImpl::StartWorker()`.
|
|
//
|
|
// TODO(crbug.com/40753993): Remove this fake empty list once we confirmed
|
|
// this approach is fine.
|
|
const std::vector<std::string> fake_cors_exempt_header_list;
|
|
initiator_task_runner->PostTask(
|
|
FROM_HERE,
|
|
base::BindOnce(&EmbeddedWorkerInstanceClientImpl::Create,
|
|
initiator_task_runner, fake_cors_exempt_header_list,
|
|
std::move(receiver)));
|
|
}
|
|
} // namespace
|
|
|
|
void ExposeRendererInterfacesToBrowser(
|
|
base::WeakPtr<RenderThreadImpl> render_thread,
|
|
mojo::BinderMap* binders) {
|
|
DCHECK(render_thread);
|
|
|
|
binders->Add<blink::mojom::SharedWorkerFactory>(
|
|
base::BindRepeating(&SharedWorkerFactoryImpl::Create),
|
|
base::SingleThreadTaskRunner::GetCurrentDefault());
|
|
binders->Add<mojom::ResourceUsageReporter>(
|
|
base::BindRepeating(&CreateResourceUsageReporter, render_thread),
|
|
base::SingleThreadTaskRunner::GetCurrentDefault());
|
|
#if BUILDFLAG(IS_ANDROID)
|
|
binders->Add<auction_worklet::mojom::AuctionWorkletService>(
|
|
base::BindRepeating(
|
|
&auction_worklet::AuctionWorkletServiceImpl::CreateForRenderer),
|
|
base::SingleThreadTaskRunner::GetCurrentDefault());
|
|
#endif
|
|
|
|
auto task_runner_for_service_worker_startup =
|
|
base::ThreadPool::CreateSingleThreadTaskRunner(
|
|
{base::MayBlock(), base::TaskPriority::USER_BLOCKING,
|
|
base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN});
|
|
// TODO(crbug.com/40753993): Remove the feature flag and
|
|
// `CreateEmbeddedWorkerWithRenderMainThread()` once we confirmed this
|
|
// approach is fine.
|
|
//
|
|
// The kServiceWorkerAvoidMainThreadForInitialization feature flag is the
|
|
// experimental flag to avoid the additional thread hop over the main thread
|
|
// for the ServiceWorker initialization. Currently it's on the main thread as
|
|
// CreateEmbeddedWorker accesses `cors_exempt_header_list` from
|
|
// `render_thread`. When this feature flag is enabled, binds on
|
|
// `task_runner_for_service_worker_startup` instead of the main thread, so
|
|
// startup isn't blocked on the main thread.
|
|
if (base::FeatureList::IsEnabled(
|
|
features::kServiceWorkerAvoidMainThreadForInitialization)) {
|
|
binders->Add<blink::mojom::EmbeddedWorkerInstanceClient>(
|
|
base::BindRepeating(&CreateEmbeddedWorker,
|
|
task_runner_for_service_worker_startup),
|
|
task_runner_for_service_worker_startup);
|
|
} else {
|
|
binders->Add<blink::mojom::EmbeddedWorkerInstanceClient>(
|
|
base::BindRepeating(&CreateEmbeddedWorkerWithRenderMainThread,
|
|
task_runner_for_service_worker_startup,
|
|
render_thread),
|
|
base::SingleThreadTaskRunner::GetCurrentDefault());
|
|
}
|
|
|
|
GetContentClient()->renderer()->ExposeInterfacesToBrowser(binders);
|
|
}
|
|
|
|
} // namespace content
|