Minor changes to get better cleanup of debug and python_eval threads, accross multiple debug sessions and PLC runs.
authoretisserant
Sun, 04 Jan 2009 17:29:12 +0100
changeset 286 a2a8a52b0d4f
parent 285 e5782a52dcea
child 287 5b3083695c8c
Minor changes to get better cleanup of debug and python_eval threads, accross multiple debug sessions and PLC runs.
plugger.py
runtime/PLCObject.py
targets/plc_common_main.c
targets/plc_python.c
--- a/plugger.py	Sun Jan 04 17:25:22 2009 +0100
+++ b/plugger.py	Sun Jan 04 17:29:12 2009 +0100
@@ -700,6 +700,8 @@
         self.BuildPath = None
         self.PLCEditor = None
         self.PLCDebug = None
+        self.DebugThread = None
+        self.debug_break = False
         # copy PluginMethods so that it can be later customized
         self.PluginMethods = [dic.copy() for dic in self.PluginMethods]
 
@@ -1309,6 +1311,8 @@
             self.logger.write_error("Build directory already clean\n")
         self.ShowMethod("_showIECcode", False)
         self.EnableMethod("_Clean", False)
+        # kill the builder
+        self._builder = None
         self.CompareLocalAndRemotePLC()
 
     ############# Real PLC object access #############
@@ -1434,8 +1438,8 @@
         """
         # This lock is used to avoid flooding wx event stack calling callafter
         self.DebugThreadSlowDownLock = Semaphore(0)
-        _break = False
-        while not _break and self._connector is not None:
+        self.debug_break = False
+        while (not self.debug_break) and (self._connector is not None):
             debug_tick, debug_vars = self._connector.GetTraceVariables()
             #print debug_tick, debug_vars
             self.IECdebug_lock.acquire()
@@ -1459,11 +1463,19 @@
                 pass
             else:
                 wx.CallAfter(self.logger.write, "Debugger disabled\n")
-                _break = True
+                self.debug_break = True
             self.IECdebug_lock.release()
             wx.CallAfter(self.DebugThreadSlowDownLock.release)
             self.DebugThreadSlowDownLock.acquire()
 
+    def KillDebugThread(self):
+        self.debug_break = True
+        self.DebugThreadSlowDownLock.release()
+        self.DebugThread.join(timeout=1)
+        if self.DebugThread.isAlive():
+            self.logger.write_warning("Debug Thread couldn't be killed")
+        self.DebugThread = None
+
     def _Debug(self):
         """
         Start PLC (Debug Mode)
@@ -1485,6 +1497,7 @@
             self.logger.write_error("Couldn't start PLC debug !\n")
         self.UpdateMethodsFromPLCStatus()
 
+
 #    def _Do_Test_Debug(self):
 #        # debug code
 #        self.temporary_non_weak_callable_refs = []
@@ -1506,6 +1519,10 @@
         """
         Stop PLC
         """
+        if self.DebugThread is not None:
+            self.logger.write("Stopping debug\n")
+            self.KillDebugThread()
+        
         if self._connector.StopPLC():
             self.logger.write("Stopping PLC\n")
         else:
--- a/runtime/PLCObject.py	Sun Jan 04 17:25:22 2009 +0100
+++ b/runtime/PLCObject.py	Sun Jan 04 17:29:12 2009 +0100
@@ -61,6 +61,10 @@
             self.PLCStatus = "Empty"
             self.CurrentPLCFilename=None
 
+    def StatusChange(self):
+        if self.statuschange is not None:
+            self.statuschange(self.PLCStatus)
+
     def _GetMD5FileName(self):
         return os.path.join(self.workingdir, "lasttransferedPLC.md5")
 
@@ -169,21 +173,30 @@
         return False
 
     def PythonThreadProc(self):
+        print "PythonThreadProc started"
+        my_globs = globals().copy()
         pyfile = os.path.join(self.workingdir, "runtime.py")
         if os.path.exists(pyfile):
             # TODO handle exceptions in runtime.py
-            execfile(pyfile)
-        res = ""
-        print "PythonThreadProc started"
+            # pyfile may redefine _runtime_cleanup
+            # or even call _PythonThreadProc itself.
+            execfile(pyfile, my_globs)
+        res,cmd = "None","None"
         while self.PLCStatus == "Started":
+            print "_PythonIterator(", res, ")",
             cmd = self._PythonIterator(res)
-            #print "_PythonIterator(", res, ") -> ", cmd
+            print " -> ", cmd
+            if cmd is None:
+                break
             try :
-                res = eval(cmd)
+                res = str(eval(cmd,my_globs))
             except Exception,e:
                 res = "#EXCEPTION : "+str(e)
                 print res
-        print "PythonThreadProc finished"
+        print "PythonThreadProc interrupted"
+        if my_globs.get("_runtime_cleanup",None) is not None:
+            my_globs["_runtime_cleanup"]()
+        print "PythonThreadProc cleaned up"
     
     def StartPLC(self, debug=False):
         print "StartPLC"
@@ -193,8 +206,7 @@
                 if debug:
                     self._resumeDebug()
                 self.PLCStatus = "Started"
-                if self.statuschange is not None:
-                    self.statuschange(self.PLCStatus)
+                self.StatusChange()
                 self.PythonThread = Thread(target=self.PythonThreadProc)
                 self.PythonThread.start()
                 return True
@@ -206,10 +218,12 @@
     def _DoStopPLC(self):
         self._stopPLC()
         self.PLCStatus = "Stopped"
-        if self.statuschange is not None:
-            self.statuschange(self.PLCStatus)
+        self.PythonThread.join(timeout=1)
+        if self.PythonThread.isAlive():
+            print "Python thread couldn't be killed"
         if self._FreePLC():
             self.PLCStatus = "Dirty"
+        self.StatusChange()
         return True
 
     def StopPLC(self):
@@ -327,25 +341,27 @@
         """
         Return a list of variables, corresponding to the list of requiered idx
         """
-        tick = self._WaitDebugData()
-        if tick == -1:
-            res = None
-        else:
-            idx = ctypes.c_int()
-            typename = ctypes.c_char_p()
-            res = []
-    
-            for given_idx in self._Idxs:
-                buffer=self._IterDebugData(ctypes.byref(idx), ctypes.byref(typename))
-                c_type,unpack_func = self.TypeTranslator.get(typename.value, (None,None))
-                if c_type is not None and given_idx == idx.value:
-                    res.append(unpack_func(ctypes.cast(buffer,
-                                                       ctypes.POINTER(c_type)).contents))
-                else:
-                    print "Debug error idx : %d, expected_idx %d, type : %s"%(idx.value, given_idx,typename.value)
-                    res.append(None)
-        self._FreeDebugData()
-        return tick, res
-        
-
-
+        if self.PLCStatus == "Started":
+            tick = self._WaitDebugData()
+            if tick == -1:
+                res = None
+            else:
+                idx = ctypes.c_int()
+                typename = ctypes.c_char_p()
+                res = []
+        
+                for given_idx in self._Idxs:
+                    buffer=self._IterDebugData(ctypes.byref(idx), ctypes.byref(typename))
+                    c_type,unpack_func = self.TypeTranslator.get(typename.value, (None,None))
+                    if c_type is not None and given_idx == idx.value:
+                        res.append(unpack_func(ctypes.cast(buffer,
+                                                           ctypes.POINTER(c_type)).contents))
+                    else:
+                        print "Debug error idx : %d, expected_idx %d, type : %s"%(idx.value, given_idx,typename.value)
+                        res.append(None)
+            self._FreeDebugData()
+            return tick, res
+        return -1, None
+        
+
+
--- a/targets/plc_common_main.c	Sun Jan 04 17:25:22 2009 +0100
+++ b/targets/plc_common_main.c	Sun Jan 04 17:29:12 2009 +0100
@@ -69,6 +69,7 @@
     int res;
     config_init__();
     __init_debug();
+    __init_python();
     %(init_calls)s
     return 0;
 }
@@ -79,6 +80,7 @@
 {
     %(cleanup_calls)s
     __cleanup_debug();
+    __cleanup_python();
 }
 
 
--- a/targets/plc_python.c	Sun Jan 04 17:25:22 2009 +0100
+++ b/targets/plc_python.c	Sun Jan 04 17:29:12 2009 +0100
@@ -29,6 +29,7 @@
 #define PYTHON_LOCKED_BY_PYTHON 0
 #define PYTHON_LOCKED_BY_PLC 1
 #define PYTHON_MUSTWAKEUP 2
+#define PYTHON_FINISHED 4
  
 /* Each python_eval FunctionBlock have it own state */
 #define PYTHON_FB_FREE 0
@@ -55,6 +56,8 @@
 
 void __cleanup_python()
 {
+	PythonState = PYTHON_FINISHED;
+	UnBlockPythonCommands();
 }
 
 void __retrieve_python()
@@ -173,7 +176,10 @@
 	{
 		UnLockPython();
 		/* wait next FB to eval */
+		//printf("PythonIterator wait\n");
 		WaitPythonCommands();
+		/*emergency exit*/
+		if(PythonState & PYTHON_FINISHED) return NULL;
 		LockPython();
 	}
 	/* Mark block as processing */