TaskOrig.py 75 KB

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