Task.py 65 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785
  1. """Undocumented Module"""
  2. __all__ = ['Task', 'TaskPriorityList', 'TaskManager']
  3. # This module may not import pandac.PandaModules, since it is imported
  4. # by the Toontown Launcher before the complete PandaModules have been
  5. # downloaded. Instead, it imports only libpandaexpressModules, the
  6. # subset of PandaModules that we know is available immediately.
  7. # Methods that require more advanced C++ methods may import the
  8. # appropriate files within their own scope.
  9. from pandac.libpandaexpressModules import *
  10. from direct.directnotify.DirectNotifyGlobal import *
  11. from direct.showbase.PythonUtil import *
  12. from direct.showbase.MessengerGlobal import *
  13. import time
  14. import fnmatch
  15. import string
  16. import signal
  17. try:
  18. Dtool_PreloadDLL("libp3heapq")
  19. from libp3heapq import heappush, heappop, heapify
  20. except:
  21. Dtool_PreloadDLL("libheapq")
  22. from libheapq import heappush, heappop, heapify
  23. import types
  24. if __debug__:
  25. # For pstats
  26. from pandac.PandaModules import PStatCollector
  27. def print_exc_plus():
  28. """
  29. Print the usual traceback information, followed by a listing of all the
  30. local variables in each frame.
  31. """
  32. import sys
  33. import traceback
  34. tb = sys.exc_info()[2]
  35. while 1:
  36. if not tb.tb_next:
  37. break
  38. tb = tb.tb_next
  39. stack = []
  40. f = tb.tb_frame
  41. while f:
  42. stack.append(f)
  43. f = f.f_back
  44. stack.reverse()
  45. traceback.print_exc()
  46. print "Locals by frame, innermost last"
  47. for frame in stack:
  48. print
  49. print "Frame %s in %s at line %s" % (frame.f_code.co_name,
  50. frame.f_code.co_filename,
  51. frame.f_lineno)
  52. for key, value in frame.f_locals.items():
  53. print "\t%20s = " % key,
  54. #We have to be careful not to cause a new error in our error
  55. #printer! Calling str() on an unknown object could cause an
  56. #error we don't want.
  57. try:
  58. print value
  59. except:
  60. print "<ERROR WHILE PRINTING VALUE>"
  61. class Task:
  62. # This enum is a copy of the one at the top-level.
  63. exit = -1
  64. done = 0
  65. cont = 1
  66. again = 2
  67. count = 0
  68. def __init__(self, callback, priority = 0):
  69. try:
  70. config
  71. except:
  72. pass
  73. else:
  74. if config.GetBool('record-task-creation-stack', 0):
  75. self.debugInitTraceback = StackTrace("Task "+str(callback), 1, 10)
  76. # Unique ID for each task
  77. self.id = Task.count
  78. Task.count += 1
  79. #set to have the task managed
  80. self.owner = None
  81. self.__call__ = callback
  82. self._priority = priority
  83. self._removed = 0
  84. self.dt = 0.0
  85. if TaskManager.taskTimerVerbose:
  86. self.avgDt = 0.0
  87. self.maxDt = 0.0
  88. self.runningTotal = 0.0
  89. self.pstats = None
  90. self.pstatCollector = None
  91. self.extraArgs = []
  92. # Used for doLaters
  93. self.wakeTime = 0.0
  94. # for repeating doLaters
  95. self.delayTime = 0.0
  96. self.time = 0.0
  97. # # Used for putting into the doLaterList
  98. # # the heapq calls __cmp__ via the rich compare function
  99. # def __cmp__(self, other):
  100. # if isinstance(other, Task):
  101. # if self.wakeTime < other.wakeTime:
  102. # return -1
  103. # elif self.wakeTime > other.wakeTime:
  104. # return 1
  105. # # If the wakeTimes happen to be the same, just
  106. # # sort them based on id
  107. # else:
  108. # return cmp(id(self), id(other))
  109. # # This is important for people doing a (task != None) and such.
  110. # else:
  111. # return cmp(id(self), id(other))
  112. # # According to the Python manual (3.3.1), if you define a cmp operator
  113. # # you should also define a hash operator or your objects will not be
  114. # # usable in dictionaries. Since no two task objects are unique, we can
  115. # # just return the unique id.
  116. # def __hash__(self):
  117. # return self.id
  118. def remove(self):
  119. if not self._removed:
  120. if(self.owner):
  121. self.owner._clearTask(self)
  122. self._removed = 1
  123. # Remove any refs to real objects
  124. # In case we hang around the doLaterList for a while
  125. del self.__call__
  126. del self.extraArgs
  127. if TaskManager.taskTimerVerbose and self.pstatCollector:
  128. self.pstatCollector.subLevelNow(1)
  129. def isRemoved(self):
  130. return self._removed
  131. def getPriority(self):
  132. return self._priority
  133. def setPriority(self, pri):
  134. self._priority = pri
  135. def setStartTimeFrame(self, startTime, startFrame):
  136. self.starttime = startTime
  137. self.startframe = startFrame
  138. def setCurrentTimeFrame(self, currentTime, currentFrame):
  139. # Calculate and store this task's time (relative to when it started)
  140. self.time = currentTime - self.starttime
  141. self.frame = currentFrame - self.startframe
  142. def setupPStats(self):
  143. if __debug__ and TaskManager.taskTimerVerbose and not self.pstats:
  144. # Get the PStats name for the task. By convention,
  145. # this is everything until the first hyphen; the part
  146. # of the task name following the hyphen is generally
  147. # used to differentiate particular tasks that do the
  148. # same thing to different objects.
  149. name = self.name
  150. hyphen = name.find('-')
  151. if hyphen >= 0:
  152. name = name[0:hyphen]
  153. self.pstats = PStatCollector("App:Show code:" + name)
  154. if self.wakeTime or self.delayTime:
  155. self.pstatCollector = PStatCollector("Tasks:doLaters:" + name)
  156. else:
  157. self.pstatCollector = PStatCollector("Tasks:" + name)
  158. self.pstatCollector.addLevelNow(1)
  159. def finishTask(self, verbose):
  160. if hasattr(self, "uponDeath"):
  161. self.uponDeath(self)
  162. if verbose:
  163. # We regret to announce...
  164. messenger.send('TaskManager-removeTask', sentArgs = [self, self.name])
  165. del self.uponDeath
  166. def __repr__(self):
  167. if hasattr(self, 'name'):
  168. return ('Task id: %s, name %s' % (self.id, self.name))
  169. else:
  170. return ('Task id: %s, no name' % (self.id))
  171. def pause(delayTime):
  172. def func(self):
  173. if (self.time < self.delayTime):
  174. return cont
  175. else:
  176. return done
  177. task = Task(func)
  178. task.name = 'pause'
  179. task.delayTime = delayTime
  180. return task
  181. Task.pause = staticmethod(pause)
  182. def sequence(*taskList):
  183. return make_sequence(taskList)
  184. Task.sequence = staticmethod(sequence)
  185. def make_sequence(taskList):
  186. def func(self):
  187. frameFinished = 0
  188. taskDoneStatus = -1
  189. while not frameFinished:
  190. task = self.taskList[self.index]
  191. # If this is a new task, set its start time and frame
  192. if self.index > self.prevIndex:
  193. task.setStartTimeFrame(self.time, self.frame)
  194. self.prevIndex = self.index
  195. # Calculate this task's time since it started
  196. task.setCurrentTimeFrame(self.time, self.frame)
  197. # Execute the current task
  198. ret = task(task)
  199. # Check the return value from the task
  200. if ret == cont:
  201. # If this current task wants to continue,
  202. # come back to it next frame
  203. taskDoneStatus = cont
  204. frameFinished = 1
  205. elif ret == done:
  206. # If this task is done, increment the index so that next frame
  207. # we will start executing the next task on the list
  208. self.index = self.index + 1
  209. taskDoneStatus = cont
  210. frameFinished = 0
  211. elif ret == exit:
  212. # If this task wants to exit, the sequence exits
  213. taskDoneStatus = exit
  214. frameFinished = 1
  215. # If we got to the end of the list, this sequence is done
  216. if self.index >= len(self.taskList):
  217. # TaskManager.notify.debug('sequence done: ' + self.name)
  218. frameFinished = 1
  219. taskDoneStatus = done
  220. return taskDoneStatus
  221. task = Task(func)
  222. task.name = 'sequence'
  223. task.taskList = taskList
  224. task.prevIndex = -1
  225. task.index = 0
  226. return task
  227. def resetSequence(task):
  228. # Should this automatically be done as part of spawnTaskNamed?
  229. # Or should one have to create a new task instance every time
  230. # one wishes to spawn a task (currently sequences and can
  231. # only be fired off once
  232. task.index = 0
  233. task.prevIndex = -1
  234. def loop(*taskList):
  235. return make_loop(taskList)
  236. Task.loop = staticmethod(loop)
  237. def make_loop(taskList):
  238. def func(self):
  239. frameFinished = 0
  240. taskDoneStatus = -1
  241. while (not frameFinished):
  242. task = self.taskList[self.index]
  243. # If this is a new task, set its start time and frame
  244. if (self.index > self.prevIndex):
  245. task.setStartTimeFrame(self.time, self.frame)
  246. self.prevIndex = self.index
  247. # Calculate this task's time since it started
  248. task.setCurrentTimeFrame(self.time, self.frame)
  249. # Execute the current task
  250. ret = task(task)
  251. # Check the return value from the task
  252. if (ret == cont):
  253. # If this current task wants to continue,
  254. # come back to it next frame
  255. taskDoneStatus = cont
  256. frameFinished = 1
  257. elif (ret == done):
  258. # If this task is done, increment the index so that next frame
  259. # we will start executing the next task on the list
  260. # TODO: we should go to the next frame now
  261. self.index = self.index + 1
  262. taskDoneStatus = cont
  263. frameFinished = 0
  264. elif (ret == exit):
  265. # If this task wants to exit, the sequence exits
  266. taskDoneStatus = exit
  267. frameFinished = 1
  268. if (self.index >= len(self.taskList)):
  269. # If we got to the end of the list, wrap back around
  270. self.prevIndex = -1
  271. self.index = 0
  272. frameFinished = 1
  273. return taskDoneStatus
  274. task = Task(func)
  275. task.name = 'loop'
  276. task.taskList = taskList
  277. task.prevIndex = -1
  278. task.index = 0
  279. return task
  280. class TaskPriorityList(list):
  281. def __init__(self, priority):
  282. self._priority = priority
  283. self.__emptyIndex = 0
  284. def getPriority(self):
  285. return self._priority
  286. def add(self, task):
  287. if (self.__emptyIndex >= len(self)):
  288. self.append(task)
  289. self.__emptyIndex += 1
  290. else:
  291. self[self.__emptyIndex] = task
  292. self.__emptyIndex += 1
  293. def remove(self, i):
  294. assert i <= len(self)
  295. if (len(self) == 1) and (i == 1):
  296. self[i] = None
  297. self.__emptyIndex = 0
  298. else:
  299. # Swap the last element for this one
  300. lastElement = self[self.__emptyIndex-1]
  301. self[i] = lastElement
  302. self[self.__emptyIndex-1] = None
  303. self.__emptyIndex -= 1
  304. class TaskManager:
  305. # These class vars are generally overwritten by Config variables which
  306. # are read in at the start of a show (ShowBase.py or AIStart.py)
  307. notify = None
  308. # TODO: there is a bit of a bug when you default this to 0. The first
  309. # task we make, the doLaterProcessor, needs to have this set to 1 or
  310. # else we get an error.
  311. taskTimerVerbose = 1
  312. extendedExceptions = 0
  313. pStatsTasks = 0
  314. doLaterCleanupCounter = 2000
  315. OsdPrefix = 'task.'
  316. DefTaskDurationWarningThreshold = 3.
  317. def __init__(self):
  318. self.running = 0
  319. self.stepping = 0
  320. self.taskList = []
  321. # Dictionary of priority to newTaskLists
  322. self.pendingTaskDict = {}
  323. # List of tasks scheduled to execute in the future
  324. self.__doLaterList = []
  325. self._profileFrames = False
  326. self.MaxEpockSpeed = 1.0/30.0;
  327. # We copy this value in from __builtins__ when it gets set.
  328. # But since the TaskManager might have to run before it gets
  329. # set--before it can even be available--we also have to have
  330. # special-case code that handles the possibility that we don't
  331. # have a globalClock yet.
  332. self.globalClock = None
  333. # To help cope with the possibly-missing globalClock, we get a
  334. # handle to Panda's low-level TrueClock object for measuring
  335. # small intervals.
  336. self.trueClock = TrueClock.getGlobalPtr()
  337. # We don't have a base yet, but we can query the config
  338. # variables directly.
  339. self.warnTaskDuration = ConfigVariableBool('task-duration-warnings', 1).getValue()
  340. self.taskDurationWarningThreshold = ConfigVariableDouble(
  341. 'task-duration-warning-threshold',
  342. TaskManager.DefTaskDurationWarningThreshold).getValue()
  343. self.currentTime, self.currentFrame = self.__getTimeFrame()
  344. if (TaskManager.notify == None):
  345. TaskManager.notify = directNotify.newCategory("TaskManager")
  346. self.fKeyboardInterrupt = 0
  347. self.interruptCount = 0
  348. self.resumeFunc = None
  349. self.fVerbose = 0
  350. # Dictionary of task name to list of tasks with that name
  351. self.nameDict = {}
  352. # A default task.
  353. self.add(self.__doLaterProcessor, "doLaterProcessor", -10)
  354. def destroy(self):
  355. del self.nameDict
  356. del self.trueClock
  357. del self.globalClock
  358. del self.__doLaterList
  359. del self.pendingTaskDict
  360. del self.taskList
  361. def setStepping(self, value):
  362. self.stepping = value
  363. def setVerbose(self, value):
  364. self.fVerbose = value
  365. messenger.send('TaskManager-setVerbose', sentArgs = [value])
  366. def invokeDefaultHandler(self, signalNumber, stackFrame):
  367. print '*** allowing mid-frame keyboard interrupt.'
  368. # Restore default interrupt handler
  369. signal.signal(signal.SIGINT, signal.default_int_handler)
  370. # and invoke it
  371. raise KeyboardInterrupt
  372. def keyboardInterruptHandler(self, signalNumber, stackFrame):
  373. self.fKeyboardInterrupt = 1
  374. self.interruptCount += 1
  375. if self.interruptCount == 1:
  376. print '* interrupt by keyboard'
  377. elif self.interruptCount == 2:
  378. print '** waiting for end of frame before interrupting...'
  379. # The user must really want to interrupt this process
  380. # Next time around invoke the default handler
  381. signal.signal(signal.SIGINT, self.invokeDefaultHandler)
  382. def hasTaskNamed(self, taskName):
  383. # TODO: check pending task list
  384. # Get the tasks with this name
  385. # If we found some, see if any of them are still active (not removed)
  386. for task in self.nameDict.get(taskName, []):
  387. if not task._removed:
  388. return 1
  389. # Didnt find any, return 0
  390. return 0
  391. def getTasksNamed(self, taskName):
  392. # TODO: check pending tasks
  393. # Get the tasks with this name
  394. return [task for task in self.nameDict.get(taskName, []) #grab all tasks with name
  395. if not task._removed] #filter removed tasks
  396. def __doLaterFilter(self):
  397. # Filter out all the tasks that have been removed like a mark and
  398. # sweep garbage collector. Returns the number of tasks that have
  399. # been removed Warning: this creates an entirely new doLaterList.
  400. oldLen = len(self.__doLaterList)
  401. # grab all the tasks being removed so we can remove them from the nameDict
  402. # TODO: would be more efficient to remove from nameDict in task.remove()
  403. removedTasks = [task for task in self.__doLaterList
  404. if task._removed]
  405. self.__doLaterList = [task for task in self.__doLaterList #grab all tasks with name
  406. if not task._removed] #filter removed tasks
  407. for task in removedTasks:
  408. self.__removeTaskFromNameDict(task)
  409. # Re heapify to maintain ordering after filter
  410. heapify(self.__doLaterList)
  411. newLen = len(self.__doLaterList)
  412. return oldLen - newLen
  413. def __getNextDoLaterTime(self):
  414. if self.__doLaterList:
  415. dl = self.__doLaterList[0]
  416. return dl.wakeTime
  417. return -1;
  418. def __doLaterProcessor(self, task):
  419. # Removing the tasks during the for loop is a bad idea
  420. # Instead we just flag them as removed
  421. # Later, somebody else cleans them out
  422. currentTime = self.__getTime()
  423. while self.__doLaterList:
  424. # Check the first one on the list to see if it is ready
  425. dl = self.__doLaterList[0]
  426. if dl._removed:
  427. # Get rid of this task forever
  428. heappop(self.__doLaterList)
  429. continue
  430. # If the time now is less than the start of the doLater + delay
  431. # then we are not ready yet, continue to next one
  432. elif currentTime < dl.wakeTime:
  433. # Since the list is sorted, the first one we get to, that
  434. # is not ready to go, we can return
  435. break
  436. else:
  437. # Take it off the doLaterList, set its time, and make
  438. # it pending
  439. heappop(self.__doLaterList)
  440. dl.setStartTimeFrame(self.currentTime, self.currentFrame)
  441. self.__addPendingTask(dl)
  442. continue
  443. # Every nth pass, let's clean out the list of removed tasks
  444. # This is basically a mark and sweep garbage collection of doLaters
  445. if ((task.frame % self.doLaterCleanupCounter) == 0):
  446. numRemoved = self.__doLaterFilter()
  447. # TaskManager.notify.debug("filtered %s removed doLaters" % numRemoved)
  448. return cont
  449. def doMethodLater(self, delayTime, funcOrTask, name, extraArgs=None,
  450. priority=0, uponDeath=None, appendTask=False, owner = None):
  451. if delayTime < 0:
  452. assert self.notify.warning('doMethodLater: added task: %s with negative delay: %s' % (name, delayTime))
  453. if isinstance(funcOrTask, Task):
  454. task = funcOrTask
  455. elif callable(funcOrTask):
  456. task = Task(funcOrTask, priority)
  457. else:
  458. self.notify.error('doMethodLater: Tried to add a task that was not a Task or a func')
  459. assert isinstance(name, str), 'Name must be a string type'
  460. task.setPriority(priority)
  461. task.name = name
  462. task.owner = owner
  463. if extraArgs == None:
  464. extraArgs = []
  465. appendTask = True
  466. # if told to, append the task object to the extra args list so the
  467. # method called will be able to access any properties on the task
  468. if appendTask:
  469. extraArgs.append(task)
  470. task.extraArgs = extraArgs
  471. if uponDeath:
  472. task.uponDeath = uponDeath
  473. # TaskManager.notify.debug('spawning doLater: %s' % (task))
  474. # Add this task to the nameDict
  475. nameList = self.nameDict.get(name)
  476. if nameList:
  477. nameList.append(task)
  478. else:
  479. self.nameDict[name] = [task]
  480. currentTime = self.__getTime()
  481. # Cache the time we should wake up for easier sorting
  482. task.delayTime = delayTime
  483. task.wakeTime = currentTime + delayTime
  484. # Push this onto the doLaterList. The heap maintains the sorting.
  485. heappush(self.__doLaterList, task)
  486. if self.fVerbose:
  487. # Alert the world, a new task is born!
  488. messenger.send('TaskManager-spawnDoLater',
  489. sentArgs = [task, task.name, task.id])
  490. return task
  491. def add(self, funcOrTask, name, priority=0, extraArgs=None, uponDeath=None,
  492. appendTask = False, owner = None):
  493. """
  494. Add a new task to the taskMgr.
  495. You can add a Task object or a method that takes one argument.
  496. """
  497. # TaskManager.notify.debug('add: %s' % (name))
  498. if isinstance(funcOrTask, Task):
  499. task = funcOrTask
  500. elif callable(funcOrTask):
  501. task = Task(funcOrTask, priority)
  502. else:
  503. self.notify.error(
  504. 'add: Tried to add a task that was not a Task or a func')
  505. assert isinstance(name, str), 'Name must be a string type'
  506. task.setPriority(priority)
  507. task.name = name
  508. task.owner = owner
  509. if extraArgs == None:
  510. extraArgs = []
  511. appendTask = True
  512. # if told to, append the task object to the extra args list so the
  513. # method called will be able to access any properties on the task
  514. if appendTask:
  515. extraArgs.append(task)
  516. task.extraArgs = extraArgs
  517. if uponDeath:
  518. task.uponDeath = uponDeath
  519. currentTime = self.__getTime()
  520. task.setStartTimeFrame(currentTime, self.currentFrame)
  521. nameList = self.nameDict.get(name)
  522. if nameList:
  523. nameList.append(task)
  524. else:
  525. self.nameDict[name] = [task]
  526. # Put it on the list for the end of this frame
  527. self.__addPendingTask(task)
  528. return task
  529. def __addPendingTask(self, task):
  530. # TaskManager.notify.debug('__addPendingTask: %s' % (task.name))
  531. pri = task._priority
  532. taskPriList = self.pendingTaskDict.get(pri)
  533. if not taskPriList:
  534. taskPriList = TaskPriorityList(pri)
  535. self.pendingTaskDict[pri] = taskPriList
  536. taskPriList.add(task)
  537. def __addNewTask(self, task):
  538. # The taskList is really an ordered list of TaskPriorityLists
  539. # search back from the end of the list until we find a
  540. # taskList with a lower priority, or we hit the start of the list
  541. taskPriority = task._priority
  542. index = len(self.taskList) - 1
  543. while (1):
  544. if (index < 0):
  545. newList = TaskPriorityList(taskPriority)
  546. newList.add(task)
  547. # Add the new list to the beginning of the taskList
  548. self.taskList.insert(0, newList)
  549. break
  550. taskListPriority = self.taskList[index]._priority
  551. if (taskListPriority == taskPriority):
  552. self.taskList[index].add(task)
  553. break
  554. elif (taskListPriority > taskPriority):
  555. index = index - 1
  556. elif (taskListPriority < taskPriority):
  557. # Time to insert
  558. newList = TaskPriorityList(taskPriority)
  559. newList.add(task)
  560. # Insert this new priority level
  561. # If we are already at the end, just append it
  562. if (index == len(self.taskList)-1):
  563. self.taskList.append(newList)
  564. else:
  565. # Otherwise insert it
  566. self.taskList.insert(index+1, newList)
  567. break
  568. if __debug__:
  569. if self.pStatsTasks and task.name != "igLoop":
  570. task.setupPStats()
  571. if self.fVerbose:
  572. # Alert the world, a new task is born!
  573. messenger.send(
  574. 'TaskManager-spawnTask', sentArgs = [task, task.name, index])
  575. return task
  576. def remove(self, taskOrName):
  577. if type(taskOrName) == type(''):
  578. return self.__removeTasksNamed(taskOrName)
  579. elif isinstance(taskOrName, Task):
  580. return self.__removeTasksEqual(taskOrName)
  581. else:
  582. self.notify.error('remove takes a string or a Task')
  583. def removeTasksMatching(self, taskPattern):
  584. """removeTasksMatching(self, string taskPattern)
  585. Removes tasks whose names match the pattern, which can include
  586. standard shell globbing characters like *, ?, and [].
  587. """
  588. # TaskManager.notify.debug('removing tasks matching: ' + taskPattern)
  589. num = 0
  590. keyList = filter(
  591. lambda key: fnmatch.fnmatchcase(key, taskPattern),
  592. self.nameDict.keys())
  593. for key in keyList:
  594. num += self.__removeTasksNamed(key)
  595. return num
  596. def __removeTasksEqual(self, task):
  597. # Remove this task from the nameDict (should be a short list)
  598. if self.__removeTaskFromNameDict(task):
  599. # TaskManager.notify.debug(
  600. # '__removeTasksEqual: removing task: %s' % (task))
  601. # Flag the task for removal from the real list
  602. task.remove()
  603. task.finishTask(self.fVerbose)
  604. return 1
  605. else:
  606. return 0
  607. def __removeTasksNamed(self, taskName):
  608. tasks = self.nameDict.get(taskName)
  609. if not tasks:
  610. return 0
  611. # TaskManager.notify.debug(
  612. # '__removeTasksNamed: removing tasks named: %s' % (taskName))
  613. for task in tasks:
  614. # Flag for removal
  615. task.remove()
  616. task.finishTask(self.fVerbose)
  617. # Record the number of tasks removed
  618. num = len(tasks)
  619. # Blow away the nameDict entry completely
  620. del self.nameDict[taskName]
  621. return num
  622. def __removeTaskFromNameDict(self, task):
  623. taskName = task.name
  624. # If this is the only task with that name, remove the dict entry
  625. tasksWithName = self.nameDict.get(taskName)
  626. if tasksWithName:
  627. if task in tasksWithName:
  628. # If this is the last element, just remove the entry
  629. # from the dictionary
  630. if len(tasksWithName) == 1:
  631. del self.nameDict[taskName]
  632. else:
  633. tasksWithName.remove(task)
  634. return 1
  635. return 0
  636. def __executeTask(self, task):
  637. task.setCurrentTimeFrame(self.currentTime, self.currentFrame)
  638. if not self.taskTimerVerbose:
  639. startTime = self.trueClock.getShortTime()
  640. # don't record timing info
  641. ret = task(*task.extraArgs)
  642. endTime = self.trueClock.getShortTime()
  643. # Record the dt
  644. dt = endTime - startTime
  645. task.dt = dt
  646. else:
  647. # Run the task and check the return value
  648. if task.pstats:
  649. task.pstats.start()
  650. startTime = self.trueClock.getShortTime()
  651. ret = task(*task.extraArgs)
  652. endTime = self.trueClock.getShortTime()
  653. if task.pstats:
  654. task.pstats.stop()
  655. # Record the dt
  656. dt = endTime - startTime
  657. task.dt = dt
  658. # See if this is the new max
  659. if dt > task.maxDt:
  660. task.maxDt = dt
  661. # Record the running total of all dts so we can compute an average
  662. task.runningTotal = task.runningTotal + dt
  663. if (task.frame > 0):
  664. task.avgDt = (task.runningTotal / task.frame)
  665. else:
  666. task.avgDt = 0
  667. # warn if the task took too long
  668. if self.warnTaskDuration:
  669. if dt >= self.taskDurationWarningThreshold:
  670. assert TaskManager.notify.warning('task %s ran for %.2f seconds' % (
  671. task.name, dt))
  672. return ret
  673. def __repeatDoMethod(self, task):
  674. """
  675. Called when a task execute function returns Task.again because
  676. it wants the task to execute again after the same or a modified
  677. delay (set 'delayTime' on the task object to change the delay)
  678. """
  679. if (not task._removed):
  680. # be sure to ask the globalClock for the current frame time
  681. # rather than use a cached value; globalClock's frame time may
  682. # have been synced since the start of this frame
  683. currentTime = self.__getTime()
  684. # Cache the time we should wake up for easier sorting
  685. task.wakeTime = currentTime + task.delayTime
  686. # Push this onto the doLaterList. The heap maintains the sorting.
  687. heappush(self.__doLaterList, task)
  688. if self.fVerbose:
  689. # Alert the world, a new task is born!
  690. messenger.send('TaskManager-againDoLater',
  691. sentArgs = [task, task.name, task.id])
  692. def __stepThroughList(self, taskPriList):
  693. # Traverse the taskPriList with an iterator
  694. i = 0
  695. while (i < len(taskPriList)):
  696. task = taskPriList[i]
  697. # See if we are at the end of the real tasks
  698. if task is None:
  699. break
  700. # See if this task has been removed in show code
  701. if task._removed:
  702. # assert TaskManager.notify.debug(
  703. # '__stepThroughList: task is flagged for removal %s' % (task))
  704. # If it was removed in show code, it will need finishTask run
  705. # If it was removed by the taskMgr, it will not, but that is ok
  706. # because finishTask is safe to call twice
  707. task.finishTask(self.fVerbose)
  708. taskPriList.remove(i)
  709. self.__removeTaskFromNameDict(task)
  710. # Do not increment the iterator
  711. continue
  712. # Now actually execute the task
  713. ret = self.__executeTask(task)
  714. # See if the task is done
  715. if (ret == cont):
  716. # Leave it for next frame, its not done yet
  717. pass
  718. elif (ret == again):
  719. # repeat doLater again after a delay
  720. self.__repeatDoMethod(task)
  721. taskPriList.remove(i)
  722. continue
  723. elif ((ret == done) or (ret == exit) or (ret == None)):
  724. # assert TaskManager.notify.debug(
  725. # '__stepThroughList: task is finished %s' % (task))
  726. # Remove the task
  727. if not task._removed:
  728. # assert TaskManager.notify.debug(
  729. # '__stepThroughList: task not removed %s' % (task))
  730. task.remove()
  731. # Note: Should not need to remove from doLaterList here
  732. # because this task is not in the doLaterList
  733. task.finishTask(self.fVerbose)
  734. self.__removeTaskFromNameDict(task)
  735. else:
  736. # assert TaskManager.notify.debug(
  737. # '__stepThroughList: task already removed %s' % (task))
  738. self.__removeTaskFromNameDict(task)
  739. taskPriList.remove(i)
  740. # Do not increment the iterator
  741. continue
  742. else:
  743. raise StandardError, \
  744. "Task named %s did not return cont, exit, done, or None" % \
  745. (task.name,)
  746. # Move to the next element
  747. i += 1
  748. def __addPendingTasksToTaskList(self):
  749. # Now that we are all done, add any left over pendingTasks
  750. # generated in priority levels lower or higher than where
  751. # we were when we iterated
  752. for taskList in self.pendingTaskDict.values():
  753. for task in taskList:
  754. if (task and not task._removed):
  755. # assert TaskManager.notify.debug(
  756. # 'step: moving %s from pending to taskList' % (task.name))
  757. self.__addNewTask(task)
  758. self.pendingTaskDict.clear()
  759. def profileFrames(self, num=None):
  760. self._profileFrames = True
  761. if num is None:
  762. num = 1
  763. self._profileFrameCount = num
  764. # in the event we want to do frame time managment.. this is the function to
  765. # replace or overload..
  766. def doYield(self , frameStartTime, nextScheuledTaksTime):
  767. None
  768. def doYieldExample(self , frameStartTime, nextScheuledTaksTime):
  769. minFinTime = frameStartTime + self.MaxEpockSpeed
  770. if nextScheuledTaksTime > 0 and nextScheuledTaksTime < minFinTime:
  771. print ' Adjusting Time'
  772. minFinTime = nextScheuledTaksTime;
  773. delta = minFinTime - self.globalClock.getRealTime();
  774. while(delta > 0.002):
  775. print ' sleep %s'% (delta)
  776. time.sleep(delta)
  777. delta = minFinTime - self.globalClock.getRealTime();
  778. @profiled()
  779. def _doProfiledFrames(self, *args, **kArgs):
  780. print '** profiling %s frames' % self._profileFrameCount
  781. for i in xrange(self._profileFrameCount):
  782. result = self.step(*args, **kArgs)
  783. return result
  784. def step(self):
  785. # assert TaskManager.notify.debug('step: begin')
  786. self.currentTime, self.currentFrame = self.__getTimeFrame()
  787. startFrameTime = None
  788. if self.globalClock:
  789. startFrameTime = self.globalClock.getRealTime()
  790. # Replace keyboard interrupt handler during task list processing
  791. # so we catch the keyboard interrupt but don't handle it until
  792. # after task list processing is complete.
  793. self.fKeyboardInterrupt = 0
  794. self.interruptCount = 0
  795. signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
  796. # Traverse the task list in order because it is in priority order
  797. priIndex = 0
  798. while priIndex < len(self.taskList):
  799. taskPriList = self.taskList[priIndex]
  800. pri = taskPriList._priority
  801. # assert TaskManager.notify.debug(
  802. # 'step: running through taskList at pri: %s, priIndex: %s' %
  803. # (pri, priIndex))
  804. self.__stepThroughList(taskPriList)
  805. # Now see if that generated any pending tasks for this taskPriList
  806. pendingTasks = self.pendingTaskDict.get(pri)
  807. while pendingTasks:
  808. # assert TaskManager.notify.debug('step: running through pending tasks at pri: %s' % (pri))
  809. # Remove them from the pendingTaskDict
  810. del self.pendingTaskDict[pri]
  811. # Execute them
  812. self.__stepThroughList(pendingTasks)
  813. # Add these to the real taskList
  814. for task in pendingTasks:
  815. if (task and not task._removed):
  816. # assert TaskManager.notify.debug('step: moving %s from pending to taskList' % (task.name))
  817. self.__addNewTask(task)
  818. # See if we generated any more for this pri level
  819. pendingTasks = self.pendingTaskDict.get(pri)
  820. # Any new tasks that were made pending should be converted
  821. # to real tasks now in case they need to run this frame at a
  822. # later priority level
  823. self.__addPendingTasksToTaskList()
  824. # Go to the next priority level
  825. priIndex += 1
  826. # Add new pending tasks
  827. self.__addPendingTasksToTaskList()
  828. if startFrameTime:
  829. #this is the spot for a Internal Yield Function
  830. nextTaskTime = self.__getNextDoLaterTime()
  831. self.doYield(startFrameTime,nextTaskTime)
  832. # Restore default interrupt handler
  833. signal.signal(signal.SIGINT, signal.default_int_handler)
  834. if self.fKeyboardInterrupt:
  835. raise KeyboardInterrupt
  836. def run(self):
  837. # Set the clock to have last frame's time in case we were
  838. # Paused at the prompt for a long time
  839. if self.globalClock:
  840. t = self.globalClock.getFrameTime()
  841. timeDelta = t - globalClock.getRealTime()
  842. self.globalClock.setRealTime(t)
  843. messenger.send("resetClock", [timeDelta])
  844. if self.resumeFunc != None:
  845. self.resumeFunc()
  846. if self.stepping:
  847. self.step()
  848. else:
  849. self.running = 1
  850. while self.running:
  851. try:
  852. if self._profileFrames:
  853. self._profileFrames = False
  854. self._doProfiledFrames()
  855. else:
  856. self.step()
  857. except KeyboardInterrupt:
  858. self.stop()
  859. except IOError, ioError:
  860. code, message = self._unpackIOError(ioError)
  861. # Since upgrading to Python 2.4.1, pausing the execution
  862. # often gives this IOError during the sleep function:
  863. # IOError: [Errno 4] Interrupted function call
  864. # So, let's just handle that specific exception and stop.
  865. # All other IOErrors should still get raised.
  866. # Only problem: legit IOError 4s will be obfuscated.
  867. if code == 4:
  868. self.stop()
  869. else:
  870. raise
  871. except:
  872. if self.extendedExceptions:
  873. self.stop()
  874. print_exc_plus()
  875. else:
  876. raise
  877. def _unpackIOError(self, ioError):
  878. # IOError unpack from http://www.python.org/doc/essays/stdexceptions/
  879. # this needs to be in its own method, exceptions that occur inside
  880. # a nested try block are not caught by the inner try block's except
  881. try:
  882. (code, message) = ioError
  883. except:
  884. code = 0
  885. message = ioError
  886. return code, message
  887. def stop(self):
  888. # Set a flag so we will stop before beginning next frame
  889. self.running = 0
  890. def __tryReplaceTaskMethod(self, task, oldMethod, newFunction):
  891. if (task is None) or task._removed:
  892. return 0
  893. method = task.__call__
  894. if (type(method) == types.MethodType):
  895. function = method.im_func
  896. else:
  897. function = method
  898. #print ('function: ' + `function` + '\n' +
  899. # 'method: ' + `method` + '\n' +
  900. # 'oldMethod: ' + `oldMethod` + '\n' +
  901. # 'newFunction: ' + `newFunction` + '\n')
  902. if (function == oldMethod):
  903. import new
  904. newMethod = new.instancemethod(newFunction,
  905. method.im_self,
  906. method.im_class)
  907. task.__call__ = newMethod
  908. # Found a match
  909. return 1
  910. return 0
  911. def replaceMethod(self, oldMethod, newFunction):
  912. numFound = 0
  913. # Look through the regular tasks
  914. for taskPriList in self.taskList:
  915. for task in taskPriList:
  916. if task:
  917. numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
  918. # Look through the pending tasks
  919. for pri, taskList in self.pendingTaskDict.items():
  920. for task in taskList:
  921. if task:
  922. numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
  923. # Look through the doLaters
  924. for task in self.__doLaterList:
  925. if task:
  926. numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
  927. return numFound
  928. def __repr__(self):
  929. taskNameWidth = 32
  930. dtWidth = 10
  931. priorityWidth = 10
  932. totalDt = 0
  933. totalAvgDt = 0
  934. str = "The taskMgr is handling:\n"
  935. str += ('taskList'.ljust(taskNameWidth)
  936. + 'dt(ms)'.rjust(dtWidth)
  937. + 'avg'.rjust(dtWidth)
  938. + 'max'.rjust(dtWidth)
  939. + 'priority'.rjust(priorityWidth)
  940. + '\n')
  941. str += '-------------------------------------------------------------------------\n'
  942. dtfmt = '%%%d.2f' % (dtWidth)
  943. for taskPriList in self.taskList:
  944. priority = `taskPriList._priority`
  945. for task in taskPriList:
  946. if task is None:
  947. break
  948. if task._removed:
  949. taskName = '(R)' + task.name
  950. else:
  951. taskName = task.name
  952. if self.taskTimerVerbose:
  953. totalDt = totalDt + task.dt
  954. totalAvgDt = totalAvgDt + task.avgDt
  955. str += (taskName.ljust(taskNameWidth)
  956. + dtfmt % (task.dt*1000)
  957. + dtfmt % (task.avgDt*1000)
  958. + dtfmt % (task.maxDt*1000)
  959. + priority.rjust(priorityWidth)
  960. + '\n')
  961. else:
  962. str += (task.name.ljust(taskNameWidth)
  963. + '----'.rjust(dtWidth)
  964. + '----'.rjust(dtWidth)
  965. + '----'.rjust(dtWidth)
  966. + priority.rjust(priorityWidth)
  967. + '\n')
  968. str += '-------------------------------------------------------------------------\n'
  969. str += 'pendingTasks\n'
  970. str += '-------------------------------------------------------------------------\n'
  971. for pri, taskList in self.pendingTaskDict.items():
  972. for task in taskList:
  973. if task._removed:
  974. taskName = '(PR)' + task.name
  975. else:
  976. taskName = '(P)' + task.name
  977. if (self.taskTimerVerbose):
  978. str += (' ' + taskName.ljust(taskNameWidth-2)
  979. + dtfmt % (pri)
  980. + '\n')
  981. else:
  982. str += (' ' + taskName.ljust(taskNameWidth-2)
  983. + '----'.rjust(dtWidth)
  984. + '\n')
  985. str += '-------------------------------------------------------------------------\n'
  986. if (self.taskTimerVerbose):
  987. str += ('total'.ljust(taskNameWidth)
  988. + dtfmt % (totalDt*1000)
  989. + dtfmt % (totalAvgDt*1000)
  990. + '\n')
  991. else:
  992. str += ('total'.ljust(taskNameWidth)
  993. + '----'.rjust(dtWidth)
  994. + '----'.rjust(dtWidth)
  995. + '\n')
  996. str += '-------------------------------------------------------------------------\n'
  997. str += ('doLaterList'.ljust(taskNameWidth)
  998. + 'waitTime(s)'.rjust(dtWidth)
  999. + '\n')
  1000. str += '-------------------------------------------------------------------------\n'
  1001. # When we print, show the doLaterList in actual sorted order.
  1002. # The priority heap is not actually in order - it is a tree
  1003. # Make a shallow copy so we can sort it
  1004. sortedDoLaterList = self.__doLaterList[:]
  1005. sortedDoLaterList.sort(lambda a, b: cmp(a.wakeTime, b.wakeTime))
  1006. sortedDoLaterList.reverse()
  1007. for task in sortedDoLaterList:
  1008. remainingTime = ((task.wakeTime) - self.currentTime)
  1009. if task._removed:
  1010. taskName = '(R)' + task.name
  1011. else:
  1012. taskName = task.name
  1013. str += (' ' + taskName.ljust(taskNameWidth-2)
  1014. + dtfmt % (remainingTime)
  1015. + '\n')
  1016. str += '-------------------------------------------------------------------------\n'
  1017. str += "End of taskMgr info\n"
  1018. return str
  1019. def getTasks(self):
  1020. # returns list of all tasks in arbitrary order
  1021. tasks = []
  1022. for taskPriList in self.taskList:
  1023. for task in taskPriList:
  1024. if task is not None and not task._removed:
  1025. tasks.append(task)
  1026. for pri, taskList in self.pendingTaskDict.iteritems():
  1027. for task in taskList:
  1028. if not task._removed:
  1029. tasks.append(task)
  1030. return tasks
  1031. def getDoLaters(self):
  1032. # returns list of all doLaters in arbitrary order
  1033. return [doLater for doLater in self.__doLaterList
  1034. if not doLater._removed]
  1035. def resetStats(self):
  1036. # WARNING: this screws up your do-later timings
  1037. if self.taskTimerVerbose:
  1038. for task in self.taskList:
  1039. task.dt = 0
  1040. task.avgDt = 0
  1041. task.maxDt = 0
  1042. task.runningTotal = 0
  1043. task.setStartTimeFrame(self.currentTime, self.currentFrame)
  1044. def popupControls(self):
  1045. from direct.tkpanels import TaskManagerPanel
  1046. return TaskManagerPanel.TaskManagerPanel(self)
  1047. def __getTimeFrame(self):
  1048. # WARNING: If you are testing tasks without an igLoop,
  1049. # you must manually tick the clock
  1050. # Ask for the time last frame
  1051. if self.globalClock:
  1052. return self.globalClock.getFrameTime(), self.globalClock.getFrameCount()
  1053. # OK, we don't have a globalClock yet. This is therefore
  1054. # running before the first frame.
  1055. return self.trueClock.getShortTime(), 0
  1056. def __getTime(self):
  1057. if self.globalClock:
  1058. return self.globalClock.getFrameTime()
  1059. return self.trueClock.getShortTime()
  1060. if __debug__:
  1061. # to catch memory leaks during the tests at the bottom of the file
  1062. def _startTrackingMemLeaks(self):
  1063. self._memUsage = ScratchPad()
  1064. mu = self._memUsage
  1065. mu.lenTaskList = len(self.taskList)
  1066. mu.lenPendingTaskDict = len(self.pendingTaskDict)
  1067. mu.lenDoLaterList = len(self.__doLaterList)
  1068. mu.lenNameDict = len(self.nameDict)
  1069. def _stopTrackingMemLeaks(self):
  1070. self._memUsage.destroy()
  1071. del self._memUsage
  1072. def _checkMemLeaks(self):
  1073. # flush removed doLaters
  1074. self.__doLaterFilter()
  1075. # give the mgr a chance to clear out removed tasks
  1076. self.step()
  1077. mu = self._memUsage
  1078. # the task list looks like it grows and never shrinks, replacing finished
  1079. # tasks with 'None' in the TaskPriorityLists.
  1080. # TODO: look at reducing memory usage here--clear out excess at the end of every frame?
  1081. #assert mu.lenTaskList == len(self.taskList)
  1082. assert mu.lenPendingTaskDict == len(self.pendingTaskDict)
  1083. assert mu.lenDoLaterList == len(self.__doLaterList)
  1084. assert mu.lenNameDict == len(self.nameDict)
  1085. def startOsd(self):
  1086. self.add(self.doOsd, 'taskMgr.doOsd')
  1087. self._osdEnabled = None
  1088. def osdEnabled(self):
  1089. return hasattr(self, '_osdEnabled')
  1090. def stopOsd(self):
  1091. onScreenDebug.removeAllWithPrefix(TaskManager.OsdPrefix)
  1092. self.remove('taskMgr.doOsd')
  1093. del self._osdEnabled
  1094. def doOsd(self, task):
  1095. if not onScreenDebug.enabled:
  1096. return
  1097. prefix = TaskManager.OsdPrefix
  1098. onScreenDebug.removeAllWithPrefix(prefix)
  1099. taskNameWidth = 32
  1100. dtWidth = 10
  1101. priorityWidth = 10
  1102. totalDt = 0
  1103. totalAvgDt = 0
  1104. i = 0
  1105. onScreenDebug.add(
  1106. ('%s%02i.taskList' % (prefix, i)).ljust(taskNameWidth),
  1107. '%s %s %s %s' % (
  1108. 'dt(ms)'.rjust(dtWidth),
  1109. 'avg'.rjust(dtWidth),
  1110. 'max'.rjust(dtWidth),
  1111. 'priority'.rjust(priorityWidth),))
  1112. i += 1
  1113. for taskPriList in self.taskList:
  1114. priority = `taskPriList._priority`
  1115. for task in taskPriList:
  1116. if task is None:
  1117. break
  1118. if task._removed:
  1119. taskName = '(R)' + task.name
  1120. else:
  1121. taskName = task.name
  1122. totalDt = totalDt + task.dt
  1123. totalAvgDt = totalAvgDt + task.avgDt
  1124. onScreenDebug.add(
  1125. ('%s%02i.%s' % (prefix, i, task.name)).ljust(taskNameWidth),
  1126. '%s %s %s %s' % (
  1127. dtfmt % (task.dt*1000),
  1128. dtfmt % (task.avgDt*1000),
  1129. dtfmt % (task.maxDt*1000),
  1130. priority.rjust(priorityWidth)))
  1131. i += 1
  1132. onScreenDebug.add(('%s%02i.total' % (prefix, i)).ljust(taskNameWidth),
  1133. '%s %s' % (
  1134. dtfmt % (totalDt*1000),
  1135. dtfmt % (totalAvgDt*1000),))
  1136. return cont
  1137. # These constants are moved to the top level of the module,
  1138. # to make it easier for legacy code. In general though, putting
  1139. # constants at the top level of a module is deprecated.
  1140. exit = Task.exit
  1141. done = Task.done
  1142. cont = Task.cont
  1143. again = Task.again
  1144. if __debug__:
  1145. # keep everything in a function namespace so it's easier to clean up
  1146. def runTests():
  1147. tm = TaskManager()
  1148. # looks like nothing runs on the first frame...?
  1149. # step to get past the first frame
  1150. tm.step()
  1151. # check for memory leaks after every test
  1152. tm._startTrackingMemLeaks()
  1153. tm._checkMemLeaks()
  1154. # run-once task
  1155. l = []
  1156. def _testDone(task, l=l):
  1157. l.append(None)
  1158. return task.done
  1159. tm.add(_testDone, 'testDone')
  1160. tm.step()
  1161. assert len(l) == 1
  1162. tm.step()
  1163. assert len(l) == 1
  1164. _testDone = None
  1165. tm._checkMemLeaks()
  1166. # remove by name
  1167. def _testRemoveByName(task):
  1168. return task.done
  1169. tm.add(_testRemoveByName, 'testRemoveByName')
  1170. assert tm.remove('testRemoveByName') == 1
  1171. assert tm.remove('testRemoveByName') == 0
  1172. _testRemoveByName = None
  1173. tm._checkMemLeaks()
  1174. # duplicate named tasks
  1175. def _testDupNamedTasks(task):
  1176. return task.done
  1177. tm.add(_testDupNamedTasks, 'testDupNamedTasks')
  1178. tm.add(_testDupNamedTasks, 'testDupNamedTasks')
  1179. assert tm.remove('testRemoveByName') == 0
  1180. _testDupNamedTasks = None
  1181. tm._checkMemLeaks()
  1182. # continued task
  1183. l = []
  1184. def _testCont(task, l = l):
  1185. l.append(None)
  1186. return task.cont
  1187. tm.add(_testCont, 'testCont')
  1188. tm.step()
  1189. assert len(l) == 1
  1190. tm.step()
  1191. assert len(l) == 2
  1192. tm.remove('testCont')
  1193. _testCont = None
  1194. tm._checkMemLeaks()
  1195. # continue until done task
  1196. l = []
  1197. def _testContDone(task, l = l):
  1198. l.append(None)
  1199. if len(l) >= 2:
  1200. return task.done
  1201. else:
  1202. return task.cont
  1203. tm.add(_testContDone, 'testContDone')
  1204. tm.step()
  1205. assert len(l) == 1
  1206. tm.step()
  1207. assert len(l) == 2
  1208. tm.step()
  1209. assert len(l) == 2
  1210. assert not tm.hasTaskNamed('testContDone')
  1211. _testContDone = None
  1212. tm._checkMemLeaks()
  1213. # hasTaskNamed
  1214. def _testHasTaskNamed(task):
  1215. return task.done
  1216. tm.add(_testHasTaskNamed, 'testHasTaskNamed')
  1217. assert tm.hasTaskNamed('testHasTaskNamed')
  1218. tm.step()
  1219. assert not tm.hasTaskNamed('testHasTaskNamed')
  1220. _testHasTaskNamed = None
  1221. tm._checkMemLeaks()
  1222. # task priority
  1223. l = []
  1224. def _testPri1(task, l = l):
  1225. l.append(1)
  1226. return task.cont
  1227. def _testPri2(task, l = l):
  1228. l.append(2)
  1229. return task.cont
  1230. tm.add(_testPri1, 'testPri1', priority = 1)
  1231. tm.add(_testPri2, 'testPri2', priority = 2)
  1232. tm.step()
  1233. assert len(l) == 2
  1234. assert l == [1, 2,]
  1235. tm.step()
  1236. assert len(l) == 4
  1237. assert l == [1, 2, 1, 2,]
  1238. tm.remove('testPri1')
  1239. tm.remove('testPri2')
  1240. _testPri1 = None
  1241. _testPri2 = None
  1242. tm._checkMemLeaks()
  1243. # task extraArgs
  1244. l = []
  1245. def _testExtraArgs(arg1, arg2, l=l):
  1246. l.extend([arg1, arg2,])
  1247. return done
  1248. tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
  1249. tm.step()
  1250. assert len(l) == 2
  1251. assert l == [4, 5,]
  1252. _testExtraArgs = None
  1253. tm._checkMemLeaks()
  1254. # task appendTask
  1255. l = []
  1256. def _testAppendTask(arg1, arg2, task, l=l):
  1257. l.extend([arg1, arg2,])
  1258. return task.done
  1259. tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
  1260. tm.step()
  1261. assert len(l) == 2
  1262. assert l == [4, 5,]
  1263. _testAppendTask = None
  1264. tm._checkMemLeaks()
  1265. # task uponDeath
  1266. l = []
  1267. def _uponDeathFunc(task, l=l):
  1268. l.append(task.name)
  1269. def _testUponDeath(task):
  1270. return done
  1271. tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
  1272. tm.step()
  1273. assert len(l) == 1
  1274. assert l == ['testUponDeath']
  1275. _testUponDeath = None
  1276. _uponDeathFunc = None
  1277. tm._checkMemLeaks()
  1278. # task owner
  1279. class _TaskOwner:
  1280. def _clearTask(self, task):
  1281. self.clearedTaskName = task.name
  1282. to = _TaskOwner()
  1283. l = []
  1284. def _testOwner(task):
  1285. return done
  1286. tm.add(_testOwner, 'testOwner', owner=to)
  1287. tm.step()
  1288. assert hasattr(to, 'clearedTaskName')
  1289. assert to.clearedTaskName == 'testOwner'
  1290. _testOwner = None
  1291. del to
  1292. _TaskOwner = None
  1293. tm._checkMemLeaks()
  1294. doLaterTests = [0,]
  1295. # doLater
  1296. l = []
  1297. def _testDoLater1(task, l=l):
  1298. l.append(1)
  1299. def _testDoLater2(task, l=l):
  1300. l.append(2)
  1301. def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
  1302. if task.time > .03:
  1303. assert l == [1, 2,]
  1304. doLaterTests[0] -= 1
  1305. return task.done
  1306. return task.cont
  1307. tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
  1308. tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
  1309. doLaterTests[0] += 1
  1310. # make sure we run this task after the doLaters if they all occur on the same frame
  1311. tm.add(_monitorDoLater, 'monitorDoLater', priority=10)
  1312. _testDoLater1 = None
  1313. _testDoLater2 = None
  1314. _monitorDoLater = None
  1315. # don't check until all the doLaters are finished
  1316. #tm._checkMemLeaks()
  1317. # doLater priority
  1318. l = []
  1319. def _testDoLaterPri1(task, l=l):
  1320. l.append(1)
  1321. def _testDoLaterPri2(task, l=l):
  1322. l.append(2)
  1323. def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
  1324. if task.time > .02:
  1325. assert l == [1, 2,]
  1326. doLaterTests[0] -= 1
  1327. return task.done
  1328. return task.cont
  1329. tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', priority=1)
  1330. tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', priority=2)
  1331. doLaterTests[0] += 1
  1332. # make sure we run this task after the doLaters if they all occur on the same frame
  1333. tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', priority=10)
  1334. _testDoLaterPri1 = None
  1335. _testDoLaterPri2 = None
  1336. _monitorDoLaterPri = None
  1337. # don't check until all the doLaters are finished
  1338. #tm._checkMemLeaks()
  1339. # doLater extraArgs
  1340. l = []
  1341. def _testDoLaterExtraArgs(arg1, l=l):
  1342. l.append(arg1)
  1343. def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
  1344. if task.time > .02:
  1345. assert l == [3,]
  1346. doLaterTests[0] -= 1
  1347. return task.done
  1348. return task.cont
  1349. tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
  1350. doLaterTests[0] += 1
  1351. # make sure we run this task after the doLaters if they all occur on the same frame
  1352. tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', priority=10)
  1353. _testDoLaterExtraArgs = None
  1354. _monitorDoLaterExtraArgs = None
  1355. # don't check until all the doLaters are finished
  1356. #tm._checkMemLeaks()
  1357. # doLater appendTask
  1358. l = []
  1359. def _testDoLaterAppendTask(arg1, task, l=l):
  1360. assert task.name == 'testDoLaterAppendTask'
  1361. l.append(arg1)
  1362. def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
  1363. if task.time > .02:
  1364. assert l == [4,]
  1365. doLaterTests[0] -= 1
  1366. return task.done
  1367. return task.cont
  1368. tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
  1369. extraArgs=[4,], appendTask=True)
  1370. doLaterTests[0] += 1
  1371. # make sure we run this task after the doLaters if they all occur on the same frame
  1372. tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', priority=10)
  1373. _testDoLaterAppendTask = None
  1374. _monitorDoLaterAppendTask = None
  1375. # don't check until all the doLaters are finished
  1376. #tm._checkMemLeaks()
  1377. # doLater uponDeath
  1378. l = []
  1379. def _testUponDeathFunc(task, l=l):
  1380. assert task.name == 'testDoLaterUponDeath'
  1381. l.append(10)
  1382. def _testDoLaterUponDeath(arg1, l=l):
  1383. return done
  1384. def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
  1385. if task.time > .02:
  1386. assert l == [10,]
  1387. doLaterTests[0] -= 1
  1388. return task.done
  1389. return task.cont
  1390. tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
  1391. uponDeath=_testUponDeathFunc)
  1392. doLaterTests[0] += 1
  1393. # make sure we run this task after the doLaters if they all occur on the same frame
  1394. tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', priority=10)
  1395. _testUponDeathFunc = None
  1396. _testDoLaterUponDeath = None
  1397. _monitorDoLaterUponDeath = None
  1398. # don't check until all the doLaters are finished
  1399. #tm._checkMemLeaks()
  1400. # doLater owner
  1401. class _DoLaterOwner:
  1402. def _clearTask(self, task):
  1403. self.clearedTaskName = task.name
  1404. doLaterOwner = _DoLaterOwner()
  1405. l = []
  1406. def _testDoLaterOwner(l=l):
  1407. pass
  1408. def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
  1409. doLaterTests=doLaterTests):
  1410. if task.time > .02:
  1411. assert hasattr(doLaterOwner, 'clearedTaskName')
  1412. assert doLaterOwner.clearedTaskName == 'testDoLaterOwner'
  1413. doLaterTests[0] -= 1
  1414. return task.done
  1415. return task.cont
  1416. tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
  1417. owner=doLaterOwner)
  1418. doLaterTests[0] += 1
  1419. # make sure we run this task after the doLaters if they all occur on the same frame
  1420. tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', priority=10)
  1421. _testDoLaterOwner = None
  1422. _monitorDoLaterOwner = None
  1423. del doLaterOwner
  1424. _DoLaterOwner = None
  1425. # don't check until all the doLaters are finished
  1426. #tm._checkMemLeaks()
  1427. # run the doLater tests
  1428. while doLaterTests[0] > 0:
  1429. tm.step()
  1430. del doLaterTests
  1431. tm._checkMemLeaks()
  1432. # getTasks
  1433. def _testGetTasks(task):
  1434. return task.cont
  1435. # the doLaterProcessor is always running
  1436. assert len(tm.getTasks()) == 1
  1437. tm.add(_testGetTasks, 'testGetTasks1')
  1438. assert len(tm.getTasks()) == 2
  1439. assert (tm.getTasks()[0].name == 'testGetTasks1' or
  1440. tm.getTasks()[1].name == 'testGetTasks1')
  1441. tm.add(_testGetTasks, 'testGetTasks2')
  1442. tm.add(_testGetTasks, 'testGetTasks3')
  1443. assert len(tm.getTasks()) == 4
  1444. tm.remove('testGetTasks2')
  1445. assert len(tm.getTasks()) == 3
  1446. tm.remove('testGetTasks1')
  1447. tm.remove('testGetTasks3')
  1448. assert len(tm.getTasks()) == 1
  1449. _testGetTasks = None
  1450. tm._checkMemLeaks()
  1451. # getDoLaters
  1452. def _testGetDoLaters():
  1453. pass
  1454. # the doLaterProcessor is always running
  1455. assert len(tm.getDoLaters()) == 0
  1456. tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
  1457. assert len(tm.getDoLaters()) == 1
  1458. assert tm.getDoLaters()[0].name == 'testDoLater1'
  1459. tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
  1460. tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
  1461. assert len(tm.getDoLaters()) == 3
  1462. tm.remove('testDoLater2')
  1463. assert len(tm.getDoLaters()) == 2
  1464. tm.remove('testDoLater1')
  1465. tm.remove('testDoLater3')
  1466. assert len(tm.getDoLaters()) == 0
  1467. _testGetDoLaters = None
  1468. tm._checkMemLeaks()
  1469. # duplicate named doLaters removed via taskMgr.remove
  1470. def _testDupNameDoLaters():
  1471. pass
  1472. # the doLaterProcessor is always running
  1473. tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
  1474. tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
  1475. assert len(tm.getDoLaters()) == 2
  1476. tm.remove('testDupNameDoLater')
  1477. assert len(tm.getDoLaters()) == 0
  1478. _testDupNameDoLaters = None
  1479. tm._checkMemLeaks()
  1480. # duplicate named doLaters removed via remove()
  1481. def _testDupNameDoLatersRemove():
  1482. pass
  1483. # the doLaterProcessor is always running
  1484. dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
  1485. dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
  1486. assert len(tm.getDoLaters()) == 2
  1487. dl2.remove()
  1488. assert len(tm.getDoLaters()) == 1
  1489. dl1.remove()
  1490. assert len(tm.getDoLaters()) == 0
  1491. _testDupNameDoLatersRemove = None
  1492. # nameDict etc. isn't cleared out right away with task.remove()
  1493. tm._checkMemLeaks()
  1494. # getTasksNamed
  1495. def _testGetTasksNamed(task):
  1496. return task.cont
  1497. assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
  1498. tm.add(_testGetTasksNamed, 'testGetTasksNamed')
  1499. assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
  1500. assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
  1501. tm.add(_testGetTasksNamed, 'testGetTasksNamed')
  1502. tm.add(_testGetTasksNamed, 'testGetTasksNamed')
  1503. assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
  1504. tm.remove('testGetTasksNamed')
  1505. assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
  1506. _testGetTasksNamed = None
  1507. tm._checkMemLeaks()
  1508. # removeTasksMatching
  1509. def _testRemoveTasksMatching(task):
  1510. return task.cont
  1511. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
  1512. assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
  1513. tm.removeTasksMatching('testRemoveTasksMatching')
  1514. assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
  1515. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
  1516. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
  1517. assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
  1518. assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
  1519. tm.removeTasksMatching('testRemoveTasksMatching*')
  1520. assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
  1521. assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
  1522. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
  1523. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
  1524. assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
  1525. assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
  1526. tm.removeTasksMatching('testRemoveTasksMatching?a')
  1527. assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
  1528. assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
  1529. _testRemoveTasksMatching = None
  1530. tm._checkMemLeaks()
  1531. # create Task object and add to mgr
  1532. l = []
  1533. def _testTaskObj(task, l=l):
  1534. l.append(None)
  1535. return task.cont
  1536. t = Task(_testTaskObj)
  1537. tm.add(t, 'testTaskObj')
  1538. tm.step()
  1539. assert len(l) == 1
  1540. tm.step()
  1541. assert len(l) == 2
  1542. tm.remove('testTaskObj')
  1543. tm.step()
  1544. assert len(l) == 2
  1545. _testTaskObj = None
  1546. tm._checkMemLeaks()
  1547. # remove Task via task.remove()
  1548. l = []
  1549. def _testTaskObjRemove(task, l=l):
  1550. l.append(None)
  1551. return task.cont
  1552. t = Task(_testTaskObjRemove)
  1553. tm.add(t, 'testTaskObjRemove')
  1554. tm.step()
  1555. assert len(l) == 1
  1556. tm.step()
  1557. assert len(l) == 2
  1558. t.remove()
  1559. tm.step()
  1560. assert len(l) == 2
  1561. del t
  1562. _testTaskObjRemove = None
  1563. tm._checkMemLeaks()
  1564. """
  1565. # this test fails, and it's not clear what the correct behavior should be.
  1566. # priority passed to Task.__init__ is always overridden by taskMgr.add()
  1567. # even if no priority is specified, and calling Task.setPriority() has no
  1568. # effect on the taskMgr's behavior.
  1569. # set/get Task priority
  1570. l = []
  1571. def _testTaskObjPriority(arg, task, l=l):
  1572. l.append(arg)
  1573. return task.cont
  1574. t1 = Task(_testTaskObjPriority, priority=1)
  1575. t2 = Task(_testTaskObjPriority, priority=2)
  1576. tm.add(t1, 'testTaskObjPriority1', extraArgs=['a',], appendTask=True)
  1577. tm.add(t2, 'testTaskObjPriority2', extraArgs=['b',], appendTask=True)
  1578. tm.step()
  1579. assert len(l) == 2
  1580. assert l == ['a', 'b']
  1581. assert t1.getPriority() == 1
  1582. assert t2.getPriority() == 2
  1583. t1.setPriority(3)
  1584. assert t1.getPriority() == 3
  1585. tm.step()
  1586. assert len(l) == 4
  1587. assert l == ['a', 'b', 'b', 'a',]
  1588. t1.remove()
  1589. t2.remove()
  1590. tm.step()
  1591. assert len(l) == 4
  1592. del t1
  1593. del t2
  1594. _testTaskObjPriority = None
  1595. tm._checkMemLeaks()
  1596. """
  1597. del l
  1598. tm.destroy()
  1599. del tm
  1600. runTests()
  1601. del runTests
  1602. """
  1603. import Task
  1604. def goo(task):
  1605. print 'goo'
  1606. return Task.done
  1607. def bar(task):
  1608. print 'bar'
  1609. taskMgr.add(goo, 'goo')
  1610. return Task.done
  1611. def foo(task):
  1612. print 'foo'
  1613. taskMgr.add(bar, 'bar')
  1614. return Task.done
  1615. taskMgr.add(foo, 'foo')
  1616. """