[Solved] Removing tasks + Garbage Collector + sys.exit()

Hi all,

hope, someone can give me a hint on the this imho weird behaviour:

#1

import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject

class Foo:
	def __init__(self):
		print 'init'
		taskMgr.add(self.innerTask, 'innerTask')
			
		
	def innerTask(self, task):
		return task.cont
		
	
	def stopInnerTask(self):
		print taskMgr.remove('innerTask')		
	
	
	def __del__(self):
		print 'delete'
	
	
	
class Tester(DirectObject):
	def __init__(self):
		DirectObject.__init__(self)
		
		self.x = Foo()
		
		self.accept('escape', self.release)
		
	
	def release(self):
		self.x.stopInnerTask()
		del self.x
		
		import sys
		sys.exit()
	
		

Tester()	
run()

Output

#2

class Foo:
	def __init__(self):
		print 'init'
		#taskMgr.add(self.innerTask, 'innerTask')
			
		
	def innerTask(self, task):
		return task.cont
		
	
	def stopInnerTask(self):
		print taskMgr.remove('innerTask')		
	
	
	def __del__(self):
		print 'delete'
	
	
	
class Tester(DirectObject):
	def __init__(self):
		DirectObject.__init__(self)
		
		self.x = Foo()
		
		self.accept('escape', self.release)
		
	
	def release(self):
		self.x.stopInnerTask()
		del self.x
		
		import sys
		sys.exit()
	
		

Tester()	
run()

Output

#3

import direct.directbase.DirectStart
from direct.showbase.DirectObject import DirectObject

class Foo:
	def __init__(self):
		print 'init'
		taskMgr.add(self.innerTask, 'innerTask')
			
		
	def innerTask(self, task):
		return task.cont
		
	
	def stopInnerTask(self):
		print taskMgr.remove('innerTask')		
	
	
	def __del__(self):
		print 'delete'
	
	
	
class Tester(DirectObject):
	def __init__(self):
		DirectObject.__init__(self)
		
		self.x = Foo()
		
		self.accept('escape', self.release)
		
	
	def release(self):
		self.x.stopInnerTask()
		del self.x
		
		#import sys
		#sys.exit()
	
		

Tester()	
run()

Output

Best regards,
rahdev

I don’t see anything weird going on. What do you see wrong here?

David

Wow, that fast :slight_smile:. Okay, we miss ‘delete’ in the first output (#1).
The weird thing is, if we comment out either ‘taskMgr.add’ in #2 or ‘sys.exit’ in #3, we’ll get it. That is unexpected to us.

The general rule is, sys.exit() exits the application immediately, it doesn’t call any destructors. So, you only see ‘delete’ if it gets called before you call sys.exit().

In the first case, it doesn’t get printed because you call sys.exit() from within the Tester method itself. So the Tester object still exists, and it still holds self.x, which is a reference to your Foo object. So your Foo object still exists at the time you called sys.exit().

In the second case, your Tester object has deleted self.x, and never added the task to the task manager, so the last reference to the Foo object is destroyed before you call sys.exit(). So the delete method gets called before you call sys.exit().

The third case is the same as the second, except that here you have added the task to the task manager and then removed it. The task would have been enough to keep a reference to Foo around, but you removed it, so the reference is done. Then you also delete self.x, so that’s the last reference, and Foo is deleted. Then you don’t call sys.exit(), but even if you did I don’t think it would matter, since Foo has already been deleted.

David

Thanks for your detailed explanation. However, there are questions remaining.

Well, it seems to matter. In fact, the only difference between #1 and #3 is the sys.exit() call. Both add and remove the task to the task manager, but in the former case I get ‘delete’, in the latter I do not.

I thought I got rid of these references by first calling taskMgr.remove and afterwards “del self.x”.

Maybe I am little confused…

Ah, I see. You’re right, I overlooked the call to del self.x in case #1. Still, that just means that something is still holding the reference; I believe it is probably the task manager, which doesn’t dump all references to its tasks the same frame that you call remove() on them. Instead, it might hang onto a task object until the next frame, for bookkeeping purposes.

The lesson, though, is that in general you shouldn’t rely on del being called at any particular time, unless you very tightly control the references to a particular object. This is true of any reference-count based memory system (or a garbage-collection system too, for that matter).

David

Hmm, that backs my sneaking suspicion that it might be a timing problem.

Okay, I’ll keep this in mind. Hmm, have I mentioned that I dislike GC systems :laughing:

Thanks for your support!

EDIT: Justed tested. Starting a sequence

Sequence(Wait(...), Func(sys.exit, 0))

will show ‘delete’… Of course, that contradicts the gc system idea.

It does? I don’t see how. The sequence is invoked by the interval manager task, not by the current task. That means it will happen either this frame or next frame, according to whether the interval manager task has already run this frame or not. In either case, it gives the task manager a chance to dump its reference to the task you just removed before the interval runs.

David

You’re right, this statement doesn’t fit well. Now I am wondering why I put it there 8)

Another question: When calling sys.exit within a sequence, we get an exception printed which is, for sys.exit, intended behaviour. Is there a way to suppress this output?