OLD | NEW |
| (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/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" | |
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/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" | |
25 #include "mojo/shell/dynamic_service_runner.h" | |
26 | |
27 namespace mojo { | |
28 namespace shell { | |
29 | |
30 namespace { | |
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. | |
36 class 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 | |
83 class AppChildControllerImpl; | |
84 | |
85 static void DestroyController(scoped_ptr<AppChildControllerImpl> controller) { | |
86 } | |
87 | |
88 // Should be created and initialized on the main thread. | |
89 class 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 | |
158 class AppChildControllerImpl : public InterfaceImpl<AppChildController> { | |
159 public: | |
160 ~AppChildControllerImpl() override { | |
161 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 | |
193 void OnConnectionError() override { | |
194 // TODO(darin): How should we handle a connection error here? | |
195 } | |
196 | |
197 // |AppChildController| methods: | |
198 void StartApp(const String& app_path, | |
199 ScopedMessagePipeHandle service) override { | |
200 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 | |
229 // 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()); | |
232 } | |
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 | |
247 AppChildProcess::AppChildProcess() { | |
248 } | |
249 | |
250 AppChildProcess::~AppChildProcess() { | |
251 } | |
252 | |
253 void 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 | |
OLD | NEW |