Index: mojo/shell/runner/host/child_process_host.cc |
diff --git a/mojo/shell/runner/host/child_process_host.cc b/mojo/shell/runner/host/child_process_host.cc |
index 35ebb196cb03d4b82812455edfaf1b1f2cb1e583..83be4715b27ed8d9723c506bcd70c0246d3cd4b8 100644 |
--- a/mojo/shell/runner/host/child_process_host.cc |
+++ b/mojo/shell/runner/host/child_process_host.cc |
@@ -18,6 +18,7 @@ |
#include "base/process/launch.h" |
#include "base/task_runner.h" |
#include "base/thread_task_runner_handle.h" |
+#include "mojo/edk/embedder/embedder.h" |
#include "mojo/public/cpp/bindings/interface_ptr_info.h" |
#include "mojo/public/cpp/system/core.h" |
#include "mojo/shell/runner/host/switches.h" |
@@ -34,6 +35,29 @@ |
namespace mojo { |
namespace shell { |
+ChildProcessHost::PipeHolder::PipeHolder() {} |
+ |
+void ChildProcessHost::PipeHolder::Reject() { |
+ base::AutoLock lock(lock_); |
+ reject_pipe_ = true; |
+ pipe_.reset(); |
+} |
+ |
+void ChildProcessHost::PipeHolder::SetPipe(ScopedMessagePipeHandle pipe) { |
+ base::AutoLock lock(lock_); |
+ DCHECK(!pipe_.is_valid()); |
+ if (!reject_pipe_) |
+ pipe_ = std::move(pipe); |
+} |
+ |
+ScopedMessagePipeHandle ChildProcessHost::PipeHolder::PassPipe() { |
+ base::AutoLock lock(lock_); |
+ DCHECK(pipe_.is_valid()); |
+ return std::move(pipe_); |
+} |
+ |
+ChildProcessHost::PipeHolder::~PipeHolder() {} |
+ |
ChildProcessHost::ChildProcessHost(base::TaskRunner* launch_process_runner, |
bool start_sandboxed, |
const base::FilePath& app_path) |
@@ -43,13 +67,17 @@ ChildProcessHost::ChildProcessHost(base::TaskRunner* launch_process_runner, |
channel_info_(nullptr), |
start_child_process_event_(false, false), |
weak_factory_(this) { |
- if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) |
- serializer_platform_channel_pair_.reset(new edk::PlatformChannelPair(true)); |
- |
- child_message_pipe_ = embedder::CreateChannel( |
- platform_channel_pair_.PassServerHandle(), |
- base::Bind(&ChildProcessHost::DidCreateChannel, base::Unretained(this)), |
- base::ThreadTaskRunnerHandle::Get()); |
+ pipe_holder_ = new PipeHolder(); |
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { |
+ node_channel_.reset(new edk::PlatformChannelPair); |
+ primordial_pipe_token_ = edk::GenerateRandomToken(); |
+ } else { |
+ pipe_holder_->SetPipe(embedder::CreateChannel( |
+ platform_channel_pair_.PassServerHandle(), |
+ base::Bind(&ChildProcessHost::DidCreateChannel, base::Unretained(this)), |
+ base::ThreadTaskRunnerHandle::Get())); |
+ OnMessagePipeCreated(); |
+ } |
} |
ChildProcessHost::ChildProcessHost(ScopedHandle channel) |
@@ -68,45 +96,61 @@ ChildProcessHost::~ChildProcessHost() { |
CHECK(!controller_) << "Destroying ChildProcessHost before calling Join"; |
} |
-void ChildProcessHost::Start( |
- const base::Callback<void(base::ProcessId)>& pid_available_callback) { |
+void ChildProcessHost::Start(const ProcessReadyCallback& callback) { |
DCHECK(!child_process_.IsValid()); |
- DCHECK(child_message_pipe_.is_valid()); |
+ DCHECK(process_ready_callback_.is_null()); |
+ process_ready_callback_ = callback; |
if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { |
- std::string client_handle_as_string = |
- serializer_platform_channel_pair_ |
- ->PrepareToPassClientHandleToChildProcessAsString( |
- &handle_passing_info_); |
- // We can't send the MP for the token serializer implementation as a |
- // platform handle, because that would require the other side to use the |
- // token initializer itself! So instead we send it as a string. |
- MojoResult rv = MojoWriteMessage( |
- child_message_pipe_.get().value(), client_handle_as_string.c_str(), |
- static_cast<uint32_t>(client_handle_as_string.size()), nullptr, 0, |
- MOJO_WRITE_MESSAGE_FLAG_NONE); |
- DCHECK_EQ(rv, MOJO_RESULT_OK); |
+ // With the new EDK, bootstrap message pipes are created asynchronously. |
+ // We recieve the bound pipe (if successful) on an arbitrary thread, |
+ // stash it in the thread-safe |pipe_holder_|, and then try to call |
+ // OnMessagePipeCreated() on the host's main thread. |
+ // |
+ // Because of the way the launcher process shuts down, it's possible for |
+ // the main thread's MessageLoop to stop running (but not yet be destroyed!) |
+ // while this boostrap is pending, resulting in OnMessagePipeCreated() never |
+ // being called. |
+ // |
+ // A typical child process (i.e. one using ApplicationImpl to bind the other |
+ // end of this pipe) may hang forever waiting for an Initialize() message |
+ // unless the pipe is closed. This in turn means that Join() could hang |
+ // waiting for the process to exit. Deadlock! |
+ // |
+ // |pipe_holder_| exists for this reason. If it's still holding onto the |
+ // pipe when Join() is called, the pipe will be closed. |
+ DCHECK(!primordial_pipe_token_.empty()); |
+ edk::CreateParentMessagePipe( |
+ primordial_pipe_token_, |
+ base::Bind(&OnParentMessagePipeCreated, pipe_holder_, |
+ base::ThreadTaskRunnerHandle::Get(), |
+ base::Bind(&ChildProcessHost::OnMessagePipeCreated, |
+ weak_factory_.GetWeakPtr()))); |
} |
- controller_.Bind( |
- InterfacePtrInfo<ChildController>(std::move(child_message_pipe_), 0u)); |
- |
launch_process_runner_->PostTaskAndReply( |
FROM_HERE, |
base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this)), |
- base::Bind(&ChildProcessHost::DidStart, weak_factory_.GetWeakPtr(), |
- pid_available_callback)); |
+ base::Bind(&ChildProcessHost::DidStart, weak_factory_.GetWeakPtr())); |
} |
int ChildProcessHost::Join() { |
if (controller_) // We use this as a signal that Start was called. |
start_child_process_event_.Wait(); |
+ |
controller_ = ChildControllerPtr(); |
DCHECK(child_process_.IsValid()); |
+ |
+ // Ensure the child pipe is closed even if it wasn't yet connected to the |
+ // controller. |
+ pipe_holder_->Reject(); |
+ |
int rv = -1; |
LOG_IF(ERROR, !child_process_.WaitForExit(&rv)) |
<< "Failed to wait for child process"; |
+ |
child_process_.Close(); |
+ |
return rv; |
} |
@@ -127,12 +171,11 @@ void ChildProcessHost::ExitNow(int32_t exit_code) { |
controller_->ExitNow(exit_code); |
} |
-void ChildProcessHost::DidStart( |
- const base::Callback<void(base::ProcessId)>& pid_available_callback) { |
+void ChildProcessHost::DidStart() { |
DVLOG(2) << "ChildProcessHost::DidStart()"; |
if (child_process_.IsValid()) { |
- pid_available_callback.Run(child_process_.Pid()); |
+ MaybeNotifyProcessReady(); |
} else { |
LOG(ERROR) << "Failed to start child process"; |
AppCompleted(MOJO_RESULT_UNKNOWN); |
@@ -158,8 +201,13 @@ void ChildProcessHost::DoLaunch() { |
if (start_sandboxed_) |
child_command_line.AppendSwitch(switches::kEnableSandbox); |
- platform_channel_pair_.PrepareToPassClientHandleToChildProcess( |
- &child_command_line, &handle_passing_info_); |
+ if (node_channel_.get()) { |
+ node_channel_->PrepareToPassClientHandleToChildProcess( |
+ &child_command_line, &handle_passing_info_); |
+ } |
+ |
+ child_command_line.AppendSwitchASCII(switches::kPrimordialPipeToken, |
+ primordial_pipe_token_); |
base::LaunchOptions options; |
#if defined(OS_WIN) |
@@ -210,13 +258,12 @@ void ChildProcessHost::DoLaunch() { |
if (child_process_.IsValid()) { |
platform_channel_pair_.ChildProcessLaunched(); |
- if (serializer_platform_channel_pair_.get()) { |
- serializer_platform_channel_pair_->ChildProcessLaunched(); |
+ if (node_channel_.get()) { |
+ node_channel_->ChildProcessLaunched(); |
mojo::embedder::ChildProcessLaunched( |
child_process_.Handle(), |
mojo::embedder::ScopedPlatformHandle(mojo::embedder::PlatformHandle( |
- serializer_platform_channel_pair_->PassServerHandle().release(). |
- handle))); |
+ node_channel_->PassServerHandle().release().handle))); |
} |
} |
start_child_process_event_.Signal(); |
@@ -238,5 +285,26 @@ void ChildProcessHost::DidCreateChannel(embedder::ChannelInfo* channel_info) { |
channel_info_ = channel_info; |
} |
+void ChildProcessHost::OnMessagePipeCreated() { |
+ controller_.Bind( |
+ InterfacePtrInfo<ChildController>(pipe_holder_->PassPipe(), 0u)); |
+ MaybeNotifyProcessReady(); |
+} |
+ |
+void ChildProcessHost::MaybeNotifyProcessReady() { |
+ if (controller_.is_bound() && child_process_.IsValid()) |
+ process_ready_callback_.Run(child_process_.Pid()); |
+} |
+ |
+// static |
+void ChildProcessHost::OnParentMessagePipeCreated( |
+ scoped_refptr<PipeHolder> holder, |
+ scoped_refptr<base::TaskRunner> callback_task_runner, |
+ const base::Closure& callback, |
+ ScopedMessagePipeHandle pipe) { |
+ holder->SetPipe(std::move(pipe)); |
+ callback_task_runner->PostTask(FROM_HERE, callback); |
+} |
+ |
} // namespace shell |
} // namespace mojo |