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.
authorEdouard Tisserant
Tue, 09 Apr 2019 13:05:35 +0200
changeset 2583 e172ab28d04e
parent 2582 8f0d6c5fd55f
child 2584 adfdeaba5a6a
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.
runtime/PLCObject.py
runtime/__init__.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 
--- 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
+
+