| OLD | NEW |
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "mojo/shell/runner/host/child_process_host.h" | 5 #include "mojo/shell/runner/host/child_process_host.h" |
| 6 | 6 |
| 7 #include <stdint.h> | 7 #include <stdint.h> |
| 8 | 8 |
| 9 #include <utility> | 9 #include <utility> |
| 10 | 10 |
| (...skipping 17 matching lines...) Expand all Loading... |
| 28 #include "sandbox/linux/services/namespace_sandbox.h" | 28 #include "sandbox/linux/services/namespace_sandbox.h" |
| 29 #endif | 29 #endif |
| 30 | 30 |
| 31 #if defined(OS_WIN) | 31 #if defined(OS_WIN) |
| 32 #include "base/win/windows_version.h" | 32 #include "base/win/windows_version.h" |
| 33 #endif | 33 #endif |
| 34 | 34 |
| 35 namespace mojo { | 35 namespace mojo { |
| 36 namespace shell { | 36 namespace shell { |
| 37 | 37 |
| 38 ChildProcessHost::PipeHolder::PipeHolder() {} | |
| 39 | |
| 40 void ChildProcessHost::PipeHolder::Reject() { | |
| 41 base::AutoLock lock(lock_); | |
| 42 reject_pipe_ = true; | |
| 43 pipe_.reset(); | |
| 44 } | |
| 45 | |
| 46 void ChildProcessHost::PipeHolder::SetPipe(ScopedMessagePipeHandle pipe) { | |
| 47 base::AutoLock lock(lock_); | |
| 48 DCHECK(!pipe_.is_valid()); | |
| 49 if (!reject_pipe_) | |
| 50 pipe_ = std::move(pipe); | |
| 51 } | |
| 52 | |
| 53 ScopedMessagePipeHandle ChildProcessHost::PipeHolder::PassPipe() { | |
| 54 base::AutoLock lock(lock_); | |
| 55 DCHECK(pipe_.is_valid()); | |
| 56 return std::move(pipe_); | |
| 57 } | |
| 58 | |
| 59 ChildProcessHost::PipeHolder::~PipeHolder() {} | |
| 60 | |
| 61 ChildProcessHost::ChildProcessHost(base::TaskRunner* launch_process_runner, | 38 ChildProcessHost::ChildProcessHost(base::TaskRunner* launch_process_runner, |
| 62 bool start_sandboxed, | 39 bool start_sandboxed, |
| 63 const base::FilePath& app_path) | 40 const base::FilePath& app_path) |
| 64 : launch_process_runner_(launch_process_runner), | 41 : launch_process_runner_(launch_process_runner), |
| 65 start_sandboxed_(start_sandboxed), | 42 start_sandboxed_(start_sandboxed), |
| 66 app_path_(app_path), | 43 app_path_(app_path), |
| 67 channel_info_(nullptr), | |
| 68 start_child_process_event_(false, false), | 44 start_child_process_event_(false, false), |
| 69 weak_factory_(this) { | 45 weak_factory_(this) { |
| 70 pipe_holder_ = new PipeHolder(); | 46 node_channel_.reset(new edk::PlatformChannelPair); |
| 71 if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { | 47 primordial_pipe_token_ = edk::GenerateRandomToken(); |
| 72 node_channel_.reset(new edk::PlatformChannelPair); | 48 controller_.Bind( |
| 73 primordial_pipe_token_ = edk::GenerateRandomToken(); | 49 InterfacePtrInfo<mojom::ChildController>( |
| 74 } else { | 50 edk::CreateParentMessagePipe(primordial_pipe_token_), 0u)); |
| 75 pipe_holder_->SetPipe(embedder::CreateChannel( | |
| 76 platform_channel_pair_.PassServerHandle(), | |
| 77 base::Bind(&ChildProcessHost::DidCreateChannel, base::Unretained(this)), | |
| 78 base::ThreadTaskRunnerHandle::Get())); | |
| 79 OnMessagePipeCreated(); | |
| 80 } | |
| 81 } | 51 } |
| 82 | 52 |
| 83 ChildProcessHost::ChildProcessHost(ScopedHandle channel) | 53 ChildProcessHost::ChildProcessHost(ScopedHandle channel) |
| 84 : launch_process_runner_(nullptr), | 54 : launch_process_runner_(nullptr), |
| 85 start_sandboxed_(false), | 55 start_sandboxed_(false), |
| 86 channel_info_(nullptr), | |
| 87 start_child_process_event_(false, false), | 56 start_child_process_event_(false, false), |
| 88 weak_factory_(this) { | 57 weak_factory_(this) { |
| 89 CHECK(channel.is_valid()); | 58 CHECK(channel.is_valid()); |
| 90 ScopedMessagePipeHandle handle(MessagePipeHandle(channel.release().value())); | 59 ScopedMessagePipeHandle handle(MessagePipeHandle(channel.release().value())); |
| 91 controller_.Bind( | 60 controller_.Bind( |
| 92 InterfacePtrInfo<mojom::ChildController>(std::move(handle), 0u)); | 61 InterfacePtrInfo<mojom::ChildController>(std::move(handle), 0u)); |
| 93 } | 62 } |
| 94 | 63 |
| 95 ChildProcessHost::~ChildProcessHost() { | 64 ChildProcessHost::~ChildProcessHost() { |
| 96 if (!app_path_.empty()) | 65 if (!app_path_.empty()) |
| 97 CHECK(!controller_) << "Destroying ChildProcessHost before calling Join"; | 66 CHECK(!controller_) << "Destroying ChildProcessHost before calling Join"; |
| 98 } | 67 } |
| 99 | 68 |
| 100 void ChildProcessHost::Start(const ProcessReadyCallback& callback) { | 69 void ChildProcessHost::Start(const ProcessReadyCallback& callback) { |
| 101 DCHECK(!child_process_.IsValid()); | 70 DCHECK(!child_process_.IsValid()); |
| 102 DCHECK(process_ready_callback_.is_null()); | |
| 103 | |
| 104 process_ready_callback_ = callback; | |
| 105 if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { | |
| 106 // With the new EDK, bootstrap message pipes are created asynchronously. | |
| 107 // We recieve the bound pipe (if successful) on an arbitrary thread, | |
| 108 // stash it in the thread-safe |pipe_holder_|, and then try to call | |
| 109 // OnMessagePipeCreated() on the host's main thread. | |
| 110 // | |
| 111 // Because of the way the launcher process shuts down, it's possible for | |
| 112 // the main thread's MessageLoop to stop running (but not yet be destroyed!) | |
| 113 // while this boostrap is pending, resulting in OnMessagePipeCreated() never | |
| 114 // being called. | |
| 115 // | |
| 116 // A typical child process (i.e. one using ShellConnection to bind the other | |
| 117 // end of this pipe) may hang forever waiting for an Initialize() message | |
| 118 // unless the pipe is closed. This in turn means that Join() could hang | |
| 119 // waiting for the process to exit. Deadlock! | |
| 120 // | |
| 121 // |pipe_holder_| exists for this reason. If it's still holding onto the | |
| 122 // pipe when Join() is called, the pipe will be closed. | |
| 123 DCHECK(!primordial_pipe_token_.empty()); | |
| 124 edk::CreateParentMessagePipe( | |
| 125 primordial_pipe_token_, | |
| 126 base::Bind(&OnParentMessagePipeCreated, pipe_holder_, | |
| 127 base::ThreadTaskRunnerHandle::Get(), | |
| 128 base::Bind(&ChildProcessHost::OnMessagePipeCreated, | |
| 129 weak_factory_.GetWeakPtr()))); | |
| 130 } | |
| 131 | |
| 132 launch_process_runner_->PostTaskAndReply( | 71 launch_process_runner_->PostTaskAndReply( |
| 133 FROM_HERE, | 72 FROM_HERE, |
| 134 base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this)), | 73 base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this)), |
| 135 base::Bind(&ChildProcessHost::DidStart, weak_factory_.GetWeakPtr())); | 74 base::Bind(&ChildProcessHost::DidStart, |
| 75 weak_factory_.GetWeakPtr(), callback)); |
| 136 } | 76 } |
| 137 | 77 |
| 138 int ChildProcessHost::Join() { | 78 int ChildProcessHost::Join() { |
| 139 if (controller_) // We use this as a signal that Start was called. | 79 if (controller_) // We use this as a signal that Start was called. |
| 140 start_child_process_event_.Wait(); | 80 start_child_process_event_.Wait(); |
| 141 | 81 |
| 142 controller_ = mojom::ChildControllerPtr(); | 82 controller_ = mojom::ChildControllerPtr(); |
| 143 DCHECK(child_process_.IsValid()); | 83 DCHECK(child_process_.IsValid()); |
| 144 | 84 |
| 145 // Ensure the child pipe is closed even if it wasn't yet connected to the | |
| 146 // controller. | |
| 147 pipe_holder_->Reject(); | |
| 148 | |
| 149 int rv = -1; | 85 int rv = -1; |
| 150 LOG_IF(ERROR, !child_process_.WaitForExit(&rv)) | 86 LOG_IF(ERROR, !child_process_.WaitForExit(&rv)) |
| 151 << "Failed to wait for child process"; | 87 << "Failed to wait for child process"; |
| 152 | 88 |
| 153 child_process_.Close(); | 89 child_process_.Close(); |
| 154 | 90 |
| 155 return rv; | 91 return rv; |
| 156 } | 92 } |
| 157 | 93 |
| 158 void ChildProcessHost::StartApp( | 94 void ChildProcessHost::StartApp( |
| 159 InterfaceRequest<mojom::ShellClient> request, | 95 InterfaceRequest<mojom::ShellClient> request, |
| 160 const mojom::ChildController::StartAppCallback& on_app_complete) { | 96 const mojom::ChildController::StartAppCallback& on_app_complete) { |
| 161 DCHECK(controller_); | 97 DCHECK(controller_); |
| 162 | 98 |
| 163 on_app_complete_ = on_app_complete; | 99 on_app_complete_ = on_app_complete; |
| 164 controller_->StartApp( | 100 controller_->StartApp( |
| 165 std::move(request), | 101 std::move(request), |
| 166 base::Bind(&ChildProcessHost::AppCompleted, weak_factory_.GetWeakPtr())); | 102 base::Bind(&ChildProcessHost::AppCompleted, weak_factory_.GetWeakPtr())); |
| 167 } | 103 } |
| 168 | 104 |
| 169 void ChildProcessHost::ExitNow(int32_t exit_code) { | 105 void ChildProcessHost::ExitNow(int32_t exit_code) { |
| 170 DCHECK(controller_); | 106 DCHECK(controller_); |
| 171 | 107 |
| 172 controller_->ExitNow(exit_code); | 108 controller_->ExitNow(exit_code); |
| 173 } | 109 } |
| 174 | 110 |
| 175 void ChildProcessHost::DidStart() { | 111 void ChildProcessHost::DidStart(const ProcessReadyCallback& callback) { |
| 176 DVLOG(2) << "ChildProcessHost::DidStart()"; | 112 DVLOG(2) << "ChildProcessHost::DidStart()"; |
| 177 | 113 |
| 178 if (child_process_.IsValid()) { | 114 if (child_process_.IsValid()) { |
| 179 MaybeNotifyProcessReady(); | 115 callback.Run(child_process_.Pid()); |
| 180 } else { | 116 } else { |
| 181 LOG(ERROR) << "Failed to start child process"; | 117 LOG(ERROR) << "Failed to start child process"; |
| 182 AppCompleted(MOJO_RESULT_UNKNOWN); | 118 AppCompleted(MOJO_RESULT_UNKNOWN); |
| 183 } | 119 } |
| 184 } | 120 } |
| 185 | 121 |
| 186 void ChildProcessHost::DoLaunch() { | 122 void ChildProcessHost::DoLaunch() { |
| 187 const base::CommandLine* parent_command_line = | 123 const base::CommandLine* parent_command_line = |
| 188 base::CommandLine::ForCurrentProcess(); | 124 base::CommandLine::ForCurrentProcess(); |
| 189 base::FilePath target_path = parent_command_line->GetProgram(); | 125 base::FilePath target_path = parent_command_line->GetProgram(); |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 271 } | 207 } |
| 272 | 208 |
| 273 void ChildProcessHost::AppCompleted(int32_t result) { | 209 void ChildProcessHost::AppCompleted(int32_t result) { |
| 274 if (!on_app_complete_.is_null()) { | 210 if (!on_app_complete_.is_null()) { |
| 275 auto on_app_complete = on_app_complete_; | 211 auto on_app_complete = on_app_complete_; |
| 276 on_app_complete_.reset(); | 212 on_app_complete_.reset(); |
| 277 on_app_complete.Run(result); | 213 on_app_complete.Run(result); |
| 278 } | 214 } |
| 279 } | 215 } |
| 280 | 216 |
| 281 void ChildProcessHost::DidCreateChannel(embedder::ChannelInfo* channel_info) { | |
| 282 DVLOG(2) << "AppChildProcessHost::DidCreateChannel()"; | |
| 283 | |
| 284 DCHECK(channel_info || | |
| 285 base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")); | |
| 286 channel_info_ = channel_info; | |
| 287 } | |
| 288 | |
| 289 void ChildProcessHost::OnMessagePipeCreated() { | |
| 290 controller_.Bind( | |
| 291 InterfacePtrInfo<mojom::ChildController>(pipe_holder_->PassPipe(), 0u)); | |
| 292 MaybeNotifyProcessReady(); | |
| 293 } | |
| 294 | |
| 295 void ChildProcessHost::MaybeNotifyProcessReady() { | |
| 296 if (controller_.is_bound() && child_process_.IsValid()) | |
| 297 process_ready_callback_.Run(child_process_.Pid()); | |
| 298 } | |
| 299 | |
| 300 // static | |
| 301 void ChildProcessHost::OnParentMessagePipeCreated( | |
| 302 scoped_refptr<PipeHolder> holder, | |
| 303 scoped_refptr<base::TaskRunner> callback_task_runner, | |
| 304 const base::Closure& callback, | |
| 305 ScopedMessagePipeHandle pipe) { | |
| 306 holder->SetPipe(std::move(pipe)); | |
| 307 callback_task_runner->PostTask(FROM_HERE, callback); | |
| 308 } | |
| 309 | |
| 310 } // namespace shell | 217 } // namespace shell |
| 311 } // namespace mojo | 218 } // namespace mojo |
| OLD | NEW |