فهرست منبع

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

Darren Ranalli 19 سال پیش
والد
کامیت
78b10c41ba
3فایلهای تغییر یافته به همراه54 افزوده شده و 17 حذف شده
  1. 16 14
      direct/src/showbase/GarbageReport.py
  2. 10 3
      direct/src/showbase/Job.py
  3. 28 0
      direct/src/showbase/JobManager.py

+ 16 - 14
direct/src/showbase/GarbageReport.py

@@ -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
 

+ 10 - 3
direct/src/showbase/Job.py

@@ -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

+ 28 - 0
direct/src/showbase/JobManager.py

@@ -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