[Common/Subplugin] Internal API to handle subplugins
authorMyungJoo Ham <myungjoo.ham@samsung.com>
Tue, 27 Nov 2018 12:28:11 +0000 (21:28 +0900)
committerJijoong Moon <jijoong.moon@samsung.com>
Mon, 3 Dec 2018 06:04:34 +0000 (15:04 +0900)
Any subplugin-using elements may call get_subplugin () to
get internal data (set of callbacks) of the subplugin.

Next Step: modify tensor_decoder to use nnstreamer_subplugin.h
and test with it.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
CMakeLists.txt
gst/nnstreamer/nnstreamer_subplugin.c [new file with mode: 0644]
gst/nnstreamer/nnstreamer_subplugin.h [new file with mode: 0644]

index c2e0d04..56f0295 100644 (file)
@@ -120,7 +120,7 @@ IF(ENABLE_TENSORFLOW)
 ENDIF(ENABLE_TENSORFLOW)
 LIST(LENGTH FILTER_DEP FILTER_DEP_LEN)
 
-ADD_LIBRARY(nnstreamerStatic STATIC gst/nnstreamer/nnstreamer.c gst/nnstreamer/tensor_common.c gst/nnstreamer/tensor_repo.c gst/nnstreamer/nnstreamer_conf.c ${OBJECTS})
+ADD_LIBRARY(nnstreamerStatic STATIC gst/nnstreamer/nnstreamer.c gst/nnstreamer/tensor_common.c gst/nnstreamer/tensor_repo.c gst/nnstreamer/nnstreamer_conf.c gst/nnstreamer/nnstreamer_subplugin.c ${OBJECTS})
 SET_TARGET_PROPERTIES(nnstreamerStatic PROPERTIES OUTPUT_NAME nnstreamer)
 TARGET_INCLUDE_DIRECTORIES(nnstreamerStatic PUBLIC ${pkgs_INCLUDE_DIRS})
 TARGET_COMPILE_OPTIONS(nnstreamerStatic PUBLIC ${pkgs_CFLAGS_OTHER})
diff --git a/gst/nnstreamer/nnstreamer_subplugin.c b/gst/nnstreamer/nnstreamer_subplugin.c
new file mode 100644 (file)
index 0000000..8a04566
--- /dev/null
@@ -0,0 +1,172 @@
+/**
+ * NNStreamer Subplugin Manager
+ * Copyright (C) 2018 MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ */
+/**
+ * @file       nnstreamer_subplugin.c
+ * @date       27 Nov 2018
+ * @brief      Subplugin Manager for NNStreamer
+ * @see                http://github.com/nnsuite/nnstreamer
+ * @author     MyungJoo Ham <myungjoo.ham@samsung.com>
+ * @bug                No known bugs except for NYI items
+ *
+ */
+
+#include <dlfcn.h>
+#include <glib.h>
+#include <gmodule.h>
+#include <gst/gstinfo.h>
+#include "nnstreamer_subplugin.h"
+#include "nnstreamer_conf.h"
+
+typedef struct
+{
+  char *name; /**< The name of subplugin */
+  const void *data; /**< subplugin specific data forwarded from the subplugin */
+  void *handle; /**< dlopen'ed handle */
+} subpluginData;
+
+static GHashTable *subplugins[NNS_SUBPLUGIN_END] = { 0 };
+
+G_LOCK_DEFINE_STATIC (splock);
+
+/** @brief Private function for g_hash_table data destructor, GDestroyNotify */
+static void
+_spdata_destroy (gpointer _data)
+{
+  subpluginData *data = _data;
+  g_free (data->name);
+  if (data->handle)
+    dlclose (data->handle);
+  g_free (data);
+}
+
+/** @brief Public function defined in the header */
+const void *
+get_subplugin (subpluginType type, const char *name)
+{
+  GHashTable *table;
+  subpluginData *data;
+  void *handle;
+
+  G_LOCK (splock);
+
+  if (subplugins[type] == NULL)
+    subplugins[type] =
+        g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+        _spdata_destroy);
+
+  table = subplugins[type];
+  data = g_hash_table_lookup (table, name);
+
+  if (data == NULL) {
+    /* Search and register if found with the conf */
+    const gchar *fullpath = nnsconf_get_fullpath (name, type);
+    char *dlsym_error;
+    nnstreamer_subplugin_data *nsdata;
+
+    if (fullpath == NULL)
+      goto error;               /* No Such Thing !!! */
+
+    handle = dlopen (fullpath, RTLD_NOW);
+    if (NULL == handle) {
+      GST_ERROR ("Cannot dlopen %s (%s).", name, fullpath);
+      goto error;
+    }
+
+    nsdata = (nnstreamer_subplugin_data *)
+        dlsym (handle, "nnstreamer_subplugin");
+    dlsym_error = dlerror ();
+
+    if (NULL == nsdata) {
+      GST_ERROR ("nnstreamer_subplugin does not exists in %s (%s)", name,
+          fullpath);
+      goto error_handle;
+    }
+    if (dlsym_error) {
+      GST_ERROR ("Loading nnstreamer_subplugin in %s (%s) incurs: %s", name,
+          fullpath, dlsym_error);
+      goto error_handle;
+    }
+
+    if (nsdata->checker != NNS_SUBPLUGIN_CHECKER) {
+      GST_ERROR ("nnstreamer_subplugin of %s (%s) is broken (first bytes)",
+          name, fullpath);
+      goto error_handle;
+    }
+
+    if (nsdata->type != type) {
+      GST_ERROR ("nnstreamer_subplugin of %s (%s) is broken (type mismatch)",
+          name, fullpath);
+      goto error_handle;
+    }
+
+    if (g_strcmp0 (nsdata->name, name)) {
+      GST_ERROR ("nnstreamer_subplugin of %s (%s) is broken (name mismatch)",
+          name, fullpath);
+      goto error_handle;
+    }
+
+    g_assert (TRUE == register_subplugin (type, name, nsdata->data));
+    g_assert ((data = g_hash_table_lookup (table, name)) != NULL);
+    data->handle = handle;
+  }
+  G_UNLOCK (splock);
+  return data->data;
+
+error_handle:
+  dlclose (handle);
+error:
+  G_UNLOCK (splock);
+  return NULL;
+}
+
+/** @brief Public function defined in the header */
+gboolean
+register_subplugin (subpluginType type, const char *name, const void *data)
+{
+  /** @todo data out of scope at add */
+  subpluginData *spdata = g_new (subpluginData, 1);
+  gboolean ret;
+
+  spdata->name = g_strdup (name);
+  spdata->data = data;
+  spdata->handle = NULL;
+
+  if (subplugins[type] == NULL)
+    subplugins[type] =
+        g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+        _spdata_destroy);
+
+  G_LOCK (splock);
+  ret = g_hash_table_insert (subplugins[type], g_strdup (name), spdata);
+  G_UNLOCK (splock);
+
+  return ret;
+}
+
+/** @brief Public function defined in the header */
+gboolean
+unregister_subplugin (subpluginType type, const char *name)
+{
+  gboolean ret;
+  if (subplugins[type] == NULL)
+    return FALSE;
+
+  G_LOCK (splock);
+  ret = g_hash_table_remove (subplugins[type], name);
+  G_UNLOCK (splock);
+
+  return ret;
+}
diff --git a/gst/nnstreamer/nnstreamer_subplugin.h b/gst/nnstreamer/nnstreamer_subplugin.h
new file mode 100644 (file)
index 0000000..26e0377
--- /dev/null
@@ -0,0 +1,90 @@
+/**
+ * NNStreamer Subplugin Manager
+ * Copyright (C) 2018 MyungJoo Ham <myungjoo.ham@samsung.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ */
+/**
+ * @file       nnstreamer_subplugin.h
+ * @date       27 Nov 2018
+ * @brief      Subplugin Manager for NNStreamer
+ * @see                http://github.com/nnsuite/nnstreamer
+ * @author     MyungJoo Ham <myungjoo.ham@samsung.com>
+ * @bug                No known bugs except for NYI items
+ *
+ * To Packagers:
+ *
+ * This fils it to be packaged as "devel" package for NN developers. (subplugin writers)
+ *
+ * @note        Any independent subplugin (existing as an independent .so)
+ *              should have an exported variable "nnstreamer_subplugin",
+ *              whose type is nnstreamer_subplugin_data.
+ *              A built-in subplugin (existing in libnnstreamer.so) does not
+ *              need this.
+ */
+#ifndef __GST_NNSTREAMER_SUBPLUGIN_H__
+#define __GST_NNSTREAMER_SUBPLUGIN_H__
+
+#include <stdint.h>
+#include "nnstreamer_conf.h"
+
+typedef enum {
+  NNS_SUBPLUGIN_FILTER = NNSCONF_FILTERS,
+  NNS_SUBPLUGIN_DECODER = NNSCONF_DECODERS,
+
+  NNS_SUBPLUGIN_END,
+} subpluginType;
+
+#define NNS_SUBPLUGIN_CHECKER (0xdeadbeef)
+
+/**
+ * @brief Any independent shared object subplugin must export this as
+ * "nnstreamer_subplugin"
+ */
+typedef struct {
+  uint32_t checker; /**< This MUST be 0xdeadbeef (NNS_SUBPLUGIN_CHECKER) */
+  subpluginType type; /**< The subplugin type */
+  const char *name; /**< The name of subplugin. The resulting .so file should be ${prefix}${name}.so */
+  void *data; /**< Subplugin specific data (usually funcptr callbacks), forwarded to subplugin's corresponding handler */
+} nnstreamer_subplugin_data;
+
+/**
+ * @brief Retrieve the resigered data with the subplugin name.
+ * @param[in] type Subplugin Type
+ * @param[in] name Subplugin Name. The filename should be libnnstreamer_${type}_${name}.so
+ * @return The registered data
+ */
+extern const void *
+get_subplugin (subpluginType type, const char *name);
+
+/**
+ * @brief Register the subplugin. If duplicated name exists, it is rejected.
+ * @param[in] type Subplugin Type
+ * @param[in] name Subplugin Name. The filename should be subplugin_prefixes[type]${name}.so
+ * @param[in] data The registered data
+ * @return TRUE if regitered as new. FALSE if duplicated (overwritten/updated).
+ */
+extern gboolean
+register_subplugin (subpluginType type, const char *name, const void *data);
+
+/**
+ * @brief Unregister the subplugin.
+ * @param[in] type Subplugin type
+ * @param[in] name Subplugin Name. The filename should be subplugin_prefixes[type]${name}.so
+ * @return TRUE if unregitered. FALSE if rejected or error.
+ *
+ * @warning Subplugins checked out with get_subplugins can still be used after unregister.
+ */
+extern gboolean
+unregister_subplugin (subpluginType type, const char *name);
+
+#endif /* __GST_NNSTREAMER_SUBPLUGIN_H__ */