Procházet zdrojové kódy

handle job never returning Job.Done, allow yield None instead of yield Job.Continue, added suspend/resume

Darren Ranalli před 19 roky
rodič
revize
ac9c7274f6
2 změnil soubory, kde provedl 37 přidání a 15 odebrání
  1. 22 11
      direct/src/showbase/Job.py
  2. 15 4
      direct/src/showbase/JobManager.py

+ 22 - 11
direct/src/showbase/Job.py

@@ -1,8 +1,11 @@
 class Job:
 class Job:
     # Base class for cpu-intensive or non-time-critical operations that
     # Base class for cpu-intensive or non-time-critical operations that
     # are run through the JobManager.
     # are run through the JobManager.
+
+    # values to yield from your run() generator method
     Done = object()
     Done = object()
-    Continue = object()
+    Continue = None # 'yield None' is acceptable in place of 'yield Job.Continue'
+
     Priorities = ScratchPad(Low=-100, Normal=0, High=100)
     Priorities = ScratchPad(Low=-100, Normal=0, High=100)
     _SerialGen = SerialNumGen()
     _SerialGen = SerialNumGen()
     
     
@@ -15,16 +18,26 @@ class Job:
         del self._name
         del self._name
         del self._generator
         del self._generator
 
 
+    def run(self):
+        # override and do your processing
+        # yield Job.Continue when possible/reasonable
+        # try not to run longer than the JobManager's timeslice between yields
+        # when done, yield Job.Done
+        raise "don't call down"
+
     def getPriority(self):
     def getPriority(self):
         # override if you want a different priority
         # override if you want a different priority
         # you can use numbers other than those in Job.Priorities
         # you can use numbers other than those in Job.Priorities
         return Job.Priorities.Normal
         return Job.Priorities.Normal
 
 
-    def run(self):
-        # override and yield Job.Continue when possible/reasonable
-        # try not to run longer than the JobManager's timeslice between yields
-        # when done, yield Job.Done
-        raise "don't call down"
+    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 _getJobId(self):
     def _getJobId(self):
         return self._id
         return self._id
@@ -48,7 +61,7 @@ if __debug__: # __dev__ not yet available at this point
                 while self._accum < 100:
                 while self._accum < 100:
                     self._accum += 1
                     self._accum += 1
                     print 'counter = %s, accum = %s' % (self._counter, self._accum)
                     print 'counter = %s, accum = %s' % (self._counter, self._accum)
-                    yield Job.Continue
+                    yield None
 
 
                 self._accum = 0
                 self._accum = 0
                 self._counter += 1
                 self._counter += 1
@@ -57,9 +70,7 @@ if __debug__: # __dev__ not yet available at this point
                     print 'Job.Done'
                     print 'Job.Done'
                     yield Job.Done
                     yield Job.Done
                 else:
                 else:
-                    yield Job.Continue
+                    yield None
 
 
     def addTestJob():
     def addTestJob():
-        t = TestJob()
-        jobMgr.add(t)
-        
+        jobMgr.add(TestJob())

+ 15 - 4
direct/src/showbase/JobManager.py

@@ -6,11 +6,11 @@ class JobManager:
     """
     """
     Similar to the taskMgr but designed for tasks that are CPU-intensive and/or
     Similar to the taskMgr but designed for tasks that are CPU-intensive and/or
     not time-critical. Jobs run one at a time, in order of priority, in
     not time-critical. Jobs run one at a time, in order of priority, in
-    the timeslice that the JobManager is allowed to run each frame.
+    the timeslice that the JobManager is allotted each frame.
     """
     """
     notify = directNotify.newCategory("JobManager")
     notify = directNotify.newCategory("JobManager")
 
 
-    # there's one main task for the JobManager, all jobs run in this task
+    # there's one task for the JobManager, all jobs run in this task
     TaskName = 'jobManager'
     TaskName = 'jobManager'
     # run for one millisecond per frame by default
     # run for one millisecond per frame by default
     DefTimeslice = .001
     DefTimeslice = .001
@@ -85,14 +85,24 @@ class JobManager:
             endT = globalClock.getRealTime() + (self._timeslice * .9)
             endT = globalClock.getRealTime() + (self._timeslice * .9)
             while True:
             while True:
                 # always process the highest priority first
                 # always process the highest priority first
+                # TODO: give occasional timeslices to lower priorities to avoid starving
+                # lower-priority jobs
                 jobId2job = self._pri2jobId2job[self._highestPriority]
                 jobId2job = self._pri2jobId2job[self._highestPriority]
                 # process jobs with equal priority in the order they came in
                 # process jobs with equal priority in the order they came in
                 jobId = self._pri2jobIds[self._highestPriority][-1]
                 jobId = self._pri2jobIds[self._highestPriority][-1]
                 job = jobId2job[jobId]
                 job = jobId2job[jobId]
                 gen = job._getGenerator()
                 gen = job._getGenerator()
+                job.resume()
                 while globalClock.getRealTime() < endT:
                 while globalClock.getRealTime() < endT:
-                    result = gen.next()
+                    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:
                     if result is Job.Done:
+                        job.suspend()
                         self.remove(job)
                         self.remove(job)
                         # highest-priority job is done.
                         # highest-priority job is done.
                         # grab the next one if there's time left
                         # grab the next one if there's time left
@@ -100,9 +110,10 @@ class JobManager:
                 else:
                 else:
                     # we've run out of time
                     # we've run out of time
                     assert self.notify.debug('out of time: %s, %s' % (endT, globalClock.getRealTime()))
                     assert self.notify.debug('out of time: %s, %s' % (endT, globalClock.getRealTime()))
+                    job.suspend()
                     break
                     break
                 
                 
                 if len(self._pri2jobId2job) == 0:
                 if len(self._pri2jobId2job) == 0:
-                    # there's nothing left to do
+                    # there's nothing left to do, all the jobs are done!
                     break
                     break
         return task.cont
         return task.cont