panda3d/direct/src/task/TaskOrig.py
2008-10-24 18:46:09 +00:00

2021 lines
76 KiB
Python

"""Undocumented Module"""
__all__ = ['Task', 'TaskSortList', 'TaskManager',
'exit', 'cont', 'done', 'again',
'sequence', 'loop', 'pause']
# 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
from direct.showbase.ProfileSession import ProfileSession
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
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, sort = 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._sort = sort
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 isAlive(self):
""" Returns true if the task is alive; that is, it has never
been removed and it has not finished. This method may
inaccurately return true before the task has been initially
added to the task manager. """
return not self._removed
def getSort(self):
return self._sort
def setSort(self, pri):
self._sort = pri
def getPriority(self):
return 0
def setPriority(self, pri):
TaskManager.notify.error("deprecated task.setPriority() called; use setSort() instead")
pass
def getName(self):
return self.name
def setName(self, name):
self.name = name
def getDelay(self):
return self.delayTime
def setDelay(self, delay):
self.delayTime = delay
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 getNamePrefix(self):
# get a version of the task name, omitting a hyphen or
# underscore followed by a string of digits at the end of the
# name.
name = self.name
trimmed = len(name)
p = trimmed
while True:
while p > 0 and name[p - 1] in string.digits:
p -= 1
if p > 0 and name[p - 1] in '-_':
p -= 1
trimmed = p
else:
p = trimmed
break
return name[:trimmed]
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):
if hasattr(self, "uponDeath"):
self.uponDeath(self)
del self.uponDeath
# We regret to announce...
messenger.send('TaskManager-removeTask', sentArgs = [self])
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 TaskSortList(list):
def __init__(self, sort):
self._sort = sort
self.__emptyIndex = 0
def getSort(self):
return self._sort
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 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.'
# multiple of average frame duration
DefTaskDurationWarningThreshold = 40.
_DidTests = False
def __init__(self):
self._destroyed = False
self.running = 0
self.stepping = 0
self.taskList = []
# Dictionary of sort to newTaskLists
self.pendingTaskDict = {}
# List of tasks scheduled to execute in the future
self.__doLaterList = []
self._frameProfileQueue = Queue()
self.MaxEpockSpeed = 1.0/30.0;
# this will be set when it's safe to import StateVar
self._profileTasks = None
self._taskProfiler = None
self._taskProfileInfo = ScratchPad(
taskId = None,
profiled = False,
session = 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
# Dictionary of task name to list of tasks with that name
self.nameDict = {}
# A default task.
self._doLaterTask = self.add(self.__doLaterProcessor, "doLaterProcessor", -10)
def finalInit(self):
# This function should be called once during startup, after
# most things are imported.
pass
def destroy(self):
if self._destroyed:
return
self._frameProfileQueue.clear()
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
self._destroyed = True
def setStepping(self, value):
self.stepping = 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 setupTaskChain(self, *args, **kw):
# This is a no-op in the original task implementation; task
# chains are not supported here.
pass
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 doMethodLater(self, delayTime, funcOrTask, name, extraArgs = None,
sort = None, priority = None, taskChain = None,
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, sort)
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'
# For historical reasons, if priority is specified but not
# sort, it really means sort.
if priority is not None and sort is None:
sort = priority
task.setSort(sort or 0)
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)
# Alert the world, a new task is born!
#messenger.send('TaskManager-spawnDoLater', sentArgs = [task])
if task.owner:
task.owner._addTask(task)
return task
def add(self, funcOrTask, name, sort = None, extraArgs = None,
priority = None, uponDeath = None, appendTask = False,
taskChain = None, owner = None):
"""
Add a new task to the taskMgr.
You can add a Task object or a method that takes one argument.
"""
# For historical reasons, if priority is specified but not
# sort, it really means sort.
if priority is not None and sort is None:
sort = priority
# TaskManager.notify.debug('add: %s' % (name))
if isinstance(funcOrTask, Task):
task = funcOrTask
elif callable(funcOrTask):
task = Task(funcOrTask, sort)
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.setSort(sort or 0)
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)
if task.owner:
task.owner._addTask(task)
return task
def __addPendingTask(self, task):
# TaskManager.notify.debug('__addPendingTask: %s' % (task.name))
pri = task._sort
taskPriList = self.pendingTaskDict.get(pri)
if not taskPriList:
taskPriList = TaskSortList(pri)
self.pendingTaskDict[pri] = taskPriList
taskPriList.add(task)
def __addNewTask(self, task):
# The taskList is really an ordered list of TaskSortLists
# search back from the end of the list until we find a
# taskList with a lower sort, or we hit the start of the list
taskSort = task._sort
index = len(self.taskList) - 1
while (1):
if (index < 0):
newList = TaskSortList(taskSort)
newList.add(task)
# Add the new list to the beginning of the taskList
self.taskList.insert(0, newList)
break
taskListSort = self.taskList[index]._sort
if (taskListSort == taskSort):
self.taskList[index].add(task)
break
elif (taskListSort > taskSort):
index = index - 1
elif (taskListSort < taskSort):
# Time to insert
newList = TaskSortList(taskSort)
newList.add(task)
# Insert this new sort 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()
# Alert the world, a new task is born!
messenger.send('TaskManager-spawnTask', sentArgs = [task])
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()
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()
# 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._taskProfileInfo might get swapped out
# by the task when it runs
profileInfo = self._taskProfileInfo
doProfile = (task.id == profileInfo.taskId)
# don't profile the same task twice in a row
doProfile = doProfile and (not profileInfo.profiled)
if not self.taskTimerVerbose:
startTime = self.trueClock.getShortTime()
# don't record timing info
if doProfile:
profileSession = ProfileSession('TASK_PROFILE:%s' % task.name,
Functor(task, *task.extraArgs))
ret = profileSession.run()
# set these values *after* profiling in case we're profiling the TaskProfiler
profileInfo.session = profileSession
profileInfo.profiled = True
else:
ret = task(*task.extraArgs)
endTime = self.trueClock.getShortTime()
# Record the dt
dt = endTime - startTime
if doProfile:
# if we profiled, don't pollute the task's recorded duration
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:
profileSession = ProfileSession('profiled-task-%s' % task.name,
Functor(task, *task.extraArgs))
ret = profileSession.run()
# set these values *after* profiling in case we're profiling the TaskProfiler
profileInfo.session = profileSession
profileInfo.profiled = True
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, don't pollute the task's recorded duration
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
# 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)
# Alert the world, a new task is born!
#messenger.send('TaskManager-againDoLater', sentArgs = [task])
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()
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.__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 sort 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 getProfileSession(self, name=None):
# call to get a profile session that you can modify before passing to profileFrames()
if name is None:
name = 'taskMgrFrameProfile'
return ProfileSession(name)
def profileFrames(self, num=None, session=None):
if num is None:
num = 1
if session is None:
session = self.getProfileSession()
self._frameProfileQueue.push((num, session))
# 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();
def _doProfiledFrames(self, numFrames):
for i in xrange(numFrames):
result = self.step()
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 logTaskProfiles(self, name=None):
if self._taskProfiler:
self._taskProfiler.logProfiles(name)
def flushTaskProfiles(self, name=None):
if self._taskProfiler:
self._taskProfiler.flush(name)
def _setProfileTask(self, task):
if self._taskProfileInfo.session:
self._taskProfileInfo.session.release()
self._taskProfileInfo.session = None
self._taskProfileInfo = ScratchPad(
taskId = task.id,
profiled = False,
session = None,
)
def _hasProfiledDesignatedTask(self):
# have we run a profile of the designated task yet?
return self._taskProfileInfo.profiled
def _getLastTaskProfileSession(self):
return self._taskProfileInfo.session
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 sort order
priIndex = 0
while priIndex < len(self.taskList):
taskPriList = self.taskList[priIndex]
pri = taskPriList._sort
# 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 sort level
self.__addPendingTasksToTaskList()
# Go to the next sort 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._profileTasks:
if 'base' in __builtins__ or \
'simbase' in __builtins__:
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 len(self._frameProfileQueue):
numFrames, session = self._frameProfileQueue.pop()
def _profileFunc(numFrames=numFrames):
self._doProfiledFrames(numFrames)
session.setFunc(_profileFunc)
session.run()
_profileFunc = None
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
sortWidth = 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)
+ 'sort'.rjust(sortWidth)
+ '\n')
str += '-------------------------------------------------------------------------\n'
dtfmt = '%%%d.2f' % (dtWidth)
for taskPriList in self.taskList:
sort = `taskPriList._sort`
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)
+ sort.rjust(sortWidth)
+ '\n')
else:
str += (task.name.ljust(taskNameWidth)
+ '----'.rjust(dtWidth)
+ '----'.rjust(dtWidth)
+ '----'.rjust(dtWidth)
+ sort.rjust(sortWidth)
+ '\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 sort 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 TaskSortLists.
# 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
sortWidth = 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),
'sort'.rjust(sortWidth),))
i += 1
for taskPriList in self.taskList:
sort = `taskPriList._sort`
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),
sort.rjust(sortWidth)))
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 sort
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', sort = 1)
tm.add(_testPri2, 'testPri2', sort = 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 _addTask(self, task):
self.addedTaskName = task.name
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 getattr(to, 'addedTaskName', None) == 'testOwner'
assert getattr(to, 'clearedTaskName', None) == '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', sort=10)
_testDoLater1 = None
_testDoLater2 = None
_monitorDoLater = None
# don't check until all the doLaters are finished
#tm._checkMemLeaks()
# doLater sort
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', sort=1)
tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', sort=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', sort=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', sort=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', sort=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', sort=10)
_testUponDeathFunc = None
_testDoLaterUponDeath = None
_monitorDoLaterUponDeath = None
# don't check until all the doLaters are finished
#tm._checkMemLeaks()
# doLater owner
class _DoLaterOwner:
def _addTask(self, task):
self.addedTaskName = task.name
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 getattr(doLaterOwner, 'addedTaskName', None) == 'testDoLaterOwner'
assert getattr(doLaterOwner, 'clearedTaskName', None) == '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', sort=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.
# sort passed to Task.__init__ is always overridden by taskMgr.add()
# even if no sort is specified, and calling Task.setSort() has no
# effect on the taskMgr's behavior.
# set/get Task sort
l = []
def _testTaskObjSort(arg, task, l=l):
l.append(arg)
return task.cont
t1 = Task(_testTaskObjSort, sort=1)
t2 = Task(_testTaskObjSort, sort=2)
tm.add(t1, 'testTaskObjSort1', extraArgs=['a',], appendTask=True)
tm.add(t2, 'testTaskObjSort2', extraArgs=['b',], appendTask=True)
tm.step()
assert len(l) == 2
assert l == ['a', 'b']
assert t1.getSort() == 1
assert t2.getSort() == 2
t1.setSort(3)
assert t1.getSort() == 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
_testTaskObjSort = 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')
"""