0

Simplify ChildProcess init by removing ChildProcessHostBootstrap interface

Before this CL, the child process start-up flow went like this:
 1.) Browser would create a Mojo invitation, with a mojom::ChildProcess
     receiver, that ChildThreadImpl would recover and bind.
 2.) ChildThreadImpl immediately creates a remote for its
     ChildProcessHost so it can start queueing messages for the browser
     process, even though it is not bound to the browser.
 3.) ChildThreadImpl asynchronously waits for ChildProcess::Initialize
     to be invoked by the browser process; this supplies a remote for
     mojom::ChildProcessHostBootstrap, which the child process uses to
     send the receiver for the remote created in (1).
 4.) Browser receives this, binding it to either
     RenderProcessHostImpl::io_thread_host_impl_ or ChildProcessHostImpl

In short, there's an awkward dance that has to be done between the
browser <-> renderer ((3) above), so that they can both immediately
initialize remotes for each other. This CL removes the extra IPC round
trip by removing ChildProcessHostBootstrap, and steps (3) & (4) above.
This is done by attaching a second message pipe to the mojo invitation
corresponding to the ChildProcessHost receiver, so the child process has
it immediately on init. At this point, the child process can create a
remote for the ChildProcessHost, and immediately bind it to the pipe
which is bound to the browser process's ChildProcessHost receiver.

Bug: N/A
R=haraken@chromium.org,rockot@google.com

Change-Id: I8f7b4b698b82e25d1c5c991c9639e57de05a6360
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2393158
Reviewed-by: Ken Rockot <rockot@google.com>
Reviewed-by: Matt Falkenhagen <falken@chromium.org>
Reviewed-by: Martin Barbella <mbarbella@chromium.org>
Reviewed-by: Tal Pressman <talp@chromium.org>
Reviewed-by: Kentaro Hara <haraken@chromium.org>
Commit-Queue: Dominic Farolino <dom@chromium.org>
Cr-Commit-Position: refs/heads/master@{#805037}
This commit is contained in:
Dominic Farolino
2020-09-08 20:03:36 +00:00
committed by Commit Bot
parent 363d1da56b
commit 1f77378d5e
10 changed files with 60 additions and 89 deletions

@ -28,8 +28,6 @@ using testing::WithArgs;
class MockChildProcess : public mojom::ChildProcess {
public:
MOCK_METHOD1(Initialize,
void(mojo::PendingRemote<mojom::ChildProcessHostBootstrap>));
MOCK_METHOD0(ProcessShutdown, void());
MOCK_METHOD1(GetTaskPort, void(GetTaskPortCallback));
#if BUILDFLAG(IPC_MESSAGE_LOG_ENABLED)

@ -143,6 +143,7 @@
#include "content/browser/webui/web_ui_controller_factory_registry.h"
#include "content/common/child_process.mojom.h"
#include "content/common/child_process_host_impl.h"
#include "content/common/content_constants_internal.h"
#include "content/common/content_switches_internal.h"
#include "content/common/frame_messages.h"
#include "content/common/in_process_child_thread_params.h"
@ -1304,28 +1305,19 @@ void InvokeBadMojoMessageCallbackForTesting(int render_process_id,
// |mojom::ChildProcessHost| interface. This exists to allow the process host
// to bind incoming receivers on the IO-thread without a main-thread hop if
// necessary. Also owns the RPHI's |mojom::ChildProcess| remote.
class RenderProcessHostImpl::IOThreadHostImpl
: public mojom::ChildProcessHostBootstrap,
public mojom::ChildProcessHost {
class RenderProcessHostImpl::IOThreadHostImpl : public mojom::ChildProcessHost {
public:
IOThreadHostImpl(int render_process_id,
base::WeakPtr<RenderProcessHostImpl> weak_host,
std::unique_ptr<service_manager::BinderRegistry> binders,
mojo::PendingReceiver<mojom::ChildProcessHostBootstrap>
bootstrap_receiver)
mojo::PendingReceiver<mojom::ChildProcessHost> host_receiver)
: render_process_id_(render_process_id),
weak_host_(std::move(weak_host)),
binders_(std::move(binders)),
bootstrap_receiver_(this, std::move(bootstrap_receiver)) {}
receiver_(this, std::move(host_receiver)) {}
~IOThreadHostImpl() override = default;
private:
// mojom::ChildProcessHostBootstrap implementation:
void BindProcessHost(
mojo::PendingReceiver<mojom::ChildProcessHost> receiver) override {
receiver_.Bind(std::move(receiver));
}
// mojom::ChildProcessHost implementation:
void BindHostReceiver(mojo::GenericPendingReceiver receiver) override {
const auto& interceptor = GetBindHostReceiverInterceptor();
@ -1394,7 +1386,6 @@ class RenderProcessHostImpl::IOThreadHostImpl
const int render_process_id_;
const base::WeakPtr<RenderProcessHostImpl> weak_host_;
std::unique_ptr<service_manager::BinderRegistry> binders_;
mojo::Receiver<mojom::ChildProcessHostBootstrap> bootstrap_receiver_;
mojo::Receiver<mojom::ChildProcessHost> receiver_{this};
DISALLOW_COPY_AND_ASSIGN(IOThreadHostImpl);
@ -1907,8 +1898,15 @@ void RenderProcessHostImpl::InitializeChannelProxy() {
// process.
mojo_invitation_ = {};
child_process_.reset();
child_process_.Bind(mojo::PendingRemote<mojom::ChildProcess>(
mojo_invitation_.AttachMessagePipe(0), /*version=*/0));
mojo::PendingRemote<mojom::ChildProcess> child_pending_remote(
mojo_invitation_.AttachMessagePipe(kChildProcessReceiverAttachmentName),
/*version=*/0);
child_process_.Bind(std::move(child_pending_remote));
// We'll bind this receiver to |io_thread_host_impl_| when it is created.
child_host_pending_receiver_ = mojo::PendingReceiver<mojom::ChildProcessHost>(
mojo_invitation_.AttachMessagePipe(
kChildProcessHostRemoteAttachmentName));
// Bootstrap the IPC Channel.
mojo::PendingRemote<IPC::mojom::ChannelBootstrap> bootstrap;
@ -2553,11 +2551,10 @@ void RenderProcessHostImpl::RegisterMojoInterfaces() {
GetContentClient()->browser()->ExposeInterfacesToRenderer(
registry.get(), associated_interfaces_.get(), this);
mojo::PendingRemote<mojom::ChildProcessHostBootstrap> bootstrap_remote;
DCHECK(child_host_pending_receiver_);
io_thread_host_impl_.emplace(
GetIOThreadTaskRunner({}), GetID(), instance_weak_factory_->GetWeakPtr(),
std::move(registry), bootstrap_remote.InitWithNewPipeAndPassReceiver());
child_process_->Initialize(std::move(bootstrap_remote));
std::move(registry), std::move(child_host_pending_receiver_));
}
void RenderProcessHostImpl::BindRouteProvider(

@ -1171,6 +1171,8 @@ class CONTENT_EXPORT RenderProcessHostImpl
std::unique_ptr<PluginRegistryImpl> plugin_registry_;
mojo::Remote<mojom::ChildProcess> child_process_;
// This will be bound to |io_thread_host_impl_|.
mojo::PendingReceiver<mojom::ChildProcessHost> child_host_pending_receiver_;
mojo::AssociatedRemote<mojom::RouteProvider> remote_route_provider_;
mojo::AssociatedRemote<mojom::Renderer> renderer_interface_;
mojo::AssociatedReceiver<mojom::RendererHost> renderer_host_receiver_{this};

@ -46,6 +46,7 @@
#include "content/child/browser_exposed_child_interfaces.h"
#include "content/child/child_process.h"
#include "content/common/child_process.mojom.h"
#include "content/common/content_constants_internal.h"
#include "content/common/field_trial_recorder.mojom.h"
#include "content/common/in_process_child_thread_params.h"
#include "content/common/mojo_core_library_support.h"
@ -244,13 +245,11 @@ class ChildThreadImpl::IOThreadState
scoped_refptr<base::SequencedTaskRunner> main_thread_task_runner,
base::WeakPtr<ChildThreadImpl> weak_main_thread,
base::RepeatingClosure quit_closure,
ChildThreadImpl::Options::ServiceBinder service_binder,
mojo::PendingReceiver<mojom::ChildProcessHost> host_receiver)
ChildThreadImpl::Options::ServiceBinder service_binder)
: main_thread_task_runner_(std::move(main_thread_task_runner)),
weak_main_thread_(std::move(weak_main_thread)),
quit_closure_(std::move(quit_closure)),
service_binder_(std::move(service_binder)),
host_receiver_(std::move(host_receiver)) {}
service_binder_(std::move(service_binder)) {}
// Used only in the deprecated Service Manager IPC mode.
void BindChildProcessReceiver(
@ -282,14 +281,6 @@ class ChildThreadImpl::IOThreadState
~IOThreadState() override = default;
// mojom::ChildProcess:
void Initialize(mojo::PendingRemote<mojom::ChildProcessHostBootstrap>
bootstrap) override {
// The browser only calls this method once.
DCHECK(host_receiver_);
mojo::Remote<mojom::ChildProcessHostBootstrap>(std::move(bootstrap))
->BindProcessHost(std::move(host_receiver_));
}
void ProcessShutdown() override {
main_thread_task_runner_->PostTask(FROM_HERE,
base::BindOnce(quit_closure_));
@ -399,7 +390,6 @@ class ChildThreadImpl::IOThreadState
mojo::BinderMap interface_binders_;
bool wait_for_interface_binders_ = true;
mojo::Receiver<mojom::ChildProcess> receiver_{this};
mojo::PendingReceiver<mojom::ChildProcessHost> host_receiver_;
// The pending legacy IPC channel endpoint to fuse with one we will eventually
// receiver on the ChildProcess interface. Only used when not in the
@ -505,14 +495,9 @@ ChildThreadImpl::ChildThreadImpl(base::RepeatingClosure quit_closure,
channel_connected_factory_(
new base::WeakPtrFactory<ChildThreadImpl>(this)),
ipc_task_runner_(options.ipc_task_runner) {
mojo::PendingRemote<mojom::ChildProcessHost> remote_host;
auto host_receiver = remote_host.InitWithNewPipeAndPassReceiver();
child_process_host_ = mojo::SharedRemote<mojom::ChildProcessHost>(
std::move(remote_host), GetIOTaskRunner());
io_thread_state_ = base::MakeRefCounted<IOThreadState>(
base::ThreadTaskRunnerHandle::Get(), weak_factory_.GetWeakPtr(),
quit_closure_, std::move(options.service_binder),
std::move(host_receiver));
quit_closure_, std::move(options.service_binder));
// |ExposeInterfacesToBrowser()| must be called exactly once. Subclasses which
// set |exposes_interfaces_to_browser| in Options signify that they take
@ -567,10 +552,8 @@ void ChildThreadImpl::Init(const Options& options) {
IPC::Logging::GetInstance()->SetIPCSender(this);
#endif
// Only one of these will be made valid by the block below. This determines
// whether we were launched in normal IPC mode or deprecated Service Manager
// IPC mode.
mojo::ScopedMessagePipeHandle child_process_pipe;
mojo::ScopedMessagePipeHandle child_process_pipe_for_receiver;
mojo::ScopedMessagePipeHandle child_process_host_pipe_for_remote;
if (!IsInBrowserProcess()) {
// If using a shared Mojo Core library, IPC support is already initialized.
if (!IsMojoCoreSharedLibraryEnabled()) {
@ -586,11 +569,26 @@ void ChildThreadImpl::Init(const Options& options) {
mojo::core::ScopedIPCSupport::ShutdownPolicy::FAST);
}
mojo::IncomingInvitation invitation = InitializeMojoIPCChannel();
child_process_pipe = invitation.ExtractMessagePipe(0);
child_process_pipe_for_receiver =
invitation.ExtractMessagePipe(kChildProcessReceiverAttachmentName);
child_process_host_pipe_for_remote =
invitation.ExtractMessagePipe(kChildProcessHostRemoteAttachmentName);
} else {
child_process_pipe = options.mojo_invitation->ExtractMessagePipe(0);
child_process_pipe_for_receiver =
options.mojo_invitation->ExtractMessagePipe(
kChildProcessReceiverAttachmentName);
child_process_host_pipe_for_remote =
options.mojo_invitation->ExtractMessagePipe(
kChildProcessHostRemoteAttachmentName);
}
// Now that we've recovered the message pipe for the ChildProcessHost, build
// our |child_process_host_| with it.
mojo::PendingRemote<mojom::ChildProcessHost> remote_host(
std::move(child_process_host_pipe_for_remote), /*version=*/0u);
child_process_host_ = mojo::SharedRemote<mojom::ChildProcessHost>(
std::move(remote_host), GetIOTaskRunner());
sync_message_filter_ = channel_->CreateSyncMessageFilter();
// In single process mode, browser-side tracing and memory will cover the
@ -635,7 +633,7 @@ void ChildThreadImpl::Init(const Options& options) {
channel_->AddFilter(startup_filter);
}
DCHECK(child_process_pipe.is_valid());
DCHECK(child_process_pipe_for_receiver.is_valid());
mojo::PendingRemote<IPC::mojom::ChannelBootstrap> legacy_ipc_bootstrap;
mojo::ScopedMessagePipeHandle legacy_ipc_channel_handle =
legacy_ipc_bootstrap.InitWithNewPipeAndPassReceiver().PassPipe();
@ -651,7 +649,7 @@ void ChildThreadImpl::Init(const Options& options) {
base::BindOnce(&IOThreadState::BindChildProcessReceiverAndLegacyIpc,
io_thread_state_,
mojo::PendingReceiver<mojom::ChildProcess>(
std::move(child_process_pipe)),
std::move(child_process_pipe_for_receiver)),
std::move(legacy_ipc_bootstrap)));
int connection_timeout = kConnectionTimeoutS;

@ -19,30 +19,9 @@ interface ChildProcessHost {
BindHostReceiver(mojo_base.mojom.GenericPendingReceiver receiver);
};
// An interface bound on the browser's IO thread to accept a ChildProcessHost
// receiver from the child process. A remote to this interface is sent to child
// processes via |ChildProcess.Initialize()| so that the child process may in
// turn send back a ChildProcessHost receiver for the browser to bind. This
// allows the child process to begin queuing messages on its ChildProcessHost
// immediately upon startup without first waiting for
// |ChildProcess.Initialize()|.
//
// NOTE: This is a separate interface and message rather than a simple reply to
// |ChildProcess.Initialize()| because RenderProcessHostImpl binds its
// ChildProcess remote on the main thread, and the ChildProcessHost receiver
// needs to be accepted and bound on the IO thread without a main-thread hop.
interface ChildProcessHostBootstrap {
BindProcessHost(pending_receiver<ChildProcessHost> receiver);
};
// A control interface the browser uses to drive the behavior of all types of
// Content child processes.
interface ChildProcess {
// The first message sent by the process host to the child process. This is
// sent to allow the child process to bootstrap its own ChildProcessHost
// interface, to which it may have already queued messages.
Initialize(pending_remote<ChildProcessHostBootstrap> boostrap);
// Tells the child process that it's safe to shutdown.
ProcessShutdown();

@ -23,6 +23,7 @@
#include "base/synchronization/lock.h"
#include "base/threading/thread_task_runner_handle.h"
#include "build/build_config.h"
#include "content/common/content_constants_internal.h"
#include "content/public/common/child_process_host_delegate.h"
#include "content/public/common/content_paths.h"
#include "content/public/common/content_switches.h"
@ -122,14 +123,19 @@ ChildProcessHostImpl::ChildProcessHostImpl(ChildProcessHostDelegate* delegate,
// disconnected pipe so it quietly discards messages.
ignore_result(child_process_.BindNewPipeAndPassReceiver());
channel_ = IPC::ChannelMojo::Create(
mojo_invitation_->AttachMessagePipe(0), IPC::Channel::MODE_SERVER, this,
base::ThreadTaskRunnerHandle::Get(),
mojo_invitation_->AttachMessagePipe(
kChildProcessReceiverAttachmentName),
IPC::Channel::MODE_SERVER, this, base::ThreadTaskRunnerHandle::Get(),
base::ThreadTaskRunnerHandle::Get(),
mojo::internal::MessageQuotaChecker::MaybeCreate());
} else if (ipc_mode_ == IpcMode::kNormal) {
child_process_.Bind(mojo::PendingRemote<mojom::ChildProcess>(
mojo_invitation_->AttachMessagePipe(0), /*version=*/0));
child_process_->Initialize(bootstrap_receiver_.BindNewPipeAndPassRemote());
mojo_invitation_->AttachMessagePipe(
kChildProcessReceiverAttachmentName),
/*version=*/0));
receiver_.Bind(mojo::PendingReceiver<mojom::ChildProcessHost>(
mojo_invitation_->AttachMessagePipe(
kChildProcessHostRemoteAttachmentName)));
}
}
@ -263,11 +269,6 @@ uint64_t ChildProcessHostImpl::ChildProcessUniqueIdToTracingProcessId(
1;
}
void ChildProcessHostImpl::BindProcessHost(
mojo::PendingReceiver<mojom::ChildProcessHost> receiver) {
receiver_.Bind(std::move(receiver));
}
void ChildProcessHostImpl::BindHostReceiver(
mojo::GenericPendingReceiver receiver) {
delegate_->BindHostReceiver(std::move(receiver));

@ -38,7 +38,6 @@ class ChildProcessHostDelegate;
class CONTENT_EXPORT ChildProcessHostImpl
: public ChildProcessHost,
public IPC::Listener,
public mojom::ChildProcessHostBootstrap,
public mojom::ChildProcessHost {
public:
~ChildProcessHostImpl() override;
@ -88,10 +87,6 @@ class CONTENT_EXPORT ChildProcessHostImpl
ChildProcessHostImpl(ChildProcessHostDelegate* delegate, IpcMode ipc_mode);
// mojom::ChildProcessHostBootstrap implementation:
void BindProcessHost(
mojo::PendingReceiver<mojom::ChildProcessHost> receiver) override;
// mojom::ChildProcessHost implementation:
void BindHostReceiver(mojo::GenericPendingReceiver receiver) override;
@ -115,7 +110,6 @@ class CONTENT_EXPORT ChildProcessHostImpl
bool opening_channel_; // True while we're waiting the channel to be opened.
std::unique_ptr<IPC::Channel> channel_;
mojo::Remote<mojom::ChildProcess> child_process_;
mojo::Receiver<mojom::ChildProcessHostBootstrap> bootstrap_receiver_{this};
mojo::Receiver<mojom::ChildProcessHost> receiver_{this};
// Holds all the IPC message filters. Since this object lives on the IO

@ -19,4 +19,7 @@ const int kTraceEventRendererMainThreadSortIndex = -1;
const char kDoNotTrackHeader[] = "DNT";
const int kChildProcessReceiverAttachmentName = 0;
const int kChildProcessHostRemoteAttachmentName = 1;
} // namespace content

@ -38,6 +38,11 @@ CONTENT_EXPORT extern const int kTraceEventRendererMainThreadSortIndex;
// HTTP header set in requests to indicate they should be marked DoNotTrack.
extern const char kDoNotTrackHeader[];
// Constants for attaching message pipes to the mojo invitation used to
// initialize child processes.
extern const int kChildProcessReceiverAttachmentName;
extern const int kChildProcessHostRemoteAttachmentName;
} // namespace content
#endif // CONTENT_COMMON_CONTENT_CONSTANTS_INTERNAL_H_

@ -58,11 +58,6 @@ class FakeChildProcessImpl
return disconnected_process_.get();
}
void Initialize(mojo::PendingRemote<content::mojom::ChildProcessHostBootstrap>
bootstrap) override {
bootstrap_.Bind(std::move(bootstrap));
}
void BootstrapLegacyIpc(
mojo::PendingReceiver<IPC::mojom::ChannelBootstrap> receiver) override {
mojo::FusePipes(std::move(receiver), std::move(legacy_ipc_bootstrap_));
@ -70,7 +65,6 @@ class FakeChildProcessImpl
private:
mojo::PendingRemote<IPC::mojom::ChannelBootstrap> legacy_ipc_bootstrap_;
mojo::Remote<content::mojom::ChildProcessHostBootstrap> bootstrap_;
mojo::Remote<content::mojom::ChildProcess> disconnected_process_;
};