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/app_child_process.h"
6
7 #include "base/bind.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/message_loop/message_loop.h"
15 #include "base/scoped_native_library.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/synchronization/waitable_event.h"
18 #include "base/threading/thread.h"
19 #include "base/threading/thread_checker.h"
20 #include "mojo/common/message_pump_mojo.h"
21 #include "mojo/embedder/embedder.h"
22 #include "mojo/public/cpp/system/core.h"
23 #include "mojo/shell/app_child_process.mojom.h"
24
25 namespace mojo {
26 namespace shell {
27
28 namespace {
29
30 // Blocker ---------------------------------------------------------------------
31
32 // Blocks a thread until another thread unblocks it, at which point it unblocks
33 // and runs a closure provided by that thread.
34 class Blocker {
35 public:
36 class Unblocker {
37 public:
~Unblocker()38 ~Unblocker() {}
39
Unblock(base::Closure run_after)40 void Unblock(base::Closure run_after) {
41 DCHECK(blocker_);
42 DCHECK(blocker_->run_after_.is_null());
43 blocker_->run_after_ = run_after;
44 blocker_->event_.Signal();
45 blocker_ = NULL;
46 }
47
48 private:
49 friend class Blocker;
Unblocker(Blocker * blocker)50 Unblocker(Blocker* blocker) : blocker_(blocker) {
51 DCHECK(blocker_);
52 }
53
54 Blocker* blocker_;
55
56 // Copy and assign allowed.
57 };
58
Blocker()59 Blocker() : event_(true, false) {}
~Blocker()60 ~Blocker() {}
61
Block()62 void Block() {
63 DCHECK(run_after_.is_null());
64 event_.Wait();
65 run_after_.Run();
66 }
67
GetUnblocker()68 Unblocker GetUnblocker() {
69 return Unblocker(this);
70 }
71
72 private:
73 base::WaitableEvent event_;
74 base::Closure run_after_;
75
76 DISALLOW_COPY_AND_ASSIGN(Blocker);
77 };
78
79 // AppContext ------------------------------------------------------------------
80
81 class AppChildControllerImpl;
82
DestroyController(scoped_ptr<AppChildControllerImpl> controller)83 static void DestroyController(scoped_ptr<AppChildControllerImpl> controller) {
84 }
85
86 // Should be created and initialized on the main thread.
87 class AppContext {
88 public:
AppContext()89 AppContext()
90 : io_thread_("io_thread"),
91 controller_thread_("controller_thread") {}
~AppContext()92 ~AppContext() {}
93
Init()94 void Init() {
95 // Initialize Mojo before starting any threads.
96 embedder::Init();
97
98 // Create and start our I/O thread.
99 base::Thread::Options io_thread_options(base::MessageLoop::TYPE_IO, 0);
100 CHECK(io_thread_.StartWithOptions(io_thread_options));
101 io_runner_ = io_thread_.message_loop_proxy().get();
102 CHECK(io_runner_);
103
104 // Create and start our controller thread.
105 base::Thread::Options controller_thread_options;
106 controller_thread_options.message_loop_type =
107 base::MessageLoop::TYPE_CUSTOM;
108 controller_thread_options.message_pump_factory =
109 base::Bind(&common::MessagePumpMojo::Create);
110 CHECK(controller_thread_.StartWithOptions(controller_thread_options));
111 controller_runner_ = controller_thread_.message_loop_proxy().get();
112 CHECK(controller_runner_);
113 }
114
Shutdown()115 void Shutdown() {
116 controller_runner_->PostTask(
117 FROM_HERE,
118 base::Bind(&DestroyController, base::Passed(&controller_)));
119 }
120
io_runner() const121 base::SingleThreadTaskRunner* io_runner() const {
122 return io_runner_.get();
123 }
124
controller_runner() const125 base::SingleThreadTaskRunner* controller_runner() const {
126 return controller_runner_.get();
127 }
128
controller() const129 AppChildControllerImpl* controller() const {
130 return controller_.get();
131 }
132
set_controller(scoped_ptr<AppChildControllerImpl> controller)133 void set_controller(scoped_ptr<AppChildControllerImpl> controller) {
134 controller_ = controller.Pass();
135 }
136
137 private:
138 // Accessed only on the controller thread.
139 // IMPORTANT: This must be BEFORE |controller_thread_|, so that the controller
140 // thread gets joined (and thus |controller_| reset) before |controller_| is
141 // destroyed.
142 scoped_ptr<AppChildControllerImpl> controller_;
143
144 base::Thread io_thread_;
145 scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
146
147 base::Thread controller_thread_;
148 scoped_refptr<base::SingleThreadTaskRunner> controller_runner_;
149
150 DISALLOW_COPY_AND_ASSIGN(AppContext);
151 };
152
153 // AppChildControllerImpl ------------------------------------------------------
154
155 class AppChildControllerImpl : public InterfaceImpl<AppChildController> {
156 public:
~AppChildControllerImpl()157 virtual ~AppChildControllerImpl() {
158 DCHECK(thread_checker_.CalledOnValidThread());
159
160 // TODO(vtl): Pass in the result from |MainMain()|.
161 client()->AppCompleted(MOJO_RESULT_UNIMPLEMENTED);
162 }
163
164 // To be executed on the controller thread. Creates the |AppChildController|,
165 // etc.
Init(AppContext * app_context,embedder::ScopedPlatformHandle platform_channel,const Blocker::Unblocker & unblocker)166 static void Init(
167 AppContext* app_context,
168 embedder::ScopedPlatformHandle platform_channel,
169 const Blocker::Unblocker& unblocker) {
170 DCHECK(app_context);
171 DCHECK(platform_channel.is_valid());
172
173 DCHECK(!app_context->controller());
174
175 scoped_ptr<AppChildControllerImpl> impl(
176 new AppChildControllerImpl(app_context, unblocker));
177
178 ScopedMessagePipeHandle host_message_pipe(embedder::CreateChannel(
179 platform_channel.Pass(),
180 app_context->io_runner(),
181 base::Bind(&AppChildControllerImpl::DidCreateChannel,
182 base::Unretained(impl.get())),
183 base::MessageLoopProxy::current()));
184
185 BindToPipe(impl.get(), host_message_pipe.Pass());
186
187 app_context->set_controller(impl.Pass());
188 }
189
OnConnectionError()190 virtual void OnConnectionError() OVERRIDE {
191 // TODO(darin): How should we handle a connection error here?
192 }
193
194 // |AppChildController| methods:
StartApp(const String & app_path,ScopedMessagePipeHandle service)195 virtual void StartApp(const String& app_path,
196 ScopedMessagePipeHandle service) OVERRIDE {
197 DVLOG(2) << "AppChildControllerImpl::StartApp(" << app_path << ", ...)";
198 DCHECK(thread_checker_.CalledOnValidThread());
199
200 unblocker_.Unblock(base::Bind(&AppChildControllerImpl::StartAppOnMainThread,
201 base::FilePath::FromUTF8Unsafe(app_path),
202 base::Passed(&service)));
203 }
204
205 private:
AppChildControllerImpl(AppContext * app_context,const Blocker::Unblocker & unblocker)206 AppChildControllerImpl(AppContext* app_context,
207 const Blocker::Unblocker& unblocker)
208 : app_context_(app_context),
209 unblocker_(unblocker),
210 channel_info_(NULL) {
211 }
212
213 // Callback for |embedder::CreateChannel()|.
DidCreateChannel(embedder::ChannelInfo * channel_info)214 void DidCreateChannel(embedder::ChannelInfo* channel_info) {
215 DVLOG(2) << "AppChildControllerImpl::DidCreateChannel()";
216 DCHECK(thread_checker_.CalledOnValidThread());
217 channel_info_ = channel_info;
218 }
219
StartAppOnMainThread(const base::FilePath & app_path,ScopedMessagePipeHandle service)220 static void StartAppOnMainThread(const base::FilePath& app_path,
221 ScopedMessagePipeHandle service) {
222 // TODO(vtl): This is copied from in_process_dynamic_service_runner.cc.
223 DVLOG(2) << "Loading/running Mojo app from " << app_path.value()
224 << " out of process";
225
226 do {
227 base::NativeLibraryLoadError load_error;
228 base::ScopedNativeLibrary app_library(
229 base::LoadNativeLibrary(app_path, &load_error));
230 if (!app_library.is_valid()) {
231 LOG(ERROR) << "Failed to load library (error: " << load_error.ToString()
232 << ")";
233 break;
234 }
235
236 typedef MojoResult (*MojoMainFunction)(MojoHandle);
237 MojoMainFunction main_function = reinterpret_cast<MojoMainFunction>(
238 app_library.GetFunctionPointer("MojoMain"));
239 if (!main_function) {
240 LOG(ERROR) << "Entrypoint MojoMain not found";
241 break;
242 }
243
244 // TODO(vtl): Report the result back to our parent process.
245 // |MojoMain()| takes ownership of the service handle.
246 MojoResult result = main_function(service.release().value());
247 if (result < MOJO_RESULT_OK)
248 LOG(ERROR) << "MojoMain returned an error: " << result;
249 } while (false);
250 }
251
252 base::ThreadChecker thread_checker_;
253 AppContext* const app_context_;
254 Blocker::Unblocker unblocker_;
255
256 embedder::ChannelInfo* channel_info_;
257
258 DISALLOW_COPY_AND_ASSIGN(AppChildControllerImpl);
259 };
260
261 } // namespace
262
263 // AppChildProcess -------------------------------------------------------------
264
AppChildProcess()265 AppChildProcess::AppChildProcess() {
266 }
267
~AppChildProcess()268 AppChildProcess::~AppChildProcess() {
269 }
270
Main()271 void AppChildProcess::Main() {
272 DVLOG(2) << "AppChildProcess::Main()";
273
274 AppContext app_context;
275 app_context.Init();
276
277 Blocker blocker;
278 app_context.controller_runner()->PostTask(
279 FROM_HERE,
280 base::Bind(&AppChildControllerImpl::Init, base::Unretained(&app_context),
281 base::Passed(platform_channel()), blocker.GetUnblocker()));
282 // This will block, then run whatever the controller wants.
283 blocker.Block();
284
285 app_context.Shutdown();
286 }
287
288 } // namespace shell
289 } // namespace mojo
290