Make ChildProcessHost::DoLaunch() not touch a random set of things on a different thread. (AFAICT, everything was "safe", except for the race condition noted in OutOfProcessNativeRunner::~OutOfProcessNativeRunner(). That race condition still exists, but at least now it's more predictable and not as bad, and it should now be possible to fix Join().) R=yzshen@chromium.org Review URL: https://codereview.chromium.org/1146273002
diff --git a/shell/child_process_host.cc b/shell/child_process_host.cc index 647f8fd..3759df8 100644 --- a/shell/child_process_host.cc +++ b/shell/child_process_host.cc
@@ -17,6 +17,7 @@ #include "base/task_runner.h" #include "base/task_runner_util.h" #include "mojo/edk/embedder/embedder.h" +#include "mojo/edk/embedder/platform_channel_pair.h" #include "mojo/public/cpp/system/message_pipe.h" #include "shell/child_switches.h" #include "shell/context.h" @@ -24,6 +25,15 @@ namespace shell { +struct ChildProcessHost::LaunchData { + LaunchData() {} + ~LaunchData() {} + + base::FilePath child_path; + mojo::embedder::PlatformChannelPair platform_channel_pair; + std::string child_connection_id; +}; + ChildProcessHost::ChildProcessHost(Context* context) : context_(context), channel_info_(nullptr) { } @@ -35,12 +45,14 @@ void ChildProcessHost::Start() { DCHECK(!child_process_.IsValid()); + scoped_ptr<LaunchData> launch_data(new LaunchData()); + launch_data->child_path = context_->mojo_shell_child_path(); + // TODO(vtl): Add something for |slave_info|. mojo::embedder::ScopedPlatformHandle platform_handle_for_channel; - std::string child_connection_id; mojo::embedder::ConnectToSlave( - nullptr, platform_channel_pair_.PassServerHandle(), - &platform_handle_for_channel, &child_connection_id); + nullptr, launch_data->platform_channel_pair.PassServerHandle(), + &platform_handle_for_channel, &launch_data->child_connection_id); mojo::ScopedMessagePipeHandle handle(mojo::embedder::CreateChannel( platform_handle_for_channel.Pass(), context_->task_runners()->io_runner(), @@ -53,7 +65,7 @@ CHECK(base::PostTaskAndReplyWithResult( context_->task_runners()->blocking_pool(), FROM_HERE, base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this), - child_connection_id), + base::Passed(&launch_data)), base::Bind(&ChildProcessHost::DidStart, base::Unretained(this)))); } @@ -84,14 +96,17 @@ controller_->ExitNow(exit_code); } -void ChildProcessHost::DidStart(bool success) { +void ChildProcessHost::DidStart(base::Process child_process) { DVLOG(2) << "ChildProcessHost::DidStart()"; + DCHECK(!child_process_.IsValid()); - if (!success) { + if (!child_process.IsValid()) { LOG(ERROR) << "Failed to start app child process"; AppCompleted(MOJO_RESULT_UNKNOWN); return; } + + child_process_ = child_process.Pass(); } // Callback for |mojo::embedder::CreateChannel()|. @@ -103,32 +118,31 @@ channel_info_ = channel_info; } -bool ChildProcessHost::DoLaunch(const std::string& child_connection_id) { +base::Process ChildProcessHost::DoLaunch(scoped_ptr<LaunchData> launch_data) { static const char* kForwardSwitches[] = { switches::kTraceToConsole, switches::kV, switches::kVModule, }; - base::CommandLine child_command_line(context_->mojo_shell_child_path()); + base::CommandLine child_command_line(launch_data->child_path); child_command_line.CopySwitchesFrom(*base::CommandLine::ForCurrentProcess(), kForwardSwitches, arraysize(kForwardSwitches)); child_command_line.AppendSwitchASCII(switches::kChildConnectionId, - child_connection_id); + launch_data->child_connection_id); mojo::embedder::HandlePassingInformation handle_passing_info; - platform_channel_pair_.PrepareToPassClientHandleToChildProcess( + launch_data->platform_channel_pair.PrepareToPassClientHandleToChildProcess( &child_command_line, &handle_passing_info); base::LaunchOptions options; options.fds_to_remap = &handle_passing_info; DVLOG(2) << "Launching child with command line: " << child_command_line.GetCommandLineString(); - child_process_ = base::LaunchProcess(child_command_line, options); - if (!child_process_.IsValid()) - return false; - - platform_channel_pair_.ChildProcessLaunched(); - return true; + base::Process child_process = + base::LaunchProcess(child_command_line, options); + if (child_process.IsValid()) + launch_data->platform_channel_pair.ChildProcessLaunched(); + return child_process.Pass(); } void ChildProcessHost::AppCompleted(int32_t result) {
diff --git a/shell/child_process_host.h b/shell/child_process_host.h index d5e4562..79fb292 100644 --- a/shell/child_process_host.h +++ b/shell/child_process_host.h
@@ -10,9 +10,9 @@ #include <string> #include "base/macros.h" +#include "base/memory/scoped_ptr.h" #include "base/process/process.h" #include "mojo/edk/embedder/channel_info_forward.h" -#include "mojo/edk/embedder/platform_channel_pair.h" #include "mojo/edk/embedder/scoped_platform_handle.h" #include "mojo/public/cpp/bindings/error_handler.h" #include "shell/child_controller.mojom.h" @@ -60,13 +60,17 @@ // TODO(vtl): This is virtual, so tests can override it, but really |Start()| // should take a callback (see above) and this should be private. - virtual void DidStart(bool success); + virtual void DidStart(base::Process child_process); private: + struct LaunchData; + // Callback for |mojo::embedder::CreateChannel()|. void DidCreateChannel(mojo::embedder::ChannelInfo* channel_info); - bool DoLaunch(const std::string& child_connection_id); + // Note: This is probably executed on a different thread (namely, using the + // blocking pool). + base::Process DoLaunch(scoped_ptr<LaunchData> launch_data); void AppCompleted(int32_t result); @@ -74,7 +78,6 @@ void OnConnectionError() override; Context* const context_; - mojo::embedder::PlatformChannelPair platform_channel_pair_; ChildControllerPtr controller_; mojo::embedder::ChannelInfo* channel_info_;
diff --git a/shell/child_process_host_unittest.cc b/shell/child_process_host_unittest.cc index bb1817b..d4dc4a8 100644 --- a/shell/child_process_host_unittest.cc +++ b/shell/child_process_host_unittest.cc
@@ -25,9 +25,9 @@ explicit TestChildProcessHost(Context* context) : ChildProcessHost(context) {} ~TestChildProcessHost() override {} - void DidStart(bool success) override { - EXPECT_TRUE(success); - ChildProcessHost::DidStart(success); + void DidStart(base::Process child_process) override { + EXPECT_TRUE(child_process.IsValid()); + ChildProcessHost::DidStart(child_process.Pass()); base::MessageLoop::current()->QuitWhenIdle(); }