Task.py 74 KB

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