Browse Source

Fix more threading problems in Python3

Suren A. Chilingaryan 8 years ago
parent
commit
cd74063b12
1 changed files with 163 additions and 97 deletions
  1. 163 97
      pcilib/py.c

+ 163 - 97
pcilib/py.c

@@ -34,14 +34,20 @@ struct pcilib_script_s {
 struct pcilib_py_s {
     int finalyze; 			/**< Indicates, that we are initialized from wrapper and should not destroy Python resources in destructor */
     PyObject *main_module;		/**< Main interpreter */
+    PyObject *pywrap_module;		/**< Pcilib python wrapper */
+    PyObject *threading_module;		/**< Threading module */
     PyObject *global_dict;		/**< Dictionary of main interpreter */
-    PyObject *pcilib_pywrap;		/**< pcilib wrapper module */
+    PyObject *pcilib_pywrap;		/**< pcilib wrapper context */
     pcilib_script_t *script_hash;	/**< Hash with loaded scripts */
     
 # if PY_MAJOR_VERSION >= 3
-    pthread_t pth;
-    pthread_cond_t cond;
-    pthread_mutex_t lock;
+    int status;				/**< Indicates if python was initialized successfuly (0) or error have occured */
+    pthread_t pth;			/**< Helper thread for Python initialization */
+    pthread_cond_t cond;		/**< Condition informing about initialization success and request for clean-up */
+    pthread_mutex_t lock;		/**< Condition lock */
+
+//    PyInterpreterState *istate;
+//    PyThreadState *tstate;
 # endif /* PY_MAJOR_VERSION > 3 */
 };
 #endif /* HAVE_PYTHON */
@@ -126,6 +132,77 @@ void pcilib_log_python_error(const char *file, int line, pcilib_log_flags_t flag
 }
 
 #ifdef HAVE_PYTHON
+static int pcilib_py_load_default_modules(pcilib_t *ctx) {
+    PyGILState_STATE gstate = PyGILState_Ensure();
+
+    ctx->py->main_module = PyImport_AddModule("__parser__");
+    if (!ctx->py->main_module) {
+	PyGILState_Release(gstate);
+	pcilib_python_warning("Error importing python parser");
+        return PCILIB_ERROR_FAILED;
+    }
+
+    ctx->py->global_dict = PyModule_GetDict(ctx->py->main_module);
+    if (!ctx->py->global_dict) {
+	PyGILState_Release(gstate);
+	pcilib_python_warning("Error locating global python dictionary");
+        return PCILIB_ERROR_FAILED;
+    }
+
+    ctx->py->pywrap_module = PyImport_ImportModule(PCILIB_PYTHON_WRAPPER);
+    if (!ctx->py->pywrap_module) {
+	PyGILState_Release(gstate);
+	pcilib_python_warning("Error importing pcilib python wrapper");
+	return PCILIB_ERROR_FAILED;
+    }
+
+    /**
+     * We need to load threading module here, otherwise if any of the scripts
+     * will use threading, on cleanup Python3 will complain:
+     * Exception KeyError: KeyError(140702199305984,) in <module 'threading' from '/usr/lib64/python3.3/threading.py'> ignored
+     * The idea to load threading module is inspired by
+     * http://stackoverflow.com/questions/8774958/keyerror-in-module-threading-after-a-successful-py-test-run
+     */
+    ctx->py->threading_module = PyImport_ImportModule("threading");
+    if (!ctx->py->threading_module) {
+	PyGILState_Release(gstate);
+	pcilib_python_warning("Error importing threading python module");
+	return PCILIB_ERROR_FAILED;
+    }
+	
+    PyObject *mod_name = PyUnicode_FromString(PCILIB_PYTHON_WRAPPER);
+    PyObject *pyctx = PyCapsule_New(ctx, "pcilib", NULL);
+    ctx->py->pcilib_pywrap = PyObject_CallMethodObjArgs(ctx->py->pywrap_module, mod_name, pyctx, NULL);
+    Py_XDECREF(pyctx);
+    Py_XDECREF(mod_name);
+	
+    if (!ctx->py->pcilib_pywrap) {
+	PyGILState_Release(gstate);
+	pcilib_python_warning("Error initializing python wrapper");
+        return PCILIB_ERROR_FAILED;
+    }
+
+    PyGILState_Release(gstate);
+    return 0;
+}
+
+static void pcilib_py_clean_default_modules(pcilib_t *ctx) {
+    PyGILState_STATE gstate;
+
+    gstate = PyGILState_Ensure();
+
+    if (ctx->py->pcilib_pywrap) Py_DECREF(ctx->py->pcilib_pywrap);
+
+    if (ctx->py->threading_module) Py_DECREF(ctx->py->threading_module);
+    if (ctx->py->pywrap_module) Py_DECREF(ctx->py->pywrap_module);
+
+	// Crashes Python2
+//    if (ctx->py->main_module) Py_DECREF(ctx->py->main_module);
+
+    PyGILState_Release(gstate);
+}
+
+
 # if PY_MAJOR_VERSION >= 3
 /**
  * Python3 specially treats the main thread intializing Python. It crashes if
@@ -146,24 +223,35 @@ void pcilib_log_python_error(const char *file, int line, pcilib_log_flags_t flag
  * http://stackoverflow.com/questions/15470367/pyeval-initthreads-in-python-3-how-when-to-call-it-the-saga-continues-ad-naus
  */
 static void *pcilib_py_run_init_thread(void *arg) {
-    pcilib_py_t *py = (pcilib_py_t*)(arg);
+    PyThreadState *state;
+    pcilib_t *ctx = (pcilib_t*)arg;
+    pcilib_py_t *py = ctx->py;
 
     Py_Initialize();
+
     PyEval_InitThreads();
-    PyEval_ReleaseLock();
+
+//    state = PyThreadState_Get();
+//    py->istate = state->interp;
+
+    py->status = pcilib_py_load_default_modules(ctx);
+
+    state = PyEval_SaveThread();
 
 	// Ensure that main thread waiting for our signal
     pthread_mutex_lock(&(py->lock));
-   
+
 	// Inform the parent thread that initialization is finished
     pthread_cond_signal(&(py->cond));
 
 	// Wait untill cleanup is requested
     pthread_cond_wait(&(py->cond), &(py->lock));
     pthread_mutex_unlock(&(py->lock));
-    
+
+    PyEval_RestoreThread(state);
+    pcilib_py_clean_default_modules(ctx);
     Py_Finalize();
-    
+
     return NULL;
 }
 # endif /* PY_MAJOR_VERSION < 3 */
@@ -171,6 +259,8 @@ static void *pcilib_py_run_init_thread(void *arg) {
 
 int pcilib_init_py(pcilib_t *ctx) {
 #ifdef HAVE_PYTHON
+    int err = 0;
+
     ctx->py = (pcilib_py_t*)malloc(sizeof(pcilib_py_t));
     if (!ctx->py) return PCILIB_ERROR_MEMORY;
     memset(ctx->py, 0, sizeof(pcilib_py_t));
@@ -181,6 +271,8 @@ int pcilib_init_py(pcilib_t *ctx) {
     	    // Since python is being initializing from c programm, it needs to initialize threads to work properly with c threads
         PyEval_InitThreads();
         PyEval_ReleaseLock();
+
+	err = pcilib_py_load_default_modules(ctx);
 # else /* PY_MAJOR_VERSION < 3 */
 	int err = pthread_mutex_init(&(ctx->py->lock), NULL);
 	if (err) return PCILIB_ERROR_FAILED;
@@ -199,7 +291,7 @@ int pcilib_init_py(pcilib_t *ctx) {
 	}
 
 	    // Create initalizer thread and wait until it releases the Lock
-        err = pthread_create(&(ctx->py->pth), NULL, pcilib_py_run_init_thread, ctx->py);
+        err = pthread_create(&(ctx->py->pth), NULL, pcilib_py_run_init_thread, (void*)ctx);
 	if (err) {
 	    pthread_mutex_unlock(&(ctx->py->lock));
 	    pthread_cond_destroy(&(ctx->py->cond));
@@ -209,50 +301,66 @@ int pcilib_init_py(pcilib_t *ctx) {
 
     	    // Wait until initialized and keep the lock afterwards until free executed
 	pthread_cond_wait(&(ctx->py->cond), &(ctx->py->lock));
+	err = ctx->py->status;
+
+//	ctx->py->tstate =  PyThreadState_New(ctx->py->istate);
 # endif /* PY_MAJOR_VERSION < 3 */
 	ctx->py->finalyze = 1;
+    } else {
+	err = pcilib_py_load_default_modules(ctx);
     }
 
-	
-    PyGILState_STATE gstate = PyGILState_Ensure();
-
-    ctx->py->main_module = PyImport_AddModule("__parser__");
-    if (!ctx->py->main_module) {
-	PyGILState_Release(gstate);
-	pcilib_python_warning("Error importing python parser");
-        return PCILIB_ERROR_FAILED;
+    if (err) {
+	pcilib_free_py(ctx);
+	return err;
     }
+#endif /* HAVE_PYTHON */
 
-    ctx->py->global_dict = PyModule_GetDict(ctx->py->main_module);
-    if (!ctx->py->global_dict) {
-	PyGILState_Release(gstate);
-	pcilib_python_warning("Error locating global python dictionary");
-        return PCILIB_ERROR_FAILED;
-    }
+    return 0;
+}
 
-    PyObject *pywrap = PyImport_ImportModule(PCILIB_PYTHON_WRAPPER);
-    if (!pywrap) {
-	PyGILState_Release(gstate);
-	pcilib_python_warning("Error importing pcilib python wrapper");
-	return PCILIB_ERROR_FAILED;
-    }
-	
-    PyObject *mod_name = PyUnicode_FromString(PCILIB_PYTHON_WRAPPER);
-    PyObject *pyctx = PyCapsule_New(ctx, "pcilib", NULL);
-    ctx->py->pcilib_pywrap = PyObject_CallMethodObjArgs(pywrap, mod_name, pyctx, NULL);
-    Py_XDECREF(pyctx);
-    Py_XDECREF(mod_name);
-	
-    if (!ctx->py->pcilib_pywrap) {
+void pcilib_free_py(pcilib_t *ctx) {
+#ifdef HAVE_PYTHON
+    if (ctx->py) {
+	PyGILState_STATE gstate;
+
+	gstate = PyGILState_Ensure();
+	if (ctx->py->script_hash) {
+	    pcilib_script_t *script, *script_tmp;
+
+	    HASH_ITER(hh, ctx->py->script_hash, script, script_tmp) {
+		Py_DECREF(script->module);
+		HASH_DEL(ctx->py->script_hash, script);
+		free(script);
+	    }
+	    ctx->py->script_hash = NULL;
+	}
 	PyGILState_Release(gstate);
-	pcilib_python_warning("Error initializing python wrapper");
-        return PCILIB_ERROR_FAILED;
+
+#if PY_MAJOR_VERSION < 3
+	pcilib_py_clean_default_modules(ctx);
+#endif /* PY_MAJOR_VERSION < 3 */
+
+	if (ctx->py->finalyze) {
+#if PY_MAJOR_VERSION < 3
+	    Py_Finalize();
+#else /* PY_MAJOR_VERSION < 3 */
+//	    PyThreadState_Delete(ctx->py->tstate);
+
+		// singal python init thread to stop and wait it to finish
+	    pthread_cond_signal(&(ctx->py->cond));
+	    pthread_mutex_unlock(&(ctx->py->lock));
+	    pthread_join(ctx->py->pth, NULL);
+
+		// destroy synchronization primitives
+	    pthread_cond_destroy(&(ctx->py->cond));
+	    pthread_mutex_destroy(&(ctx->py->lock));
+#endif /* PY_MAJOR_VERSION < 3 */
+	}
+        free(ctx->py);
+        ctx->py = NULL;
     }
-    
-    PyGILState_Release(gstate);
 #endif /* HAVE_PYTHON */
-
-    return 0;
 }
 
 int pcilib_py_add_script_dir(pcilib_t *ctx, const char *dir) {
@@ -276,7 +384,7 @@ int pcilib_py_add_script_dir(pcilib_t *ctx, const char *dir) {
 	if (!script_dir) return PCILIB_ERROR_MEMORY;
 	sprintf(script_dir, "%s/%s", model_dir, dir);
     }
-    
+
     PyGILState_STATE gstate = PyGILState_Ensure();
 
     pypath = PySys_GetObject("path");
@@ -292,7 +400,7 @@ int pcilib_py_add_script_dir(pcilib_t *ctx, const char *dir) {
 	pcilib_python_warning("Can't create python string");
 	return PCILIB_ERROR_MEMORY;
     }
-    
+
     // Checking if the directory already in the path?
     pydict = PyDict_New();
     if (pydict) {
@@ -329,55 +437,6 @@ int pcilib_py_add_script_dir(pcilib_t *ctx, const char *dir) {
     return 0;
 }
 
-void pcilib_free_py(pcilib_t *ctx) {
-#ifdef HAVE_PYTHON
-    int finalyze = 0;
-    
-    if (ctx->py) {		
-	PyGILState_STATE gstate;
-
-	if (ctx->py->finalyze) finalyze = 1;
-	
-	gstate = PyGILState_Ensure();
-
-	if (ctx->py->script_hash) {
-	    pcilib_script_t *script, *script_tmp;
-
-	    HASH_ITER(hh, ctx->py->script_hash, script, script_tmp) {
-		Py_DECREF(script->module);
-		HASH_DEL(ctx->py->script_hash, script);
-		free(script);
-	    }
-	    ctx->py->script_hash = NULL;
-	}
-
-	if (ctx->py->pcilib_pywrap)
-	    Py_DECREF(ctx->py->pcilib_pywrap);
-
-	PyGILState_Release(gstate);
-    
-	
-        free(ctx->py);
-        ctx->py = NULL;
-    }
-    
-    
-    if (finalyze) {
-#if PY_MAJOR_VERSION < 3
-       Py_Finalize();
-#else /* PY_MAJOR_VERSION < 3 */
-          // singal python init thread to stop and wait it to finish
-       pthread_cond_signal(&(ctx->py->cond));
-       pthread_mutex_unlock(&(ctx->py->lock));
-       pthread_join(ctx->py->pth, NULL);
-  
-          // destroy synchronization primitives
-       pthread_cond_destroy(&(ctx->py->cond));
-       pthread_mutex_destroy(&(ctx->py->lock));
-#endif /* PY_MAJOR_VERSION < 3 */
-    }
-#endif /* HAVE_PYTHON */
-}
 
 int pcilib_py_load_script(pcilib_t *ctx, const char *script_name) {
 #ifdef HAVE_PYTHON
@@ -407,10 +466,15 @@ int pcilib_py_load_script(pcilib_t *ctx, const char *script_name) {
 	pcilib_python_error("Error importing script (%s)", script_name);
 	return PCILIB_ERROR_FAILED;
     }
-    PyGILState_Release(gstate);
 
     module = (pcilib_script_t*)malloc(sizeof(pcilib_script_t));
-    if (!module) return PCILIB_ERROR_MEMORY;
+    if (!module) {
+	Py_DECREF(pymodule);
+	PyGILState_Release(gstate);
+	return PCILIB_ERROR_MEMORY;
+    }
+
+    PyGILState_Release(gstate);
 
     module->module = pymodule;
     module->name = script_name;
@@ -464,6 +528,8 @@ int pcilib_py_get_transform_script_properties(pcilib_t *ctx, const char *script_
     PyGILState_Release(gstate);
 #endif /* HAVE_PYTHON */
 
+//    pcilib_py_load_default_modules(py->ctx);
+
     if (mode_ret) *mode_ret = mode;
     return 0;
 }