11 Commits 8b46cd779e ... ec554b1be3

Author SHA1 Message Date
  Michael Buesch ec554b1be3 coreserver: Remove data blocks 5 years ago
  Michael Buesch ab52111edf coreserver: Remove dangling refs 5 years ago
  Michael Buesch 730f93794d coreserver: Link FUP source to generated AWL source 5 years ago
  Michael Buesch e546990fc0 coreserver: Destroy refs to related source managers 5 years ago
  Michael Buesch 157a44b16d common/refmanager: Add method to get ref by obj 5 years ago
  Michael Buesch 48141f3b46 common/sources: Add method to get compiled code blocks 5 years ago
  Michael Buesch 877395c490 common/sources: Add method to remove source from container 5 years ago
  Michael Buesch 593af9f810 common/refmanager: Return frozenset of refs 5 years ago
  Michael Buesch c59690aaa8 common/refmanager: Catch exceptions in destroy method 5 years ago
  Michael Buesch dc60543b08 common/refmanager: Add __slots__ 5 years ago
  Michael Buesch a6dbb6539f common/refmanager: Add default name 5 years ago
3 changed files with 106 additions and 26 deletions
  1. 44 14
      awlsim/common/refmanager.py
  2. 41 7
      awlsim/common/sources.py
  3. 21 5
      awlsim/coreserver/server.py

+ 44 - 14
awlsim/common/refmanager.py

@@ -2,7 +2,7 @@
 #
 # AWL simulator - object reference manager
 #
-# Copyright 2015 Michael Buesch <m@bues.ch>
+# Copyright 2015-2018 Michael Buesch <m@bues.ch>
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -33,10 +33,18 @@ __all__ = [
 
 
 class ObjRef(object):
-	"""An object reference."""
+	"""Object reference.
+	This represents a reference from 'obj' to the ObjRefManager 'manager'.
+	"""
+
+	__slots__ = (
+		"__name",
+		"__obj",
+		"__manager",
+	)
 
 	@classmethod
-	def make(cls, name,
+	def make(cls, name=None,
 		 manager=None, ref=None, inheritRef=False,
 		 obj=None):
 		"""Make a new ObjRef instance.
@@ -47,19 +55,24 @@ class ObjRef(object):
 			      If True and ref is not None, the ref is inherited.
 		obj -> The object that is associated with this ref.
 		"""
+		if name is None:
+			# Default name
+			name = lambda _self: ("ObjRef(manager=(%s), obj=(%s))" %
+					      (str(_self.manager), str(_self.obj)))
+
 		if manager is not None and ref is not None:
 			raise RuntimeError
-		if manager is None and ref is None:
-			return None
 
 		if manager is not None:
 			return cls(name, manager, obj)
-		if ref is not None:
+		elif ref is not None:
 			oldRef = ref
 			newRef = cls(name, oldRef.__manager, obj)
 			if inheritRef and oldRef.alive:
 				oldRef.destroy()
 			return newRef
+		else:
+			return None
 
 	def __init__(self, name, manager, obj = None):
 		"""Contruct object reference.
@@ -78,10 +91,12 @@ class ObjRef(object):
 		This removes the reference from the manager.
 		"""
 		if self.alive:
-			self.__manager.refDestroyed(self)
-			self.__name = None
-			self.__obj = None
-			self.__manager = None
+			try:
+				self.__manager.refDestroyed(self)
+			finally:
+				self.__name = None
+				self.__obj = None
+				self.__manager = None
 
 	@property
 	def name(self):
@@ -118,9 +133,16 @@ class ObjRefManager(object):
 	The manager belongs to the object that actually is referenced.
 	"""
 
+	__slots__ = (
+		"__name",
+		"__oneDestroyedCallback",
+		"__allDestroyedCallback",
+		"__refs",
+	)
+
 	def __init__(self, name,
-		     oneDestroyedCallback = None,
-		     allDestroyedCallback = None):
+		     oneDestroyedCallback=None,
+		     allDestroyedCallback=None):
 		"""Contruct reference manager.
 		name: Informational name string or callable returing a string.
 		oneDestroyedCallback: Optional callback. Called, if one ref was destroyed.
@@ -147,9 +169,17 @@ class ObjRefManager(object):
 
 	@property
 	def refs(self):
-		"""Get a set of all references to this object.
+		"""Get a set of all references (ObjRef()s) to this object.
+		"""
+		return frozenset(self.__refs)
+
+	def getRefForObj(self, obj):
+		"""Get the ObjRef() that is managed by this manager for 'obj'.
 		"""
-		return set(self.__refs)
+		for ref in self.__refs:
+			if ref.obj is obj:
+				return ref
+		return None
 
 	def _addRef(self, objRef):
 		self.__refs.add(objRef)

+ 41 - 7
awlsim/common/sources.py

@@ -2,7 +2,7 @@
 #
 # AWL simulator - source management
 #
-# Copyright 2014-2017 Michael Buesch <m@bues.ch>
+# Copyright 2014-2018 Michael Buesch <m@bues.ch>
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -421,6 +421,11 @@ class SymTabSource(GenericSource):
 class SourceManager(ObjRefManager):
 	"""Manages one source."""
 
+	__slots__ = (
+		"source",
+		"container",
+	)
+
 	def __init__(self, source, container=None):
 		"""source -> An AwlSource or SymTabSource instance.
 		container -> A SourceContainer instance or None.
@@ -437,23 +442,52 @@ class SourceManager(ObjRefManager):
 		if container:
 			container.addManager(self)
 
+	def removeFromContainer(self):
+		"""Remove this source from the SourceContainer,
+		if it is inserted into one.
+		"""
+		if self.container:
+			self.container.removeManager(self)
+		self.container = None
+
 	def allRefsDestroyed(self):
 		"""Called, if all source references are destroyed.
 		"""
 		super(SourceManager, self).allRefsDestroyed()
-		if self.container:
-			self.container.removeManager(self)
-		self.source = self.container = None
+		self.removeFromContainer()
+		self.source = None
 
-	def getBlocks(self):
-		"""Get the compiled blocks that were created from the
+	def getCodeBlocks(self):
+		"""Get all compiled CodeBlock()s that were created from the
 		source managed here.
 		"""
-		return { ref.obj for ref in self.refs }
+		from awlsim.core.blocks import CodeBlock #@nocy
+		#from awlsim.core.blocks cimport CodeBlock #@cy
+		return { ref.obj for ref in self.refs
+			 if isinstance(ref.obj, CodeBlock) }
+
+	def getDataBlocks(self):
+		"""Get all compiled DB()s that were created from the
+		source managed here.
+		"""
+		from awlsim.core.datablocks import DB #@nocy
+		#from awlsim.core.datablocks cimport DB #@cy
+		return { ref.obj for ref in self.refs
+			 if isinstance(ref.obj, DB) }
+
+	def getRelatedSourceManagers(self):
+		"""Get all related source managers (e.g. sources created from sources).
+		"""
+		return { ref.obj for ref in self.refs
+			 if isinstance(ref.obj, self.__class__) }
 
 class SourceContainer(object):
 	"""Container for source managers."""
 
+	__slots__ = (
+		"__sourceManagers",
+	)
+
 	def __init__(self):
 		self.__sourceManagers = []
 

+ 21 - 5
awlsim/coreserver/server.py

@@ -2,7 +2,7 @@
 #
 # AWL simulator - PLC core server
 #
-# Copyright 2013-2017 Michael Buesch <m@bues.ch>
+# Copyright 2013-2018 Michael Buesch <m@bues.ch>
 #
 # This program is free software; you can redistribute it and/or modify
 # it under the terms of the GNU General Public License as published by
@@ -524,17 +524,29 @@ class AwlSimServer(object): #+cdef
 
 	def __removeSource(self, sourceContainer, sourceManager):
 		# Remove all blocks that were created from this source.
-		for block in sourceManager.getBlocks():
-			if not isinstance(block, CodeBlock):
-				# This is not a compiled block.
-				continue
+		for block in itertools.chain(sourceManager.getCodeBlocks(),
+					     sourceManager.getDataBlocks()):
 			blockInfo = block.getBlockInfo()
 			if not blockInfo:
 				continue
 			self.__sim.removeBlock(blockInfo, sanityChecks=False)
 
+		# Unref all related source managers.
+		for relatedSourceManager in sourceManager.getRelatedSourceManagers():
+			ref = relatedSourceManager.getRefForObj(sourceManager)
+			if ref:
+				ref.destroy()
+			ref = sourceManager.getRefForObj(relatedSourceManager)
+			if ref:
+				ref.destroy()
+
 		#TODO remove symbols from CPU.
 
+		# Destroy all references, that have not been destroyed, yet.
+		for ref in sourceManager.refs:
+			printError("Killing dangling reference: %s" % str(ref))
+			ref.destroy()
+
 		# Remove the source, if it's not gone already.
 		if sourceContainer:
 			sourceContainer.removeManager(sourceManager)
@@ -588,6 +600,10 @@ class AwlSimServer(object): #+cdef
 						     mnemonics=self.__getMnemonics())
 			awlSrcManager = self.loadAwlSource(awlSource)
 
+			# Cross-reference the generated AWL source to the FUP source.
+			ObjRef.make(manager=srcManager, obj=awlSrcManager)
+			ObjRef.make(manager=awlSrcManager, obj=srcManager)
+
 		self.fupSourceContainer.addManager(srcManager)
 		self.__updateProjectFile()
 		return srcManager