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 |
38 ChildProcessHost::ChildProcessHost(base::TaskRunner* launch_process_runner, | 61 ChildProcessHost::ChildProcessHost(base::TaskRunner* launch_process_runner, |
39 bool start_sandboxed, | 62 bool start_sandboxed, |
40 const base::FilePath& app_path) | 63 const base::FilePath& app_path) |
41 : launch_process_runner_(launch_process_runner), | 64 : launch_process_runner_(launch_process_runner), |
42 start_sandboxed_(start_sandboxed), | 65 start_sandboxed_(start_sandboxed), |
43 app_path_(app_path), | 66 app_path_(app_path), |
| 67 channel_info_(nullptr), |
44 start_child_process_event_(false, false), | 68 start_child_process_event_(false, false), |
45 weak_factory_(this) { | 69 weak_factory_(this) { |
46 node_channel_.reset(new edk::PlatformChannelPair); | 70 pipe_holder_ = new PipeHolder(); |
47 primordial_pipe_token_ = edk::GenerateRandomToken(); | 71 if (base::CommandLine::ForCurrentProcess()->HasSwitch("use-new-edk")) { |
48 controller_.Bind( | 72 node_channel_.reset(new edk::PlatformChannelPair); |
49 InterfacePtrInfo<mojom::ChildController>( | 73 primordial_pipe_token_ = edk::GenerateRandomToken(); |
50 edk::CreateParentMessagePipe(primordial_pipe_token_), 0u)); | 74 } else { |
| 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 } |
51 } | 81 } |
52 | 82 |
53 ChildProcessHost::ChildProcessHost(ScopedHandle channel) | 83 ChildProcessHost::ChildProcessHost(ScopedHandle channel) |
54 : launch_process_runner_(nullptr), | 84 : launch_process_runner_(nullptr), |
55 start_sandboxed_(false), | 85 start_sandboxed_(false), |
| 86 channel_info_(nullptr), |
56 start_child_process_event_(false, false), | 87 start_child_process_event_(false, false), |
57 weak_factory_(this) { | 88 weak_factory_(this) { |
58 CHECK(channel.is_valid()); | 89 CHECK(channel.is_valid()); |
59 ScopedMessagePipeHandle handle(MessagePipeHandle(channel.release().value())); | 90 ScopedMessagePipeHandle handle(MessagePipeHandle(channel.release().value())); |
60 controller_.Bind( | 91 controller_.Bind( |
61 InterfacePtrInfo<mojom::ChildController>(std::move(handle), 0u)); | 92 InterfacePtrInfo<mojom::ChildController>(std::move(handle), 0u)); |
62 } | 93 } |
63 | 94 |
64 ChildProcessHost::~ChildProcessHost() { | 95 ChildProcessHost::~ChildProcessHost() { |
65 if (!app_path_.empty()) | 96 if (!app_path_.empty()) |
66 CHECK(!controller_) << "Destroying ChildProcessHost before calling Join"; | 97 CHECK(!controller_) << "Destroying ChildProcessHost before calling Join"; |
67 } | 98 } |
68 | 99 |
69 void ChildProcessHost::Start(const ProcessReadyCallback& callback) { | 100 void ChildProcessHost::Start(const ProcessReadyCallback& callback) { |
70 DCHECK(!child_process_.IsValid()); | 101 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 |
71 launch_process_runner_->PostTaskAndReply( | 132 launch_process_runner_->PostTaskAndReply( |
72 FROM_HERE, | 133 FROM_HERE, |
73 base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this)), | 134 base::Bind(&ChildProcessHost::DoLaunch, base::Unretained(this)), |
74 base::Bind(&ChildProcessHost::DidStart, | 135 base::Bind(&ChildProcessHost::DidStart, weak_factory_.GetWeakPtr())); |
75 weak_factory_.GetWeakPtr(), callback)); | |
76 } | 136 } |
77 | 137 |
78 int ChildProcessHost::Join() { | 138 int ChildProcessHost::Join() { |
79 if (controller_) // We use this as a signal that Start was called. | 139 if (controller_) // We use this as a signal that Start was called. |
80 start_child_process_event_.Wait(); | 140 start_child_process_event_.Wait(); |
81 | 141 |
82 controller_ = mojom::ChildControllerPtr(); | 142 controller_ = mojom::ChildControllerPtr(); |
83 DCHECK(child_process_.IsValid()); | 143 DCHECK(child_process_.IsValid()); |
84 | 144 |
| 145 // Ensure the child pipe is closed even if it wasn't yet connected to the |
| 146 // controller. |
| 147 pipe_holder_->Reject(); |
| 148 |
85 int rv = -1; | 149 int rv = -1; |
86 LOG_IF(ERROR, !child_process_.WaitForExit(&rv)) | 150 LOG_IF(ERROR, !child_process_.WaitForExit(&rv)) |
87 << "Failed to wait for child process"; | 151 << "Failed to wait for child process"; |
88 | 152 |
89 child_process_.Close(); | 153 child_process_.Close(); |
90 | 154 |
91 return rv; | 155 return rv; |
92 } | 156 } |
93 | 157 |
94 void ChildProcessHost::StartApp( | 158 void ChildProcessHost::StartApp( |
95 InterfaceRequest<mojom::ShellClient> request, | 159 InterfaceRequest<mojom::ShellClient> request, |
96 const mojom::ChildController::StartAppCallback& on_app_complete) { | 160 const mojom::ChildController::StartAppCallback& on_app_complete) { |
97 DCHECK(controller_); | 161 DCHECK(controller_); |
98 | 162 |
99 on_app_complete_ = on_app_complete; | 163 on_app_complete_ = on_app_complete; |
100 controller_->StartApp( | 164 controller_->StartApp( |
101 std::move(request), | 165 std::move(request), |
102 base::Bind(&ChildProcessHost::AppCompleted, weak_factory_.GetWeakPtr())); | 166 base::Bind(&ChildProcessHost::AppCompleted, weak_factory_.GetWeakPtr())); |
103 } | 167 } |
104 | 168 |
105 void ChildProcessHost::ExitNow(int32_t exit_code) { | 169 void ChildProcessHost::ExitNow(int32_t exit_code) { |
106 DCHECK(controller_); | 170 DCHECK(controller_); |
107 | 171 |
108 controller_->ExitNow(exit_code); | 172 controller_->ExitNow(exit_code); |
109 } | 173 } |
110 | 174 |
111 void ChildProcessHost::DidStart(const ProcessReadyCallback& callback) { | 175 void ChildProcessHost::DidStart() { |
112 DVLOG(2) << "ChildProcessHost::DidStart()"; | 176 DVLOG(2) << "ChildProcessHost::DidStart()"; |
113 | 177 |
114 if (child_process_.IsValid()) { | 178 if (child_process_.IsValid()) { |
115 callback.Run(child_process_.Pid()); | 179 MaybeNotifyProcessReady(); |
116 } else { | 180 } else { |
117 LOG(ERROR) << "Failed to start child process"; | 181 LOG(ERROR) << "Failed to start child process"; |
118 AppCompleted(MOJO_RESULT_UNKNOWN); | 182 AppCompleted(MOJO_RESULT_UNKNOWN); |
119 } | 183 } |
120 } | 184 } |
121 | 185 |
122 void ChildProcessHost::DoLaunch() { | 186 void ChildProcessHost::DoLaunch() { |
123 const base::CommandLine* parent_command_line = | 187 const base::CommandLine* parent_command_line = |
124 base::CommandLine::ForCurrentProcess(); | 188 base::CommandLine::ForCurrentProcess(); |
125 base::FilePath target_path = parent_command_line->GetProgram(); | 189 base::FilePath target_path = parent_command_line->GetProgram(); |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
207 } | 271 } |
208 | 272 |
209 void ChildProcessHost::AppCompleted(int32_t result) { | 273 void ChildProcessHost::AppCompleted(int32_t result) { |
210 if (!on_app_complete_.is_null()) { | 274 if (!on_app_complete_.is_null()) { |
211 auto on_app_complete = on_app_complete_; | 275 auto on_app_complete = on_app_complete_; |
212 on_app_complete_.reset(); | 276 on_app_complete_.reset(); |
213 on_app_complete.Run(result); | 277 on_app_complete.Run(result); |
214 } | 278 } |
215 } | 279 } |
216 | 280 |
| 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 |
217 } // namespace shell | 310 } // namespace shell |
218 } // namespace mojo | 311 } // namespace mojo |
OLD | NEW |