# HG changeset patch
# User Edouard Tisserant
# Date 1554807935 -7200
# Node ID e172ab28d04e5522220d8168eda9e8583c160ab9
# Parent  8f0d6c5fd55fca52acb165302a4b77701393b31d
Continue fixing deadlock of previous commit, this time occuring when waiting for 'cleanup' python runtime call to finish. Now 'init' and 'cleanup' python runtime calls are called directly from main thread, and aren't anymore invoked in the context of wxMainloop and/or twisted reactor.

diff -r 8f0d6c5fd55f -r e172ab28d04e runtime/PLCObject.py
--- a/runtime/PLCObject.py	Tue Apr 09 09:13:55 2019 +0200
+++ b/runtime/PLCObject.py	Tue Apr 09 13:05:35 2019 +0200
@@ -42,6 +42,7 @@
 from runtime.Stunnel import getPSKID
 from runtime import PlcStatus
 from runtime import MainWorker
+from runtime import default_evaluator
 
 if os.name in ("nt", "ce"):
     dlopen = _ctypes.LoadLibrary
@@ -319,13 +320,16 @@
 
         return False
 
-    def PythonRuntimeCall(self, methodname):
+    def PythonRuntimeCall(self, methodname, use_evaluator=True):
         """
         Calls init, start, stop or cleanup method provided by
         runtime python files, loaded when new PLC uploaded
         """
         for method in self.python_runtime_vars.get("_runtime_%s" % methodname, []):
-            _res, exp = self.evaluator(method)
+            if use_evaluator:
+                _res, exp = self.evaluator(method)
+            else:
+                _res, exp = default_evaluator(method)
             if exp is not None:
                 self.LogMessage(0, '\n'.join(traceback.format_exception(*exp)))
 
@@ -379,6 +383,8 @@
             self.LogMessage(0, traceback.format_exc())
             raise
 
+        self.PythonRuntimeCall("init", use_evaluator=False)
+
         self.PythonThreadCondLock = Lock()
         self.PythonThreadCond = Condition(self.PythonThreadCondLock)
         self.PythonThreadCmd = "Wait"
@@ -391,6 +397,7 @@
         if self.python_runtime_vars is not None:
             self.PythonThreadCommand("Finish")
             self.PythonThread.join()
+            self.PythonRuntimeCall("cleanup", use_evaluator=False)
 
         self.python_runtime_vars = None
 
@@ -421,9 +428,6 @@
                 self.LogMessage(1, ('PyEval@0x%x(Code="%s") Exception "%s"') % (FBID, cmd, str(e)))
 
     def PythonThreadProc(self):
-        print('self.PythonRuntimeCall("init")')
-        self.PythonRuntimeCall("init")
-
         while True:
             self.PythonThreadCondLock.acquire()
             cmd = self.PythonThreadCmd
@@ -434,7 +438,6 @@
             self.PythonThreadCondLock.release()
             
             if cmd == "Activate" :
-                print('self.PythonRuntimeCall("start")')
                 self.PythonRuntimeCall("start")
 
                 self.PythonThreadLoop()
@@ -443,8 +446,6 @@
             else:  # "Finish"
                 break
 
-        self.PythonRuntimeCall("cleanup")
-
     def PythonThreadCommand(self, cmd):
         self.PythonThreadCondLock.acquire()
         self.PythonThreadCmd = cmd 
diff -r 8f0d6c5fd55f -r e172ab28d04e runtime/__init__.py
--- a/runtime/__init__.py	Tue Apr 09 09:13:55 2019 +0200
+++ b/runtime/__init__.py	Tue Apr 09 13:05:35 2019 +0200
@@ -30,3 +30,13 @@
     global _PLCObjectSingleton
     from runtime.PLCObject import PLCObject  # noqa # pylint: disable=wrong-import-position
     _PLCObjectSingleton = PLCObject(*args, **kwargs)
+
+
+def default_evaluator(tocall, *args, **kwargs):
+    try:
+        res = (tocall(*args, **kwargs), None)
+    except Exception:
+        res = (None, sys.exc_info())
+    return res
+
+