Task.py 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893
  1. from libpandaexpressModules import *
  2. from DirectNotifyGlobal import *
  3. from PythonUtil import *
  4. from MessengerGlobal import *
  5. import time
  6. import fnmatch
  7. import string
  8. import signal
  9. from bisect import bisect
  10. # MRM: Need to make internal task variables like time, name, index
  11. # more unique (less likely to have name clashes)
  12. exit = -1
  13. done = 0
  14. cont = 1
  15. # Task needs this because it might run before __builtin__.globalClock
  16. # can be set.
  17. globalClock = ClockObject.getGlobalClock()
  18. def print_exc_plus():
  19. """
  20. Print the usual traceback information, followed by a listing of all the
  21. local variables in each frame.
  22. """
  23. import sys
  24. import traceback
  25. tb = sys.exc_info()[2]
  26. while 1:
  27. if not tb.tb_next:
  28. break
  29. tb = tb.tb_next
  30. stack = []
  31. f = tb.tb_frame
  32. while f:
  33. stack.append(f)
  34. f = f.f_back
  35. stack.reverse()
  36. traceback.print_exc()
  37. print "Locals by frame, innermost last"
  38. for frame in stack:
  39. print
  40. print "Frame %s in %s at line %s" % (frame.f_code.co_name,
  41. frame.f_code.co_filename,
  42. frame.f_lineno)
  43. for key, value in frame.f_locals.items():
  44. print "\t%20s = " % key,
  45. #We have to be careful not to cause a new error in our error
  46. #printer! Calling str() on an unknown object could cause an
  47. #error we don't want.
  48. try:
  49. print value
  50. except:
  51. print "<ERROR WHILE PRINTING VALUE>"
  52. class Task:
  53. count = 0
  54. def __init__(self, callback, priority = 0):
  55. # Unique ID for each task
  56. self.id = Task.count
  57. Task.count += 1
  58. self.__call__ = callback
  59. self.__priority = priority
  60. self.uponDeath = None
  61. self.dt = 0.0
  62. self.maxDt = 0.0
  63. self.avgDt = 0.0
  64. self.runningTotal = 0.0
  65. self.pstats = None
  66. self.__removed = 0
  67. self.__onDoLaterList = 0
  68. def setOnDoLaterList(self, status):
  69. self.__onDoLaterList = status
  70. def isOnDoLaterList(self):
  71. return self.__onDoLaterList
  72. def remove(self):
  73. self.__removed = 1
  74. def isRemoved(self):
  75. return self.__removed
  76. def getPriority(self):
  77. return self.__priority
  78. def setPriority(self, pri):
  79. self.__priority = pri
  80. def setStartTimeFrame(self, startTime, startFrame):
  81. self.starttime = startTime
  82. self.startframe = startFrame
  83. def setCurrentTimeFrame(self, currentTime, currentFrame):
  84. # Calculate and store this task's time (relative to when it started)
  85. self.time = currentTime - self.starttime
  86. self.frame = currentFrame - self.startframe
  87. def setupPStats(self, name):
  88. if __debug__:
  89. import PStatCollector
  90. self.pstats = PStatCollector.PStatCollector("App:Show code:" + name)
  91. def finishTask(self, verbose):
  92. if self.uponDeath:
  93. self.uponDeath(self)
  94. if verbose:
  95. # We regret to announce...
  96. messenger.send('TaskManager-removeTask', sentArgs = [self, self.name])
  97. def __repr__(self):
  98. if hasattr(self, 'name'):
  99. return ('Task id: %s, name %s' % (self.id, self.name))
  100. else:
  101. return ('Task id: %s, no name' % (self.id))
  102. def pause(delayTime):
  103. def func(self):
  104. if (self.time < self.delayTime):
  105. return cont
  106. else:
  107. return done
  108. task = Task(func)
  109. task.name = 'pause'
  110. task.delayTime = delayTime
  111. return task
  112. def sequence(*taskList):
  113. return make_sequence(taskList)
  114. def make_sequence(taskList):
  115. def func(self):
  116. frameFinished = 0
  117. taskDoneStatus = -1
  118. while (not frameFinished):
  119. task = self.taskList[self.index]
  120. # If this is a new task, set its start time and frame
  121. if (self.index > self.prevIndex):
  122. task.setStartTimeFrame(self.time, self.frame)
  123. self.prevIndex = self.index
  124. # Calculate this task's time since it started
  125. task.setCurrentTimeFrame(self.time, self.frame)
  126. # Execute the current task
  127. ret = task(task)
  128. # Check the return value from the task
  129. # If this current task wants to continue,
  130. # come back to it next frame
  131. if (ret == cont):
  132. taskDoneStatus = cont
  133. frameFinished = 1
  134. # If this task is done, increment the index so that next frame
  135. # we will start executing the next task on the list
  136. elif (ret == done):
  137. self.index = self.index + 1
  138. taskDoneStatus = cont
  139. frameFinished = 0
  140. # If this task wants to exit, the sequence exits
  141. elif (ret == exit):
  142. taskDoneStatus = exit
  143. frameFinished = 1
  144. # If we got to the end of the list, this sequence is done
  145. if (self.index >= len(self.taskList)):
  146. assert(TaskManager.notify.debug('sequence done: ' + self.name))
  147. frameFinished = 1
  148. taskDoneStatus = done
  149. return taskDoneStatus
  150. task = Task(func)
  151. task.name = 'sequence'
  152. task.taskList = taskList
  153. task.prevIndex = -1
  154. task.index = 0
  155. return task
  156. def resetSequence(task):
  157. # Should this automatically be done as part of spawnTaskNamed?
  158. # Or should one have to create a new task instance every time
  159. # one wishes to spawn a task (currently sequences and can
  160. # only be fired off once
  161. task.index = 0
  162. task.prevIndex = -1
  163. def loop(*taskList):
  164. return make_loop(taskList)
  165. def make_loop(taskList):
  166. def func(self):
  167. frameFinished = 0
  168. taskDoneStatus = -1
  169. while (not frameFinished):
  170. task = self.taskList[self.index]
  171. # If this is a new task, set its start time and frame
  172. if (self.index > self.prevIndex):
  173. task.setStartTimeFrame(self.time, self.frame)
  174. self.prevIndex = self.index
  175. # Calculate this task's time since it started
  176. task.setCurrentTimeFrame(self.time, self.frame)
  177. # Execute the current task
  178. ret = task(task)
  179. # Check the return value from the task
  180. # If this current task wants to continue,
  181. # come back to it next frame
  182. if (ret == cont):
  183. taskDoneStatus = cont
  184. frameFinished = 1
  185. # If this task is done, increment the index so that next frame
  186. # we will start executing the next task on the list
  187. # TODO: we should go to the next frame now
  188. elif (ret == done):
  189. self.index = self.index + 1
  190. taskDoneStatus = cont
  191. frameFinished = 0
  192. # If this task wants to exit, the sequence exits
  193. elif (ret == exit):
  194. taskDoneStatus = exit
  195. frameFinished = 1
  196. # If we got to the end of the list, wrap back around
  197. if (self.index >= len(self.taskList)):
  198. self.prevIndex = -1
  199. self.index = 0
  200. frameFinished = 1
  201. return taskDoneStatus
  202. task = Task(func)
  203. task.name = 'loop'
  204. task.taskList = taskList
  205. task.prevIndex = -1
  206. task.index = 0
  207. return task
  208. class TaskPriorityList(list):
  209. def __init__(self, priority):
  210. self.__priority = priority
  211. self.__emptyIndex = 0
  212. def getPriority(self):
  213. return self.__priority
  214. def getEmptyIndex(self):
  215. return self.__emptyIndex
  216. def setEmptyIndex(self, index):
  217. self.__emptyIndex = index
  218. def add(self, task):
  219. if (self.__emptyIndex >= len(self)):
  220. self.append(task)
  221. self.__emptyIndex += 1
  222. else:
  223. self[self.__emptyIndex] = task
  224. self.__emptyIndex += 1
  225. def remove(self, i):
  226. assert(i <= len(self))
  227. if (len(self) == 1) and (i == 1):
  228. self[i] = None
  229. self.__emptyIndex = 0
  230. else:
  231. # Swap the last element for this one
  232. lastElement = self[self.__emptyIndex-1]
  233. self[i] = lastElement
  234. self[self.__emptyIndex-1] = None
  235. self.__emptyIndex -= 1
  236. class DoLaterList(list):
  237. """
  238. This is a list that maintains sorted order of wakeTimes on tasks
  239. """
  240. def __init__(self):
  241. list.__init__(self)
  242. def add(self, task):
  243. """
  244. Add task, keeping the list sorted.
  245. This does a binary search for the index to insert into.
  246. Returns the index at which task was inserted.
  247. """
  248. lo = 0
  249. hi = len(self)
  250. while lo < hi:
  251. mid = (lo+hi)//2
  252. if task.wakeTime < self[mid].wakeTime:
  253. hi = mid
  254. else:
  255. lo = mid+1
  256. list.insert(self, lo, task)
  257. return lo
  258. class TaskManager:
  259. notify = None
  260. def __init__(self):
  261. self.running = 0
  262. self.stepping = 0
  263. self.taskList = []
  264. # Dictionary of priority to newTaskLists
  265. self.pendingTaskDict = {}
  266. self.doLaterList = DoLaterList()
  267. self.currentTime, self.currentFrame = self.__getTimeFrame()
  268. if (TaskManager.notify == None):
  269. TaskManager.notify = directNotify.newCategory("TaskManager")
  270. self.taskTimerVerbose = 0
  271. self.extendedExceptions = 0
  272. self.fKeyboardInterrupt = 0
  273. self.interruptCount = 0
  274. self.pStatsTasks = 0
  275. self.resumeFunc = None
  276. self.fVerbose = 0
  277. self.nameDict = {}
  278. self.add(self.__doLaterProcessor, "doLaterProcessor")
  279. # Dictionary of task name to list of tasks with that name
  280. def stepping(self, value):
  281. self.stepping = value
  282. def setVerbose(self, value):
  283. self.fVerbose = value
  284. messenger.send('TaskManager-setVerbose', sentArgs = [value])
  285. def keyboardInterruptHandler(self, signalNumber, stackFrame):
  286. self.fKeyboardInterrupt = 1
  287. self.interruptCount += 1
  288. if self.interruptCount == 2:
  289. # The user must really want to interrupt this process
  290. # Next time around use the default interrupt handler
  291. signal.signal(signal.SIGINT, signal.default_int_handler)
  292. def hasTaskNamed(self, taskName):
  293. # TODO: check pending task list
  294. # Get the tasks with this name
  295. tasks = self.nameDict.get(taskName)
  296. # If we found some, see if any of them are still active (not removed)
  297. if tasks:
  298. for task in tasks:
  299. if not task.isRemoved():
  300. return 1
  301. # Didnt find any, return 0
  302. return 0
  303. def getTasksNamed(self, taskName):
  304. # TODO: check pending tasks
  305. # Get the tasks with this name
  306. tasks = self.nameDict.get(taskName, [])
  307. # Filter out the tasks that have been removed
  308. if tasks:
  309. tasks = filter(lambda task: not task.isRemoved(), tasks)
  310. return tasks
  311. def __doLaterProcessor(self, task):
  312. # Make a temp list of all the dolaters that expired this time
  313. # through so we can remove them after we are done with the
  314. # for loop. Removing them during the for loop is a bad idea
  315. while self.doLaterList:
  316. # TODO: because this processor breaks out early, some tasks
  317. # which have been flagged for removal may stay on the end of
  318. # the doLaterList longer than expected. One brute force fix
  319. # would be to cycle through all tasks removing the ones that
  320. # are flagged each frame.
  321. dl = self.doLaterList[0]
  322. if dl.isRemoved():
  323. del self.doLaterList[0]
  324. dl.setOnDoLaterList(0)
  325. continue
  326. # If the time now is less than the start of the doLater + delay
  327. # then we are not ready yet, continue to next one
  328. elif task.time < dl.wakeTime:
  329. # Since the list is sorted, the first one we get to, that
  330. # is not ready to go, we can return
  331. break
  332. else:
  333. assert(TaskManager.notify.debug('__doLaterProcessor: spawning %s' % (dl)))
  334. del self.doLaterList[0]
  335. dl.setStartTimeFrame(self.currentTime, self.currentFrame)
  336. # No longer on the doLaterList
  337. dl.setOnDoLaterList(0)
  338. self.__addPendingTask(dl)
  339. continue
  340. return cont
  341. def __spawnDoLater(self, task):
  342. assert(TaskManager.notify.debug('spawning doLater: %s' % (task)))
  343. # Add this task to the nameDict
  344. nameList = self.nameDict.setdefault(task.name, [])
  345. nameList.append(task)
  346. # be sure to ask the globalClock for the current frame time
  347. # rather than use a cached value; globalClock's frame time may
  348. # have been synced since the start of this frame
  349. currentTime = globalClock.getFrameTime()
  350. task.setStartTimeFrame(currentTime, self.currentFrame)
  351. # Cache the time we should wake up for easier sorting
  352. task.wakeTime = task.starttime + task.delayTime
  353. # For more efficient removal, note that it is on the doLaterList
  354. task.setOnDoLaterList(1)
  355. index = self.doLaterList.add(task)
  356. if self.fVerbose:
  357. # Alert the world, a new task is born!
  358. messenger.send('TaskManager-spawnDoLater',
  359. sentArgs = [task, task.name, task.id])
  360. return task
  361. def doLater(self, delayTime, task, taskName):
  362. assert(TaskManager.notify.debug('doLater: %s' % (taskName)))
  363. task.delayTime = delayTime
  364. task.name = taskName
  365. return self.__spawnDoLater(task)
  366. def doMethodLater(self, delayTime, func, taskName):
  367. task = Task(func)
  368. return self.doLater(delayTime, task, taskName)
  369. def add(self, funcOrTask, name, priority = 0):
  370. """
  371. Add a new task to the taskMgr.
  372. You can add a Task object or a method that takes one argument.
  373. """
  374. assert(TaskManager.notify.debug('add: %s' % (name)))
  375. if isinstance(funcOrTask, Task):
  376. funcOrTask.setPriority(priority)
  377. return self.__spawnTaskNamed(funcOrTask, name)
  378. elif callable(funcOrTask):
  379. return self.__spawnMethodNamed(funcOrTask, name, priority)
  380. else:
  381. self.notify.error('add: Tried to add a task that was not a Task or a func')
  382. def __spawnMethodNamed(self, func, name, priority=0):
  383. task = Task(func, priority)
  384. return self.__spawnTaskNamed(task, name)
  385. def __spawnTaskNamed(self, task, name):
  386. assert(TaskManager.notify.debug('__spawnTaskNamed: %s' % (name)))
  387. # Init params
  388. task.name = name
  389. # be sure to ask the globalClock for the current frame time
  390. # rather than use a cached value; globalClock's frame time may
  391. # have been synced since the start of this frame
  392. currentTime = globalClock.getFrameTime()
  393. task.setStartTimeFrame(currentTime, self.currentFrame)
  394. nameList = self.nameDict.setdefault(name, [])
  395. nameList.append(task)
  396. # Put it on the list for the end of this frame
  397. self.__addPendingTask(task)
  398. return task
  399. def __addPendingTask(self, task):
  400. assert(TaskManager.notify.debug('__addPendingTask: %s' % (task.name)))
  401. pri = task.getPriority()
  402. if self.pendingTaskDict.has_key(pri):
  403. taskPriList = self.pendingTaskDict[pri]
  404. else:
  405. taskPriList = TaskPriorityList(pri)
  406. self.pendingTaskDict[pri] = taskPriList
  407. taskPriList.add(task)
  408. def __addNewTask(self, task):
  409. # The taskList is really an ordered list of TaskPriorityLists
  410. # search back from the end of the list until we find a
  411. # taskList with a lower priority, or we hit the start of the list
  412. taskPriority = task.getPriority()
  413. index = len(self.taskList) - 1
  414. while (1):
  415. if (index < 0):
  416. newList = TaskPriorityList(taskPriority)
  417. newList.add(task)
  418. # Add the new list to the beginning of the taskList
  419. self.taskList.insert(0, newList)
  420. break
  421. taskListPriority = self.taskList[index].getPriority()
  422. if (taskListPriority == taskPriority):
  423. self.taskList[index].add(task)
  424. break
  425. elif (taskListPriority > taskPriority):
  426. index = index - 1
  427. elif (taskListPriority < taskPriority):
  428. # Time to insert
  429. newList = TaskPriorityList(taskPriority)
  430. newList.add(task)
  431. # Insert this new priority level
  432. # If we are already at the end, just append it
  433. if (index == len(self.taskList)-1):
  434. self.taskList.append(newList)
  435. else:
  436. # Otherwise insert it
  437. self.taskList.insert(index+1, newList)
  438. break
  439. if __debug__:
  440. if self.pStatsTasks and task.name != "igloop":
  441. # Get the PStats name for the task. By convention,
  442. # this is everything until the first hyphen; the part
  443. # of the task name following the hyphen is generally
  444. # used to differentiate particular tasks that do the
  445. # same thing to different objects.
  446. name = task.name
  447. hyphen = name.find('-')
  448. if hyphen >= 0:
  449. name = name[0:hyphen]
  450. task.setupPStats(name)
  451. if self.fVerbose:
  452. # Alert the world, a new task is born!
  453. messenger.send('TaskManager-spawnTask', sentArgs = [task, task.name, index])
  454. return task
  455. def remove(self, taskOrName):
  456. if type(taskOrName) == type(''):
  457. return self.__removeTasksNamed(taskOrName)
  458. elif isinstance(taskOrName, Task):
  459. return self.__removeTasksEqual(taskOrName)
  460. else:
  461. self.notify.error('remove takes a string or a Task')
  462. def removeTasksMatching(self, taskPattern):
  463. """removeTasksMatching(self, string taskPattern)
  464. Removes tasks whose names match the pattern, which can include
  465. standard shell globbing characters like *, ?, and [].
  466. """
  467. assert(TaskManager.notify.debug('removing tasks matching: ' + taskPattern))
  468. num = 0
  469. keyList = filter(lambda key: fnmatch.fnmatchcase(key, taskPattern), self.nameDict.keys())
  470. for key in keyList:
  471. num += self.__removeTasksNamed(key)
  472. return num
  473. def __removeTasksEqual(self, task):
  474. # Remove this task from the nameDict (should be a short list)
  475. if self.__removeTaskFromNameDict(task):
  476. assert(TaskManager.notify.debug('__removeTasksEqual: removing task: %s' % (task)))
  477. # Flag the task for removal from the real list
  478. task.remove()
  479. if task.isOnDoLaterList():
  480. self.doLaterList.remove(task)
  481. # Cleanup stuff
  482. task.finishTask(self.fVerbose)
  483. return 1
  484. else:
  485. return 0
  486. def __removeTasksNamed(self, taskName):
  487. if not self.nameDict.has_key(taskName):
  488. return 0
  489. assert(TaskManager.notify.debug('__removeTasksNamed: removing tasks named: %s' % (taskName)))
  490. for task in self.nameDict[taskName]:
  491. # Flag for removal
  492. task.remove()
  493. if task.isOnDoLaterList():
  494. self.doLaterList.remove(task)
  495. # Cleanup stuff
  496. task.finishTask(self.fVerbose)
  497. # Record the number of tasks removed
  498. num = len(self.nameDict[taskName])
  499. # Blow away the nameDict entry completely
  500. del self.nameDict[taskName]
  501. return num
  502. def __removeTaskFromNameDict(self, task):
  503. taskName = task.name
  504. # If this is the only task with that name, remove the dict entry
  505. tasksWithName = self.nameDict.get(taskName)
  506. if tasksWithName:
  507. if task in tasksWithName:
  508. tasksWithName.remove(task)
  509. if len(tasksWithName) == 0:
  510. del self.nameDict[taskName]
  511. return 1
  512. return 0
  513. def __executeTask(self, task):
  514. task.setCurrentTimeFrame(self.currentTime, self.currentFrame)
  515. if not self.taskTimerVerbose:
  516. # don't record timing info
  517. ret = task(task)
  518. else:
  519. # Run the task and check the return value
  520. if task.pstats:
  521. task.pstats.start()
  522. startTime = globalClock.getRealTime()
  523. ret = task(task)
  524. endTime = globalClock.getRealTime()
  525. if task.pstats:
  526. task.pstats.stop()
  527. # Record the dt
  528. dt = endTime - startTime
  529. task.dt = dt
  530. # See if this is the new max
  531. if dt > task.maxDt:
  532. task.maxDt = dt
  533. # Record the running total of all dts so we can compute an average
  534. task.runningTotal = task.runningTotal + dt
  535. if (task.frame > 0):
  536. task.avgDt = (task.runningTotal / task.frame)
  537. else:
  538. task.avgDt = 0
  539. return ret
  540. def __stepThroughList(self, taskPriList):
  541. # Traverse the taskPriList with an iterator
  542. i = 0
  543. while (i < len(taskPriList)):
  544. task = taskPriList[i]
  545. # See if we are at the end of the real tasks
  546. if task is None:
  547. break
  548. # See if this task has been removed in show code
  549. if task.isRemoved():
  550. assert(TaskManager.notify.debug('__stepThroughList: task is flagged for removal %s' % (task)))
  551. task.finishTask(self.fVerbose)
  552. taskPriList.remove(i)
  553. # Do not increment the iterator
  554. continue
  555. # Now actually execute the task
  556. ret = self.__executeTask(task)
  557. # See if the task is done
  558. if (ret == cont):
  559. # Leave it for next frame, its not done yet
  560. pass
  561. elif ((ret == done) or (ret == exit)):
  562. assert(TaskManager.notify.debug('__stepThroughList: task is finished %s' % (task)))
  563. # Remove the task
  564. if not task.isRemoved():
  565. assert(TaskManager.notify.debug('__stepThroughList: task not removed %s' % (task)))
  566. task.remove()
  567. # Note: Should not need to remove from doLaterList here because
  568. # this task is not in the doLaterList
  569. task.finishTask(self.fVerbose)
  570. self.__removeTaskFromNameDict(task)
  571. else:
  572. assert(TaskManager.notify.debug('__stepThroughList: task already removed %s' % (task)))
  573. self.__removeTaskFromNameDict(task)
  574. taskPriList.remove(i)
  575. # Do not increment the iterator
  576. continue
  577. else:
  578. raise StandardError, "Task named %s did not return cont, exit, or done" % task.name
  579. # Move to the next element
  580. i += 1
  581. def __addPendingTasksToTaskList(self):
  582. # Now that we are all done, add any left over pendingTasks generated in
  583. # priority levels lower or higher than where we were when we iterated
  584. for taskList in self.pendingTaskDict.values():
  585. for task in taskList:
  586. if (task and not task.isRemoved()):
  587. assert(TaskManager.notify.debug('step: moving %s from pending to taskList' % (task.name)))
  588. self.__addNewTask(task)
  589. self.pendingTaskDict.clear()
  590. def step(self):
  591. assert(TaskManager.notify.debug('step: begin'))
  592. self.currentTime, self.currentFrame = self.__getTimeFrame()
  593. # Replace keyboard interrupt handler during task list processing
  594. # so we catch the keyboard interrupt but don't handle it until
  595. # after task list processing is complete.
  596. self.fKeyboardInterrupt = 0
  597. self.interruptCount = 0
  598. signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
  599. # Traverse the task list in order because it is in priority order
  600. priIndex = 0
  601. while priIndex < len(self.taskList):
  602. taskPriList = self.taskList[priIndex]
  603. pri = taskPriList.getPriority()
  604. assert(TaskManager.notify.debug('step: running through taskList at pri: %s, priIndex: %s' % (pri, priIndex)))
  605. self.__stepThroughList(taskPriList)
  606. # Now see if that generated any pending tasks for this taskPriList
  607. pendingTasks = self.pendingTaskDict.get(pri, [])
  608. while pendingTasks:
  609. assert(TaskManager.notify.debug('step: running through pending tasks at pri: %s' % (pri)))
  610. # Remove them from the pendingTaskDict
  611. del self.pendingTaskDict[pri]
  612. # Execute them
  613. self.__stepThroughList(pendingTasks)
  614. # Add these to the real taskList
  615. for task in pendingTasks:
  616. if (task and not task.isRemoved()):
  617. assert(TaskManager.notify.debug('step: moving %s from pending to taskList' % (task.name)))
  618. self.__addNewTask(task)
  619. # See if we generated any more for this pri level
  620. pendingTasks = self.pendingTaskDict.get(pri, [])
  621. # Any new tasks that were made pending should be converted
  622. # to real tasks now in case they need to run this frame at a
  623. # later priority level
  624. self.__addPendingTasksToTaskList()
  625. # Go to the next priority level
  626. priIndex += 1
  627. # Add new pending tasks
  628. self.__addPendingTasksToTaskList()
  629. # Restore default interrupt handler
  630. signal.signal(signal.SIGINT, signal.default_int_handler)
  631. if self.fKeyboardInterrupt:
  632. raise KeyboardInterrupt
  633. return
  634. def run(self):
  635. # Set the clock to have last frame's time in case we were
  636. # Paused at the prompt for a long time
  637. t = globalClock.getFrameTime()
  638. timeDelta = t - globalClock.getRealTime()
  639. globalClock.setRealTime(t)
  640. messenger.send("resetClock", [timeDelta])
  641. if self.resumeFunc != None:
  642. self.resumeFunc()
  643. if self.stepping:
  644. self.step()
  645. else:
  646. self.running = 1
  647. while self.running:
  648. try:
  649. self.step()
  650. except KeyboardInterrupt:
  651. self.stop()
  652. except:
  653. if self.extendedExceptions:
  654. self.stop()
  655. print_exc_plus()
  656. else:
  657. raise
  658. def stop(self):
  659. # Set a flag so we will stop before beginning next frame
  660. self.running = 0
  661. def replaceMethod(self, oldMethod, newFunction):
  662. import new
  663. for taskPriList in self.taskList:
  664. for task in taskPriList:
  665. if task is None:
  666. break
  667. method = task.__call__
  668. if (type(method) == types.MethodType):
  669. function = method.im_func
  670. else:
  671. function = method
  672. #print ('function: ' + `function` + '\n' +
  673. # 'method: ' + `method` + '\n' +
  674. # 'oldMethod: ' + `oldMethod` + '\n' +
  675. # 'newFunction: ' + `newFunction` + '\n')
  676. if (function == oldMethod):
  677. newMethod = new.instancemethod(newFunction,
  678. method.im_self,
  679. method.im_class)
  680. task.__call__ = newMethod
  681. # Found it return true
  682. return 1
  683. return 0
  684. def __repr__(self):
  685. taskNameWidth = 32
  686. dtWidth = 10
  687. priorityWidth = 10
  688. totalDt = 0
  689. totalAvgDt = 0
  690. str = ('taskList'.ljust(taskNameWidth)
  691. + 'dt(ms)'.rjust(dtWidth)
  692. + 'avg'.rjust(dtWidth)
  693. + 'max'.rjust(dtWidth)
  694. + 'priority'.rjust(priorityWidth)
  695. + '\n')
  696. str = str + '---------------------------------------------------------------\n'
  697. for taskPriList in self.taskList:
  698. priority = `taskPriList.getPriority()`
  699. for task in taskPriList:
  700. if task is None:
  701. break
  702. totalDt = totalDt + task.dt
  703. totalAvgDt = totalAvgDt + task.avgDt
  704. if task.isRemoved():
  705. taskName = '(R)' + task.name
  706. else:
  707. taskName = task.name
  708. if (self.taskTimerVerbose):
  709. import fpformat
  710. str = str + (taskName.ljust(taskNameWidth)
  711. + fpformat.fix(task.dt*1000, 2).rjust(dtWidth)
  712. + fpformat.fix(task.avgDt*1000, 2).rjust(dtWidth)
  713. + fpformat.fix(task.maxDt*1000, 2).rjust(dtWidth)
  714. + priority.rjust(priorityWidth)
  715. + '\n')
  716. else:
  717. str = str + (task.name.ljust(taskNameWidth)
  718. + '----'.rjust(dtWidth)
  719. + '----'.rjust(dtWidth)
  720. + '----'.rjust(dtWidth)
  721. + priority.rjust(priorityWidth)
  722. + '\n')
  723. str = str + '---------------------------------------------------------------\n'
  724. str = str + ' pendingTasks\n'
  725. str = str + '---------------------------------------------------------------\n'
  726. for pri, taskList in self.pendingTaskDict.items():
  727. for task in taskList:
  728. remainingTime = ((task.starttime) - self.currentTime)
  729. if task.isRemoved():
  730. taskName = '(PR)' + task.name
  731. else:
  732. taskName = '(P)' + task.name
  733. if (self.taskTimerVerbose):
  734. import fpformat
  735. str = str + (' ' + taskName.ljust(taskNameWidth-2)
  736. + fpformat.fix(pri, 2).rjust(dtWidth)
  737. + '\n')
  738. else:
  739. str = str + (' ' + taskName.ljust(taskNameWidth-2)
  740. + '----'.rjust(dtWidth)
  741. + '\n')
  742. str = str + '---------------------------------------------------------------\n'
  743. str = str + ' doLaterList\n'
  744. str = str + '---------------------------------------------------------------\n'
  745. for task in self.doLaterList:
  746. remainingTime = ((task.wakeTime) - self.currentTime)
  747. if task.isRemoved():
  748. taskName = '(R)' + task.name
  749. else:
  750. taskName = task.name
  751. if (self.taskTimerVerbose):
  752. import fpformat
  753. str = str + (' ' + taskName.ljust(taskNameWidth-2)
  754. + fpformat.fix(remainingTime, 2).rjust(dtWidth)
  755. + '\n')
  756. else:
  757. str = str + (' ' + taskName.ljust(taskNameWidth-2)
  758. + '----'.rjust(dtWidth)
  759. + '\n')
  760. str = str + '---------------------------------------------------------------\n'
  761. if (self.taskTimerVerbose):
  762. import fpformat
  763. str = str + ('total'.ljust(taskNameWidth)
  764. + fpformat.fix(totalDt*1000, 2).rjust(dtWidth)
  765. + fpformat.fix(totalAvgDt*1000, 2).rjust(dtWidth)
  766. + '\n')
  767. else:
  768. str = str + ('total'.ljust(taskNameWidth)
  769. + '----'.rjust(dtWidth)
  770. + '----'.rjust(dtWidth)
  771. + '\n')
  772. return str
  773. def resetStats(self):
  774. # WARNING: this screws up your do-later timings
  775. for task in self.taskList:
  776. task.dt = 0
  777. task.avgDt = 0
  778. task.maxDt = 0
  779. task.runningTotal = 0
  780. task.setStartTimeFrame(self.currentTime, self.currentFrame)
  781. def popupControls(self):
  782. import TaskManagerPanel
  783. return TaskManagerPanel.TaskManagerPanel(self)
  784. def __getTimeFrame(self):
  785. # WARNING: If you are testing tasks without an igloop,
  786. # you must manually tick the clock
  787. # Ask for the time last frame
  788. return globalClock.getFrameTime(), globalClock.getFrameCount()
  789. """
  790. import Task
  791. def goo(task):
  792. print 'goo'
  793. return Task.done
  794. def bar(task):
  795. print 'bar'
  796. taskMgr.add(goo, 'goo')
  797. return Task.done
  798. def foo(task):
  799. print 'foo'
  800. taskMgr.add(bar, 'bar')
  801. return Task.done
  802. taskMgr.add(foo, 'foo')
  803. """