LCOV - code coverage report
Current view: top level - nnstreamer-2.4.2/gst/nnstreamer - nnstreamer_subplugin.c (source / functions) Coverage Total Hit
Test: nnstreamer 2.4.2-0 nnstreamer/nnstreamer#eca68b8d050408568af95d831a8eef62aaee7784 Lines: 91.7 % 145 133
Test Date: 2025-03-13 05:38:21 Functions: 100.0 % 12 12

            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 : }
        

Generated by: LCOV version 2.0-1