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 2b3cfc943c7d5893ecb6ab3e6835d4355c37d0fb..9a5b439fa2d8ecbbe6d4eb866f361b093d185bcc 100644 |
--- a/mojo/shell/runner/host/child_process_host.cc |
+++ b/mojo/shell/runner/host/child_process_host.cc |
@@ -35,24 +35,55 @@ |
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) |
: launch_process_runner_(launch_process_runner), |
start_sandboxed_(start_sandboxed), |
app_path_(app_path), |
+ channel_info_(nullptr), |
start_child_process_event_(false, false), |
weak_factory_(this) { |
- node_channel_.reset(new edk::PlatformChannelPair); |
- primordial_pipe_token_ = edk::GenerateRandomToken(); |
- controller_.Bind( |
- InterfacePtrInfo<mojom::ChildController>( |
- edk::CreateParentMessagePipe(primordial_pipe_token_), 0u)); |
+ 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) |
: launch_process_runner_(nullptr), |
start_sandboxed_(false), |
+ channel_info_(nullptr), |
start_child_process_event_(false, false), |
weak_factory_(this) { |
CHECK(channel.is_valid()); |
@@ -68,11 +99,40 @@ |
void ChildProcessHost::Start(const ProcessReadyCallback& callback) { |
DCHECK(!child_process_.IsValid()); |
+ DCHECK(process_ready_callback_.is_null()); |
+ |
+ process_ready_callback_ = callback; |
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { |
+ // 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 ShellConnection 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()))); |
+ } |
+ |
launch_process_runner_->PostTaskAndReply( |
FROM_HERE, |
base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this)), |
- base::Bind(&ChildProcessHost::DidStart, |
- weak_factory_.GetWeakPtr(), callback)); |
+ base::Bind(&ChildProcessHost::DidStart, weak_factory_.GetWeakPtr())); |
} |
int ChildProcessHost::Join() { |
@@ -81,6 +141,10 @@ |
controller_ = mojom::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)) |
@@ -108,11 +172,11 @@ |
controller_->ExitNow(exit_code); |
} |
-void ChildProcessHost::DidStart(const ProcessReadyCallback& callback) { |
+void ChildProcessHost::DidStart() { |
DVLOG(2) << "ChildProcessHost::DidStart()"; |
if (child_process_.IsValid()) { |
- callback.Run(child_process_.Pid()); |
+ MaybeNotifyProcessReady(); |
} else { |
LOG(ERROR) << "Failed to start child process"; |
AppCompleted(MOJO_RESULT_UNKNOWN); |
@@ -214,5 +278,34 @@ |
} |
} |
+void ChildProcessHost::DidCreateChannel(embedder::ChannelInfo* channel_info) { |
+ DVLOG(2) << "AppChildProcessHost::DidCreateChannel()"; |
+ |
+ DCHECK(channel_info || |
+ base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")); |
+ channel_info_ = channel_info; |
+} |
+ |
+void ChildProcessHost::OnMessagePipeCreated() { |
+ controller_.Bind( |
+ InterfacePtrInfo<mojom::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 |