Centralize python interpreter init/fini for subplugins
authorMyungJoo Ham <myungjoo.ham@samsung.com>
Wed, 13 Dec 2023 05:41:54 +0000 (14:41 +0900)
committerjaeyun-jung <39614140+jaeyun-jung@users.noreply.github.com>
Mon, 8 Jan 2024 10:17:42 +0000 (19:17 +0900)
Python subplugins of decoder, converter, filter should
share the init/fini status.

Signed-off-by: MyungJoo Ham <myungjoo.ham@samsung.com>
ext/nnstreamer/extra/nnstreamer_python3_helper.cc
ext/nnstreamer/extra/nnstreamer_python3_helper.h
ext/nnstreamer/tensor_converter/tensor_converter_python3.cc
ext/nnstreamer/tensor_decoder/tensordec-python3.cc
ext/nnstreamer/tensor_filter/tensor_filter_python3.cc

index b2abc06..f346500 100644 (file)
@@ -529,6 +529,61 @@ PyTensorShape_New (PyObject *shape_cls, const GstTensorInfo *info)
   /* Its value is checked by setInputTensorDim */
 }
 
+static int python_init_counter = 0;
+static PyThreadState *st = NULL;
+/**
+ * @brief Py_Initialize common wrapper for Python subplugins
+ * @note This prevents a python-using subplugin finalizing another subplugin's
+ * python interpreter by sharing the reference counter.
+ */
+void
+nnstreamer_python_init_refcnt ()
+{
+  if (!Py_IsInitialized ()) {
+    Py_Initialize ();
+    PyEval_InitThreads_IfGood ();
+    st = PyEval_SaveThread ();
+  }
+  python_init_counter++;
+}
+
+/**
+ * @brief Py_Finalize common wrapper for Python subplugins
+ * @note This prevents a python-using subplugin finalizing another subplugin's
+ * python interpreter by sharing the reference counter.
+ */
+void
+nnstreamer_python_fini_refcnt ()
+{
+  python_init_counter--;
+  if (python_init_counter == 0) {
+    /**
+     * @todo Python Finalize() is buggy and leaky.
+     *   Do not call it until Python is fixed. (not fixed as in 2023-12)
+     *   Calling Finalize at this state will leave modules randomly, which
+     *   may cause assertion failure at exit.
+      PyEval_RestoreThread (st);
+      Py_Finalize ();
+     */
+  }
+}
+
+/**
+ * @brief Check Py_Init status for python eval functions.
+ * @return 0 if it's ready. negative error value if it's not ready.
+ */
+int
+nnstreamer_python_status_check ()
+{
+  if (python_init_counter == 0) {
+    fprintf (stderr, "nnstreamer_python_init_refcnt() is not called or it's already closed.");
+    return -EINVAL;
+  }
+
+  assert (Py_IsInitialized ());
+  return 0;
+}
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
index ae62b3a..80347a3 100644 (file)
@@ -67,6 +67,24 @@ extern int addToSysPath (const gchar *path);
 extern int parseTensorsInfo (PyObject *result, GstTensorsInfo *info);
 extern PyObject * PyTensorShape_New (PyObject * shape_cls, const GstTensorInfo *info);
 
+/**
+ * @brief Py_Initialize common wrapper for Python subplugins
+ * @note This prevents a python-using subplugin finalizing another subplugin's python interpreter by sharing the reference counter.
+ */
+extern void nnstreamer_python_init_refcnt ();
+
+/**
+ * @brief Py_Finalize common wrapper for Python subplugins
+ * @note This prevents a python-using subplugin finalizing another subplugin's python interpreter by sharing the reference counter.
+ */
+extern void nnstreamer_python_fini_refcnt ();
+
+/**
+ * @brief Check Py_Init status for python eval functions.
+ * @return 0 if it's ready. negative error value if it's not ready.
+ */
+extern int nnstreamer_python_status_check ();
+
 #ifdef __cplusplus
 } /* extern "C" */
 #endif
index 4a47968..d549ef3 100644 (file)
@@ -375,17 +375,12 @@ static NNStreamerExternalConverter Python = {
 #ifdef __cplusplus
 extern "C" {
 #endif /* __cplusplus */
-static PyThreadState *st;
 /** @brief Initialize this object for tensor converter sub-plugin */
 void
 init_converter_py (void)
 {
   /** Python should be initialized and finalized only once */
-  if (!Py_IsInitialized ()) {
-    Py_Initialize ();
-  }
-  PyEval_InitThreads_IfGood ();
-  st = PyEval_SaveThread ();
+  nnstreamer_python_init_refcnt ();
   registerExternalConverter (&Python);
 }
 
@@ -393,7 +388,8 @@ init_converter_py (void)
 void
 fini_converter_py (void)
 {
-  PyEval_RestoreThread (st);
+  nnstreamer_python_status_check ();
+  nnstreamer_python_fini_refcnt ();
   unregisterExternalConverter (Python.name);
 /**
  * @todo Remove below lines after this issue is addressed.
index c7fa591..6072e63 100644 (file)
@@ -382,17 +382,12 @@ static GstTensorDecoderDef Python = { .modename = decoder_subplugin_python3,
 #ifdef __cplusplus
 extern "C" {
 #endif /* __cplusplus */
-static PyThreadState *st;
 /** @brief Initialize this object for tensordec-plugin */
 void
 init_decoder_py (void)
 {
   /** Python should be initialized and finalized only once */
-  if (!Py_IsInitialized ())
-    Py_Initialize ();
-  PyEval_InitThreads_IfGood ();
-  st = PyEval_SaveThread ();
-
+  nnstreamer_python_init_refcnt ();
   nnstreamer_decoder_probe (&Python);
 }
 
@@ -400,7 +395,8 @@ init_decoder_py (void)
 void
 fini_decoder_py (void)
 {
-  PyEval_RestoreThread (st);
+  nnstreamer_python_status_check ();
+  nnstreamer_python_fini_refcnt ();
   nnstreamer_decoder_exit (Python.modename);
 
 /**
index 8a871bd..4f1e71b 100644 (file)
@@ -858,17 +858,12 @@ static GstTensorFilterFramework NNS_support_python = { .version = GST_TENSOR_FIL
         .allocateInInvoke = nullptr,
     } } };
 
-static PyThreadState *st;
 /** @brief Initialize this object for tensor_filter subplugin runtime register */
 void
 init_filter_py (void)
 {
   /** Python should be initialized and finalized only once */
-  if (!Py_IsInitialized ())
-    Py_Initialize ();
-  PyEval_InitThreads_IfGood ();
-  st = PyEval_SaveThread ();
-
+  nnstreamer_python_init_refcnt ();
   nnstreamer_filter_probe (&NNS_support_python);
   nnstreamer_filter_set_custom_property_desc (filter_subplugin_python, "${GENERAL_STRING}",
       "There is no key-value pair defined by python3 subplugin. "
@@ -880,7 +875,8 @@ init_filter_py (void)
 void
 fini_filter_py (void)
 {
-  PyEval_RestoreThread (st);
+  nnstreamer_python_status_check ();
+  nnstreamer_python_fini_refcnt ();
   nnstreamer_filter_exit (NNS_support_python.v0.name);
 
 /**