diff --git a/direct/src/showbase/ShowBase.py b/direct/src/showbase/ShowBase.py index 4869bdbfab..4d5a4b7ba4 100644 --- a/direct/src/showbase/ShowBase.py +++ b/direct/src/showbase/ShowBase.py @@ -423,6 +423,8 @@ class ShowBase(DirectObject.DirectObject): This function is designed to be safe to call multiple times.""" + taskMgr.destroy() + if getattr(self, 'musicManager', None): self.musicManager.shutdown() self.musicManager = None @@ -431,7 +433,7 @@ class ShowBase(DirectObject.DirectObject): self.loader.destroy() self.loader = None if getattr(self, 'graphicsEngine', None): - self.graphicsEngine.removeAllWindows() + self.graphicsEngine.removeAllWindows() try: self.direct.panel.destroy() diff --git a/direct/src/task/TaskNew.py b/direct/src/task/TaskNew.py index 018a20330f..02a1f92a1f 100644 --- a/direct/src/task/TaskNew.py +++ b/direct/src/task/TaskNew.py @@ -77,6 +77,10 @@ class TaskManager: self.fKeyboardInterrupt = False self.interruptCount = 0 + def destroy(self): + self.mgr.stopThreads() + self.removeTasksMatching('*') + def keyboardInterruptHandler(self, signalNumber, stackFrame): self.fKeyboardInterrupt = 1 self.interruptCount += 1 @@ -88,6 +92,50 @@ class TaskManager: # Next time around invoke the default handler signal.signal(signal.SIGINT, self.invokeDefaultHandler) + def setupTaskChain(self, chainName, numThreads = None, tickClock = None, + threadPriority = None, frameBudget = None): + """Defines a new task chain. Each task chain executes tasks + potentially in parallel with all of the other task chains (if + numThreads is more than zero). When a new task is created, it + may be associated with any of the task chains, by name (or you + can move a task to another task chain with + task.setTaskChain()). You can have any number of task chains, + but each must have a unique name. + + numThreads is the number of threads to allocate for this task + chain. If it is 1 or more, then the tasks on this task chain + will execute in parallel with the tasks on other task chains. + If it is greater than 1, then the tasks on this task chain may + execute in parallel with themselves (within tasks of the same + sort value). + + If tickClock is True, then this task chain will be responsible + for ticking the global clock each frame (and thereby + incrementing the frame counter). There should be just one + task chain responsible for ticking the clock, and usually it + is the default, unnamed task chain. + + threadPriority specifies the priority level to assign to + threads on this task chain. It may be one of TPLow, TPNormal, + TPHigh, or TPUrgent. This is passed to the underlying + threading system to control the way the threads are scheduled. + + frameBudget is the maximum amount of time (in seconds) to + allow this task chain to run per frame. Set it to -1 to mean + no limit (the default). It's not directly related to + threadPriority. + """ + + chain = self.mgr.makeTaskChain(chainName) + if numThreads is not None: + chain.setNumThreads(numThreads) + if tickClock is not None: + chain.setTickClock(tickClock) + if threadPriority is not None: + chain.setThreadPriority(threadPriority) + if frameBudget is not None: + chain.setFrameBudget(frameBudget) + def hasTaskNamed(self, taskName): return bool(self.mgr.findTask(taskName)) @@ -100,28 +148,29 @@ class TaskManager: l.append(taskCollection.getTask(i)) return l - def doMethodLater(self, delayTime, funcOrTask, name, extraArgs=None, - priority=0, appendTask=False, owner = None): + def doMethodLater(self, delayTime, funcOrTask, name, priority = None, + sort = None, extraArgs = None, taskChain = None, + appendTask = False): if delayTime < 0: assert self.notify.warning('doMethodLater: added task: %s with negative delay: %s' % (name, delayTime)) - task = self.__setupTask(funcOrTask, name, priority, extraArgs, appendTask) + task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask) task.setDelay(delayTime) self.mgr.add(task) return task - def add(self, funcOrTask, name, priority=0, extraArgs=None, - appendTask = False): + def add(self, funcOrTask, name, priority = None, sort = None, + extraArgs = None, taskChain = None, appendTask = False): """ Add a new task to the taskMgr. You can add a Task object or a method that takes one argument. """ - task = self.__setupTask(funcOrTask, name, priority, extraArgs, appendTask) + task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask) self.mgr.add(task) return task - def __setupTask(self, funcOrTask, name, priority, extraArgs, appendTask): + def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask): if isinstance(funcOrTask, PythonTask): task = funcOrTask elif callable(funcOrTask): @@ -131,9 +180,21 @@ class TaskManager: 'add: Tried to add a task that was not a Task or a func') assert isinstance(name, types.StringTypes), 'Name must be a string type' task.setName(name) - task.setSort(priority) - if extraArgs == None: + # For historical reasons, if priority is specified but not + # sort, it really means sort. + if priority is not None and sort is None: + task.setSort(priority) + else: + if priority is not None: + task.setPriority(priority) + if sort is not None: + task.setSort(sort) + + if taskChain is not None: + task.setTaskChain(taskChain) + + if extraArgs is None: extraArgs = [] appendTask = True task.setArgs(extraArgs, appendTask) diff --git a/direct/src/task/TaskOrig.py b/direct/src/task/TaskOrig.py index ed4fc15fc6..ff9b090fc7 100644 --- a/direct/src/task/TaskOrig.py +++ b/direct/src/task/TaskOrig.py @@ -1,6 +1,6 @@ """Undocumented Module""" -__all__ = ['Task', 'TaskPriorityList', 'TaskManager', +__all__ = ['Task', 'TaskSortList', 'TaskManager', 'exit', 'cont', 'done', 'again', 'sequence', 'loop', 'pause'] @@ -79,7 +79,7 @@ class Task: again = 2 count = 0 - def __init__(self, callback, priority = 0): + def __init__(self, callback, sort = 0): try: config except: @@ -95,7 +95,7 @@ class Task: self.owner = None self.__call__ = callback - self._priority = priority + self._sort = sort self._removed = 0 self.dt = 0.0 if TaskManager.taskTimerVerbose: @@ -149,11 +149,18 @@ class Task: def isRemoved(self): return self._removed + def getSort(self): + return self._sort + + def setSort(self, pri): + self._sort = pri + def getPriority(self): - return self._priority + return 0 def setPriority(self, pri): - self._priority = pri + TaskManager.notify.error("deprecated task.setPriority() called; use setSort() instead") + pass def getDelay(self): return self.delayTime @@ -329,12 +336,12 @@ def make_loop(taskList): return task -class TaskPriorityList(list): - def __init__(self, priority): - self._priority = priority +class TaskSortList(list): + def __init__(self, sort): + self._sort = sort self.__emptyIndex = 0 - def getPriority(self): - return self._priority + def getSort(self): + return self._sort def add(self, task): if (self.__emptyIndex >= len(self)): self.append(task) @@ -386,7 +393,7 @@ class TaskManager: self.running = 0 self.stepping = 0 self.taskList = [] - # Dictionary of priority to newTaskLists + # Dictionary of sort to newTaskLists self.pendingTaskDict = {} # List of tasks scheduled to execute in the future self.__doLaterList = [] @@ -484,6 +491,12 @@ class TaskManager: # Next time around invoke the default handler signal.signal(signal.SIGINT, self.invokeDefaultHandler) + def setupTaskChain(self, chainName, numThreads = None, tickClock = None, + threadPriority = None, frameBudget = None): + # 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 @@ -568,17 +581,24 @@ class TaskManager: return cont def doMethodLater(self, delayTime, funcOrTask, name, extraArgs=None, - priority=0, uponDeath=None, appendTask=False, owner = 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, priority) + 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' - task.setPriority(priority) + + # 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: @@ -613,23 +633,29 @@ class TaskManager: sentArgs = [task, task.name, task.id]) return task - def add(self, funcOrTask, name, priority=0, extraArgs=None, uponDeath=None, - appendTask = False, owner = None): + def add(self, funcOrTask, name, priority=None, sort=None, extraArgs=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, priority) + 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.setPriority(priority) + task.setSort(sort or 0) task.name = name task.owner = owner if extraArgs == None: @@ -657,37 +683,37 @@ class TaskManager: def __addPendingTask(self, task): # TaskManager.notify.debug('__addPendingTask: %s' % (task.name)) - pri = task._priority + pri = task._sort taskPriList = self.pendingTaskDict.get(pri) if not taskPriList: - taskPriList = TaskPriorityList(pri) + taskPriList = TaskSortList(pri) self.pendingTaskDict[pri] = taskPriList taskPriList.add(task) def __addNewTask(self, task): - # The taskList is really an ordered list of TaskPriorityLists + # 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 priority, or we hit the start of the list - taskPriority = task._priority + # 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 = TaskPriorityList(taskPriority) + newList = TaskSortList(taskSort) 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): + taskListSort = self.taskList[index]._sort + if (taskListSort == taskSort): self.taskList[index].add(task) break - elif (taskListPriority > taskPriority): + elif (taskListSort > taskSort): index = index - 1 - elif (taskListPriority < taskPriority): + elif (taskListSort < taskSort): # Time to insert - newList = TaskPriorityList(taskPriority) + newList = TaskSortList(taskSort) newList.add(task) - # Insert this new priority level + # 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) @@ -926,7 +952,7 @@ class TaskManager: def __addPendingTasksToTaskList(self): # Now that we are all done, add any left over pendingTasks - # generated in priority levels lower or higher than where + # generated in sort levels lower or higher than where # we were when we iterated for taskList in self.pendingTaskDict.values(): for task in taskList: @@ -1050,11 +1076,11 @@ class TaskManager: self.interruptCount = 0 signal.signal(signal.SIGINT, self.keyboardInterruptHandler) - # Traverse the task list in order because it is in priority order + # 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._priority + pri = taskPriList._sort # assert TaskManager.notify.debug( # 'step: running through taskList at pri: %s, priIndex: %s' % # (pri, priIndex)) @@ -1078,10 +1104,10 @@ class TaskManager: # 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 + # later sort level self.__addPendingTasksToTaskList() - # Go to the next priority level + # Go to the next sort level priIndex += 1 # Add new pending tasks @@ -1228,7 +1254,7 @@ class TaskManager: def __repr__(self): taskNameWidth = 32 dtWidth = 10 - priorityWidth = 10 + sortWidth = 10 totalDt = 0 totalAvgDt = 0 str = "The taskMgr is handling:\n" @@ -1236,12 +1262,12 @@ class TaskManager: + 'dt(ms)'.rjust(dtWidth) + 'avg'.rjust(dtWidth) + 'max'.rjust(dtWidth) - + 'priority'.rjust(priorityWidth) + + 'sort'.rjust(sortWidth) + '\n') str += '-------------------------------------------------------------------------\n' dtfmt = '%%%d.2f' % (dtWidth) for taskPriList in self.taskList: - priority = `taskPriList._priority` + sort = `taskPriList._sort` for task in taskPriList: if task is None: break @@ -1256,14 +1282,14 @@ class TaskManager: + dtfmt % (task.dt*1000) + dtfmt % (task.avgDt*1000) + dtfmt % (task.maxDt*1000) - + priority.rjust(priorityWidth) + + sort.rjust(sortWidth) + '\n') else: str += (task.name.ljust(taskNameWidth) + '----'.rjust(dtWidth) + '----'.rjust(dtWidth) + '----'.rjust(dtWidth) - + priority.rjust(priorityWidth) + + sort.rjust(sortWidth) + '\n') str += '-------------------------------------------------------------------------\n' str += 'pendingTasks\n' @@ -1299,7 +1325,7 @@ class TaskManager: + '\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 + # 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)) @@ -1385,7 +1411,7 @@ class TaskManager: self.step() mu = self._memUsage # the task list looks like it grows and never shrinks, replacing finished - # tasks with 'None' in the TaskPriorityLists. + # 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) @@ -1408,7 +1434,7 @@ class TaskManager: onScreenDebug.removeAllWithPrefix(prefix) taskNameWidth = 32 dtWidth = 10 - priorityWidth = 10 + sortWidth = 10 totalDt = 0 totalAvgDt = 0 i = 0 @@ -1418,10 +1444,10 @@ class TaskManager: 'dt(ms)'.rjust(dtWidth), 'avg'.rjust(dtWidth), 'max'.rjust(dtWidth), - 'priority'.rjust(priorityWidth),)) + 'sort'.rjust(sortWidth),)) i += 1 for taskPriList in self.taskList: - priority = `taskPriList._priority` + sort = `taskPriList._sort` for task in taskPriList: if task is None: break @@ -1437,7 +1463,7 @@ class TaskManager: dtfmt % (task.dt*1000), dtfmt % (task.avgDt*1000), dtfmt % (task.maxDt*1000), - priority.rjust(priorityWidth))) + sort.rjust(sortWidth))) i += 1 onScreenDebug.add(('%s%02i.total' % (prefix, i)).ljust(taskNameWidth), '%s %s' % ( @@ -1530,7 +1556,7 @@ class TaskManager: _testHasTaskNamed = None tm._checkMemLeaks() - # task priority + # task sort l = [] def _testPri1(task, l = l): l.append(1) @@ -1538,8 +1564,8 @@ class TaskManager: def _testPri2(task, l = l): l.append(2) return task.cont - tm.add(_testPri1, 'testPri1', priority = 1) - tm.add(_testPri2, 'testPri2', priority = 2) + tm.add(_testPri1, 'testPri1', sort = 1) + tm.add(_testPri2, 'testPri2', sort = 2) tm.step() assert len(l) == 2 assert l == [1, 2,] @@ -1626,14 +1652,14 @@ class TaskManager: 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) + tm.add(_monitorDoLater, 'monitorDoLater', sort=10) _testDoLater1 = None _testDoLater2 = None _monitorDoLater = None # don't check until all the doLaters are finished #tm._checkMemLeaks() - # doLater priority + # doLater sort l = [] def _testDoLaterPri1(task, l=l): l.append(1) @@ -1645,11 +1671,11 @@ class TaskManager: doLaterTests[0] -= 1 return task.done return task.cont - tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', priority=1) - tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', priority=2) + 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', priority=10) + tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', sort=10) _testDoLaterPri1 = None _testDoLaterPri2 = None _monitorDoLaterPri = None @@ -1669,7 +1695,7 @@ class TaskManager: 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) + tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10) _testDoLaterExtraArgs = None _monitorDoLaterExtraArgs = None # don't check until all the doLaters are finished @@ -1690,7 +1716,7 @@ class TaskManager: 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) + tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10) _testDoLaterAppendTask = None _monitorDoLaterAppendTask = None # don't check until all the doLaters are finished @@ -1713,7 +1739,7 @@ class TaskManager: 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) + tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', sort=10) _testUponDeathFunc = None _testDoLaterUponDeath = None _monitorDoLaterUponDeath = None @@ -1740,7 +1766,7 @@ class TaskManager: 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) + tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', sort=10) _testDoLaterOwner = None _monitorDoLaterOwner = None del doLaterOwner @@ -1896,25 +1922,25 @@ class TaskManager: """ # 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 + # 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 priority + # set/get Task sort l = [] - def _testTaskObjPriority(arg, task, l=l): + def _testTaskObjSort(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) + 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.getPriority() == 1 - assert t2.getPriority() == 2 - t1.setPriority(3) - assert t1.getPriority() == 3 + 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',] @@ -1924,7 +1950,7 @@ class TaskManager: assert len(l) == 4 del t1 del t2 - _testTaskObjPriority = None + _testTaskObjSort = None tm._checkMemLeaks() """