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/runner/child_process.h"
7 #include "base/base_switches.h"
9 #include "base/callback_helpers.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/macros.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/synchronization/waitable_event.h"
20 #include "base/threading/thread.h"
21 #include "base/threading/thread_checker.h"
22 #include "mojo/common/message_pump_mojo.h"
23 #include "mojo/edk/embedder/embedder.h"
24 #include "mojo/edk/embedder/platform_channel_pair.h"
25 #include "mojo/edk/embedder/process_delegate.h"
26 #include "mojo/edk/embedder/scoped_platform_handle.h"
27 #include "mojo/edk/embedder/simple_platform_support.h"
28 #include "mojo/public/cpp/system/core.h"
29 #include "mojo/runner/child_process.mojom.h"
30 #include "mojo/runner/native_application_support.h"
31 #include "mojo/runner/switches.h"
38 // Blocker ---------------------------------------------------------------------
40 // Blocks a thread until another thread unblocks it, at which point it unblocks
41 // and runs a closure provided by that thread.
46 explicit Unblocker(Blocker
* blocker
= nullptr) : blocker_(blocker
) {}
49 void Unblock(base::Closure run_after
) {
51 DCHECK(blocker_
->run_after_
.is_null());
52 blocker_
->run_after_
= run_after
;
53 blocker_
->event_
.Signal();
60 // Copy and assign allowed.
63 Blocker() : event_(true, false) {}
67 DCHECK(run_after_
.is_null());
69 if (!run_after_
.is_null())
73 Unblocker
GetUnblocker() { return Unblocker(this); }
76 base::WaitableEvent event_
;
77 base::Closure run_after_
;
79 DISALLOW_COPY_AND_ASSIGN(Blocker
);
82 // AppContext ------------------------------------------------------------------
84 class ChildControllerImpl
;
86 // Should be created and initialized on the main thread.
87 class AppContext
: public embedder::ProcessDelegate
{
90 : io_thread_("io_thread"), controller_thread_("controller_thread") {}
91 ~AppContext() override
{}
94 // Initialize Mojo before starting any threads.
95 embedder::Init(make_scoped_ptr(new embedder::SimplePlatformSupport()));
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());
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());
113 // TODO(vtl): This should be SLAVE, not NONE.
114 embedder::InitIPCSupport(embedder::ProcessType::NONE
, controller_runner_
,
116 embedder::ScopedPlatformHandle());
121 shutdown_unblocker_
= blocker
.GetUnblocker();
122 controller_runner_
->PostTask(
123 FROM_HERE
, base::Bind(&AppContext::ShutdownOnControllerThread
,
124 base::Unretained(this)));
128 base::SingleThreadTaskRunner
* io_runner() const { return io_runner_
.get(); }
130 base::SingleThreadTaskRunner
* controller_runner() const {
131 return controller_runner_
.get();
134 ChildControllerImpl
* controller() const { return controller_
.get(); }
136 void set_controller(scoped_ptr
<ChildControllerImpl
> controller
) {
137 controller_
= controller
.Pass();
141 void ShutdownOnControllerThread() {
142 // First, destroy the controller.
145 // Next shutdown IPC. We'll unblock the main thread in OnShutdownComplete().
146 embedder::ShutdownIPCSupport();
149 // ProcessDelegate implementation.
150 void OnShutdownComplete() override
{
151 shutdown_unblocker_
.Unblock(base::Closure());
154 base::Thread io_thread_
;
155 scoped_refptr
<base::SingleThreadTaskRunner
> io_runner_
;
157 base::Thread controller_thread_
;
158 scoped_refptr
<base::SingleThreadTaskRunner
> controller_runner_
;
160 // Accessed only on the controller thread.
161 scoped_ptr
<ChildControllerImpl
> controller_
;
163 // Used to unblock the main thread on shutdown.
164 Blocker::Unblocker shutdown_unblocker_
;
166 DISALLOW_COPY_AND_ASSIGN(AppContext
);
169 // ChildControllerImpl ------------------------------------------------------
171 class ChildControllerImpl
: public ChildController
, public ErrorHandler
{
173 ~ChildControllerImpl() override
{
174 DCHECK(thread_checker_
.CalledOnValidThread());
176 // TODO(vtl): Pass in the result from |MainMain()|.
177 on_app_complete_
.Run(MOJO_RESULT_UNIMPLEMENTED
);
180 // To be executed on the controller thread. Creates the |ChildController|,
182 static void Init(AppContext
* app_context
,
183 embedder::ScopedPlatformHandle platform_channel
,
184 const Blocker::Unblocker
& unblocker
) {
186 DCHECK(platform_channel
.is_valid());
188 DCHECK(!app_context
->controller());
190 scoped_ptr
<ChildControllerImpl
> impl(
191 new ChildControllerImpl(app_context
, unblocker
));
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()));
199 impl
->Bind(host_message_pipe
.Pass());
201 app_context
->set_controller(impl
.Pass());
204 void Bind(ScopedMessagePipeHandle handle
) { binding_
.Bind(handle
.Pass()); }
206 // |ErrorHandler| methods:
207 void OnConnectionError() override
{
208 // A connection error means the connection to the shell is lost. This is not
210 LOG(ERROR
) << "Connection error to the shell.";
214 // |ChildController| methods:
215 void StartApp(const String
& 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());
222 on_app_complete_
= on_app_complete
;
223 unblocker_
.Unblock(base::Bind(
224 &ChildControllerImpl::StartAppOnMainThread
,
225 base::FilePath::FromUTF8Unsafe(app_path
),
226 clean_app_path
? shell::NativeApplicationCleanup::DELETE
227 : shell::NativeApplicationCleanup::DONT_DELETE
,
228 base::Passed(&application_request
)));
231 void ExitNow(int32_t exit_code
) override
{
232 DVLOG(2) << "ChildControllerImpl::ExitNow(" << exit_code
<< ")";
237 ChildControllerImpl(AppContext
* app_context
,
238 const Blocker::Unblocker
& unblocker
)
239 : app_context_(app_context
),
240 unblocker_(unblocker
),
241 channel_info_(nullptr),
243 binding_
.set_error_handler(this);
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
;
253 static void StartAppOnMainThread(
254 const base::FilePath
& app_path
,
255 shell::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";
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());
267 base::ThreadChecker thread_checker_
;
268 AppContext
* const app_context_
;
269 Blocker::Unblocker unblocker_
;
270 StartAppCallback on_app_complete_
;
272 embedder::ChannelInfo
* channel_info_
;
273 Binding
<ChildController
> binding_
;
275 DISALLOW_COPY_AND_ASSIGN(ChildControllerImpl
);
280 int ChildProcessMain() {
281 DVLOG(2) << "ChildProcessMain()";
282 const base::CommandLine
& command_line
=
283 *base::CommandLine::ForCurrentProcess();
284 embedder::ScopedPlatformHandle platform_channel
=
285 embedder::PlatformChannelPair::PassClientHandleFromParentProcess(
287 CHECK(platform_channel
.is_valid());
289 DCHECK(!base::MessageLoop::current());
291 AppContext app_context
;
295 app_context
.controller_runner()->PostTask(
297 base::Bind(&ChildControllerImpl::Init
, base::Unretained(&app_context
),
298 base::Passed(&platform_channel
), blocker
.GetUnblocker()));
299 // This will block, then run whatever the controller wants.
302 app_context
.Shutdown();
307 } // namespace runner