Task.py 74 KB

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