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 |