summaryrefslogtreecommitdiffstats
path: root/mojo/shell/runner
diff options
context:
space:
mode:
Diffstat (limited to 'mojo/shell/runner')
-rw-r--r--mojo/shell/runner/child/runner_connection.cc16
-rw-r--r--mojo/shell/runner/host/child_process.cc53
-rw-r--r--mojo/shell/runner/host/child_process_host.cc111
-rw-r--r--mojo/shell/runner/host/child_process_host.h62
4 files changed, 212 insertions, 30 deletions
diff --git a/mojo/shell/runner/child/runner_connection.cc b/mojo/shell/runner/child/runner_connection.cc
index 313c00a..75b4648 100644
--- a/mojo/shell/runner/child/runner_connection.cc
+++ b/mojo/shell/runner/child/runner_connection.cc
@@ -74,6 +74,13 @@ class Blocker {
using GotApplicationRequestCallback =
base::Callback<void(InterfaceRequest<mojom::ShellClient>)>;
+void OnCreateMessagePipe(ScopedMessagePipeHandle* result,
+ Blocker::Unblocker unblocker,
+ ScopedMessagePipeHandle pipe) {
+ *result = std::move(pipe);
+ unblocker.Unblock(base::Bind(&base::DoNothing));
+}
+
void OnGotApplicationRequest(InterfaceRequest<mojom::ShellClient>* out_request,
InterfaceRequest<mojom::ShellClient> request) {
*out_request = std::move(request);
@@ -222,11 +229,14 @@ bool RunnerConnectionImpl::WaitForApplicationRequest(
std::string primordial_pipe_token =
base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
switches::kPrimordialPipeToken);
- handle = edk::CreateChildMessagePipe(primordial_pipe_token);
+ Blocker blocker;
+ edk::CreateChildMessagePipe(
+ primordial_pipe_token,
+ base::Bind(&OnCreateMessagePipe, base::Unretained(&handle),
+ blocker.GetUnblocker()));
+ blocker.Block();
}
- DCHECK(handle.is_valid());
-
Blocker blocker;
controller_runner_->PostTask(
FROM_HERE,
diff --git a/mojo/shell/runner/host/child_process.cc b/mojo/shell/runner/host/child_process.cc
index 75230f0..0208b84 100644
--- a/mojo/shell/runner/host/child_process.cc
+++ b/mojo/shell/runner/host/child_process.cc
@@ -51,6 +51,10 @@ namespace shell {
namespace {
+void DidCreateChannel(embedder::ChannelInfo* channel_info) {
+ DVLOG(2) << "ChildControllerImpl::DidCreateChannel()";
+}
+
// Blocker ---------------------------------------------------------------------
// Blocks a thread until another thread unblocks it, at which point it unblocks
@@ -296,14 +300,34 @@ scoped_ptr<mojo::shell::LinuxSandbox> InitializeSandbox() {
}
#endif
-ScopedMessagePipeHandle InitializeHostMessagePipe(
- edk::ScopedPlatformHandle platform_channel,
- scoped_refptr<base::TaskRunner> io_task_runner) {
- edk::SetParentPipeHandle(std::move(platform_channel));
- std::string primordial_pipe_token =
- base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
- switches::kPrimordialPipeToken);
- return edk::CreateChildMessagePipe(primordial_pipe_token);
+void InitializeHostMessagePipe(
+ embedder::ScopedPlatformHandle platform_channel,
+ scoped_refptr<base::TaskRunner> io_task_runner,
+ const base::Callback<void(ScopedMessagePipeHandle)>& callback) {
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) {
+ embedder::SetParentPipeHandle(std::move(platform_channel));
+ std::string primordial_pipe_token =
+ base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kPrimordialPipeToken);
+ edk::CreateChildMessagePipe(primordial_pipe_token, callback);
+ } else {
+ ScopedMessagePipeHandle host_message_pipe;
+ host_message_pipe =
+ embedder::CreateChannel(std::move(platform_channel),
+ base::Bind(&DidCreateChannel), io_task_runner);
+ callback.Run(std::move(host_message_pipe));
+ }
+}
+
+void OnHostMessagePipeCreated(AppContext* app_context,
+ base::NativeLibrary app_library,
+ const Blocker::Unblocker& unblocker,
+ ScopedMessagePipeHandle pipe) {
+ app_context->controller_runner()->PostTask(
+ FROM_HERE,
+ base::Bind(&ChildControllerImpl::Init, base::Unretained(app_context),
+ base::Unretained(app_library), base::Passed(&pipe),
+ unblocker));
}
} // namespace
@@ -336,8 +360,8 @@ int ChildProcessMain() {
sandbox = InitializeSandbox();
#endif
- edk::ScopedPlatformHandle platform_channel =
- edk::PlatformChannelPair::PassClientHandleFromParentProcess(
+ embedder::ScopedPlatformHandle platform_channel =
+ embedder::PlatformChannelPair::PassClientHandleFromParentProcess(
command_line);
CHECK(platform_channel.is_valid());
@@ -348,12 +372,13 @@ int ChildProcessMain() {
app_context.Init();
app_context.StartControllerThread();
- ScopedMessagePipeHandle host_pipe = InitializeHostMessagePipe(
- std::move(platform_channel), app_context.io_runner());
app_context.controller_runner()->PostTask(
FROM_HERE,
- base::Bind(&ChildControllerImpl::Init, &app_context, app_library,
- base::Passed(&host_pipe), blocker.GetUnblocker()));
+ base::Bind(
+ &InitializeHostMessagePipe, base::Passed(&platform_channel),
+ make_scoped_refptr(app_context.io_runner()),
+ base::Bind(&OnHostMessagePipeCreated, base::Unretained(&app_context),
+ base::Unretained(app_library), blocker.GetUnblocker())));
// This will block, then run whatever the controller wants.
blocker.Block();
diff --git a/mojo/shell/runner/host/child_process_host.cc b/mojo/shell/runner/host/child_process_host.cc
index 2b3cfc9..9a5b439 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 @@ ChildProcessHost::~ChildProcessHost() {
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() {
@@ -82,6 +142,10 @@ int ChildProcessHost::Join() {
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))
<< "Failed to wait for child process";
@@ -108,11 +172,11 @@ void ChildProcessHost::ExitNow(int32_t exit_code) {
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::AppCompleted(int32_t result) {
}
}
+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
diff --git a/mojo/shell/runner/host/child_process_host.h b/mojo/shell/runner/host/child_process_host.h
index 2664d30..906a1a2 100644
--- a/mojo/shell/runner/host/child_process_host.h
+++ b/mojo/shell/runner/host/child_process_host.h
@@ -71,24 +71,70 @@ class ChildProcessHost {
void ExitNow(int32_t exit_code);
protected:
- void DidStart(const ProcessReadyCallback& callback);
+ void DidStart();
private:
+ // A thread-safe holder for the bootstrap message pipe to this child process.
+ // The pipe is established on an arbitrary thread and may not be connected
+ // until the host's message loop has stopped running.
+ class PipeHolder : public base::RefCountedThreadSafe<PipeHolder> {
+ public:
+ PipeHolder();
+
+ void Reject();
+ void SetPipe(ScopedMessagePipeHandle pipe);
+ ScopedMessagePipeHandle PassPipe();
+
+ private:
+ friend class base::RefCountedThreadSafe<PipeHolder>;
+
+ ~PipeHolder();
+
+ base::Lock lock_;
+ bool reject_pipe_ = false;
+ ScopedMessagePipeHandle pipe_;
+
+ DISALLOW_COPY_AND_ASSIGN(PipeHolder);
+ };
+
void DoLaunch();
void AppCompleted(int32_t result);
+ // Callback for |embedder::CreateChannel()|.
+ void DidCreateChannel(embedder::ChannelInfo* channel_info);
+
+ // Called once |pipe_holder_| is bound to a pipe.
+ void OnMessagePipeCreated();
+
+ // Called when the child process is launched and when the bootstrap
+ // message pipe is created. Once both things have happened (which may happen
+ // in either order), |process_ready_callback_| is invoked.
+ void MaybeNotifyProcessReady();
+
+ // Callback used to receive the child message pipe from the ports EDK.
+ // This may be called on any thread. It will always stash the pipe in
+ // |holder|, and it will then attempt to call |callback| on
+ // |callback_task_runner| (which may or may not still be running tasks.)
+ static void OnParentMessagePipeCreated(
+ scoped_refptr<PipeHolder> holder,
+ scoped_refptr<base::TaskRunner> callback_task_runner,
+ const base::Closure& callback,
+ ScopedMessagePipeHandle pipe);
+
scoped_refptr<base::TaskRunner> launch_process_runner_;
bool start_sandboxed_;
const base::FilePath app_path_;
base::Process child_process_;
// Used for the ChildController binding.
- edk::PlatformChannelPair platform_channel_pair_;
+ embedder::PlatformChannelPair platform_channel_pair_;
mojom::ChildControllerPtr controller_;
+ embedder::ChannelInfo* channel_info_;
mojom::ChildController::StartAppCallback on_app_complete_;
- edk::HandlePassingInformation handle_passing_info_;
+ embedder::HandlePassingInformation handle_passing_info_;
- // Used to back the NodeChannel between the parent and child node.
+ // Used only when --use-new-edk is specified. Used to back the NodeChannel
+ // between the parent and child node.
scoped_ptr<edk::PlatformChannelPair> node_channel_;
// Since Start() calls a method on another thread, we use an event to block
@@ -98,6 +144,14 @@ class ChildProcessHost {
// A token the child can use to connect a primordial pipe to the host.
std::string primordial_pipe_token_;
+ // Holds the message pipe to the child process until it is either closed or
+ // bound to the controller interface.
+ scoped_refptr<PipeHolder> pipe_holder_;
+
+ // Invoked exactly once, as soon as the child process's ID is known and
+ // a pipe to the child has been established.
+ ProcessReadyCallback process_ready_callback_;
+
base::WeakPtrFactory<ChildProcessHost> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ChildProcessHost);