| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952 |
- """Undocumented Module"""
- __all__ = ['Task', 'TaskPriorityList', 'TaskManager']
- # This module may not import pandac.PandaModules, since it is imported
- # by the Toontown Launcher before the complete PandaModules have been
- # downloaded. Instead, it imports only libpandaexpressModules, the
- # subset of PandaModules that we know is available immediately.
- # Methods that require more advanced C++ methods may import the
- # appropriate files within their own scope.
- from pandac.libpandaexpressModules import *
- from direct.directnotify.DirectNotifyGlobal import *
- from direct.showbase.PythonUtil import *
- from direct.showbase.MessengerGlobal import *
- from direct.showbase import ExceptionVarDump
- import time
- import fnmatch
- import string
- import signal
- import random
- try:
- Dtool_PreloadDLL("libp3heapq")
- from libp3heapq import heappush, heappop, heapify
- except:
- Dtool_PreloadDLL("libheapq")
- from libheapq import heappush, heappop, heapify
- import types
- import gc
- if __debug__:
- # For pstats
- from pandac.PandaModules import PStatCollector
- def print_exc_plus():
- """
- Print the usual traceback information, followed by a listing of all the
- local variables in each frame.
- """
- import sys
- import traceback
- tb = sys.exc_info()[2]
- while 1:
- if not tb.tb_next:
- break
- tb = tb.tb_next
- stack = []
- f = tb.tb_frame
- while f:
- stack.append(f)
- f = f.f_back
- stack.reverse()
- traceback.print_exc()
- print "Locals by frame, innermost last"
- for frame in stack:
- print
- print "Frame %s in %s at line %s" % (frame.f_code.co_name,
- frame.f_code.co_filename,
- frame.f_lineno)
- for key, value in frame.f_locals.items():
- print "\t%20s = " % key,
- #We have to be careful not to cause a new error in our error
- #printer! Calling str() on an unknown object could cause an
- #error we don't want.
- try:
- print value
- except:
- print "<ERROR WHILE PRINTING VALUE>"
- class Task:
- # This enum is a copy of the one at the top-level.
- exit = -1
- done = 0
- cont = 1
- again = 2
- count = 0
- def __init__(self, callback, priority = 0):
- try:
- config
- except:
- pass
- else:
- if config.GetBool('record-task-creation-stack', 0):
- self.debugInitTraceback = StackTrace("Task "+str(callback), 1, 10)
- # Unique ID for each task
- self.id = Task.count
- Task.count += 1
- #set to have the task managed
- self.owner = None
- self.__call__ = callback
- self._priority = priority
- self._removed = 0
- self.dt = 0.0
- if TaskManager.taskTimerVerbose:
- self.avgDt = 0.0
- self.maxDt = 0.0
- self.runningTotal = 0.0
- self.pstats = None
- self.pstatCollector = None
- self.extraArgs = []
- # Used for doLaters
- self.wakeTime = 0.0
- # for repeating doLaters
- self.delayTime = 0.0
- self.time = 0.0
- # # Used for putting into the doLaterList
- # # the heapq calls __cmp__ via the rich compare function
- # def __cmp__(self, other):
- # if isinstance(other, Task):
- # if self.wakeTime < other.wakeTime:
- # return -1
- # elif self.wakeTime > other.wakeTime:
- # return 1
- # # If the wakeTimes happen to be the same, just
- # # sort them based on id
- # else:
- # return cmp(id(self), id(other))
- # # This is important for people doing a (task != None) and such.
- # else:
- # return cmp(id(self), id(other))
- # # According to the Python manual (3.3.1), if you define a cmp operator
- # # you should also define a hash operator or your objects will not be
- # # usable in dictionaries. Since no two task objects are unique, we can
- # # just return the unique id.
- # def __hash__(self):
- # return self.id
- def remove(self):
- if not self._removed:
- if(self.owner):
- self.owner._clearTask(self)
- self._removed = 1
- # Remove any refs to real objects
- # In case we hang around the doLaterList for a while
- del self.__call__
- del self.extraArgs
- if TaskManager.taskTimerVerbose and self.pstatCollector:
- self.pstatCollector.subLevelNow(1)
- def isRemoved(self):
- return self._removed
- def getPriority(self):
- return self._priority
- def setPriority(self, pri):
- self._priority = pri
- def setStartTimeFrame(self, startTime, startFrame):
- self.starttime = startTime
- self.startframe = startFrame
- def setCurrentTimeFrame(self, currentTime, currentFrame):
- # Calculate and store this task's time (relative to when it started)
- self.time = currentTime - self.starttime
- self.frame = currentFrame - self.startframe
- def getNamePattern(self, taskName=None):
- # get a version of the task name that doesn't contain any numbers
- digits = '0123456789'
- if taskName is None:
- taskName = self.name
- return ''.join([c for c in taskName if c not in digits])
- def setupPStats(self):
- if __debug__ and TaskManager.taskTimerVerbose and not self.pstats:
- # Get the PStats name for the task. By convention,
- # this is everything until the first hyphen; the part
- # of the task name following the hyphen is generally
- # used to differentiate particular tasks that do the
- # same thing to different objects.
- name = self.name
- hyphen = name.find('-')
- if hyphen >= 0:
- name = name[0:hyphen]
- self.pstats = PStatCollector("App:Show code:" + name)
- if self.wakeTime or self.delayTime:
- self.pstatCollector = PStatCollector("Tasks:doLaters:" + name)
- else:
- self.pstatCollector = PStatCollector("Tasks:" + name)
- self.pstatCollector.addLevelNow(1)
- def finishTask(self, verbose):
- if hasattr(self, "uponDeath"):
- self.uponDeath(self)
- if verbose:
- # We regret to announce...
- messenger.send('TaskManager-removeTask', sentArgs = [self, self.name])
- del self.uponDeath
- def __repr__(self):
- if hasattr(self, 'name'):
- return ('Task id: %s, name %s' % (self.id, self.name))
- else:
- return ('Task id: %s, no name' % (self.id))
- def pause(delayTime):
- def func(self):
- if (self.time < self.delayTime):
- return cont
- else:
- return done
- task = Task(func)
- task.name = 'pause'
- task.delayTime = delayTime
- return task
- Task.pause = staticmethod(pause)
- def sequence(*taskList):
- return make_sequence(taskList)
- Task.sequence = staticmethod(sequence)
- def make_sequence(taskList):
- def func(self):
- frameFinished = 0
- taskDoneStatus = -1
- while not frameFinished:
- task = self.taskList[self.index]
- # If this is a new task, set its start time and frame
- if self.index > self.prevIndex:
- task.setStartTimeFrame(self.time, self.frame)
- self.prevIndex = self.index
- # Calculate this task's time since it started
- task.setCurrentTimeFrame(self.time, self.frame)
- # Execute the current task
- ret = task(task)
- # Check the return value from the task
- if ret == cont:
- # If this current task wants to continue,
- # come back to it next frame
- taskDoneStatus = cont
- frameFinished = 1
- elif ret == done:
- # If this task is done, increment the index so that next frame
- # we will start executing the next task on the list
- self.index = self.index + 1
- taskDoneStatus = cont
- frameFinished = 0
- elif ret == exit:
- # If this task wants to exit, the sequence exits
- taskDoneStatus = exit
- frameFinished = 1
- # If we got to the end of the list, this sequence is done
- if self.index >= len(self.taskList):
- # TaskManager.notify.debug('sequence done: ' + self.name)
- frameFinished = 1
- taskDoneStatus = done
- return taskDoneStatus
- task = Task(func)
- task.name = 'sequence'
- task.taskList = taskList
- task.prevIndex = -1
- task.index = 0
- return task
- def resetSequence(task):
- # Should this automatically be done as part of spawnTaskNamed?
- # Or should one have to create a new task instance every time
- # one wishes to spawn a task (currently sequences and can
- # only be fired off once
- task.index = 0
- task.prevIndex = -1
- def loop(*taskList):
- return make_loop(taskList)
- Task.loop = staticmethod(loop)
- def make_loop(taskList):
- def func(self):
- frameFinished = 0
- taskDoneStatus = -1
- while (not frameFinished):
- task = self.taskList[self.index]
- # If this is a new task, set its start time and frame
- if (self.index > self.prevIndex):
- task.setStartTimeFrame(self.time, self.frame)
- self.prevIndex = self.index
- # Calculate this task's time since it started
- task.setCurrentTimeFrame(self.time, self.frame)
- # Execute the current task
- ret = task(task)
- # Check the return value from the task
- if (ret == cont):
- # If this current task wants to continue,
- # come back to it next frame
- taskDoneStatus = cont
- frameFinished = 1
- elif (ret == done):
- # If this task is done, increment the index so that next frame
- # we will start executing the next task on the list
- # TODO: we should go to the next frame now
- self.index = self.index + 1
- taskDoneStatus = cont
- frameFinished = 0
- elif (ret == exit):
- # If this task wants to exit, the sequence exits
- taskDoneStatus = exit
- frameFinished = 1
- if (self.index >= len(self.taskList)):
- # If we got to the end of the list, wrap back around
- self.prevIndex = -1
- self.index = 0
- frameFinished = 1
- return taskDoneStatus
- task = Task(func)
- task.name = 'loop'
- task.taskList = taskList
- task.prevIndex = -1
- task.index = 0
- return task
- class TaskPriorityList(list):
- def __init__(self, priority):
- self._priority = priority
- self.__emptyIndex = 0
- def getPriority(self):
- return self._priority
- def add(self, task):
- if (self.__emptyIndex >= len(self)):
- self.append(task)
- self.__emptyIndex += 1
- else:
- self[self.__emptyIndex] = task
- self.__emptyIndex += 1
- def remove(self, i):
- assert i <= len(self)
- if (len(self) == 1) and (i == 1):
- self[i] = None
- self.__emptyIndex = 0
- else:
- # Swap the last element for this one
- lastElement = self[self.__emptyIndex-1]
- self[i] = lastElement
- self[self.__emptyIndex-1] = None
- self.__emptyIndex -= 1
- class GCTrigger:
- # used to trigger garbage collection
- pass
- class TaskManager:
- # These class vars are generally overwritten by Config variables which
- # are read in at the start of a show (ShowBase.py or AIStart.py)
- notify = None
- # TODO: there is a bit of a bug when you default this to 0. The first
- # task we make, the doLaterProcessor, needs to have this set to 1 or
- # else we get an error.
- taskTimerVerbose = 1
- extendedExceptions = 0
- pStatsTasks = 0
- doLaterCleanupCounter = 2000
- OsdPrefix = 'task.'
- GarbageCollectTaskName = "allowGarbageCollect"
- # multiple of average frame duration
- DefTaskDurationWarningThreshold = 40.
- _DidTests = False
- def __init__(self):
- self.running = 0
- self.stepping = 0
- self.taskList = []
- # Dictionary of priority to newTaskLists
- self.pendingTaskDict = {}
- # List of tasks scheduled to execute in the future
- self.__doLaterList = []
- self._profileFrames = False
- self.MaxEpockSpeed = 1.0/30.0;
- # this will be set when it's safe to import StateVar
- self._profileTasks = None
- self._taskProfiler = None
- self._profileInfo = ScratchPad(
- taskId = None,
- dt = None,
- lastProfileResultString = None,
- )
- # We copy this value in from __builtins__ when it gets set.
- # But since the TaskManager might have to run before it gets
- # set--before it can even be available--we also have to have
- # special-case code that handles the possibility that we don't
- # have a globalClock yet.
- self.globalClock = None
- # To help cope with the possibly-missing globalClock, we get a
- # handle to Panda's low-level TrueClock object for measuring
- # small intervals.
- self.trueClock = TrueClock.getGlobalPtr()
- # We don't have a base yet, but we can query the config
- # variables directly.
- self.warnTaskDuration = ConfigVariableBool('want-task-duration-warnings', 1).getValue()
- self.taskDurationWarningThreshold = ConfigVariableDouble(
- 'task-duration-warning-threshold',
- TaskManager.DefTaskDurationWarningThreshold).getValue()
- self.currentTime, self.currentFrame = self.__getTimeFrame()
- if (TaskManager.notify == None):
- TaskManager.notify = directNotify.newCategory("TaskManager")
- self.fKeyboardInterrupt = 0
- self.interruptCount = 0
- self.resumeFunc = None
- self.fVerbose = 0
- # Dictionary of task name to list of tasks with that name
- self.nameDict = {}
- # A default task.
- self._doLaterTask = self.add(self.__doLaterProcessor, "doLaterProcessor", -10)
- # start this when config is available
- self._gcTask = None
- self._wantGcTask = None
- def destroy(self):
- if self._gcTask:
- self._gcTask.remove()
- if self._doLaterTask:
- self._doLaterTask.remove()
- if self._taskProfiler:
- self._taskProfiler.destroy()
- del self.nameDict
- del self.trueClock
- del self.globalClock
- del self.__doLaterList
- del self.pendingTaskDict
- del self.taskList
- def setStepping(self, value):
- self.stepping = value
- def setVerbose(self, value):
- self.fVerbose = value
- messenger.send('TaskManager-setVerbose', sentArgs = [value])
- def getTaskDurationWarningThreshold(self):
- return self.taskDurationWarningThreshold
- def setTaskDurationWarningThreshold(self, threshold):
- self.taskDurationWarningThreshold = threshold
- def invokeDefaultHandler(self, signalNumber, stackFrame):
- print '*** allowing mid-frame keyboard interrupt.'
- # Restore default interrupt handler
- signal.signal(signal.SIGINT, signal.default_int_handler)
- # and invoke it
- raise KeyboardInterrupt
- def keyboardInterruptHandler(self, signalNumber, stackFrame):
- self.fKeyboardInterrupt = 1
- self.interruptCount += 1
- if self.interruptCount == 1:
- print '* interrupt by keyboard'
- elif self.interruptCount == 2:
- print '** waiting for end of frame before interrupting...'
- # The user must really want to interrupt this process
- # Next time around invoke the default handler
- signal.signal(signal.SIGINT, self.invokeDefaultHandler)
- def hasTaskNamed(self, taskName):
- # TODO: check pending task list
- # Get the tasks with this name
- # If we found some, see if any of them are still active (not removed)
- for task in self.nameDict.get(taskName, []):
- if not task._removed:
- return 1
- # Didnt find any, return 0
- return 0
- def getTasksNamed(self, taskName):
- # TODO: check pending tasks
- # Get the tasks with this name
- return [task for task in self.nameDict.get(taskName, []) #grab all tasks with name
- if not task._removed] #filter removed tasks
- def __doLaterFilter(self):
- # Filter out all the tasks that have been removed like a mark and
- # sweep garbage collector. Returns the number of tasks that have
- # been removed Warning: this creates an entirely new doLaterList.
- oldLen = len(self.__doLaterList)
- # grab all the tasks being removed so we can remove them from the nameDict
- # TODO: would be more efficient to remove from nameDict in task.remove()
- removedTasks = [task for task in self.__doLaterList
- if task._removed]
- self.__doLaterList = [task for task in self.__doLaterList #grab all tasks with name
- if not task._removed] #filter removed tasks
- for task in removedTasks:
- self.__removeTaskFromNameDict(task)
- # Re heapify to maintain ordering after filter
- heapify(self.__doLaterList)
- newLen = len(self.__doLaterList)
- return oldLen - newLen
-
- def __getNextDoLaterTime(self):
- if self.__doLaterList:
- dl = self.__doLaterList[0]
- return dl.wakeTime
- return -1;
-
- def __doLaterProcessor(self, task):
- # Removing the tasks during the for loop is a bad idea
- # Instead we just flag them as removed
- # Later, somebody else cleans them out
- currentTime = self.__getTime()
- while self.__doLaterList:
- # Check the first one on the list to see if it is ready
- dl = self.__doLaterList[0]
- if dl._removed:
- # Get rid of this task forever
- heappop(self.__doLaterList)
- continue
- # If the time now is less than the start of the doLater + delay
- # then we are not ready yet, continue to next one
- elif currentTime < dl.wakeTime:
- # Since the list is sorted, the first one we get to, that
- # is not ready to go, we can return
- break
- else:
- # Take it off the doLaterList, set its time, and make
- # it pending
- heappop(self.__doLaterList)
- dl.setStartTimeFrame(self.currentTime, self.currentFrame)
- self.__addPendingTask(dl)
- continue
-
- # Every nth pass, let's clean out the list of removed tasks
- # This is basically a mark and sweep garbage collection of doLaters
- if ((task.frame % self.doLaterCleanupCounter) == 0):
- numRemoved = self.__doLaterFilter()
- # TaskManager.notify.debug("filtered %s removed doLaters" % numRemoved)
- return cont
- def _garbageCollect(self, task=None):
- # enable automatic garbage collection
- gc.enable()
- # creating an object with gc enabled causes garbage collection to trigger if appropriate
- gct = GCTrigger()
- # disable the automatic garbage collect during the rest of the frame
- gc.disable()
- return cont
- def doMethodLater(self, delayTime, funcOrTask, name, extraArgs=None,
- priority=0, uponDeath=None, appendTask=False, owner = None):
- if delayTime < 0:
- assert self.notify.warning('doMethodLater: added task: %s with negative delay: %s' % (name, delayTime))
- if isinstance(funcOrTask, Task):
- task = funcOrTask
- elif callable(funcOrTask):
- task = Task(funcOrTask, priority)
- else:
- self.notify.error('doMethodLater: Tried to add a task that was not a Task or a func')
- assert isinstance(name, str), 'Name must be a string type'
- task.setPriority(priority)
- task.name = name
- task.owner = owner
- if extraArgs == None:
- extraArgs = []
- appendTask = True
- # if told to, append the task object to the extra args list so the
- # method called will be able to access any properties on the task
- if appendTask:
- extraArgs.append(task)
-
- task.extraArgs = extraArgs
- if uponDeath:
- task.uponDeath = uponDeath
- # TaskManager.notify.debug('spawning doLater: %s' % (task))
- # Add this task to the nameDict
- nameList = self.nameDict.get(name)
- if nameList:
- nameList.append(task)
- else:
- self.nameDict[name] = [task]
- currentTime = self.__getTime()
- # Cache the time we should wake up for easier sorting
- task.delayTime = delayTime
- task.wakeTime = currentTime + delayTime
- # Push this onto the doLaterList. The heap maintains the sorting.
- heappush(self.__doLaterList, task)
- if self.fVerbose:
- # Alert the world, a new task is born!
- messenger.send('TaskManager-spawnDoLater',
- sentArgs = [task, task.name, task.id])
- return task
- def add(self, funcOrTask, name, priority=0, extraArgs=None, uponDeath=None,
- appendTask = False, owner = None):
-
- """
- Add a new task to the taskMgr.
- You can add a Task object or a method that takes one argument.
- """
- # TaskManager.notify.debug('add: %s' % (name))
- if isinstance(funcOrTask, Task):
- task = funcOrTask
- elif callable(funcOrTask):
- task = Task(funcOrTask, priority)
- else:
- self.notify.error(
- 'add: Tried to add a task that was not a Task or a func')
- assert isinstance(name, str), 'Name must be a string type'
- task.setPriority(priority)
- task.name = name
- task.owner = owner
- if extraArgs == None:
- extraArgs = []
- appendTask = True
- # if told to, append the task object to the extra args list so the
- # method called will be able to access any properties on the task
- if appendTask:
- extraArgs.append(task)
- task.extraArgs = extraArgs
- if uponDeath:
- task.uponDeath = uponDeath
- currentTime = self.__getTime()
- task.setStartTimeFrame(currentTime, self.currentFrame)
- nameList = self.nameDict.get(name)
- if nameList:
- nameList.append(task)
- else:
- self.nameDict[name] = [task]
- # Put it on the list for the end of this frame
- self.__addPendingTask(task)
- return task
- def __addPendingTask(self, task):
- # TaskManager.notify.debug('__addPendingTask: %s' % (task.name))
- pri = task._priority
- taskPriList = self.pendingTaskDict.get(pri)
- if not taskPriList:
- taskPriList = TaskPriorityList(pri)
- self.pendingTaskDict[pri] = taskPriList
- taskPriList.add(task)
- def __addNewTask(self, task):
- # The taskList is really an ordered list of TaskPriorityLists
- # search back from the end of the list until we find a
- # taskList with a lower priority, or we hit the start of the list
- taskPriority = task._priority
- index = len(self.taskList) - 1
- while (1):
- if (index < 0):
- newList = TaskPriorityList(taskPriority)
- newList.add(task)
- # Add the new list to the beginning of the taskList
- self.taskList.insert(0, newList)
- break
- taskListPriority = self.taskList[index]._priority
- if (taskListPriority == taskPriority):
- self.taskList[index].add(task)
- break
- elif (taskListPriority > taskPriority):
- index = index - 1
- elif (taskListPriority < taskPriority):
- # Time to insert
- newList = TaskPriorityList(taskPriority)
- newList.add(task)
- # Insert this new priority level
- # If we are already at the end, just append it
- if (index == len(self.taskList)-1):
- self.taskList.append(newList)
- else:
- # Otherwise insert it
- self.taskList.insert(index+1, newList)
- break
- if __debug__:
- if self.pStatsTasks and task.name != "igLoop":
- task.setupPStats()
- if self.fVerbose:
- # Alert the world, a new task is born!
- messenger.send(
- 'TaskManager-spawnTask', sentArgs = [task, task.name, index])
- return task
- def remove(self, taskOrName):
- if type(taskOrName) == type(''):
- return self.__removeTasksNamed(taskOrName)
- elif isinstance(taskOrName, Task):
- return self.__removeTasksEqual(taskOrName)
- else:
- self.notify.error('remove takes a string or a Task')
- def removeTasksMatching(self, taskPattern):
- """removeTasksMatching(self, string taskPattern)
- Removes tasks whose names match the pattern, which can include
- standard shell globbing characters like *, ?, and [].
- """
- # TaskManager.notify.debug('removing tasks matching: ' + taskPattern)
- num = 0
- keyList = filter(
- lambda key: fnmatch.fnmatchcase(key, taskPattern),
- self.nameDict.keys())
- for key in keyList:
- num += self.__removeTasksNamed(key)
- return num
- def __removeTasksEqual(self, task):
- # Remove this task from the nameDict (should be a short list)
- if self.__removeTaskFromNameDict(task):
- # TaskManager.notify.debug(
- # '__removeTasksEqual: removing task: %s' % (task))
- # Flag the task for removal from the real list
- task.remove()
- task.finishTask(self.fVerbose)
- return 1
- else:
- return 0
- def __removeTasksNamed(self, taskName):
- tasks = self.nameDict.get(taskName)
- if not tasks:
- return 0
- # TaskManager.notify.debug(
- # '__removeTasksNamed: removing tasks named: %s' % (taskName))
- for task in tasks:
- # Flag for removal
- task.remove()
- task.finishTask(self.fVerbose)
- # Record the number of tasks removed
- num = len(tasks)
- # Blow away the nameDict entry completely
- del self.nameDict[taskName]
- return num
- def __removeTaskFromNameDict(self, task):
- taskName = task.name
- # If this is the only task with that name, remove the dict entry
- tasksWithName = self.nameDict.get(taskName)
- if tasksWithName:
- if task in tasksWithName:
- # If this is the last element, just remove the entry
- # from the dictionary
- if len(tasksWithName) == 1:
- del self.nameDict[taskName]
- else:
- tasksWithName.remove(task)
- return 1
- return 0
- def __executeTask(self, task):
- task.setCurrentTimeFrame(self.currentTime, self.currentFrame)
-
- # cache reference to profile info here, self._profileInfo might get swapped out
- # by the task when it runs
- profileInfo = self._profileInfo
- doProfile = (task.id == profileInfo.taskId)
-
- if not self.taskTimerVerbose:
- startTime = self.trueClock.getShortTime()
-
- # don't record timing info
- if doProfile:
- ret = profile(Functor(task, *task.extraArgs),
- 'TASK_PROFILE:%s' % task.name, True, log=False)
- else:
- ret = task(*task.extraArgs)
- endTime = self.trueClock.getShortTime()
-
- # Record the dt
- dt = endTime - startTime
- if doProfile:
- # if we profiled, record the measured duration but don't pollute the task's
- # normal duration
- profileInfo.dt = dt
- dt = task.avgDt
- task.dt = dt
- else:
- # Run the task and check the return value
- if task.pstats:
- task.pstats.start()
- startTime = self.trueClock.getShortTime()
- if doProfile:
- ret = profile(Functor(task, *task.extraArgs),
- 'profiled-task-%s' % task.name, True, log=False)
- else:
- ret = task(*task.extraArgs)
- endTime = self.trueClock.getShortTime()
- if task.pstats:
- task.pstats.stop()
- # Record the dt
- dt = endTime - startTime
- if doProfile:
- # if we profiled, record the measured duration but don't pollute the task's
- # normal duration
- profileInfo.dt = dt
- dt = task.avgDt
- task.dt = dt
- # See if this is the new max
- if dt > task.maxDt:
- task.maxDt = dt
- # Record the running total of all dts so we can compute an average
- task.runningTotal = task.runningTotal + dt
- if (task.frame > 0):
- task.avgDt = (task.runningTotal / task.frame)
- else:
- task.avgDt = 0
- if doProfile:
- profileInfo.lastProfileResultString = self._getProfileResultString()
- # warn if the task took too long
- if self.warnTaskDuration and self.globalClock:
- avgFrameRate = self.globalClock.getAverageFrameRate()
- if avgFrameRate > .00001:
- avgFrameDur = (1. / avgFrameRate)
- if dt >= (self.taskDurationWarningThreshold * avgFrameDur):
- assert TaskManager.notify.warning('frame %s: task %s ran for %.2f seconds, avg frame duration=%.2f seconds' % (
- globalClock.getFrameCount(), task.name, dt, avgFrameDur))
-
- return ret
- def __repeatDoMethod(self, task):
- """
- Called when a task execute function returns Task.again because
- it wants the task to execute again after the same or a modified
- delay (set 'delayTime' on the task object to change the delay)
- """
- if (not task._removed):
- # be sure to ask the globalClock for the current frame time
- # rather than use a cached value; globalClock's frame time may
- # have been synced since the start of this frame
- currentTime = self.__getTime()
- # Cache the time we should wake up for easier sorting
- task.wakeTime = currentTime + task.delayTime
- # Push this onto the doLaterList. The heap maintains the sorting.
- heappush(self.__doLaterList, task)
- if self.fVerbose:
- # Alert the world, a new task is born!
- messenger.send('TaskManager-againDoLater',
- sentArgs = [task, task.name, task.id])
- def __stepThroughList(self, taskPriList):
- # Traverse the taskPriList with an iterator
- i = 0
- while (i < len(taskPriList)):
- task = taskPriList[i]
- # See if we are at the end of the real tasks
- if task is None:
- break
- # See if this task has been removed in show code
- if task._removed:
- # assert TaskManager.notify.debug(
- # '__stepThroughList: task is flagged for removal %s' % (task))
- # If it was removed in show code, it will need finishTask run
- # If it was removed by the taskMgr, it will not, but that is ok
- # because finishTask is safe to call twice
- task.finishTask(self.fVerbose)
- taskPriList.remove(i)
- self.__removeTaskFromNameDict(task)
- # Do not increment the iterator
- continue
- # Now actually execute the task
- ret = self.__executeTask(task)
- # See if the task is done
- if (ret == cont):
- # Leave it for next frame, its not done yet
- pass
- elif (ret == again):
- # repeat doLater again after a delay
- self.__repeatDoMethod(task)
- taskPriList.remove(i)
- continue
- elif ((ret == done) or (ret == exit) or (ret == None)):
- # assert TaskManager.notify.debug(
- # '__stepThroughList: task is finished %s' % (task))
- # Remove the task
- if not task._removed:
- # assert TaskManager.notify.debug(
- # '__stepThroughList: task not removed %s' % (task))
- task.remove()
- # Note: Should not need to remove from doLaterList here
- # because this task is not in the doLaterList
- task.finishTask(self.fVerbose)
- self.__removeTaskFromNameDict(task)
- else:
- # assert TaskManager.notify.debug(
- # '__stepThroughList: task already removed %s' % (task))
- self.__removeTaskFromNameDict(task)
- taskPriList.remove(i)
- # Do not increment the iterator
- continue
- else:
- raise StandardError, \
- "Task named %s did not return cont, exit, done, or None" % \
- (task.name,)
- # Move to the next element
- i += 1
- def __addPendingTasksToTaskList(self):
- # Now that we are all done, add any left over pendingTasks
- # generated in priority levels lower or higher than where
- # we were when we iterated
- for taskList in self.pendingTaskDict.values():
- for task in taskList:
- if (task and not task._removed):
- # assert TaskManager.notify.debug(
- # 'step: moving %s from pending to taskList' % (task.name))
- self.__addNewTask(task)
- self.pendingTaskDict.clear()
- def profileFrames(self, num=None):
- self._profileFrames = True
- if num is None:
- num = 1
- self._profileFrameCount = num
-
- # in the event we want to do frame time managment.. this is the function to
- # replace or overload..
- def doYield(self , frameStartTime, nextScheuledTaksTime):
- None
-
- def doYieldExample(self , frameStartTime, nextScheuledTaksTime):
- minFinTime = frameStartTime + self.MaxEpockSpeed
- if nextScheuledTaksTime > 0 and nextScheuledTaksTime < minFinTime:
- print ' Adjusting Time'
- minFinTime = nextScheuledTaksTime;
- delta = minFinTime - self.globalClock.getRealTime();
- while(delta > 0.002):
- print ' sleep %s'% (delta)
- time.sleep(delta)
- delta = minFinTime - self.globalClock.getRealTime();
-
- @profiled()
- def _doProfiledFrames(self, *args, **kArgs):
- print '** profiling %s frames' % self._profileFrameCount
- for i in xrange(self._profileFrameCount):
- result = self.step(*args, **kArgs)
- return result
- def getProfileTasks(self):
- return self._profileTasks.get()
- def getProfileTasksSV(self):
- return self._profileTasks
- def setProfileTasks(self, profileTasks):
- self._profileTasks.set(profileTasks)
- if (not self._taskProfiler) and profileTasks:
- # import here due to import dependencies
- from direct.task.TaskProfiler import TaskProfiler
- self._taskProfiler = TaskProfiler()
- def _setProfileTask(self, task):
- self._profileInfo = ScratchPad(
- taskId = task.id,
- dt = None,
- lastProfileResultString = None,
- )
- def _getTaskProfileDt(self):
- return self._profileInfo.dt
- def _getLastProfileResultString(self):
- return self._profileInfo.lastProfileResultString
- def _getRandomTask(self):
- numTasks = 0
- for name in self.nameDict.iterkeys():
- numTasks += len(self.nameDict[name])
- numDoLaters = len(self.__doLaterList)
- if random.random() < (numDoLaters / float(numTasks + numDoLaters)):
- # grab a doLater that will most likely trigger in the next frame
- tNow = globalClock.getFrameTime()
- avgFrameRate = globalClock.getAverageFrameRate()
- if avgFrameRate < .00001:
- avgFrameDur = 0.
- else:
- avgFrameDur = (1. / globalClock.getAverageFrameRate())
- tNext = tNow + avgFrameDur
- # binary search to find doLaters that are likely to trigger on the next frame
- curIndex = int(numDoLaters / 2)
- rangeStart = 0
- rangeEnd = numDoLaters
- while True:
- if tNext < self.__doLaterList[curIndex].wakeTime:
- rangeEnd = curIndex
- else:
- rangeStart = curIndex
- prevIndex = curIndex
- curIndex = int((rangeStart + rangeEnd) / 2)
- if curIndex == prevIndex:
- break
- index = curIndex
- task = self.__doLaterList[random.randrange(index+1)]
- else:
- # grab a task
- name = random.choice(self.nameDict.keys())
- task = random.choice(self.nameDict[name])
- return task
- def step(self):
- # assert TaskManager.notify.debug('step: begin')
- self.currentTime, self.currentFrame = self.__getTimeFrame()
- startFrameTime = None
- if self.globalClock:
- startFrameTime = self.globalClock.getRealTime()
-
- # Replace keyboard interrupt handler during task list processing
- # so we catch the keyboard interrupt but don't handle it until
- # after task list processing is complete.
- self.fKeyboardInterrupt = 0
- self.interruptCount = 0
- signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
- # Traverse the task list in order because it is in priority order
- priIndex = 0
- while priIndex < len(self.taskList):
- taskPriList = self.taskList[priIndex]
- pri = taskPriList._priority
- # assert TaskManager.notify.debug(
- # 'step: running through taskList at pri: %s, priIndex: %s' %
- # (pri, priIndex))
- self.__stepThroughList(taskPriList)
- # Now see if that generated any pending tasks for this taskPriList
- pendingTasks = self.pendingTaskDict.get(pri)
- while pendingTasks:
- # assert TaskManager.notify.debug('step: running through pending tasks at pri: %s' % (pri))
- # Remove them from the pendingTaskDict
- del self.pendingTaskDict[pri]
- # Execute them
- self.__stepThroughList(pendingTasks)
- # Add these to the real taskList
- for task in pendingTasks:
- if (task and not task._removed):
- # assert TaskManager.notify.debug('step: moving %s from pending to taskList' % (task.name))
- self.__addNewTask(task)
- # See if we generated any more for this pri level
- pendingTasks = self.pendingTaskDict.get(pri)
- # Any new tasks that were made pending should be converted
- # to real tasks now in case they need to run this frame at a
- # later priority level
- self.__addPendingTasksToTaskList()
- # Go to the next priority level
- priIndex += 1
- # Add new pending tasks
- self.__addPendingTasksToTaskList()
-
- if startFrameTime:
- #this is the spot for a Internal Yield Function
- nextTaskTime = self.__getNextDoLaterTime()
- self.doYield(startFrameTime,nextTaskTime)
-
- # Restore default interrupt handler
- signal.signal(signal.SIGINT, signal.default_int_handler)
- if self.fKeyboardInterrupt:
- raise KeyboardInterrupt
- def run(self):
- # do things that couldn't be done earlier because of import dependencies
- if (not TaskManager._DidTests) and __debug__:
- TaskManager._DidTests = True
- self._runTests()
- if not self._gcTask:
- if self._wantGcTask is None:
- self._wantGcTask = config.GetBool('want-garbage-collect-task', 1)
- if self._wantGcTask:
- # manual garbage-collect task
- self._gcTask = self.add(self._garbageCollect, TaskManager.GarbageCollectTaskName, 200)
- if not self._profileTasks:
- from direct.fsm.StatePush import StateVar
- self._profileTasks = StateVar(False)
- self.setProfileTasks(getBase().config.GetBool('profile-task-spikes', 0))
- # Set the clock to have last frame's time in case we were
- # Paused at the prompt for a long time
- if self.globalClock:
- t = self.globalClock.getFrameTime()
- timeDelta = t - globalClock.getRealTime()
- self.globalClock.setRealTime(t)
- messenger.send("resetClock", [timeDelta])
- if self.resumeFunc != None:
- self.resumeFunc()
- if self.stepping:
- self.step()
- else:
- self.running = 1
- while self.running:
- try:
- if self._profileFrames:
- self._profileFrames = False
- self._doProfiledFrames()
- else:
- self.step()
- except KeyboardInterrupt:
- self.stop()
- except IOError, ioError:
- code, message = self._unpackIOError(ioError)
- # Since upgrading to Python 2.4.1, pausing the execution
- # often gives this IOError during the sleep function:
- # IOError: [Errno 4] Interrupted function call
- # So, let's just handle that specific exception and stop.
- # All other IOErrors should still get raised.
- # Only problem: legit IOError 4s will be obfuscated.
- if code == 4:
- self.stop()
- else:
- raise
- except Exception, e:
- if self.extendedExceptions:
- self.stop()
- print_exc_plus()
- else:
- if (ExceptionVarDump.wantVariableDump and
- ExceptionVarDump.dumpOnExceptionInit):
- ExceptionVarDump._varDump__print(e)
- raise
- except:
- if self.extendedExceptions:
- self.stop()
- print_exc_plus()
- else:
- raise
- def _unpackIOError(self, ioError):
- # IOError unpack from http://www.python.org/doc/essays/stdexceptions/
- # this needs to be in its own method, exceptions that occur inside
- # a nested try block are not caught by the inner try block's except
- try:
- (code, message) = ioError
- except:
- code = 0
- message = ioError
- return code, message
- def stop(self):
- # Set a flag so we will stop before beginning next frame
- self.running = 0
- def __tryReplaceTaskMethod(self, task, oldMethod, newFunction):
- if (task is None) or task._removed:
- return 0
- method = task.__call__
- if (type(method) == types.MethodType):
- function = method.im_func
- else:
- function = method
- #print ('function: ' + `function` + '\n' +
- # 'method: ' + `method` + '\n' +
- # 'oldMethod: ' + `oldMethod` + '\n' +
- # 'newFunction: ' + `newFunction` + '\n')
- if (function == oldMethod):
- import new
- newMethod = new.instancemethod(newFunction,
- method.im_self,
- method.im_class)
- task.__call__ = newMethod
- # Found a match
- return 1
- return 0
- def replaceMethod(self, oldMethod, newFunction):
- numFound = 0
- # Look through the regular tasks
- for taskPriList in self.taskList:
- for task in taskPriList:
- if task:
- numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
- # Look through the pending tasks
- for pri, taskList in self.pendingTaskDict.items():
- for task in taskList:
- if task:
- numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
- # Look through the doLaters
- for task in self.__doLaterList:
- if task:
- numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
- return numFound
- def __repr__(self):
- taskNameWidth = 32
- dtWidth = 10
- priorityWidth = 10
- totalDt = 0
- totalAvgDt = 0
- str = "The taskMgr is handling:\n"
- str += ('taskList'.ljust(taskNameWidth)
- + 'dt(ms)'.rjust(dtWidth)
- + 'avg'.rjust(dtWidth)
- + 'max'.rjust(dtWidth)
- + 'priority'.rjust(priorityWidth)
- + '\n')
- str += '-------------------------------------------------------------------------\n'
- dtfmt = '%%%d.2f' % (dtWidth)
- for taskPriList in self.taskList:
- priority = `taskPriList._priority`
- for task in taskPriList:
- if task is None:
- break
- if task._removed:
- taskName = '(R)' + task.name
- else:
- taskName = task.name
- if self.taskTimerVerbose:
- totalDt = totalDt + task.dt
- totalAvgDt = totalAvgDt + task.avgDt
- str += (taskName.ljust(taskNameWidth)
- + dtfmt % (task.dt*1000)
- + dtfmt % (task.avgDt*1000)
- + dtfmt % (task.maxDt*1000)
- + priority.rjust(priorityWidth)
- + '\n')
- else:
- str += (task.name.ljust(taskNameWidth)
- + '----'.rjust(dtWidth)
- + '----'.rjust(dtWidth)
- + '----'.rjust(dtWidth)
- + priority.rjust(priorityWidth)
- + '\n')
- str += '-------------------------------------------------------------------------\n'
- str += 'pendingTasks\n'
- str += '-------------------------------------------------------------------------\n'
- for pri, taskList in self.pendingTaskDict.items():
- for task in taskList:
- if task._removed:
- taskName = '(PR)' + task.name
- else:
- taskName = '(P)' + task.name
- if (self.taskTimerVerbose):
- str += (' ' + taskName.ljust(taskNameWidth-2)
- + dtfmt % (pri)
- + '\n')
- else:
- str += (' ' + taskName.ljust(taskNameWidth-2)
- + '----'.rjust(dtWidth)
- + '\n')
- str += '-------------------------------------------------------------------------\n'
- if (self.taskTimerVerbose):
- str += ('total'.ljust(taskNameWidth)
- + dtfmt % (totalDt*1000)
- + dtfmt % (totalAvgDt*1000)
- + '\n')
- else:
- str += ('total'.ljust(taskNameWidth)
- + '----'.rjust(dtWidth)
- + '----'.rjust(dtWidth)
- + '\n')
- str += '-------------------------------------------------------------------------\n'
- str += ('doLaterList'.ljust(taskNameWidth)
- + 'waitTime(s)'.rjust(dtWidth)
- + '\n')
- str += '-------------------------------------------------------------------------\n'
- # When we print, show the doLaterList in actual sorted order.
- # The priority heap is not actually in order - it is a tree
- # Make a shallow copy so we can sort it
- sortedDoLaterList = self.__doLaterList[:]
- sortedDoLaterList.sort(lambda a, b: cmp(a.wakeTime, b.wakeTime))
- sortedDoLaterList.reverse()
- for task in sortedDoLaterList:
- remainingTime = ((task.wakeTime) - self.currentTime)
- if task._removed:
- taskName = '(R)' + task.name
- else:
- taskName = task.name
- str += (' ' + taskName.ljust(taskNameWidth-2)
- + dtfmt % (remainingTime)
- + '\n')
- str += '-------------------------------------------------------------------------\n'
- str += "End of taskMgr info\n"
- return str
- def getTasks(self):
- # returns list of all tasks in arbitrary order
- tasks = []
- for taskPriList in self.taskList:
- for task in taskPriList:
- if task is not None and not task._removed:
- tasks.append(task)
- for pri, taskList in self.pendingTaskDict.iteritems():
- for task in taskList:
- if not task._removed:
- tasks.append(task)
- return tasks
- def getDoLaters(self):
- # returns list of all doLaters in arbitrary order
- return [doLater for doLater in self.__doLaterList
- if not doLater._removed]
- def resetStats(self):
- # WARNING: this screws up your do-later timings
- if self.taskTimerVerbose:
- for task in self.taskList:
- task.dt = 0
- task.avgDt = 0
- task.maxDt = 0
- task.runningTotal = 0
- task.setStartTimeFrame(self.currentTime, self.currentFrame)
- def popupControls(self):
- from direct.tkpanels import TaskManagerPanel
- return TaskManagerPanel.TaskManagerPanel(self)
- def __getTimeFrame(self):
- # WARNING: If you are testing tasks without an igLoop,
- # you must manually tick the clock
- # Ask for the time last frame
- if self.globalClock:
- return self.globalClock.getFrameTime(), self.globalClock.getFrameCount()
- # OK, we don't have a globalClock yet. This is therefore
- # running before the first frame.
- return self.trueClock.getShortTime(), 0
- def __getTime(self):
- if self.globalClock:
- return self.globalClock.getFrameTime()
- return self.trueClock.getShortTime()
- if __debug__:
- # to catch memory leaks during the tests at the bottom of the file
- def _startTrackingMemLeaks(self):
- self._memUsage = ScratchPad()
- mu = self._memUsage
- mu.lenTaskList = len(self.taskList)
- mu.lenPendingTaskDict = len(self.pendingTaskDict)
- mu.lenDoLaterList = len(self.__doLaterList)
- mu.lenNameDict = len(self.nameDict)
- def _stopTrackingMemLeaks(self):
- self._memUsage.destroy()
- del self._memUsage
- def _checkMemLeaks(self):
- # flush removed doLaters
- self.__doLaterFilter()
- # give the mgr a chance to clear out removed tasks
- self.step()
- mu = self._memUsage
- # the task list looks like it grows and never shrinks, replacing finished
- # tasks with 'None' in the TaskPriorityLists.
- # TODO: look at reducing memory usage here--clear out excess at the end of every frame?
- #assert mu.lenTaskList == len(self.taskList)
- assert mu.lenPendingTaskDict == len(self.pendingTaskDict)
- assert mu.lenDoLaterList == len(self.__doLaterList)
- assert mu.lenNameDict == len(self.nameDict)
- def startOsd(self):
- self.add(self.doOsd, 'taskMgr.doOsd')
- self._osdEnabled = None
- def osdEnabled(self):
- return hasattr(self, '_osdEnabled')
- def stopOsd(self):
- onScreenDebug.removeAllWithPrefix(TaskManager.OsdPrefix)
- self.remove('taskMgr.doOsd')
- del self._osdEnabled
- def doOsd(self, task):
- if not onScreenDebug.enabled:
- return
- prefix = TaskManager.OsdPrefix
- onScreenDebug.removeAllWithPrefix(prefix)
- taskNameWidth = 32
- dtWidth = 10
- priorityWidth = 10
- totalDt = 0
- totalAvgDt = 0
- i = 0
- onScreenDebug.add(
- ('%s%02i.taskList' % (prefix, i)).ljust(taskNameWidth),
- '%s %s %s %s' % (
- 'dt(ms)'.rjust(dtWidth),
- 'avg'.rjust(dtWidth),
- 'max'.rjust(dtWidth),
- 'priority'.rjust(priorityWidth),))
- i += 1
- for taskPriList in self.taskList:
- priority = `taskPriList._priority`
- for task in taskPriList:
- if task is None:
- break
- if task._removed:
- taskName = '(R)' + task.name
- else:
- taskName = task.name
- totalDt = totalDt + task.dt
- totalAvgDt = totalAvgDt + task.avgDt
- onScreenDebug.add(
- ('%s%02i.%s' % (prefix, i, task.name)).ljust(taskNameWidth),
- '%s %s %s %s' % (
- dtfmt % (task.dt*1000),
- dtfmt % (task.avgDt*1000),
- dtfmt % (task.maxDt*1000),
- priority.rjust(priorityWidth)))
- i += 1
- onScreenDebug.add(('%s%02i.total' % (prefix, i)).ljust(taskNameWidth),
- '%s %s' % (
- dtfmt % (totalDt*1000),
- dtfmt % (totalAvgDt*1000),))
- return cont
- def _runTests(self):
- if __debug__:
- tm = TaskManager()
- # looks like nothing runs on the first frame...?
- # step to get past the first frame
- tm.step()
- # check for memory leaks after every test
- tm._startTrackingMemLeaks()
- tm._checkMemLeaks()
- # run-once task
- l = []
- def _testDone(task, l=l):
- l.append(None)
- return task.done
- tm.add(_testDone, 'testDone')
- tm.step()
- assert len(l) == 1
- tm.step()
- assert len(l) == 1
- _testDone = None
- tm._checkMemLeaks()
- # remove by name
- def _testRemoveByName(task):
- return task.done
- tm.add(_testRemoveByName, 'testRemoveByName')
- assert tm.remove('testRemoveByName') == 1
- assert tm.remove('testRemoveByName') == 0
- _testRemoveByName = None
- tm._checkMemLeaks()
- # duplicate named tasks
- def _testDupNamedTasks(task):
- return task.done
- tm.add(_testDupNamedTasks, 'testDupNamedTasks')
- tm.add(_testDupNamedTasks, 'testDupNamedTasks')
- assert tm.remove('testRemoveByName') == 0
- _testDupNamedTasks = None
- tm._checkMemLeaks()
- # continued task
- l = []
- def _testCont(task, l = l):
- l.append(None)
- return task.cont
- tm.add(_testCont, 'testCont')
- tm.step()
- assert len(l) == 1
- tm.step()
- assert len(l) == 2
- tm.remove('testCont')
- _testCont = None
- tm._checkMemLeaks()
- # continue until done task
- l = []
- def _testContDone(task, l = l):
- l.append(None)
- if len(l) >= 2:
- return task.done
- else:
- return task.cont
- tm.add(_testContDone, 'testContDone')
- tm.step()
- assert len(l) == 1
- tm.step()
- assert len(l) == 2
- tm.step()
- assert len(l) == 2
- assert not tm.hasTaskNamed('testContDone')
- _testContDone = None
- tm._checkMemLeaks()
- # hasTaskNamed
- def _testHasTaskNamed(task):
- return task.done
- tm.add(_testHasTaskNamed, 'testHasTaskNamed')
- assert tm.hasTaskNamed('testHasTaskNamed')
- tm.step()
- assert not tm.hasTaskNamed('testHasTaskNamed')
- _testHasTaskNamed = None
- tm._checkMemLeaks()
- # task priority
- l = []
- def _testPri1(task, l = l):
- l.append(1)
- return task.cont
- def _testPri2(task, l = l):
- l.append(2)
- return task.cont
- tm.add(_testPri1, 'testPri1', priority = 1)
- tm.add(_testPri2, 'testPri2', priority = 2)
- tm.step()
- assert len(l) == 2
- assert l == [1, 2,]
- tm.step()
- assert len(l) == 4
- assert l == [1, 2, 1, 2,]
- tm.remove('testPri1')
- tm.remove('testPri2')
- _testPri1 = None
- _testPri2 = None
- tm._checkMemLeaks()
- # task extraArgs
- l = []
- def _testExtraArgs(arg1, arg2, l=l):
- l.extend([arg1, arg2,])
- return done
- tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
- tm.step()
- assert len(l) == 2
- assert l == [4, 5,]
- _testExtraArgs = None
- tm._checkMemLeaks()
- # task appendTask
- l = []
- def _testAppendTask(arg1, arg2, task, l=l):
- l.extend([arg1, arg2,])
- return task.done
- tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
- tm.step()
- assert len(l) == 2
- assert l == [4, 5,]
- _testAppendTask = None
- tm._checkMemLeaks()
- # task uponDeath
- l = []
- def _uponDeathFunc(task, l=l):
- l.append(task.name)
- def _testUponDeath(task):
- return done
- tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
- tm.step()
- assert len(l) == 1
- assert l == ['testUponDeath']
- _testUponDeath = None
- _uponDeathFunc = None
- tm._checkMemLeaks()
- # task owner
- class _TaskOwner:
- def _clearTask(self, task):
- self.clearedTaskName = task.name
- to = _TaskOwner()
- l = []
- def _testOwner(task):
- return done
- tm.add(_testOwner, 'testOwner', owner=to)
- tm.step()
- assert hasattr(to, 'clearedTaskName')
- assert to.clearedTaskName == 'testOwner'
- _testOwner = None
- del to
- _TaskOwner = None
- tm._checkMemLeaks()
- doLaterTests = [0,]
- # doLater
- l = []
- def _testDoLater1(task, l=l):
- l.append(1)
- def _testDoLater2(task, l=l):
- l.append(2)
- def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
- if task.time > .03:
- assert l == [1, 2,]
- doLaterTests[0] -= 1
- return task.done
- return task.cont
- tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
- tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
- doLaterTests[0] += 1
- # make sure we run this task after the doLaters if they all occur on the same frame
- tm.add(_monitorDoLater, 'monitorDoLater', priority=10)
- _testDoLater1 = None
- _testDoLater2 = None
- _monitorDoLater = None
- # don't check until all the doLaters are finished
- #tm._checkMemLeaks()
- # doLater priority
- l = []
- def _testDoLaterPri1(task, l=l):
- l.append(1)
- def _testDoLaterPri2(task, l=l):
- l.append(2)
- def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
- if task.time > .02:
- assert l == [1, 2,]
- doLaterTests[0] -= 1
- return task.done
- return task.cont
- tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', priority=1)
- tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', priority=2)
- doLaterTests[0] += 1
- # make sure we run this task after the doLaters if they all occur on the same frame
- tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', priority=10)
- _testDoLaterPri1 = None
- _testDoLaterPri2 = None
- _monitorDoLaterPri = None
- # don't check until all the doLaters are finished
- #tm._checkMemLeaks()
- # doLater extraArgs
- l = []
- def _testDoLaterExtraArgs(arg1, l=l):
- l.append(arg1)
- def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
- if task.time > .02:
- assert l == [3,]
- doLaterTests[0] -= 1
- return task.done
- return task.cont
- tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
- doLaterTests[0] += 1
- # make sure we run this task after the doLaters if they all occur on the same frame
- tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', priority=10)
- _testDoLaterExtraArgs = None
- _monitorDoLaterExtraArgs = None
- # don't check until all the doLaters are finished
- #tm._checkMemLeaks()
- # doLater appendTask
- l = []
- def _testDoLaterAppendTask(arg1, task, l=l):
- assert task.name == 'testDoLaterAppendTask'
- l.append(arg1)
- def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
- if task.time > .02:
- assert l == [4,]
- doLaterTests[0] -= 1
- return task.done
- return task.cont
- tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
- extraArgs=[4,], appendTask=True)
- doLaterTests[0] += 1
- # make sure we run this task after the doLaters if they all occur on the same frame
- tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', priority=10)
- _testDoLaterAppendTask = None
- _monitorDoLaterAppendTask = None
- # don't check until all the doLaters are finished
- #tm._checkMemLeaks()
- # doLater uponDeath
- l = []
- def _testUponDeathFunc(task, l=l):
- assert task.name == 'testDoLaterUponDeath'
- l.append(10)
- def _testDoLaterUponDeath(arg1, l=l):
- return done
- def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
- if task.time > .02:
- assert l == [10,]
- doLaterTests[0] -= 1
- return task.done
- return task.cont
- tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
- uponDeath=_testUponDeathFunc)
- doLaterTests[0] += 1
- # make sure we run this task after the doLaters if they all occur on the same frame
- tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', priority=10)
- _testUponDeathFunc = None
- _testDoLaterUponDeath = None
- _monitorDoLaterUponDeath = None
- # don't check until all the doLaters are finished
- #tm._checkMemLeaks()
- # doLater owner
- class _DoLaterOwner:
- def _clearTask(self, task):
- self.clearedTaskName = task.name
- doLaterOwner = _DoLaterOwner()
- l = []
- def _testDoLaterOwner(l=l):
- pass
- def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
- doLaterTests=doLaterTests):
- if task.time > .02:
- assert hasattr(doLaterOwner, 'clearedTaskName')
- assert doLaterOwner.clearedTaskName == 'testDoLaterOwner'
- doLaterTests[0] -= 1
- return task.done
- return task.cont
- tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
- owner=doLaterOwner)
- doLaterTests[0] += 1
- # make sure we run this task after the doLaters if they all occur on the same frame
- tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', priority=10)
- _testDoLaterOwner = None
- _monitorDoLaterOwner = None
- del doLaterOwner
- _DoLaterOwner = None
- # don't check until all the doLaters are finished
- #tm._checkMemLeaks()
- # run the doLater tests
- while doLaterTests[0] > 0:
- tm.step()
- del doLaterTests
- tm._checkMemLeaks()
- # getTasks
- def _testGetTasks(task):
- return task.cont
- # the doLaterProcessor is always running
- assert len(tm.getTasks()) == 1
- tm.add(_testGetTasks, 'testGetTasks1')
- assert len(tm.getTasks()) == 2
- assert (tm.getTasks()[0].name == 'testGetTasks1' or
- tm.getTasks()[1].name == 'testGetTasks1')
- tm.add(_testGetTasks, 'testGetTasks2')
- tm.add(_testGetTasks, 'testGetTasks3')
- assert len(tm.getTasks()) == 4
- tm.remove('testGetTasks2')
- assert len(tm.getTasks()) == 3
- tm.remove('testGetTasks1')
- tm.remove('testGetTasks3')
- assert len(tm.getTasks()) == 1
- _testGetTasks = None
- tm._checkMemLeaks()
- # getDoLaters
- def _testGetDoLaters():
- pass
- # the doLaterProcessor is always running
- assert len(tm.getDoLaters()) == 0
- tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
- assert len(tm.getDoLaters()) == 1
- assert tm.getDoLaters()[0].name == 'testDoLater1'
- tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
- tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
- assert len(tm.getDoLaters()) == 3
- tm.remove('testDoLater2')
- assert len(tm.getDoLaters()) == 2
- tm.remove('testDoLater1')
- tm.remove('testDoLater3')
- assert len(tm.getDoLaters()) == 0
- _testGetDoLaters = None
- tm._checkMemLeaks()
- # duplicate named doLaters removed via taskMgr.remove
- def _testDupNameDoLaters():
- pass
- # the doLaterProcessor is always running
- tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
- tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
- assert len(tm.getDoLaters()) == 2
- tm.remove('testDupNameDoLater')
- assert len(tm.getDoLaters()) == 0
- _testDupNameDoLaters = None
- tm._checkMemLeaks()
- # duplicate named doLaters removed via remove()
- def _testDupNameDoLatersRemove():
- pass
- # the doLaterProcessor is always running
- dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
- dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
- assert len(tm.getDoLaters()) == 2
- dl2.remove()
- assert len(tm.getDoLaters()) == 1
- dl1.remove()
- assert len(tm.getDoLaters()) == 0
- _testDupNameDoLatersRemove = None
- # nameDict etc. isn't cleared out right away with task.remove()
- tm._checkMemLeaks()
- # getTasksNamed
- def _testGetTasksNamed(task):
- return task.cont
- assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
- tm.add(_testGetTasksNamed, 'testGetTasksNamed')
- assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
- assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
- tm.add(_testGetTasksNamed, 'testGetTasksNamed')
- tm.add(_testGetTasksNamed, 'testGetTasksNamed')
- assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
- tm.remove('testGetTasksNamed')
- assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
- _testGetTasksNamed = None
- tm._checkMemLeaks()
- # removeTasksMatching
- def _testRemoveTasksMatching(task):
- return task.cont
- tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
- assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
- tm.removeTasksMatching('testRemoveTasksMatching')
- assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
- tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
- tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
- assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
- assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
- tm.removeTasksMatching('testRemoveTasksMatching*')
- assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
- assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
- tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
- tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
- assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
- assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
- tm.removeTasksMatching('testRemoveTasksMatching?a')
- assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
- assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
- _testRemoveTasksMatching = None
- tm._checkMemLeaks()
- # create Task object and add to mgr
- l = []
- def _testTaskObj(task, l=l):
- l.append(None)
- return task.cont
- t = Task(_testTaskObj)
- tm.add(t, 'testTaskObj')
- tm.step()
- assert len(l) == 1
- tm.step()
- assert len(l) == 2
- tm.remove('testTaskObj')
- tm.step()
- assert len(l) == 2
- _testTaskObj = None
- tm._checkMemLeaks()
- # remove Task via task.remove()
- l = []
- def _testTaskObjRemove(task, l=l):
- l.append(None)
- return task.cont
- t = Task(_testTaskObjRemove)
- tm.add(t, 'testTaskObjRemove')
- tm.step()
- assert len(l) == 1
- tm.step()
- assert len(l) == 2
- t.remove()
- tm.step()
- assert len(l) == 2
- del t
- _testTaskObjRemove = None
- tm._checkMemLeaks()
- """
- # this test fails, and it's not clear what the correct behavior should be.
- # priority passed to Task.__init__ is always overridden by taskMgr.add()
- # even if no priority is specified, and calling Task.setPriority() has no
- # effect on the taskMgr's behavior.
- # set/get Task priority
- l = []
- def _testTaskObjPriority(arg, task, l=l):
- l.append(arg)
- return task.cont
- t1 = Task(_testTaskObjPriority, priority=1)
- t2 = Task(_testTaskObjPriority, priority=2)
- tm.add(t1, 'testTaskObjPriority1', extraArgs=['a',], appendTask=True)
- tm.add(t2, 'testTaskObjPriority2', extraArgs=['b',], appendTask=True)
- tm.step()
- assert len(l) == 2
- assert l == ['a', 'b']
- assert t1.getPriority() == 1
- assert t2.getPriority() == 2
- t1.setPriority(3)
- assert t1.getPriority() == 3
- tm.step()
- assert len(l) == 4
- assert l == ['a', 'b', 'b', 'a',]
- t1.remove()
- t2.remove()
- tm.step()
- assert len(l) == 4
- del t1
- del t2
- _testTaskObjPriority = None
- tm._checkMemLeaks()
- """
- del l
- tm.destroy()
- del tm
- # These constants are moved to the top level of the module,
- # to make it easier for legacy code. In general though, putting
- # constants at the top level of a module is deprecated.
- exit = Task.exit
- done = Task.done
- cont = Task.cont
- again = Task.again
- if __debug__:
- pass # 'if __debug__' is hint for CVS diff output
- """
- import Task
- def goo(task):
- print 'goo'
- return Task.done
- def bar(task):
- print 'bar'
- taskMgr.add(goo, 'goo')
- return Task.done
- def foo(task):
- print 'foo'
- taskMgr.add(bar, 'bar')
- return Task.done
- taskMgr.add(foo, 'foo')
- """
|