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/in_process_dynamic_service_runner.h"
6
7#include "base/bind.h"
8#include "base/callback_helpers.h"
9#include "base/location.h"
10#include "base/logging.h"
11#include "base/message_loop/message_loop_proxy.h"
12#include "mojo/public/platform/native/gles2_impl_chromium_sync_point_thunks.h"
13#include "mojo/public/platform/native/gles2_impl_chromium_texture_mailbox_thunks.h"
14#include "mojo/public/platform/native/gles2_impl_thunks.h"
15#include "mojo/public/platform/native/gles2_thunks.h"
16#include "mojo/public/platform/native/system_thunks.h"
17
18namespace mojo {
19namespace shell {
20
21namespace {
22
23template <typename Thunks>
24bool SetThunks(Thunks (*make_thunks)(),
25               const char* function_name,
26               base::ScopedNativeLibrary* library) {
27  typedef size_t (*SetThunksFn)(const Thunks* thunks);
28  SetThunksFn set_thunks =
29      reinterpret_cast<SetThunksFn>(library->GetFunctionPointer(function_name));
30  if (!set_thunks)
31    return false;
32  Thunks thunks = make_thunks();
33  size_t expected_size = set_thunks(&thunks);
34  if (expected_size > sizeof(Thunks)) {
35    LOG(ERROR) << "Invalid app library: expected " << function_name
36               << " to return thunks of size: " << expected_size;
37    return false;
38  }
39  return true;
40}
41}
42
43InProcessDynamicServiceRunner::InProcessDynamicServiceRunner(
44    Context* context) {
45}
46
47InProcessDynamicServiceRunner::~InProcessDynamicServiceRunner() {
48  if (thread_) {
49    DCHECK(thread_->HasBeenStarted());
50    DCHECK(!thread_->HasBeenJoined());
51    thread_->Join();
52  }
53
54  // It is important to let the thread exit before unloading the DSO because
55  // the library may have registered thread-local data and destructors to run
56  // on thread termination.
57  app_library_.Reset(base::NativeLibrary());
58}
59
60void InProcessDynamicServiceRunner::Start(
61    const base::FilePath& app_path,
62    ScopedMessagePipeHandle service_handle,
63    const base::Closure& app_completed_callback) {
64  app_path_ = app_path;
65
66  DCHECK(!service_handle_.is_valid());
67  service_handle_ = service_handle.Pass();
68
69  DCHECK(app_completed_callback_runner_.is_null());
70  app_completed_callback_runner_ = base::Bind(&base::TaskRunner::PostTask,
71                                              base::MessageLoopProxy::current(),
72                                              FROM_HERE,
73                                              app_completed_callback);
74
75  DCHECK(!thread_);
76  thread_.reset(new base::DelegateSimpleThread(this, "app_thread"));
77  thread_->Start();
78}
79
80void InProcessDynamicServiceRunner::Run() {
81  DVLOG(2) << "Loading/running Mojo app in process from library: "
82           << app_path_.value();
83
84  do {
85    base::NativeLibraryLoadError error;
86    app_library_.Reset(base::LoadNativeLibrary(app_path_, &error));
87    if (!app_library_.is_valid()) {
88      LOG(ERROR) << "Failed to load app library (error: " << error.ToString()
89                 << ")";
90      break;
91    }
92
93    if (!SetThunks(
94            &MojoMakeSystemThunks, "MojoSetSystemThunks", &app_library_)) {
95      // In the component build, Mojo Apps link against mojo_system_impl.
96#if !defined(COMPONENT_BUILD)
97      // Strictly speaking this is not required, but it's very unusual to have
98      // an app that doesn't require the basic system library.
99      LOG(WARNING) << "MojoSetSystemThunks not found in app library";
100#endif
101    }
102
103    if (SetThunks(&MojoMakeGLES2ControlThunks,
104                  "MojoSetGLES2ControlThunks",
105                  &app_library_)) {
106      // If we have the control thunks, we probably also have the
107      // GLES2 implementation thunks.
108      if (!SetThunks(&MojoMakeGLES2ImplThunks,
109                     "MojoSetGLES2ImplThunks",
110                     &app_library_)) {
111        // In the component build, Mojo Apps link against mojo_gles2_impl.
112#if !defined(COMPONENT_BUILD)
113        // Warn on this really weird case: The library requires the GLES2
114        // control functions, but doesn't require the GLES2 implementation.
115        LOG(WARNING) << app_path_.value()
116                     << " has MojoSetGLES2ControlThunks, "
117                        "but doesn't have MojoSetGLES2ImplThunks.";
118#endif
119      }
120
121      // If the application is using GLES2 extension points, register those
122      // thunks. Applications may use or not use any of these, so don't warn if
123      // they are missing.
124      SetThunks(MojoMakeGLES2ImplChromiumTextureMailboxThunks,
125                "MojoSetGLES2ImplChromiumTextureMailboxThunks",
126                &app_library_);
127      SetThunks(MojoMakeGLES2ImplChromiumSyncPointThunks,
128                "MojoSetGLES2ImplChromiumSyncPointThunks",
129                &app_library_);
130    }
131    // Unlike system thunks, we don't warn on a lack of GLES2 thunks because
132    // not everything is a visual app.
133
134    typedef MojoResult (*MojoMainFunction)(MojoHandle);
135    MojoMainFunction main_function = reinterpret_cast<MojoMainFunction>(
136        app_library_.GetFunctionPointer("MojoMain"));
137    if (!main_function) {
138      LOG(ERROR) << "Entrypoint MojoMain not found: " << app_path_.value();
139      break;
140    }
141
142    // |MojoMain()| takes ownership of the service handle.
143    MojoResult result = main_function(service_handle_.release().value());
144    if (result < MOJO_RESULT_OK)
145      LOG(ERROR) << "MojoMain returned an error: " << result;
146  } while (false);
147
148  app_completed_callback_runner_.Run();
149  app_completed_callback_runner_.Reset();
150}
151
152}  // namespace shell
153}  // namespace mojo
154