mirror of
https://github.com/panda3d/panda3d.git
synced 2025-10-01 17:35:34 -04:00
prevent Job generator leak, added JobManager.finish(job), logging GarbageReports auto-destroy
This commit is contained in:
parent
e2ac31938f
commit
78b10c41ba
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user