1/******************************************************************************
2 *
3 *  Copyright 2014 Google, Inc.
4 *
5 *  Licensed under the Apache License, Version 2.0 (the "License");
6 *  you may not use this file except in compliance with the License.
7 *  You may obtain a copy of the License at:
8 *
9 *  http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 *
17 ******************************************************************************/
18
19#define LOG_TAG "bt_core_module"
20
21#include <base/logging.h>
22#include <dlfcn.h>
23#include <string.h>
24
25#include <mutex>
26#include <unordered_map>
27
28#include "btcore/include/module.h"
29#include "osi/include/allocator.h"
30#include "osi/include/log.h"
31#include "osi/include/osi.h"
32
33typedef enum {
34  MODULE_STATE_NONE = 0,
35  MODULE_STATE_INITIALIZED = 1,
36  MODULE_STATE_STARTED = 2
37} module_state_t;
38
39static std::unordered_map<const module_t*, module_state_t> metadata;
40
41// TODO(jamuraa): remove this lock after the startup sequence is clean
42static std::mutex metadata_mutex;
43
44static bool call_lifecycle_function(module_lifecycle_fn function);
45static module_state_t get_module_state(const module_t* module);
46static void set_module_state(const module_t* module, module_state_t state);
47
48void module_management_start(void) {}
49
50void module_management_stop(void) {
51  metadata.clear();
52}
53
54const module_t* get_module(const char* name) {
55  module_t* module = (module_t*)dlsym(RTLD_DEFAULT, name);
56  CHECK(module);
57  return module;
58}
59
60bool module_init(const module_t* module) {
61  CHECK(module != NULL);
62  CHECK(get_module_state(module) == MODULE_STATE_NONE);
63
64  if (!call_lifecycle_function(module->init)) {
65    LOG_ERROR(LOG_TAG, "%s Failed to initialize module \"%s\"", __func__,
66              module->name);
67    return false;
68  }
69
70  set_module_state(module, MODULE_STATE_INITIALIZED);
71  return true;
72}
73
74bool module_start_up(const module_t* module) {
75  CHECK(module != NULL);
76  // TODO(zachoverflow): remove module->init check once automagic order/call is
77  // in place.
78  // This hack is here so modules which don't require init don't have to have
79  // useless calls
80  // as we're converting the startup sequence.
81  CHECK(get_module_state(module) == MODULE_STATE_INITIALIZED ||
82        module->init == NULL);
83
84  LOG_INFO(LOG_TAG, "%s Starting module \"%s\"", __func__, module->name);
85  if (!call_lifecycle_function(module->start_up)) {
86    LOG_ERROR(LOG_TAG, "%s Failed to start up module \"%s\"", __func__,
87              module->name);
88    return false;
89  }
90  LOG_INFO(LOG_TAG, "%s Started module \"%s\"", __func__, module->name);
91
92  set_module_state(module, MODULE_STATE_STARTED);
93  return true;
94}
95
96void module_shut_down(const module_t* module) {
97  CHECK(module != NULL);
98  module_state_t state = get_module_state(module);
99  CHECK(state <= MODULE_STATE_STARTED);
100
101  // Only something to do if the module was actually started
102  if (state < MODULE_STATE_STARTED) return;
103
104  LOG_INFO(LOG_TAG, "%s Shutting down module \"%s\"", __func__, module->name);
105  if (!call_lifecycle_function(module->shut_down)) {
106    LOG_ERROR(LOG_TAG,
107              "%s Failed to shutdown module \"%s\". Continuing anyway.",
108              __func__, module->name);
109  }
110  LOG_INFO(LOG_TAG, "%s Shutdown of module \"%s\" completed", __func__,
111           module->name);
112
113  set_module_state(module, MODULE_STATE_INITIALIZED);
114}
115
116void module_clean_up(const module_t* module) {
117  CHECK(module != NULL);
118  module_state_t state = get_module_state(module);
119  CHECK(state <= MODULE_STATE_INITIALIZED);
120
121  // Only something to do if the module was actually initialized
122  if (state < MODULE_STATE_INITIALIZED) return;
123
124  LOG_INFO(LOG_TAG, "%s Cleaning up module \"%s\"", __func__, module->name);
125  if (!call_lifecycle_function(module->clean_up)) {
126    LOG_ERROR(LOG_TAG, "%s Failed to cleanup module \"%s\". Continuing anyway.",
127              __func__, module->name);
128  }
129  LOG_INFO(LOG_TAG, "%s Cleanup of module \"%s\" completed", __func__,
130           module->name);
131
132  set_module_state(module, MODULE_STATE_NONE);
133}
134
135static bool call_lifecycle_function(module_lifecycle_fn function) {
136  // A NULL lifecycle function means it isn't needed, so assume success
137  if (!function) return true;
138
139  future_t* future = function();
140
141  // A NULL future means synchronous success
142  if (!future) return true;
143
144  // Otherwise fall back to the future
145  return future_await(future);
146}
147
148static module_state_t get_module_state(const module_t* module) {
149  std::lock_guard<std::mutex> lock(metadata_mutex);
150  auto map_ptr = metadata.find(module);
151
152  return (map_ptr != metadata.end()) ? map_ptr->second : MODULE_STATE_NONE;
153}
154
155static void set_module_state(const module_t* module, module_state_t state) {
156  std::lock_guard<std::mutex> lock(metadata_mutex);
157  metadata[module] = state;
158}
159
160// TODO(zachoverflow): remove when everything modulized
161// Temporary callback-wrapper-related code
162
163typedef struct {
164  const module_t* module;
165  thread_t* lifecycle_thread;
166  thread_t* callback_thread;  // we don't own this thread
167  thread_fn callback;
168  bool success;
169} callbacked_wrapper_t;
170
171static void run_wrapped_start_up(void* context);
172static void post_result_to_callback(void* context);
173
174void module_start_up_callbacked_wrapper(const module_t* module,
175                                        thread_t* callback_thread,
176                                        thread_fn callback) {
177  callbacked_wrapper_t* wrapper =
178      (callbacked_wrapper_t*)osi_calloc(sizeof(callbacked_wrapper_t));
179
180  wrapper->module = module;
181  wrapper->lifecycle_thread = thread_new("module_wrapper");
182  wrapper->callback_thread = callback_thread;
183  wrapper->callback = callback;
184
185  // Run the actual module start up
186  thread_post(wrapper->lifecycle_thread, run_wrapped_start_up, wrapper);
187}
188
189static void run_wrapped_start_up(void* context) {
190  CHECK(context);
191
192  callbacked_wrapper_t* wrapper = (callbacked_wrapper_t*)context;
193  wrapper->success = module_start_up(wrapper->module);
194
195  // Post the result back to the callback
196  thread_post(wrapper->callback_thread, post_result_to_callback, wrapper);
197}
198
199static void post_result_to_callback(void* context) {
200  CHECK(context);
201
202  callbacked_wrapper_t* wrapper = (callbacked_wrapper_t*)context;
203
204  // Save the values we need for callback
205  void* result = wrapper->success ? FUTURE_SUCCESS : FUTURE_FAIL;
206  thread_fn callback = wrapper->callback;
207
208  // Clean up the resources we used
209  thread_free(wrapper->lifecycle_thread);
210  osi_free(wrapper);
211
212  callback(result);
213}
214