Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(199)

Side by Side Diff: mojo/shell/child_process.cc

Issue 1091513005: Separate mojo/shell into a runner and the application manager (shell) (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 5 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « mojo/shell/child_process.h ('k') | mojo/shell/child_process.mojom » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "mojo/shell/child_process.h"
6
7 #include "base/base_switches.h"
8 #include "base/bind.h"
9 #include "base/callback_helpers.h"
10 #include "base/command_line.h"
11 #include "base/debug/debugger.h"
12 #include "base/files/file_path.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/macros.h"
16 #include "base/memory/ref_counted.h"
17 #include "base/memory/scoped_ptr.h"
18 #include "base/message_loop/message_loop.h"
19 #include "base/single_thread_task_runner.h"
20 #include "base/synchronization/waitable_event.h"
21 #include "base/threading/thread.h"
22 #include "base/threading/thread_checker.h"
23 #include "mojo/common/message_pump_mojo.h"
24 #include "mojo/edk/embedder/embedder.h"
25 #include "mojo/edk/embedder/platform_channel_pair.h"
26 #include "mojo/edk/embedder/process_delegate.h"
27 #include "mojo/edk/embedder/scoped_platform_handle.h"
28 #include "mojo/edk/embedder/simple_platform_support.h"
29 #include "mojo/public/cpp/system/core.h"
30 #include "mojo/shell/child_process.mojom.h"
31 #include "mojo/shell/native_application_support.h"
32 #include "mojo/shell/switches.h"
33
34 namespace mojo {
35 namespace shell {
36
37 namespace {
38
39 // Blocker ---------------------------------------------------------------------
40
41 // Blocks a thread until another thread unblocks it, at which point it unblocks
42 // and runs a closure provided by that thread.
43 class Blocker {
44 public:
45 class Unblocker {
46 public:
47 explicit Unblocker(Blocker* blocker = nullptr) : blocker_(blocker) {}
48 ~Unblocker() {}
49
50 void Unblock(base::Closure run_after) {
51 DCHECK(blocker_);
52 DCHECK(blocker_->run_after_.is_null());
53 blocker_->run_after_ = run_after;
54 blocker_->event_.Signal();
55 blocker_ = nullptr;
56 }
57
58 private:
59 Blocker* blocker_;
60
61 // Copy and assign allowed.
62 };
63
64 Blocker() : event_(true, false) {}
65 ~Blocker() {}
66
67 void Block() {
68 DCHECK(run_after_.is_null());
69 event_.Wait();
70 run_after_.Run();
71 }
72
73 Unblocker GetUnblocker() { return Unblocker(this); }
74
75 private:
76 base::WaitableEvent event_;
77 base::Closure run_after_;
78
79 DISALLOW_COPY_AND_ASSIGN(Blocker);
80 };
81
82 // AppContext ------------------------------------------------------------------
83
84 class ChildControllerImpl;
85
86 // Should be created and initialized on the main thread.
87 class AppContext : public embedder::ProcessDelegate {
88 public:
89 AppContext()
90 : io_thread_("io_thread"), controller_thread_("controller_thread") {}
91 ~AppContext() override {}
92
93 void Init() {
94 // Initialize Mojo before starting any threads.
95 embedder::Init(make_scoped_ptr(new embedder::SimplePlatformSupport()));
96
97 // Create and start our I/O thread.
98 base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0);
99 CHECK(io_thread_.StartWithOptions(io_thread_options));
100 io_runner_ = io_thread_.message_loop_proxy().get();
101 CHECK(io_runner_.get());
102
103 // Create and start our controller thread.
104 base::Thread::Options controller_thread_options;
105 controller_thread_options.message_loop_type =
106 base::MessageLoop::TYPE_CUSTOM;
107 controller_thread_options.message_pump_factory =
108 base::Bind(&common::MessagePumpMojo::Create);
109 CHECK(controller_thread_.StartWithOptions(controller_thread_options));
110 controller_runner_ = controller_thread_.message_loop_proxy().get();
111 CHECK(controller_runner_.get());
112
113 // TODO(vtl): This should be SLAVE, not NONE.
114 embedder::InitIPCSupport(embedder::ProcessType::NONE, controller_runner_,
115 this, io_runner_,
116 embedder::ScopedPlatformHandle());
117 }
118
119 void Shutdown() {
120 Blocker blocker;
121 shutdown_unblocker_ = blocker.GetUnblocker();
122 controller_runner_->PostTask(
123 FROM_HERE, base::Bind(&AppContext::ShutdownOnControllerThread,
124 base::Unretained(this)));
125 blocker.Block();
126 }
127
128 base::SingleThreadTaskRunner* io_runner() const { return io_runner_.get(); }
129
130 base::SingleThreadTaskRunner* controller_runner() const {
131 return controller_runner_.get();
132 }
133
134 ChildControllerImpl* controller() const { return controller_.get(); }
135
136 void set_controller(scoped_ptr<ChildControllerImpl> controller) {
137 controller_ = controller.Pass();
138 }
139
140 private:
141 void ShutdownOnControllerThread() {
142 // First, destroy the controller.
143 controller_.reset();
144
145 // Next shutdown IPC. We'll unblock the main thread in OnShutdownComplete().
146 embedder::ShutdownIPCSupport();
147 }
148
149 // ProcessDelegate implementation.
150 void OnShutdownComplete() override {
151 shutdown_unblocker_.Unblock(base::Closure());
152 }
153
154 base::Thread io_thread_;
155 scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
156
157 base::Thread controller_thread_;
158 scoped_refptr<base::SingleThreadTaskRunner> controller_runner_;
159
160 // Accessed only on the controller thread.
161 scoped_ptr<ChildControllerImpl> controller_;
162
163 // Used to unblock the main thread on shutdown.
164 Blocker::Unblocker shutdown_unblocker_;
165
166 DISALLOW_COPY_AND_ASSIGN(AppContext);
167 };
168
169 // ChildControllerImpl ------------------------------------------------------
170
171 class ChildControllerImpl : public ChildController, public ErrorHandler {
172 public:
173 ~ChildControllerImpl() override {
174 DCHECK(thread_checker_.CalledOnValidThread());
175
176 // TODO(vtl): Pass in the result from |MainMain()|.
177 on_app_complete_.Run(MOJO_RESULT_UNIMPLEMENTED);
178 }
179
180 // To be executed on the controller thread. Creates the |ChildController|,
181 // etc.
182 static void Init(AppContext* app_context,
183 embedder::ScopedPlatformHandle platform_channel,
184 const Blocker::Unblocker& unblocker) {
185 DCHECK(app_context);
186 DCHECK(platform_channel.is_valid());
187
188 DCHECK(!app_context->controller());
189
190 scoped_ptr<ChildControllerImpl> impl(
191 new ChildControllerImpl(app_context, unblocker));
192
193 ScopedMessagePipeHandle host_message_pipe(embedder::CreateChannel(
194 platform_channel.Pass(), app_context->io_runner(),
195 base::Bind(&ChildControllerImpl::DidCreateChannel,
196 base::Unretained(impl.get())),
197 base::MessageLoopProxy::current()));
198
199 impl->Bind(host_message_pipe.Pass());
200
201 app_context->set_controller(impl.Pass());
202 }
203
204 void Bind(ScopedMessagePipeHandle handle) { binding_.Bind(handle.Pass()); }
205
206 // |ErrorHandler| methods:
207 void OnConnectionError() override {
208 // A connection error means the connection to the shell is lost. This is not
209 // recoverable.
210 LOG(ERROR) << "Connection error to the shell.";
211 _exit(1);
212 }
213
214 // |ChildController| methods:
215 void StartApp(const String& app_path,
216 bool clean_app_path,
217 InterfaceRequest<Application> application_request,
218 const StartAppCallback& on_app_complete) override {
219 DVLOG(2) << "ChildControllerImpl::StartApp(" << app_path << ", ...)";
220 DCHECK(thread_checker_.CalledOnValidThread());
221
222 on_app_complete_ = on_app_complete;
223 unblocker_.Unblock(base::Bind(&ChildControllerImpl::StartAppOnMainThread,
224 base::FilePath::FromUTF8Unsafe(app_path),
225 clean_app_path
226 ? NativeApplicationCleanup::DELETE
227 : NativeApplicationCleanup::DONT_DELETE,
228 base::Passed(&application_request)));
229 }
230
231 void ExitNow(int32_t exit_code) override {
232 DVLOG(2) << "ChildControllerImpl::ExitNow(" << exit_code << ")";
233 _exit(exit_code);
234 }
235
236 private:
237 ChildControllerImpl(AppContext* app_context,
238 const Blocker::Unblocker& unblocker)
239 : app_context_(app_context),
240 unblocker_(unblocker),
241 channel_info_(nullptr),
242 binding_(this) {
243 binding_.set_error_handler(this);
244 }
245
246 // Callback for |embedder::CreateChannel()|.
247 void DidCreateChannel(embedder::ChannelInfo* channel_info) {
248 DVLOG(2) << "ChildControllerImpl::DidCreateChannel()";
249 DCHECK(thread_checker_.CalledOnValidThread());
250 channel_info_ = channel_info;
251 }
252
253 static void StartAppOnMainThread(
254 const base::FilePath& app_path,
255 NativeApplicationCleanup cleanup,
256 InterfaceRequest<Application> application_request) {
257 // TODO(vtl): This is copied from in_process_native_runner.cc.
258 DVLOG(2) << "Loading/running Mojo app from " << app_path.value()
259 << " out of process";
260
261 // We intentionally don't unload the native library as its lifetime is the
262 // same as that of the process.
263 base::NativeLibrary app_library = LoadNativeApplication(app_path, cleanup);
264 RunNativeApplication(app_library, application_request.Pass());
265 }
266
267 base::ThreadChecker thread_checker_;
268 AppContext* const app_context_;
269 Blocker::Unblocker unblocker_;
270 StartAppCallback on_app_complete_;
271
272 embedder::ChannelInfo* channel_info_;
273 Binding<ChildController> binding_;
274
275 DISALLOW_COPY_AND_ASSIGN(ChildControllerImpl);
276 };
277
278 } // namespace
279
280 int ChildProcessMain() {
281 DVLOG(2) << "ChildProcessMain()";
282 const base::CommandLine& command_line =
283 *base::CommandLine::ForCurrentProcess();
284 if (command_line.HasSwitch(switches::kWaitForDebugger)) {
285 std::string app = command_line.GetSwitchValueASCII(switches::kApp);
286 #if defined(OS_WIN)
287 MessageBox(NULL, command_line.GetSwitchValueNative(switches::kApp).c_str(),
288 command_line.GetSwitchValueNative(switches::kApp).c_str(),
289 MB_OK | MB_SETFOREGROUND);
290 #else
291 LOG(ERROR) << command_line.GetSwitchValueASCII(switches::kApp)
292 << " waiting for GDB. pid: " << getpid();
293 base::debug::WaitForDebugger(60, true);
294 #endif
295 }
296
297 embedder::ScopedPlatformHandle platform_channel =
298 embedder::PlatformChannelPair::PassClientHandleFromParentProcess(
299 command_line);
300 CHECK(platform_channel.is_valid());
301
302 DCHECK(!base::MessageLoop::current());
303
304 AppContext app_context;
305 app_context.Init();
306
307 Blocker blocker;
308 app_context.controller_runner()->PostTask(
309 FROM_HERE,
310 base::Bind(&ChildControllerImpl::Init, base::Unretained(&app_context),
311 base::Passed(&platform_channel), blocker.GetUnblocker()));
312 // This will block, then run whatever the controller wants.
313 blocker.Block();
314
315 app_context.Shutdown();
316
317 return 0;
318 }
319
320 } // namespace shell
321 } // namespace mojo
OLDNEW
« no previous file with comments | « mojo/shell/child_process.h ('k') | mojo/shell/child_process.mojom » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698