diff --git a/direct/src/showbase/ContainerLeakDetector.py b/direct/src/showbase/ContainerLeakDetector.py index 5e48effc93..12f310a412 100755 --- a/direct/src/showbase/ContainerLeakDetector.py +++ b/direct/src/showbase/ContainerLeakDetector.py @@ -820,6 +820,7 @@ class ContainerLeakDetector(Job): ContainerLeakDetector.addPrivateObj(ContainerLeakDetector.PrivateIds) ContainerLeakDetector.addPrivateObj(self.__dict__) + self.setPriority(Job.Priorities.Min) jobMgr.add(self) def destroy(self): @@ -840,9 +841,6 @@ class ContainerLeakDetector(Job): # passes description string as argument return 'containerLeakDetected-%s' % self._serialNum - def getPriority(self): - return Job.Priorities.Min - @classmethod def addPrivateObj(cls, obj): cls.PrivateIds.add(id(obj)) diff --git a/direct/src/showbase/GarbageReport.py b/direct/src/showbase/GarbageReport.py index ffb88cf1fc..efe7eb4816 100755 --- a/direct/src/showbase/GarbageReport.py +++ b/direct/src/showbase/GarbageReport.py @@ -26,7 +26,7 @@ class GarbageReport(Job): NotGarbage = 'NG' def __init__(self, name, log=True, verbose=False, fullReport=False, findCycles=True, - threaded=False, doneCallback=None, autoDestroy=False): + threaded=False, doneCallback=None, autoDestroy=False, priority=None): # if autoDestroy 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 @@ -35,6 +35,8 @@ class GarbageReport(Job): self._args = ScratchPad(name=name, log=log, verbose=verbose, fullReport=fullReport, findCycles=findCycles, doneCallback=doneCallback, autoDestroy=autoDestroy) + if priority is not None: + self.setPriority(priority) jobMgr.add(self) if not threaded: jobMgr.finish(self) @@ -114,8 +116,12 @@ class GarbageReport(Job): yield None self.cycleIds.update(set(cycle)) - s = ['===== GarbageReport: \'%s\' (%s items) =====' % ( - self._args.name, self.numGarbage)] + if self._args.findCycles: + s = ['===== GarbageReport: \'%s\' (%s items, %s cycles) =====' % ( + self._args.name, self.numGarbage, len(self.cycles))] + else: + s = ['===== GarbageReport: \'%s\' (%s items) =====' % ( + self._args.name, self.numGarbage)] if self.numGarbage > 0: # make a list of the ids we will actually be printing if self._args.fullReport: @@ -265,7 +271,6 @@ class GarbageReport(Job): yield byNum, byRef def _getCycles(self, index, cycleSets=None): - # TODO: make this a generator # detect garbage cycles for a particular item of garbage assert self.notify.debugCall() # returns list of lists, sublists are garbage reference cycles diff --git a/direct/src/showbase/GarbageReportScheduler.py b/direct/src/showbase/GarbageReportScheduler.py new file mode 100755 index 0000000000..4705cd3344 --- /dev/null +++ b/direct/src/showbase/GarbageReportScheduler.py @@ -0,0 +1,40 @@ +from direct.showbase.GarbageReport import GarbageReport + +class GarbageReportScheduler: + # runs a garbage report every once in a while and logs the results + def __init__(self, waitBetween=None, waitScale=None): + # waitBetween is in seconds + # waitScale is a multiplier for the waitBetween every time around + if waitBetween is None: + waitBetween = 30*60 + if waitScale is None: + waitScale = 1.5 + self._waitBetween = waitBetween + self._waitScale = waitScale + self._taskName = 'startScheduledGarbageReport-%s' % serialNum() + self._garbageReport = None + self._scheduleNextGarbageReport() + + def getTaskName(self): + return self._taskName + + def _scheduleNextGarbageReport(self, garbageReport=None): + if garbageReport: + # this report finished, wait a bit then start another + assert garbageReport is self._garbageReport + # garbagereport will clean itself up + self._garbageReport = None + # run another garbagereport after a delay + taskMgr.doMethodLater(self._waitBetween, + self._runGarbageReport, + self._taskName) + # and increase the delay every time around + self._waitBetween = self._waitBetween * self._waitScale + def _runGarbageReport(self, task): + # run a garbage report and schedule the next one after this one finishes + # give this job 3 times as many timeslices as normal-priority jobs + self._garbageReport = GarbageReport('ScheduledGarbageReport', threaded=True, + doneCallback=self._scheduleNextGarbageReport, + autoDestroy=True, + priority=GarbageReport.Priorities.Normal * 3) + return task.done diff --git a/direct/src/showbase/Job.py b/direct/src/showbase/Job.py index dfb5013986..342dffb3f9 100755 --- a/direct/src/showbase/Job.py +++ b/direct/src/showbase/Job.py @@ -23,6 +23,7 @@ class Job(DirectObject): self._generator = None self._id = Job._SerialGen.next() self._printing = False + self._priority = Job.Priorities.Normal if __debug__: self._pstats = PStatCollector("App:Show code:jobManager:%s" % self._name) @@ -45,8 +46,9 @@ class Job(DirectObject): raise "don't call down" def getPriority(self): - # override if you want a different priority - return Job.Priorities.Normal + return self._priority + def setPriority(self, priority): + self._priority = priority def printingBegin(self): self._printing = True