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.
5 #include "mojo/shell/app_child_process.h"
8 #include "base/callback_helpers.h"
9 #include "base/files/file_path.h"
10 #include "base/location.h"
11 #include "base/logging.h"
12 #include "base/macros.h"
13 #include "base/memory/ref_counted.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/message_loop/message_loop.h"
16 #include "base/scoped_native_library.h"
17 #include "base/single_thread_task_runner.h"
18 #include "base/synchronization/waitable_event.h"
19 #include "base/threading/thread.h"
20 #include "base/threading/thread_checker.h"
21 #include "mojo/common/message_pump_mojo.h"
22 #include "mojo/embedder/embedder.h"
23 #include "mojo/embedder/simple_platform_support.h"
24 #include "mojo/public/cpp/system/core.h"
25 #include "mojo/shell/app_child_process.mojom.h"
32 // Blocker ---------------------------------------------------------------------
34 // Blocks a thread until another thread unblocks it, at which point it unblocks
35 // and runs a closure provided by that thread.
42 void Unblock(base::Closure run_after
) {
44 DCHECK(blocker_
->run_after_
.is_null());
45 blocker_
->run_after_
= run_after
;
46 blocker_
->event_
.Signal();
52 Unblocker(Blocker
* blocker
) : blocker_(blocker
) {
58 // Copy and assign allowed.
61 Blocker() : event_(true, false) {}
65 DCHECK(run_after_
.is_null());
70 Unblocker
GetUnblocker() {
71 return Unblocker(this);
75 base::WaitableEvent event_
;
76 base::Closure run_after_
;
78 DISALLOW_COPY_AND_ASSIGN(Blocker
);
81 // AppContext ------------------------------------------------------------------
83 class AppChildControllerImpl
;
85 static void DestroyController(scoped_ptr
<AppChildControllerImpl
> controller
) {
88 // Should be created and initialized on the main thread.
92 : io_thread_("io_thread"),
93 controller_thread_("controller_thread") {}
97 // Initialize Mojo before starting any threads.
98 embedder::Init(scoped_ptr
<mojo::embedder::PlatformSupport
>(
99 new mojo::embedder::SimplePlatformSupport()));
101 // Create and start our I/O thread.
102 base::Thread::Options
io_thread_options(base::MessageLoop::TYPE_IO
, 0);
103 CHECK(io_thread_
.StartWithOptions(io_thread_options
));
104 io_runner_
= io_thread_
.message_loop_proxy().get();
105 CHECK(io_runner_
.get());
107 // Create and start our controller thread.
108 base::Thread::Options controller_thread_options
;
109 controller_thread_options
.message_loop_type
=
110 base::MessageLoop::TYPE_CUSTOM
;
111 controller_thread_options
.message_pump_factory
=
112 base::Bind(&common::MessagePumpMojo::Create
);
113 CHECK(controller_thread_
.StartWithOptions(controller_thread_options
));
114 controller_runner_
= controller_thread_
.message_loop_proxy().get();
115 CHECK(controller_runner_
.get());
119 controller_runner_
->PostTask(
121 base::Bind(&DestroyController
, base::Passed(&controller_
)));
124 base::SingleThreadTaskRunner
* io_runner() const {
125 return io_runner_
.get();
128 base::SingleThreadTaskRunner
* controller_runner() const {
129 return controller_runner_
.get();
132 AppChildControllerImpl
* controller() const {
133 return controller_
.get();
136 void set_controller(scoped_ptr
<AppChildControllerImpl
> controller
) {
137 controller_
= controller
.Pass();
141 // Accessed only on the controller thread.
142 // IMPORTANT: This must be BEFORE |controller_thread_|, so that the controller
143 // thread gets joined (and thus |controller_| reset) before |controller_| is
145 scoped_ptr
<AppChildControllerImpl
> controller_
;
147 base::Thread io_thread_
;
148 scoped_refptr
<base::SingleThreadTaskRunner
> io_runner_
;
150 base::Thread controller_thread_
;
151 scoped_refptr
<base::SingleThreadTaskRunner
> controller_runner_
;
153 DISALLOW_COPY_AND_ASSIGN(AppContext
);
156 // AppChildControllerImpl ------------------------------------------------------
158 class AppChildControllerImpl
: public InterfaceImpl
<AppChildController
> {
160 virtual ~AppChildControllerImpl() {
161 DCHECK(thread_checker_
.CalledOnValidThread());
163 // TODO(vtl): Pass in the result from |MainMain()|.
164 client()->AppCompleted(MOJO_RESULT_UNIMPLEMENTED
);
167 // To be executed on the controller thread. Creates the |AppChildController|,
170 AppContext
* app_context
,
171 embedder::ScopedPlatformHandle platform_channel
,
172 const Blocker::Unblocker
& unblocker
) {
174 DCHECK(platform_channel
.is_valid());
176 DCHECK(!app_context
->controller());
178 scoped_ptr
<AppChildControllerImpl
> impl(
179 new AppChildControllerImpl(app_context
, unblocker
));
181 ScopedMessagePipeHandle
host_message_pipe(embedder::CreateChannel(
182 platform_channel
.Pass(),
183 app_context
->io_runner(),
184 base::Bind(&AppChildControllerImpl::DidCreateChannel
,
185 base::Unretained(impl
.get())),
186 base::MessageLoopProxy::current()));
188 BindToPipe(impl
.get(), host_message_pipe
.Pass());
190 app_context
->set_controller(impl
.Pass());
193 virtual void OnConnectionError() OVERRIDE
{
194 // TODO(darin): How should we handle a connection error here?
197 // |AppChildController| methods:
198 virtual void StartApp(const String
& app_path
,
199 ScopedMessagePipeHandle service
) OVERRIDE
{
200 DVLOG(2) << "AppChildControllerImpl::StartApp(" << app_path
<< ", ...)";
201 DCHECK(thread_checker_
.CalledOnValidThread());
203 unblocker_
.Unblock(base::Bind(&AppChildControllerImpl::StartAppOnMainThread
,
204 base::FilePath::FromUTF8Unsafe(app_path
),
205 base::Passed(&service
)));
209 AppChildControllerImpl(AppContext
* app_context
,
210 const Blocker::Unblocker
& unblocker
)
211 : app_context_(app_context
),
212 unblocker_(unblocker
),
213 channel_info_(NULL
) {
216 // Callback for |embedder::CreateChannel()|.
217 void DidCreateChannel(embedder::ChannelInfo
* channel_info
) {
218 DVLOG(2) << "AppChildControllerImpl::DidCreateChannel()";
219 DCHECK(thread_checker_
.CalledOnValidThread());
220 channel_info_
= channel_info
;
223 static void StartAppOnMainThread(const base::FilePath
& app_path
,
224 ScopedMessagePipeHandle service
) {
225 // TODO(vtl): This is copied from in_process_dynamic_service_runner.cc.
226 DVLOG(2) << "Loading/running Mojo app from " << app_path
.value()
227 << " out of process";
230 base::NativeLibraryLoadError load_error
;
231 base::ScopedNativeLibrary
app_library(
232 base::LoadNativeLibrary(app_path
, &load_error
));
233 if (!app_library
.is_valid()) {
234 LOG(ERROR
) << "Failed to load library (error: " << load_error
.ToString()
239 typedef MojoResult (*MojoMainFunction
)(MojoHandle
);
240 MojoMainFunction main_function
= reinterpret_cast<MojoMainFunction
>(
241 app_library
.GetFunctionPointer("MojoMain"));
242 if (!main_function
) {
243 LOG(ERROR
) << "Entrypoint MojoMain not found";
247 // TODO(vtl): Report the result back to our parent process.
248 // |MojoMain()| takes ownership of the service handle.
249 MojoResult result
= main_function(service
.release().value());
250 if (result
< MOJO_RESULT_OK
)
251 LOG(ERROR
) << "MojoMain returned an error: " << result
;
255 base::ThreadChecker thread_checker_
;
256 AppContext
* const app_context_
;
257 Blocker::Unblocker unblocker_
;
259 embedder::ChannelInfo
* channel_info_
;
261 DISALLOW_COPY_AND_ASSIGN(AppChildControllerImpl
);
266 // AppChildProcess -------------------------------------------------------------
268 AppChildProcess::AppChildProcess() {
271 AppChildProcess::~AppChildProcess() {
274 void AppChildProcess::Main() {
275 DVLOG(2) << "AppChildProcess::Main()";
277 AppContext app_context
;
281 app_context
.controller_runner()->PostTask(
283 base::Bind(&AppChildControllerImpl::Init
, base::Unretained(&app_context
),
284 base::Passed(platform_channel()), blocker
.GetUnblocker()));
285 // This will block, then run whatever the controller wants.
288 app_context
.Shutdown();