Line data Source code
1 : /**
2 : * NNStreamer Subplugin Manager
3 : * Copyright (C) 2018 MyungJoo Ham <myungjoo.ham@samsung.com>
4 : *
5 : * This library is free software; you can redistribute it and/or
6 : * modify it under the terms of the GNU Library General Public
7 : * License as published by the Free Software Foundation;
8 : * version 2.1 of the License.
9 : *
10 : * This library is distributed in the hope that it will be useful,
11 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 : * Library General Public License for more details.
14 : *
15 : */
16 : /**
17 : * @file nnstreamer_subplugin.c
18 : * @date 27 Nov 2018
19 : * @brief Subplugin Manager for NNStreamer
20 : * @see http://github.com/nnstreamer/nnstreamer
21 : * @author MyungJoo Ham <myungjoo.ham@samsung.com>
22 : * @bug No known bugs except for NYI items
23 : *
24 : */
25 :
26 : #include <glib.h>
27 : #include <gmodule.h>
28 :
29 : #include "nnstreamer_log.h"
30 : #include "nnstreamer_subplugin.h"
31 : #include "nnstreamer_conf.h"
32 : #include <nnstreamer_util.h>
33 :
34 : /** @brief Array of dynamic loaded handles */
35 : static GPtrArray *handles = NULL;
36 :
37 : static void init_subplugin (void) __attribute__((constructor));
38 : static void fini_subplugin (void) __attribute__((destructor));
39 :
40 : typedef struct
41 : {
42 : char *name; /**< The name of subplugin */
43 : const void *data; /**< subplugin specific data forwarded from the subplugin */
44 : GData *custom_dlist; /**< [OPTIONAL] subplugin specific custom property desc list */
45 : } subpluginData;
46 :
47 : static GHashTable *subplugins[NNS_SUBPLUGIN_END] = { 0 };
48 :
49 : /** @brief Protects handles and subplugins */
50 : G_LOCK_DEFINE_STATIC (splock);
51 :
52 : /** @brief Private function for g_hash_table data destructor, GDestroyNotify */
53 : static void
54 2573 : _spdata_destroy (gpointer _data)
55 : {
56 2573 : subpluginData *data = _data;
57 :
58 2573 : g_datalist_clear (&data->custom_dlist);
59 :
60 2573 : g_free (data->name);
61 2573 : g_free (data);
62 2573 : }
63 :
64 : typedef enum
65 : {
66 : NNS_SEARCH_FILENAME,
67 : NNS_SEARCH_GETALL,
68 : NNS_SEARCH_NO_OP,
69 : } subpluginSearchLogic;
70 :
71 : static subpluginSearchLogic searchAlgorithm[] = {
72 : [NNS_SUBPLUGIN_FILTER] = NNS_SEARCH_FILENAME,
73 : [NNS_SUBPLUGIN_DECODER] = NNS_SEARCH_FILENAME,
74 : [NNS_EASY_CUSTOM_FILTER] = NNS_SEARCH_FILENAME,
75 : [NNS_SUBPLUGIN_CONVERTER] = NNS_SEARCH_GETALL,
76 : [NNS_SUBPLUGIN_TRAINER] = NNS_SEARCH_FILENAME,
77 : [NNS_CUSTOM_CONVERTER] = NNS_SEARCH_NO_OP,
78 : [NNS_CUSTOM_DECODER] = NNS_SEARCH_NO_OP,
79 : [NNS_IF_CUSTOM] = NNS_SEARCH_NO_OP,
80 : [NNS_SUBPLUGIN_END] = NNS_SEARCH_NO_OP,
81 : };
82 :
83 : /**
84 : * @brief Internal function to get sub-plugin data.
85 : */
86 : static subpluginData *
87 7975 : _get_subplugin_data (subpluginType type, const gchar * name)
88 : {
89 7975 : subpluginData *spdata = NULL;
90 :
91 7975 : G_LOCK (splock);
92 7975 : if (subplugins[type] == NULL) {
93 1067 : subplugins[type] =
94 1067 : g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
95 : _spdata_destroy);
96 : } else {
97 6908 : spdata = g_hash_table_lookup (subplugins[type], name);
98 : }
99 7975 : G_UNLOCK (splock);
100 :
101 7975 : return spdata;
102 : }
103 :
104 : /**
105 : * @brief Internal function to scan sub-plugin.
106 : */
107 : static subpluginData *
108 1907 : _search_subplugin (subpluginType type, const gchar * name, const gchar * path)
109 : {
110 1907 : subpluginData *spdata = NULL;
111 : GModule *module;
112 :
113 1907 : g_return_val_if_fail (name != NULL, NULL);
114 1907 : g_return_val_if_fail (path != NULL, NULL);
115 :
116 1907 : module = g_module_open (path, G_MODULE_BIND_LOCAL);
117 : /* If this is a correct subplugin, it will register itself */
118 1907 : if (module == NULL) {
119 0 : ml_loge ("Cannot open %s(%s) with error %s.", name, path,
120 : g_module_error ());
121 0 : return NULL;
122 : }
123 :
124 1907 : spdata = _get_subplugin_data (type, name);
125 1907 : if (spdata) {
126 1907 : G_LOCK (splock);
127 1907 : g_ptr_array_add (handles, (gpointer) module);
128 1907 : G_UNLOCK (splock);
129 : } else {
130 0 : ml_loge
131 : ("nnstreamer_subplugin of %s(%s) is broken. It does not call register_subplugin with its init function.",
132 : name, path);
133 0 : g_module_close (module);
134 : }
135 :
136 1907 : return spdata;
137 : }
138 :
139 : /** @brief Public function defined in the header */
140 : const void *
141 2795 : get_subplugin (subpluginType type, const char *name)
142 : {
143 2795 : subpluginData *spdata = NULL;
144 :
145 2795 : g_return_val_if_fail (name, NULL);
146 :
147 2790 : if (searchAlgorithm[type] == NNS_SEARCH_GETALL) {
148 419 : nnsconf_type_path conf_type = (nnsconf_type_path) type;
149 : subplugin_info_s info;
150 : guint i;
151 419 : guint ret = nnsconf_get_subplugin_info (conf_type, &info);
152 :
153 2095 : for (i = 0; i < ret; i++) {
154 1676 : _search_subplugin (type, info.names[i], info.paths[i]);
155 : }
156 :
157 419 : searchAlgorithm[type] = NNS_SEARCH_NO_OP;
158 : }
159 :
160 2790 : spdata = _get_subplugin_data (type, name);
161 2790 : if (spdata == NULL && searchAlgorithm[type] == NNS_SEARCH_FILENAME) {
162 : /** Search and register if found with the conf */
163 380 : nnsconf_type_path conf_type = (nnsconf_type_path) type;
164 380 : const gchar *fullpath = nnsconf_get_fullpath (name, conf_type);
165 :
166 380 : if (nnsconf_validate_file (conf_type, fullpath)) {
167 231 : spdata = _search_subplugin (type, name, fullpath);
168 : }
169 : }
170 :
171 2790 : return (spdata != NULL) ? spdata->data : NULL;
172 : }
173 :
174 : /** @brief Public function defined in the header */
175 : gchar **
176 745 : get_all_subplugins (subpluginType type)
177 : {
178 : GString *names;
179 : subplugin_info_s info;
180 745 : gchar **list = NULL;
181 : gchar *name;
182 : guint i, total;
183 :
184 745 : names = g_string_new (NULL);
185 :
186 : /* get registered subplugins */
187 745 : G_LOCK (splock);
188 745 : if (subplugins[type]) {
189 208 : list = (gchar **) g_hash_table_get_keys_as_array (subplugins[type], NULL);
190 : }
191 745 : G_UNLOCK (splock);
192 :
193 745 : if (list) {
194 208 : name = g_strjoinv (",", list);
195 : g_string_append (names, name);
196 208 : g_free (name);
197 : }
198 :
199 : /* get subplugins from configuration */
200 745 : total = nnsconf_get_subplugin_info ((nnsconf_type_path) type, &info);
201 :
202 5728 : for (i = 0; i < total; i++) {
203 4983 : name = info.names[i];
204 :
205 4983 : if (!list || !g_strv_contains ((const gchar * const *) list, name)) {
206 4670 : if (list || i > 0)
207 8266 : g_string_append (names, ",");
208 :
209 : g_string_append (names, name);
210 : }
211 : }
212 :
213 745 : g_free (list);
214 :
215 : /* finally get the list of subplugins */
216 745 : name = g_string_free (names, FALSE);
217 745 : list = g_strsplit (name, ",", -1);
218 745 : g_free (name);
219 :
220 745 : return list;
221 : }
222 :
223 : /** @brief Public function defined in the header */
224 : gboolean
225 3108 : register_subplugin (subpluginType type, const char *name, const void *data)
226 : {
227 : /** @todo data out of scope at add */
228 3108 : subpluginData *spdata = NULL;
229 3108 : gchar *sp_name = NULL;
230 : gboolean ret;
231 :
232 3108 : g_return_val_if_fail (name, FALSE);
233 3107 : g_return_val_if_fail (data, FALSE);
234 :
235 3107 : switch (type) {
236 3107 : case NNS_SUBPLUGIN_FILTER:
237 : case NNS_SUBPLUGIN_DECODER:
238 : case NNS_EASY_CUSTOM_FILTER:
239 : case NNS_SUBPLUGIN_CONVERTER:
240 : case NNS_SUBPLUGIN_TRAINER:
241 : case NNS_CUSTOM_DECODER:
242 : case NNS_IF_CUSTOM:
243 : case NNS_CUSTOM_CONVERTER:
244 3107 : break;
245 0 : default:
246 : /* unknown sub-plugin type */
247 0 : return FALSE;
248 : }
249 :
250 : /* check the sub-pugin name */
251 3107 : if (g_ascii_strcasecmp (name, "any") == 0 ||
252 3104 : g_ascii_strcasecmp (name, "auto") == 0) {
253 4 : ml_loge ("Failed, the name %s is not allowed.", name);
254 4 : return FALSE;
255 : }
256 :
257 3103 : spdata = _get_subplugin_data (type, name);
258 3103 : if (spdata) {
259 : /* already exists */
260 3 : ml_logw ("Subplugin %s is already registered.", name);
261 3 : return FALSE;
262 : }
263 :
264 3100 : spdata = g_new0 (subpluginData, 1);
265 3100 : if (spdata == NULL) {
266 0 : ml_loge ("Failed to allocate memory for subplugin registration.");
267 0 : return FALSE;
268 : }
269 :
270 3100 : spdata->name = g_strdup (name);
271 3100 : spdata->data = data;
272 3100 : g_datalist_init (&spdata->custom_dlist);
273 :
274 3100 : G_LOCK (splock);
275 3100 : sp_name = g_strdup (name);
276 3100 : ret = g_hash_table_insert (subplugins[type], sp_name, spdata);
277 3100 : if (!ret) {
278 0 : _spdata_destroy (spdata);
279 0 : g_free (sp_name);
280 0 : ml_loge ("Failed to add subplugin data into the table.");
281 : }
282 3100 : G_UNLOCK (splock);
283 :
284 3100 : return ret;
285 : }
286 :
287 : /** @brief Public function defined in the header */
288 : gboolean
289 3107 : unregister_subplugin (subpluginType type, const char *name)
290 : {
291 : gboolean ret;
292 :
293 3107 : g_return_val_if_fail (name, FALSE);
294 3104 : g_return_val_if_fail (subplugins[type], FALSE);
295 :
296 3104 : G_LOCK (splock);
297 3104 : ret = g_hash_table_remove (subplugins[type], name);
298 3104 : G_UNLOCK (splock);
299 :
300 3104 : return ret;
301 : }
302 :
303 : /** @brief dealloc function for handles */
304 : static void
305 1907 : _close_handle (gpointer data)
306 : {
307 : /**
308 : * Ubuntu 16.04 / GLIBC 2.23 Workaround
309 : * If we do dlclose at exit() function, it may incur
310 : * https://bugzilla.redhat.com/show_bug.cgi?id=1264556#c42
311 : * , which is a GLIBC bug at 2.23.
312 : * The corresponding error message is:
313 : * Inconsistency detected by ld.so: dl-close.c: 811:
314 : * _dl_close: Assertion `map->l_init_called' failed!
315 : * Note that Tizen 5.5 / GLIBC 2.24 has the same bug!
316 : */
317 : #if defined(__GLIBC__) && (__GLIBC__ == 2) && (__GLIBC_MINOR__ <= 24)
318 : UNUSED (data);
319 : return; /* Do not call close and return */
320 : #else
321 1907 : g_module_close ((GModule *) data);
322 : #endif
323 1907 : }
324 :
325 : /**
326 : * @brief common interface to set custom property description of a sub-plugin.
327 : */
328 : void
329 173 : subplugin_set_custom_property_desc (subpluginType type, const char *name,
330 : const gchar * prop, va_list varargs)
331 : {
332 : subpluginData *spdata;
333 :
334 173 : g_return_if_fail (name != NULL);
335 173 : g_return_if_fail (subplugins[type] != NULL);
336 :
337 173 : spdata = _get_subplugin_data (type, name);
338 173 : g_return_if_fail (spdata != NULL);
339 :
340 173 : g_datalist_clear (&spdata->custom_dlist);
341 :
342 732 : while (prop) {
343 560 : gchar *desc = va_arg (varargs, gchar *);
344 :
345 560 : if (G_UNLIKELY (desc == NULL)) {
346 1 : ml_logw ("No description for %s", prop);
347 1 : return;
348 : }
349 :
350 559 : g_datalist_set_data (&spdata->custom_dlist, prop, desc);
351 559 : prop = va_arg (varargs, gchar *);
352 : }
353 : }
354 :
355 : /**
356 : * @brief common interface to get custom property description of a sub-plugin.
357 : */
358 : GData *
359 2 : subplugin_get_custom_property_desc (subpluginType type, const char *name)
360 : {
361 : subpluginData *spdata;
362 :
363 2 : g_return_val_if_fail (name != NULL, NULL);
364 2 : g_return_val_if_fail (subplugins[type] != NULL, NULL);
365 :
366 2 : spdata = _get_subplugin_data (type, name);
367 2 : if (spdata)
368 2 : return spdata->custom_dlist;
369 :
370 0 : return NULL;
371 : }
372 :
373 : /** @brief Create handles at the start of library */
374 : static void
375 527 : init_subplugin (void)
376 : {
377 527 : G_LOCK (splock);
378 527 : g_assert (NULL == handles); /** Internal error (duplicated init call?) */
379 527 : handles = g_ptr_array_new_full (16, _close_handle);
380 527 : G_UNLOCK (splock);
381 527 : }
382 :
383 : /** @brief Free handles at the start of library */
384 : static void
385 527 : fini_subplugin (void)
386 : {
387 527 : G_LOCK (splock);
388 527 : g_assert (handles); /** Internal error (init not called?) */
389 :
390 : /* iterate and call close by calling g_array_clear */
391 527 : g_ptr_array_free (handles, TRUE);
392 527 : handles = NULL;
393 527 : G_UNLOCK (splock);
394 527 : }
|