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/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"
26
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:
160  virtual ~AppChildControllerImpl() {
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  virtual void OnConnectionError() OVERRIDE {
194    // TODO(darin): How should we handle a connection error here?
195  }
196
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());
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    do {
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()
235                   << ")";
236        break;
237      }
238
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";
244        break;
245      }
246
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;
252    } while (false);
253  }
254
255  base::ThreadChecker thread_checker_;
256  AppContext* const app_context_;
257  Blocker::Unblocker unblocker_;
258
259  embedder::ChannelInfo* channel_info_;
260
261  DISALLOW_COPY_AND_ASSIGN(AppChildControllerImpl);
262};
263
264}  // namespace
265
266// AppChildProcess -------------------------------------------------------------
267
268AppChildProcess::AppChildProcess() {
269}
270
271AppChildProcess::~AppChildProcess() {
272}
273
274void AppChildProcess::Main() {
275  DVLOG(2) << "AppChildProcess::Main()";
276
277  AppContext app_context;
278  app_context.Init();
279
280  Blocker blocker;
281  app_context.controller_runner()->PostTask(
282      FROM_HERE,
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.
286  blocker.Block();
287
288  app_context.Shutdown();
289}
290
291}  // namespace shell
292}  // namespace mojo
293