blob: ed254257bf5bb49f5e65e5e8cd689edecdafb20d [file] [log] [blame]
James Robinson646469d2014-10-03 15:33:28 -07001// 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/memory/scoped_ptr.h"
15#include "base/message_loop/message_loop.h"
James Robinson646469d2014-10-03 15:33:28 -070016#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/edk/embedder/embedder.h"
22#include "mojo/edk/embedder/simple_platform_support.h"
23#include "mojo/public/cpp/system/core.h"
24#include "mojo/shell/app_child_process.mojom.h"
Dave Moore3a99b932014-10-30 08:38:23 -070025#include "mojo/shell/dynamic_service_runner.h"
James Robinson646469d2014-10-03 15:33:28 -070026
27namespace mojo {
28namespace shell {
29
30namespace {
31
32// Blocker ---------------------------------------------------------------------
33
34// Blocks a thread until another thread unblocks it, at which point it unblocks
35// and runs a closure provided by that thread.
36class Blocker {
37 public:
38 class Unblocker {
39 public:
40 ~Unblocker() {}
41
42 void Unblock(base::Closure run_after) {
43 DCHECK(blocker_);
44 DCHECK(blocker_->run_after_.is_null());
45 blocker_->run_after_ = run_after;
46 blocker_->event_.Signal();
47 blocker_ = NULL;
48 }
49
50 private:
51 friend class Blocker;
52 Unblocker(Blocker* blocker) : blocker_(blocker) {
53 DCHECK(blocker_);
54 }
55
56 Blocker* blocker_;
57
58 // Copy and assign allowed.
59 };
60
61 Blocker() : event_(true, false) {}
62 ~Blocker() {}
63
64 void Block() {
65 DCHECK(run_after_.is_null());
66 event_.Wait();
67 run_after_.Run();
68 }
69
70 Unblocker GetUnblocker() {
71 return Unblocker(this);
72 }
73
74 private:
75 base::WaitableEvent event_;
76 base::Closure run_after_;
77
78 DISALLOW_COPY_AND_ASSIGN(Blocker);
79};
80
81// AppContext ------------------------------------------------------------------
82
83class AppChildControllerImpl;
84
85static void DestroyController(scoped_ptr<AppChildControllerImpl> controller) {
86}
87
88// Should be created and initialized on the main thread.
89class AppContext {
90 public:
91 AppContext()
92 : io_thread_("io_thread"),
93 controller_thread_("controller_thread") {}
94 ~AppContext() {}
95
96 void Init() {
97 // Initialize Mojo before starting any threads.
98 embedder::Init(scoped_ptr<mojo::embedder::PlatformSupport>(
99 new mojo::embedder::SimplePlatformSupport()));
100
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());
106
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());
116 }
117
118 void Shutdown() {
119 controller_runner_->PostTask(
120 FROM_HERE,
121 base::Bind(&DestroyController, base::Passed(&controller_)));
122 }
123
124 base::SingleThreadTaskRunner* io_runner() const {
125 return io_runner_.get();
126 }
127
128 base::SingleThreadTaskRunner* controller_runner() const {
129 return controller_runner_.get();
130 }
131
132 AppChildControllerImpl* controller() const {
133 return controller_.get();
134 }
135
136 void set_controller(scoped_ptr<AppChildControllerImpl> controller) {
137 controller_ = controller.Pass();
138 }
139
140 private:
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
144 // destroyed.
145 scoped_ptr<AppChildControllerImpl> controller_;
146
147 base::Thread io_thread_;
148 scoped_refptr<base::SingleThreadTaskRunner> io_runner_;
149
150 base::Thread controller_thread_;
151 scoped_refptr<base::SingleThreadTaskRunner> controller_runner_;
152
153 DISALLOW_COPY_AND_ASSIGN(AppContext);
154};
155
156// AppChildControllerImpl ------------------------------------------------------
157
158class AppChildControllerImpl : public InterfaceImpl<AppChildController> {
159 public:
James Robinsone1b30cf2014-10-21 12:25:40 -0700160 ~AppChildControllerImpl() override {
James Robinson646469d2014-10-03 15:33:28 -0700161 DCHECK(thread_checker_.CalledOnValidThread());
162
163 // TODO(vtl): Pass in the result from |MainMain()|.
164 client()->AppCompleted(MOJO_RESULT_UNIMPLEMENTED);
165 }
166
167 // To be executed on the controller thread. Creates the |AppChildController|,
168 // etc.
169 static void Init(
170 AppContext* app_context,
171 embedder::ScopedPlatformHandle platform_channel,
172 const Blocker::Unblocker& unblocker) {
173 DCHECK(app_context);
174 DCHECK(platform_channel.is_valid());
175
176 DCHECK(!app_context->controller());
177
178 scoped_ptr<AppChildControllerImpl> impl(
179 new AppChildControllerImpl(app_context, unblocker));
180
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()));
187
188 BindToPipe(impl.get(), host_message_pipe.Pass());
189
190 app_context->set_controller(impl.Pass());
191 }
192
James Robinsone1b30cf2014-10-21 12:25:40 -0700193 void OnConnectionError() override {
James Robinson646469d2014-10-03 15:33:28 -0700194 // TODO(darin): How should we handle a connection error here?
195 }
196
197 // |AppChildController| methods:
James Robinsone1b30cf2014-10-21 12:25:40 -0700198 void StartApp(const String& app_path,
199 ScopedMessagePipeHandle service) override {
James Robinson646469d2014-10-03 15:33:28 -0700200 DVLOG(2) << "AppChildControllerImpl::StartApp(" << app_path << ", ...)";
201 DCHECK(thread_checker_.CalledOnValidThread());
202
203 unblocker_.Unblock(base::Bind(&AppChildControllerImpl::StartAppOnMainThread,
204 base::FilePath::FromUTF8Unsafe(app_path),
205 base::Passed(&service)));
206 }
207
208 private:
209 AppChildControllerImpl(AppContext* app_context,
210 const Blocker::Unblocker& unblocker)
211 : app_context_(app_context),
212 unblocker_(unblocker),
213 channel_info_(NULL) {
214 }
215
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;
221 }
222
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";
228
Dave Moore3a99b932014-10-30 08:38:23 -0700229 // We intentionally don't unload the native library as its lifetime is the
230 // same as that of the process.
231 DynamicServiceRunner::LoadAndRunService(app_path, service.Pass());
James Robinson646469d2014-10-03 15:33:28 -0700232 }
233
234 base::ThreadChecker thread_checker_;
235 AppContext* const app_context_;
236 Blocker::Unblocker unblocker_;
237
238 embedder::ChannelInfo* channel_info_;
239
240 DISALLOW_COPY_AND_ASSIGN(AppChildControllerImpl);
241};
242
243} // namespace
244
245// AppChildProcess -------------------------------------------------------------
246
247AppChildProcess::AppChildProcess() {
248}
249
250AppChildProcess::~AppChildProcess() {
251}
252
253void AppChildProcess::Main() {
254 DVLOG(2) << "AppChildProcess::Main()";
255
256 AppContext app_context;
257 app_context.Init();
258
259 Blocker blocker;
260 app_context.controller_runner()->PostTask(
261 FROM_HERE,
262 base::Bind(&AppChildControllerImpl::Init, base::Unretained(&app_context),
263 base::Passed(platform_channel()), blocker.GetUnblocker()));
264 // This will block, then run whatever the controller wants.
265 blocker.Block();
266
267 app_context.Shutdown();
268}
269
270} // namespace shell
271} // namespace mojo