From ac9c7274f63475cf1b54d3ba698baf3a6ce930d4 Mon Sep 17 00:00:00 2001 From: Darren Ranalli Date: Sat, 10 Mar 2007 13:05:09 +0000 Subject: [PATCH] handle job never returning Job.Done, allow yield None instead of yield Job.Continue, added suspend/resume --- direct/src/showbase/Job.py | 33 ++++++++++++++++++++----------- direct/src/showbase/JobManager.py | 19 ++++++++++++++---- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/direct/src/showbase/Job.py b/direct/src/showbase/Job.py index e3c152ca86..e2af54b5d1 100755 --- a/direct/src/showbase/Job.py +++ b/direct/src/showbase/Job.py @@ -1,8 +1,11 @@ class Job: # Base class for cpu-intensive or non-time-critical operations that # are run through the JobManager. + + # values to yield from your run() generator method Done = object() - Continue = object() + Continue = None # 'yield None' is acceptable in place of 'yield Job.Continue' + Priorities = ScratchPad(Low=-100, Normal=0, High=100) _SerialGen = SerialNumGen() @@ -15,16 +18,26 @@ class Job: del self._name 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): # override if you want a different priority # you can use numbers other than those in Job.Priorities 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): return self._id @@ -48,7 +61,7 @@ if __debug__: # __dev__ not yet available at this point while self._accum < 100: self._accum += 1 print 'counter = %s, accum = %s' % (self._counter, self._accum) - yield Job.Continue + yield None self._accum = 0 self._counter += 1 @@ -57,9 +70,7 @@ if __debug__: # __dev__ not yet available at this point print 'Job.Done' yield Job.Done else: - yield Job.Continue + yield None def addTestJob(): - t = TestJob() - jobMgr.add(t) - + jobMgr.add(TestJob()) diff --git a/direct/src/showbase/JobManager.py b/direct/src/showbase/JobManager.py index 2228519329..75e18d0d01 100755 --- a/direct/src/showbase/JobManager.py +++ b/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 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") - # 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' # run for one millisecond per frame by default DefTimeslice = .001 @@ -85,14 +85,24 @@ class JobManager: endT = globalClock.getRealTime() + (self._timeslice * .9) while True: # always process the highest priority first + # TODO: give occasional timeslices to lower priorities to avoid starving + # lower-priority jobs jobId2job = self._pri2jobId2job[self._highestPriority] # process jobs with equal priority in the order they came in jobId = self._pri2jobIds[self._highestPriority][-1] job = jobId2job[jobId] gen = job._getGenerator() + job.resume() 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: + job.suspend() self.remove(job) # highest-priority job is done. # grab the next one if there's time left @@ -100,9 +110,10 @@ class JobManager: else: # we've run out of time assert self.notify.debug('out of time: %s, %s' % (endT, globalClock.getRealTime())) + job.suspend() break if len(self._pri2jobId2job) == 0: - # there's nothing left to do + # there's nothing left to do, all the jobs are done! break return task.cont