prevent Job generator leak, added JobManager.finish(job), logging GarbageReports auto-destroy

This commit is contained in:
Darren Ranalli 2007-03-14 03:09:23 +00:00
parent e2ac31938f
commit 78b10c41ba
3 changed files with 54 additions and 17 deletions

View File

@ -4,7 +4,6 @@ __all__ = ['FakeObject', '_createGarbage', 'GarbageReport', 'GarbageLogger']
from direct.directnotify.DirectNotifyGlobal import directNotify
from direct.showbase.PythonUtil import gcDebugOn, safeRepr, fastRepr
#from direct.showbase.TaskThreaded import TaskThreaded, TaskThread
from direct.showbase.Job import Job
import gc
@ -26,18 +25,18 @@ class GarbageReport(Job):
NotGarbage = 'NG'
def __init__(self, name, log=True, verbose=False, fullReport=False, findCycles=True,
threaded=False, timeslice=None, doneCallback=None):
threaded=False, doneCallback=None):
# if log is True, GarbageReport will self-destroy after logging
# if false, caller is responsible for calling destroy()
# if threaded is True, processing will be performed over multiple frames
Job.__init__(self, name)
# stick the arguments onto a ScratchPad so we can access them from the thread
# functions and delete them all at once
# stick the arguments onto a ScratchPad so we can delete them all at once
self._args = ScratchPad(name=name, log=log, verbose=verbose, fullReport=fullReport,
findCycles=findCycles, doneCallback=doneCallback)
self._printing = False
jobMgr.add(self)
self.numGarbage = 0
if threaded == False:
jobMgr.finish(self)
def run(self):
# do the garbage collection
@ -183,10 +182,20 @@ class GarbageReport(Job):
yield None
self._printing = False
yield Job.Done
def suspend(self):
if self._printing:
self.notify.info('SUSPEND')
def resume(self):
if self._printing:
self.notify.info('RESUME')
def finished(self):
if self._args.doneCallback:
self._args.doneCallback(self)
yield Job.Done
if self._args.log:
self.destroy()
def destroy(self):
#print 'GarbageReport.destroy'
@ -204,13 +213,6 @@ class GarbageReport(Job):
del self._reportStr
Job.destroy(self)
def suspend(self):
if self._printing:
self.notify.info('SUSPEND')
def resume(self):
if self._printing:
self.notify.info('RESUME')
def getNumItems(self):
return self.numGarbage

View File

@ -6,6 +6,7 @@ class Job:
Done = object()
Continue = None # 'yield None' is acceptable in place of 'yield Job.Continue'
# these priorities are reference points, you can use whatever numbers you want
Priorities = ScratchPad(Low=-100, Normal=0, High=100)
_SerialGen = SerialNumGen()
@ -27,18 +28,21 @@ class Job:
def getPriority(self):
# override if you want a different priority
# you can use numbers other than those in Job.Priorities
return Job.Priorities.Normal
def suspend(self):
# called when JobManager is going to stop running this job for a while
# most jobs don't need to override this
pass
def resume(self):
# called when JobManager is going to start running this job again
# most jobs don't need to override this
pass
def finished(self):
# called when the job finishes and has been removed from the JobManager
pass
def getJobName(self):
return self._name
def _getJobId(self):
return self._id
@ -46,6 +50,9 @@ class Job:
if self._generator is None:
self._generator = self.run()
return self._generator
def _cleanupGenerator(self):
if self._generator is not None:
self._generator = None
if __debug__: # __dev__ not yet available at this point
from direct.showbase.Job import Job

View File

@ -59,6 +59,8 @@ class JobManager:
self._pri2jobIds[pri].remove(jobId)
# remove the job from the main table
del self._pri2jobId2job[pri][jobId]
# clean up the job's generator, if any
job._cleanupGenerator()
if len(self._pri2jobId2job[pri]) == 0:
del self._pri2jobId2job[pri]
if pri == self._highestPriority:
@ -72,6 +74,31 @@ class JobManager:
taskMgr.remove(JobManager.TaskName)
self._highestPriority = 0
def finish(self, job):
# run this job, right now, until it finishes
assert self.notify.debugCall()
jobId = job._getJobId()
# look up the job's priority
pri = self._jobId2pri[jobId]
# grab the job
job = self._pri2jobId2job[pri][jobId]
gen = job._getGenerator()
job.resume()
while True:
try:
result = gen.next()
except StopIteration:
# Job didn't yield Job.Done, it ran off the end and returned
# treat it as if it returned Job.Done
self.notify.warning('job %s never yielded Job.Done' % job)
result = Job.Done
if result is Job.Done:
job.suspend()
self.remove(job)
job.finished()
# job is done.
break
# how long should we run per frame?
def getTimeslice(self):
return self._timeslice
@ -104,6 +131,7 @@ class JobManager:
if result is Job.Done:
job.suspend()
self.remove(job)
job.finished()
# highest-priority job is done.
# grab the next one if there's time left
break