Task.py 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263
  1. """ This module defines a Python-level wrapper around the C++
  2. AsyncTaskManager interface. It replaces the old full-Python
  3. implementation of the Task system. """
  4. __all__ = ['Task', 'TaskManager',
  5. 'cont', 'done', 'again', 'pickup', 'exit',
  6. 'sequence', 'loop', 'pause']
  7. from direct.directnotify.DirectNotifyGlobal import *
  8. from direct.showbase import ExceptionVarDump
  9. from direct.showbase.PythonUtil import *
  10. from direct.showbase.MessengerGlobal import messenger
  11. import signal
  12. import types
  13. import time
  14. import random
  15. import string
  16. from pandac.PandaModules import *
  17. def print_exc_plus():
  18. """
  19. Print the usual traceback information, followed by a listing of all the
  20. local variables in each frame.
  21. """
  22. import sys
  23. import traceback
  24. tb = sys.exc_info()[2]
  25. while 1:
  26. if not tb.tb_next:
  27. break
  28. tb = tb.tb_next
  29. stack = []
  30. f = tb.tb_frame
  31. while f:
  32. stack.append(f)
  33. f = f.f_back
  34. stack.reverse()
  35. traceback.print_exc()
  36. print "Locals by frame, innermost last"
  37. for frame in stack:
  38. print
  39. print "Frame %s in %s at line %s" % (frame.f_code.co_name,
  40. frame.f_code.co_filename,
  41. frame.f_lineno)
  42. for key, value in frame.f_locals.items():
  43. print "\t%20s = " % key,
  44. #We have to be careful not to cause a new error in our error
  45. #printer! Calling str() on an unknown object could cause an
  46. #error we don't want.
  47. try:
  48. print value
  49. except:
  50. print "<ERROR WHILE PRINTING VALUE>"
  51. # For historical purposes, we remap the C++-defined enumeration to
  52. # these Python names, and define them both at the module level, here,
  53. # and at the class level (below). The preferred access is via the
  54. # class level.
  55. done = AsyncTask.DSDone
  56. cont = AsyncTask.DSCont
  57. again = AsyncTask.DSAgain
  58. pickup = AsyncTask.DSPickup
  59. exit = AsyncTask.DSExit
  60. # Alias PythonTask to Task for historical purposes.
  61. Task = PythonTask
  62. # Copy the module-level enums above into the class level. This funny
  63. # syntax is necessary because it's a C++-wrapped extension type, not a
  64. # true Python class.
  65. Task.DtoolClassDict['done'] = done
  66. Task.DtoolClassDict['cont'] = cont
  67. Task.DtoolClassDict['again'] = again
  68. Task.DtoolClassDict['pickup'] = pickup
  69. Task.DtoolClassDict['exit'] = exit
  70. # Alias the AsyncTaskPause constructor as Task.pause().
  71. pause = AsyncTaskPause
  72. Task.DtoolClassDict['pause'] = staticmethod(pause)
  73. def sequence(*taskList):
  74. seq = AsyncTaskSequence('sequence')
  75. for task in taskList:
  76. seq.addTask(task)
  77. return seq
  78. Task.DtoolClassDict['sequence'] = staticmethod(sequence)
  79. def loop(*taskList):
  80. seq = AsyncTaskSequence('loop')
  81. for task in taskList:
  82. seq.addTask(task)
  83. seq.setRepeatCount(-1)
  84. return seq
  85. Task.DtoolClassDict['loop'] = staticmethod(loop)
  86. class TaskManager:
  87. notify = directNotify.newCategory("TaskManager")
  88. extendedExceptions = False
  89. MaxEpochSpeed = 1.0/30.0
  90. def __init__(self):
  91. self.mgr = AsyncTaskManager.getGlobalPtr()
  92. self.resumeFunc = None
  93. self.globalClock = self.mgr.getClock()
  94. self.stepping = False
  95. self.running = False
  96. self.destroyed = False
  97. self.fKeyboardInterrupt = False
  98. self.interruptCount = 0
  99. self._frameProfileQueue = Queue()
  100. # this will be set when it's safe to import StateVar
  101. self._profileFrames = None
  102. self._frameProfiler = None
  103. self._profileTasks = None
  104. self._taskProfiler = None
  105. self._taskProfileInfo = ScratchPad(
  106. taskId = None,
  107. profiled = False,
  108. session = None,
  109. )
  110. def finalInit(self):
  111. # This function should be called once during startup, after
  112. # most things are imported.
  113. from direct.fsm.StatePush import StateVar
  114. self._profileTasks = StateVar(False)
  115. self.setProfileTasks(ConfigVariableBool('profile-task-spikes', 0).getValue())
  116. self._profileFrames = StateVar(False)
  117. self.setProfileFrames(ConfigVariableBool('profile-frames', 0).getValue())
  118. def destroy(self):
  119. # This should be safe to call multiple times.
  120. self.notify.info("TaskManager.destroy()")
  121. self.destroyed = True
  122. self._frameProfileQueue.clear()
  123. self.mgr.cleanup()
  124. def setClock(self, clockObject):
  125. self.mgr.setClock(clockObject)
  126. self.globalClock = clockObject
  127. def invokeDefaultHandler(self, signalNumber, stackFrame):
  128. print '*** allowing mid-frame keyboard interrupt.'
  129. # Restore default interrupt handler
  130. signal.signal(signal.SIGINT, signal.default_int_handler)
  131. # and invoke it
  132. raise KeyboardInterrupt
  133. def keyboardInterruptHandler(self, signalNumber, stackFrame):
  134. self.fKeyboardInterrupt = 1
  135. self.interruptCount += 1
  136. if self.interruptCount == 1:
  137. print '* interrupt by keyboard'
  138. elif self.interruptCount == 2:
  139. print '** waiting for end of frame before interrupting...'
  140. # The user must really want to interrupt this process
  141. # Next time around invoke the default handler
  142. signal.signal(signal.SIGINT, self.invokeDefaultHandler)
  143. def getCurrentTask(self):
  144. """ Returns the task currently executing on this thread, or
  145. None if this is being called outside of the task manager. """
  146. return Thread.getCurrentThread().getCurrentTask()
  147. def hasTaskChain(self, chainName):
  148. """ Returns true if a task chain with the indicated name has
  149. already been defined, or false otherwise. Note that
  150. setupTaskChain() will implicitly define a task chain if it has
  151. not already been defined, or modify an existing one if it has,
  152. so in most cases there is no need to check this method
  153. first. """
  154. return (self.mgr.findTaskChain(chainName) != None)
  155. def setupTaskChain(self, chainName, numThreads = None, tickClock = None,
  156. threadPriority = None, frameBudget = None,
  157. frameSync = None, timeslicePriority = None):
  158. """Defines a new task chain. Each task chain executes tasks
  159. potentially in parallel with all of the other task chains (if
  160. numThreads is more than zero). When a new task is created, it
  161. may be associated with any of the task chains, by name (or you
  162. can move a task to another task chain with
  163. task.setTaskChain()). You can have any number of task chains,
  164. but each must have a unique name.
  165. numThreads is the number of threads to allocate for this task
  166. chain. If it is 1 or more, then the tasks on this task chain
  167. will execute in parallel with the tasks on other task chains.
  168. If it is greater than 1, then the tasks on this task chain may
  169. execute in parallel with themselves (within tasks of the same
  170. sort value).
  171. If tickClock is True, then this task chain will be responsible
  172. for ticking the global clock each frame (and thereby
  173. incrementing the frame counter). There should be just one
  174. task chain responsible for ticking the clock, and usually it
  175. is the default, unnamed task chain.
  176. threadPriority specifies the priority level to assign to
  177. threads on this task chain. It may be one of TPLow, TPNormal,
  178. TPHigh, or TPUrgent. This is passed to the underlying
  179. threading system to control the way the threads are scheduled.
  180. frameBudget is the maximum amount of time (in seconds) to
  181. allow this task chain to run per frame. Set it to -1 to mean
  182. no limit (the default). It's not directly related to
  183. threadPriority.
  184. frameSync is true to force the task chain to sync to the
  185. clock. When this flag is false, the default, the task chain
  186. will finish all of its tasks and then immediately start from
  187. the first task again, regardless of the clock frame. When it
  188. is true, the task chain will finish all of its tasks and then
  189. wait for the clock to tick to the next frame before resuming
  190. the first task. This only makes sense for threaded tasks
  191. chains; non-threaded task chains are automatically
  192. synchronous.
  193. timeslicePriority is False in the default mode, in which each
  194. task runs exactly once each frame, round-robin style,
  195. regardless of the task's priority value; or True to change the
  196. meaning of priority so that certain tasks are run less often,
  197. in proportion to their time used and to their priority value.
  198. See AsyncTaskManager.setTimeslicePriority() for more.
  199. """
  200. chain = self.mgr.makeTaskChain(chainName)
  201. if numThreads is not None:
  202. chain.setNumThreads(numThreads)
  203. if tickClock is not None:
  204. chain.setTickClock(tickClock)
  205. if threadPriority is not None:
  206. chain.setThreadPriority(threadPriority)
  207. if frameBudget is not None:
  208. chain.setFrameBudget(frameBudget)
  209. if frameSync is not None:
  210. chain.setFrameSync(frameSync)
  211. if timeslicePriority is not None:
  212. chain.setTimeslicePriority(timeslicePriority)
  213. def hasTaskNamed(self, taskName):
  214. """Returns true if there is at least one task, active or
  215. sleeping, with the indicated name. """
  216. return bool(self.mgr.findTask(taskName))
  217. def getTasksNamed(self, taskName):
  218. """Returns a list of all tasks, active or sleeping, with the
  219. indicated name. """
  220. return self.__makeTaskList(self.mgr.findTasks(taskName))
  221. def getTasksMatching(self, taskPattern):
  222. """Returns a list of all tasks, active or sleeping, with a
  223. name that matches the pattern, which can include standard
  224. shell globbing characters like *, ?, and []. """
  225. return self.__makeTaskList(self.mgr.findTasksMatching(GlobPattern(taskPattern)))
  226. def getAllTasks(self):
  227. """Returns list of all tasks, active and sleeping, in
  228. arbitrary order. """
  229. return self.__makeTaskList(self.mgr.getTasks())
  230. def getTasks(self):
  231. """Returns list of all active tasks in arbitrary order. """
  232. return self.__makeTaskList(self.mgr.getActiveTasks())
  233. def getDoLaters(self):
  234. """Returns list of all sleeping tasks in arbitrary order. """
  235. return self.__makeTaskList(self.mgr.getSleepingTasks())
  236. def __makeTaskList(self, taskCollection):
  237. l = []
  238. for i in range(taskCollection.getNumTasks()):
  239. l.append(taskCollection.getTask(i))
  240. return l
  241. def doMethodLater(self, delayTime, funcOrTask, name, extraArgs = None,
  242. sort = None, priority = None, taskChain = None,
  243. uponDeath = None, appendTask = False, owner = None):
  244. """Adds a task to be performed at some time in the future.
  245. This is identical to add(), except that the specified
  246. delayTime is applied to the Task object first, which means
  247. that the task will not begin executing until at least the
  248. indicated delayTime (in seconds) has elapsed.
  249. After delayTime has elapsed, the task will become active, and
  250. will run in the soonest possible frame thereafter. If you
  251. wish to specify a task that will run in the next frame, use a
  252. delayTime of 0.
  253. """
  254. if delayTime < 0:
  255. assert self.notify.warning('doMethodLater: added task: %s with negative delay: %s' % (name, delayTime))
  256. task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
  257. task.setDelay(delayTime)
  258. self.mgr.add(task)
  259. return task
  260. def add(self, funcOrTask, name = None, sort = None, extraArgs = None,
  261. priority = None, uponDeath = None, appendTask = False,
  262. taskChain = None, owner = None):
  263. """
  264. Add a new task to the taskMgr. The task will begin executing
  265. immediately, or next frame if its sort value has already
  266. passed this frame.
  267. The parameters are:
  268. funcOrTask - either an existing Task object (not already added
  269. to the task manager), or a callable function object. If this
  270. is a function, a new Task object will be created and returned.
  271. name - the name to assign to the Task. Required, unless you
  272. are passing in a Task object that already has a name.
  273. extraArgs - the list of arguments to pass to the task
  274. function. If this is omitted, the list is just the task
  275. object itself.
  276. appendTask - a boolean flag. If this is true, then the task
  277. object itself will be appended to the end of the extraArgs
  278. list before calling the function.
  279. sort - the sort value to assign the task. The default sort is
  280. 0. Within a particular task chain, it is guaranteed that the
  281. tasks with a lower sort value will all run before tasks with a
  282. higher sort value run.
  283. priority - the priority at which to run the task. The default
  284. priority is 0. Higher priority tasks are run sooner, and/or
  285. more often. For historical purposes, if you specify a
  286. priority without also specifying a sort, the priority value is
  287. understood to actually be a sort value. (Previously, there
  288. was no priority value, only a sort value, and it was called
  289. "priority".)
  290. uponDeath - a function to call when the task terminates,
  291. either because it has run to completion, or because it has
  292. been explicitly removed.
  293. taskChain - the name of the task chain to assign the task to.
  294. owner - an optional Python object that is declared as the
  295. "owner" of this task for maintenance purposes. The owner must
  296. have two methods: owner._addTask(self, task), which is called
  297. when the task begins, and owner._clearTask(self, task), which
  298. is called when the task terminates. This is all the owner
  299. means.
  300. The return value of add() is the new Task object that has been
  301. added, or the original Task object that was passed in.
  302. """
  303. task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
  304. self.mgr.add(task)
  305. return task
  306. def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath):
  307. if isinstance(funcOrTask, AsyncTask):
  308. task = funcOrTask
  309. elif hasattr(funcOrTask, '__call__'):
  310. task = PythonTask(funcOrTask)
  311. else:
  312. self.notify.error(
  313. 'add: Tried to add a task that was not a Task or a func')
  314. if hasattr(task, 'setArgs'):
  315. # It will only accept arguments if it's a PythonTask.
  316. if extraArgs is None:
  317. extraArgs = []
  318. appendTask = True
  319. task.setArgs(extraArgs, appendTask)
  320. elif extraArgs is not None:
  321. self.notify.error(
  322. 'Task %s does not accept arguments.' % (repr(task)))
  323. if name is not None:
  324. assert isinstance(name, types.StringTypes), 'Name must be a string type'
  325. task.setName(name)
  326. assert task.hasName()
  327. # For historical reasons, if priority is specified but not
  328. # sort, it really means sort.
  329. if priority is not None and sort is None:
  330. task.setSort(priority)
  331. else:
  332. if priority is not None:
  333. task.setPriority(priority)
  334. if sort is not None:
  335. task.setSort(sort)
  336. if taskChain is not None:
  337. task.setTaskChain(taskChain)
  338. if owner is not None:
  339. task.setOwner(owner)
  340. if uponDeath is not None:
  341. task.setUponDeath(uponDeath)
  342. return task
  343. def remove(self, taskOrName):
  344. """Removes a task from the task manager. The task is stopped,
  345. almost as if it had returned task.done. (But if the task is
  346. currently executing, it will finish out its current frame
  347. before being removed.) You may specify either an explicit
  348. Task object, or the name of a task. If you specify a name,
  349. all tasks with the indicated name are removed. Returns the
  350. number of tasks removed. """
  351. if isinstance(taskOrName, types.StringTypes):
  352. tasks = self.mgr.findTasks(taskOrName)
  353. return self.mgr.remove(tasks)
  354. elif isinstance(taskOrName, AsyncTask):
  355. return self.mgr.remove(taskOrName)
  356. elif isinstance(taskOrName, types.ListType):
  357. for task in taskOrName:
  358. self.remove(task)
  359. else:
  360. self.notify.error('remove takes a string or a Task')
  361. def removeTasksMatching(self, taskPattern):
  362. """Removes all tasks whose names match the pattern, which can
  363. include standard shell globbing characters like *, ?, and [].
  364. See also remove().
  365. Returns the number of tasks removed.
  366. """
  367. tasks = self.mgr.findTasksMatching(GlobPattern(taskPattern))
  368. return self.mgr.remove(tasks)
  369. def step(self):
  370. """Invokes the task manager for one frame, and then returns.
  371. Normally, this executes each task exactly once, though task
  372. chains that are in sub-threads or that have frame budgets
  373. might execute their tasks differently. """
  374. # Replace keyboard interrupt handler during task list processing
  375. # so we catch the keyboard interrupt but don't handle it until
  376. # after task list processing is complete.
  377. self.fKeyboardInterrupt = 0
  378. self.interruptCount = 0
  379. signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
  380. startFrameTime = self.globalClock.getRealTime()
  381. self.mgr.poll()
  382. # This is the spot for an internal yield function
  383. nextTaskTime = self.mgr.getNextWakeTime()
  384. self.doYield(startFrameTime, nextTaskTime)
  385. # Restore default interrupt handler
  386. signal.signal(signal.SIGINT, signal.default_int_handler)
  387. if self.fKeyboardInterrupt:
  388. raise KeyboardInterrupt
  389. def run(self):
  390. """Starts the task manager running. Does not return until an
  391. exception is encountered (including KeyboardInterrupt). """
  392. # Set the clock to have last frame's time in case we were
  393. # Paused at the prompt for a long time
  394. t = self.globalClock.getFrameTime()
  395. timeDelta = t - self.globalClock.getRealTime()
  396. self.globalClock.setRealTime(t)
  397. messenger.send("resetClock", [timeDelta])
  398. if self.resumeFunc != None:
  399. self.resumeFunc()
  400. if self.stepping:
  401. self.step()
  402. else:
  403. self.running = True
  404. while self.running:
  405. try:
  406. if len(self._frameProfileQueue):
  407. numFrames, session, callback = self._frameProfileQueue.pop()
  408. def _profileFunc(numFrames=numFrames):
  409. self._doProfiledFrames(numFrames)
  410. session.setFunc(_profileFunc)
  411. session.run()
  412. _profileFunc = None
  413. if callback:
  414. callback()
  415. session.release()
  416. else:
  417. self.step()
  418. except KeyboardInterrupt:
  419. self.stop()
  420. except IOError, ioError:
  421. code, message = self._unpackIOError(ioError)
  422. # Since upgrading to Python 2.4.1, pausing the execution
  423. # often gives this IOError during the sleep function:
  424. # IOError: [Errno 4] Interrupted function call
  425. # So, let's just handle that specific exception and stop.
  426. # All other IOErrors should still get raised.
  427. # Only problem: legit IOError 4s will be obfuscated.
  428. if code == 4:
  429. self.stop()
  430. else:
  431. raise
  432. except Exception, e:
  433. if self.extendedExceptions:
  434. self.stop()
  435. print_exc_plus()
  436. else:
  437. if (ExceptionVarDump.wantStackDumpLog and
  438. ExceptionVarDump.dumpOnExceptionInit):
  439. ExceptionVarDump._varDump__print(e)
  440. raise
  441. except:
  442. if self.extendedExceptions:
  443. self.stop()
  444. print_exc_plus()
  445. else:
  446. raise
  447. self.mgr.stopThreads()
  448. def _unpackIOError(self, ioError):
  449. # IOError unpack from http://www.python.org/doc/essays/stdexceptions/
  450. # this needs to be in its own method, exceptions that occur inside
  451. # a nested try block are not caught by the inner try block's except
  452. try:
  453. (code, message) = ioError
  454. except:
  455. code = 0
  456. message = ioError
  457. return code, message
  458. def stop(self):
  459. # Set a flag so we will stop before beginning next frame
  460. self.running = False
  461. def __tryReplaceTaskMethod(self, task, oldMethod, newFunction):
  462. if not isinstance(task, PythonTask):
  463. return 0
  464. method = task.getFunction()
  465. if (type(method) == types.MethodType):
  466. function = method.im_func
  467. else:
  468. function = method
  469. if (function == oldMethod):
  470. import new
  471. newMethod = new.instancemethod(newFunction,
  472. method.im_self,
  473. method.im_class)
  474. task.setFunction(newMethod)
  475. # Found a match
  476. return 1
  477. return 0
  478. def replaceMethod(self, oldMethod, newFunction):
  479. numFound = 0
  480. for task in self.getAllTasks():
  481. numFound += self.__tryReplaceTaskMethod(task, oldMethod, newFunction)
  482. return numFound
  483. def popupControls(self):
  484. from direct.tkpanels import TaskManagerPanel
  485. return TaskManagerPanel.TaskManagerPanel(self)
  486. def getProfileSession(self, name=None):
  487. # call to get a profile session that you can modify before passing to profileFrames()
  488. if name is None:
  489. name = 'taskMgrFrameProfile'
  490. # Defer this import until we need it: some Python
  491. # distributions don't provide the profile and pstats modules.
  492. from direct.showbase.ProfileSession import ProfileSession
  493. return ProfileSession(name)
  494. def profileFrames(self, num=None, session=None, callback=None):
  495. if num is None:
  496. num = 1
  497. if session is None:
  498. session = self.getProfileSession()
  499. # make sure the profile session doesn't get destroyed before we're done with it
  500. session.acquire()
  501. self._frameProfileQueue.push((num, session, callback))
  502. def _doProfiledFrames(self, numFrames):
  503. for i in xrange(numFrames):
  504. result = self.step()
  505. return result
  506. def getProfileFrames(self):
  507. return self._profileFrames.get()
  508. def getProfileFramesSV(self):
  509. return self._profileFrames
  510. def setProfileFrames(self, profileFrames):
  511. self._profileFrames.set(profileFrames)
  512. if (not self._frameProfiler) and profileFrames:
  513. # import here due to import dependencies
  514. from direct.task.FrameProfiler import FrameProfiler
  515. self._frameProfiler = FrameProfiler()
  516. def getProfileTasks(self):
  517. return self._profileTasks.get()
  518. def getProfileTasksSV(self):
  519. return self._profileTasks
  520. def setProfileTasks(self, profileTasks):
  521. self._profileTasks.set(profileTasks)
  522. if (not self._taskProfiler) and profileTasks:
  523. # import here due to import dependencies
  524. from direct.task.TaskProfiler import TaskProfiler
  525. self._taskProfiler = TaskProfiler()
  526. def logTaskProfiles(self, name=None):
  527. if self._taskProfiler:
  528. self._taskProfiler.logProfiles(name)
  529. def flushTaskProfiles(self, name=None):
  530. if self._taskProfiler:
  531. self._taskProfiler.flush(name)
  532. def _setProfileTask(self, task):
  533. if self._taskProfileInfo.session:
  534. self._taskProfileInfo.session.release()
  535. self._taskProfileInfo.session = None
  536. self._taskProfileInfo = ScratchPad(
  537. taskFunc = task.getFunction(),
  538. taskArgs = task.getArgs(),
  539. task = task,
  540. profiled = False,
  541. session = None,
  542. )
  543. # Temporarily replace the task's own function with our
  544. # _profileTask method.
  545. task.setFunction(self._profileTask)
  546. task.setArgs([self._taskProfileInfo], True)
  547. def _profileTask(self, profileInfo, task):
  548. # This is called instead of the task function when we have
  549. # decided to profile a task.
  550. assert profileInfo.task == task
  551. # don't profile the same task twice in a row
  552. assert not profileInfo.profiled
  553. # Restore the task's proper function for next time.
  554. appendTask = False
  555. taskArgs = profileInfo.taskArgs
  556. if taskArgs and taskArgs[-1] == task:
  557. appendTask = True
  558. taskArgs = taskArgs[:-1]
  559. task.setArgs(taskArgs, appendTask)
  560. task.setFunction(profileInfo.taskFunc)
  561. # Defer this import until we need it: some Python
  562. # distributions don't provide the profile and pstats modules.
  563. from direct.showbase.ProfileSession import ProfileSession
  564. profileSession = ProfileSession('profiled-task-%s' % task.getName(),
  565. Functor(profileInfo.taskFunc, *profileInfo.taskArgs))
  566. ret = profileSession.run()
  567. # set these values *after* profiling in case we're profiling the TaskProfiler
  568. profileInfo.session = profileSession
  569. profileInfo.profiled = True
  570. return ret
  571. def _hasProfiledDesignatedTask(self):
  572. # have we run a profile of the designated task yet?
  573. return self._taskProfileInfo.profiled
  574. def _getLastTaskProfileSession(self):
  575. return self._taskProfileInfo.session
  576. def _getRandomTask(self):
  577. # Figure out when the next frame is likely to expire, so we
  578. # won't grab any tasks that are sleeping for a long time.
  579. now = globalClock.getFrameTime()
  580. avgFrameRate = globalClock.getAverageFrameRate()
  581. if avgFrameRate < .00001:
  582. avgFrameDur = 0.
  583. else:
  584. avgFrameDur = (1. / globalClock.getAverageFrameRate())
  585. next = now + avgFrameDur
  586. # Now grab a task at random, until we find one that we like.
  587. tasks = self.mgr.getTasks()
  588. i = random.randrange(tasks.getNumTasks())
  589. task = tasks.getTask(i)
  590. while not isinstance(task, PythonTask) or \
  591. task.getWakeTime() > next:
  592. tasks.removeTask(i)
  593. i = random.randrange(tasks.getNumTasks())
  594. task = tasks.getTask(i)
  595. return task
  596. def __repr__(self):
  597. return str(self.mgr)
  598. # In the event we want to do frame time managment, this is the
  599. # function to replace or overload.
  600. def doYield(self, frameStartTime, nextScheduledTaskTime):
  601. pass
  602. """
  603. def doYieldExample(self, frameStartTime, nextScheduledTaskTime):
  604. minFinTime = frameStartTime + self.MaxEpochSpeed
  605. if nextScheduledTaskTime > 0 and nextScheduledTaskTime < minFinTime:
  606. print ' Adjusting Time'
  607. minFinTime = nextScheduledTaskTime
  608. delta = minFinTime - self.globalClock.getRealTime()
  609. while(delta > 0.002):
  610. print ' sleep %s'% (delta)
  611. time.sleep(delta)
  612. delta = minFinTime - self.globalClock.getRealTime()
  613. """
  614. if __debug__:
  615. # to catch memory leaks during the tests at the bottom of the file
  616. def _startTrackingMemLeaks(self):
  617. pass
  618. def _stopTrackingMemLeaks(self):
  619. pass
  620. def _checkMemLeaks(self):
  621. pass
  622. def _runTests(self):
  623. if __debug__:
  624. tm = TaskManager()
  625. tm.setClock(ClockObject())
  626. tm.setupTaskChain("default", tickClock = True)
  627. # check for memory leaks after every test
  628. tm._startTrackingMemLeaks()
  629. tm._checkMemLeaks()
  630. # run-once task
  631. l = []
  632. def _testDone(task, l=l):
  633. l.append(None)
  634. return task.done
  635. tm.add(_testDone, 'testDone')
  636. tm.step()
  637. assert len(l) == 1
  638. tm.step()
  639. assert len(l) == 1
  640. _testDone = None
  641. tm._checkMemLeaks()
  642. # remove by name
  643. def _testRemoveByName(task):
  644. return task.done
  645. tm.add(_testRemoveByName, 'testRemoveByName')
  646. assert tm.remove('testRemoveByName') == 1
  647. assert tm.remove('testRemoveByName') == 0
  648. _testRemoveByName = None
  649. tm._checkMemLeaks()
  650. # duplicate named tasks
  651. def _testDupNamedTasks(task):
  652. return task.done
  653. tm.add(_testDupNamedTasks, 'testDupNamedTasks')
  654. tm.add(_testDupNamedTasks, 'testDupNamedTasks')
  655. assert tm.remove('testRemoveByName') == 0
  656. _testDupNamedTasks = None
  657. tm._checkMemLeaks()
  658. # continued task
  659. l = []
  660. def _testCont(task, l = l):
  661. l.append(None)
  662. return task.cont
  663. tm.add(_testCont, 'testCont')
  664. tm.step()
  665. assert len(l) == 1
  666. tm.step()
  667. assert len(l) == 2
  668. tm.remove('testCont')
  669. _testCont = None
  670. tm._checkMemLeaks()
  671. # continue until done task
  672. l = []
  673. def _testContDone(task, l = l):
  674. l.append(None)
  675. if len(l) >= 2:
  676. return task.done
  677. else:
  678. return task.cont
  679. tm.add(_testContDone, 'testContDone')
  680. tm.step()
  681. assert len(l) == 1
  682. tm.step()
  683. assert len(l) == 2
  684. tm.step()
  685. assert len(l) == 2
  686. assert not tm.hasTaskNamed('testContDone')
  687. _testContDone = None
  688. tm._checkMemLeaks()
  689. # hasTaskNamed
  690. def _testHasTaskNamed(task):
  691. return task.done
  692. tm.add(_testHasTaskNamed, 'testHasTaskNamed')
  693. assert tm.hasTaskNamed('testHasTaskNamed')
  694. tm.step()
  695. assert not tm.hasTaskNamed('testHasTaskNamed')
  696. _testHasTaskNamed = None
  697. tm._checkMemLeaks()
  698. # task sort
  699. l = []
  700. def _testPri1(task, l = l):
  701. l.append(1)
  702. return task.cont
  703. def _testPri2(task, l = l):
  704. l.append(2)
  705. return task.cont
  706. tm.add(_testPri1, 'testPri1', sort = 1)
  707. tm.add(_testPri2, 'testPri2', sort = 2)
  708. tm.step()
  709. assert len(l) == 2
  710. assert l == [1, 2,]
  711. tm.step()
  712. assert len(l) == 4
  713. assert l == [1, 2, 1, 2,]
  714. tm.remove('testPri1')
  715. tm.remove('testPri2')
  716. _testPri1 = None
  717. _testPri2 = None
  718. tm._checkMemLeaks()
  719. # task extraArgs
  720. l = []
  721. def _testExtraArgs(arg1, arg2, l=l):
  722. l.extend([arg1, arg2,])
  723. return done
  724. tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
  725. tm.step()
  726. assert len(l) == 2
  727. assert l == [4, 5,]
  728. _testExtraArgs = None
  729. tm._checkMemLeaks()
  730. # task appendTask
  731. l = []
  732. def _testAppendTask(arg1, arg2, task, l=l):
  733. l.extend([arg1, arg2,])
  734. return task.done
  735. tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
  736. tm.step()
  737. assert len(l) == 2
  738. assert l == [4, 5,]
  739. _testAppendTask = None
  740. tm._checkMemLeaks()
  741. # task uponDeath
  742. l = []
  743. def _uponDeathFunc(task, l=l):
  744. l.append(task.name)
  745. def _testUponDeath(task):
  746. return done
  747. tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
  748. tm.step()
  749. assert len(l) == 1
  750. assert l == ['testUponDeath']
  751. _testUponDeath = None
  752. _uponDeathFunc = None
  753. tm._checkMemLeaks()
  754. # task owner
  755. class _TaskOwner:
  756. def _addTask(self, task):
  757. self.addedTaskName = task.name
  758. def _clearTask(self, task):
  759. self.clearedTaskName = task.name
  760. to = _TaskOwner()
  761. l = []
  762. def _testOwner(task):
  763. return done
  764. tm.add(_testOwner, 'testOwner', owner=to)
  765. tm.step()
  766. assert getattr(to, 'addedTaskName', None) == 'testOwner'
  767. assert getattr(to, 'clearedTaskName', None) == 'testOwner'
  768. _testOwner = None
  769. del to
  770. _TaskOwner = None
  771. tm._checkMemLeaks()
  772. doLaterTests = [0,]
  773. # doLater
  774. l = []
  775. def _testDoLater1(task, l=l):
  776. l.append(1)
  777. def _testDoLater2(task, l=l):
  778. l.append(2)
  779. def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
  780. if task.time > .03:
  781. assert l == [1, 2,]
  782. doLaterTests[0] -= 1
  783. return task.done
  784. return task.cont
  785. tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
  786. tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
  787. doLaterTests[0] += 1
  788. # make sure we run this task after the doLaters if they all occur on the same frame
  789. tm.add(_monitorDoLater, 'monitorDoLater', sort=10)
  790. _testDoLater1 = None
  791. _testDoLater2 = None
  792. _monitorDoLater = None
  793. # don't check until all the doLaters are finished
  794. #tm._checkMemLeaks()
  795. # doLater sort
  796. l = []
  797. def _testDoLaterPri1(task, l=l):
  798. l.append(1)
  799. def _testDoLaterPri2(task, l=l):
  800. l.append(2)
  801. def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
  802. if task.time > .02:
  803. assert l == [1, 2,]
  804. doLaterTests[0] -= 1
  805. return task.done
  806. return task.cont
  807. tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', sort=1)
  808. tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', sort=2)
  809. doLaterTests[0] += 1
  810. # make sure we run this task after the doLaters if they all occur on the same frame
  811. tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', sort=10)
  812. _testDoLaterPri1 = None
  813. _testDoLaterPri2 = None
  814. _monitorDoLaterPri = None
  815. # don't check until all the doLaters are finished
  816. #tm._checkMemLeaks()
  817. # doLater extraArgs
  818. l = []
  819. def _testDoLaterExtraArgs(arg1, l=l):
  820. l.append(arg1)
  821. def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
  822. if task.time > .02:
  823. assert l == [3,]
  824. doLaterTests[0] -= 1
  825. return task.done
  826. return task.cont
  827. tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
  828. doLaterTests[0] += 1
  829. # make sure we run this task after the doLaters if they all occur on the same frame
  830. tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10)
  831. _testDoLaterExtraArgs = None
  832. _monitorDoLaterExtraArgs = None
  833. # don't check until all the doLaters are finished
  834. #tm._checkMemLeaks()
  835. # doLater appendTask
  836. l = []
  837. def _testDoLaterAppendTask(arg1, task, l=l):
  838. assert task.name == 'testDoLaterAppendTask'
  839. l.append(arg1)
  840. def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
  841. if task.time > .02:
  842. assert l == [4,]
  843. doLaterTests[0] -= 1
  844. return task.done
  845. return task.cont
  846. tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
  847. extraArgs=[4,], appendTask=True)
  848. doLaterTests[0] += 1
  849. # make sure we run this task after the doLaters if they all occur on the same frame
  850. tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10)
  851. _testDoLaterAppendTask = None
  852. _monitorDoLaterAppendTask = None
  853. # don't check until all the doLaters are finished
  854. #tm._checkMemLeaks()
  855. # doLater uponDeath
  856. l = []
  857. def _testUponDeathFunc(task, l=l):
  858. assert task.name == 'testDoLaterUponDeath'
  859. l.append(10)
  860. def _testDoLaterUponDeath(arg1, l=l):
  861. return done
  862. def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
  863. if task.time > .02:
  864. assert l == [10,]
  865. doLaterTests[0] -= 1
  866. return task.done
  867. return task.cont
  868. tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
  869. uponDeath=_testUponDeathFunc)
  870. doLaterTests[0] += 1
  871. # make sure we run this task after the doLaters if they all occur on the same frame
  872. tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', sort=10)
  873. _testUponDeathFunc = None
  874. _testDoLaterUponDeath = None
  875. _monitorDoLaterUponDeath = None
  876. # don't check until all the doLaters are finished
  877. #tm._checkMemLeaks()
  878. # doLater owner
  879. class _DoLaterOwner:
  880. def _addTask(self, task):
  881. self.addedTaskName = task.name
  882. def _clearTask(self, task):
  883. self.clearedTaskName = task.name
  884. doLaterOwner = _DoLaterOwner()
  885. l = []
  886. def _testDoLaterOwner(l=l):
  887. pass
  888. def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
  889. doLaterTests=doLaterTests):
  890. if task.time > .02:
  891. assert getattr(doLaterOwner, 'addedTaskName', None) == 'testDoLaterOwner'
  892. assert getattr(doLaterOwner, 'clearedTaskName', None) == 'testDoLaterOwner'
  893. doLaterTests[0] -= 1
  894. return task.done
  895. return task.cont
  896. tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
  897. owner=doLaterOwner)
  898. doLaterTests[0] += 1
  899. # make sure we run this task after the doLaters if they all occur on the same frame
  900. tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', sort=10)
  901. _testDoLaterOwner = None
  902. _monitorDoLaterOwner = None
  903. del doLaterOwner
  904. _DoLaterOwner = None
  905. # don't check until all the doLaters are finished
  906. #tm._checkMemLeaks()
  907. # run the doLater tests
  908. while doLaterTests[0] > 0:
  909. tm.step()
  910. del doLaterTests
  911. tm._checkMemLeaks()
  912. # getTasks
  913. def _testGetTasks(task):
  914. return task.cont
  915. # No doLaterProcessor in the new world.
  916. assert len(tm.getTasks()) == 0
  917. tm.add(_testGetTasks, 'testGetTasks1')
  918. assert len(tm.getTasks()) == 1
  919. assert (tm.getTasks()[0].name == 'testGetTasks1' or
  920. tm.getTasks()[1].name == 'testGetTasks1')
  921. tm.add(_testGetTasks, 'testGetTasks2')
  922. tm.add(_testGetTasks, 'testGetTasks3')
  923. assert len(tm.getTasks()) == 3
  924. tm.remove('testGetTasks2')
  925. assert len(tm.getTasks()) == 2
  926. tm.remove('testGetTasks1')
  927. tm.remove('testGetTasks3')
  928. assert len(tm.getTasks()) == 0
  929. _testGetTasks = None
  930. tm._checkMemLeaks()
  931. # getDoLaters
  932. def _testGetDoLaters():
  933. pass
  934. assert len(tm.getDoLaters()) == 0
  935. tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
  936. assert len(tm.getDoLaters()) == 1
  937. assert tm.getDoLaters()[0].name == 'testDoLater1'
  938. tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
  939. tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
  940. assert len(tm.getDoLaters()) == 3
  941. tm.remove('testDoLater2')
  942. assert len(tm.getDoLaters()) == 2
  943. tm.remove('testDoLater1')
  944. tm.remove('testDoLater3')
  945. assert len(tm.getDoLaters()) == 0
  946. _testGetDoLaters = None
  947. tm._checkMemLeaks()
  948. # duplicate named doLaters removed via taskMgr.remove
  949. def _testDupNameDoLaters():
  950. pass
  951. # the doLaterProcessor is always running
  952. tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
  953. tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
  954. assert len(tm.getDoLaters()) == 2
  955. tm.remove('testDupNameDoLater')
  956. assert len(tm.getDoLaters()) == 0
  957. _testDupNameDoLaters = None
  958. tm._checkMemLeaks()
  959. # duplicate named doLaters removed via remove()
  960. def _testDupNameDoLatersRemove():
  961. pass
  962. # the doLaterProcessor is always running
  963. dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
  964. dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
  965. assert len(tm.getDoLaters()) == 2
  966. dl2.remove()
  967. assert len(tm.getDoLaters()) == 1
  968. dl1.remove()
  969. assert len(tm.getDoLaters()) == 0
  970. _testDupNameDoLatersRemove = None
  971. # nameDict etc. isn't cleared out right away with task.remove()
  972. tm._checkMemLeaks()
  973. # getTasksNamed
  974. def _testGetTasksNamed(task):
  975. return task.cont
  976. assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
  977. tm.add(_testGetTasksNamed, 'testGetTasksNamed')
  978. assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
  979. assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
  980. tm.add(_testGetTasksNamed, 'testGetTasksNamed')
  981. tm.add(_testGetTasksNamed, 'testGetTasksNamed')
  982. assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
  983. tm.remove('testGetTasksNamed')
  984. assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
  985. _testGetTasksNamed = None
  986. tm._checkMemLeaks()
  987. # removeTasksMatching
  988. def _testRemoveTasksMatching(task):
  989. return task.cont
  990. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
  991. assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
  992. tm.removeTasksMatching('testRemoveTasksMatching')
  993. assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
  994. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
  995. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
  996. assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
  997. assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
  998. tm.removeTasksMatching('testRemoveTasksMatching*')
  999. assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
  1000. assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
  1001. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
  1002. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
  1003. assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
  1004. assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
  1005. tm.removeTasksMatching('testRemoveTasksMatching?a')
  1006. assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
  1007. assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
  1008. _testRemoveTasksMatching = None
  1009. tm._checkMemLeaks()
  1010. # create Task object and add to mgr
  1011. l = []
  1012. def _testTaskObj(task, l=l):
  1013. l.append(None)
  1014. return task.cont
  1015. t = Task(_testTaskObj)
  1016. tm.add(t, 'testTaskObj')
  1017. tm.step()
  1018. assert len(l) == 1
  1019. tm.step()
  1020. assert len(l) == 2
  1021. tm.remove('testTaskObj')
  1022. tm.step()
  1023. assert len(l) == 2
  1024. _testTaskObj = None
  1025. tm._checkMemLeaks()
  1026. # remove Task via task.remove()
  1027. l = []
  1028. def _testTaskObjRemove(task, l=l):
  1029. l.append(None)
  1030. return task.cont
  1031. t = Task(_testTaskObjRemove)
  1032. tm.add(t, 'testTaskObjRemove')
  1033. tm.step()
  1034. assert len(l) == 1
  1035. tm.step()
  1036. assert len(l) == 2
  1037. t.remove()
  1038. tm.step()
  1039. assert len(l) == 2
  1040. del t
  1041. _testTaskObjRemove = None
  1042. tm._checkMemLeaks()
  1043. """
  1044. # this test fails, and it's not clear what the correct behavior should be.
  1045. # sort passed to Task.__init__ is always overridden by taskMgr.add()
  1046. # even if no sort is specified, and calling Task.setSort() has no
  1047. # effect on the taskMgr's behavior.
  1048. # set/get Task sort
  1049. l = []
  1050. def _testTaskObjSort(arg, task, l=l):
  1051. l.append(arg)
  1052. return task.cont
  1053. t1 = Task(_testTaskObjSort, sort=1)
  1054. t2 = Task(_testTaskObjSort, sort=2)
  1055. tm.add(t1, 'testTaskObjSort1', extraArgs=['a',], appendTask=True)
  1056. tm.add(t2, 'testTaskObjSort2', extraArgs=['b',], appendTask=True)
  1057. tm.step()
  1058. assert len(l) == 2
  1059. assert l == ['a', 'b']
  1060. assert t1.getSort() == 1
  1061. assert t2.getSort() == 2
  1062. t1.setSort(3)
  1063. assert t1.getSort() == 3
  1064. tm.step()
  1065. assert len(l) == 4
  1066. assert l == ['a', 'b', 'b', 'a',]
  1067. t1.remove()
  1068. t2.remove()
  1069. tm.step()
  1070. assert len(l) == 4
  1071. del t1
  1072. del t2
  1073. _testTaskObjSort = None
  1074. tm._checkMemLeaks()
  1075. """
  1076. del l
  1077. tm.destroy()
  1078. del tm
  1079. if __debug__:
  1080. def checkLeak():
  1081. import sys
  1082. import gc
  1083. gc.enable()
  1084. from direct.showbase.DirectObject import DirectObject
  1085. class TestClass(DirectObject):
  1086. def doTask(self, task):
  1087. return task.done
  1088. obj = TestClass()
  1089. startRefCount = sys.getrefcount(obj)
  1090. print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
  1091. print '** addTask'
  1092. t = obj.addTask(obj.doTask, 'test')
  1093. print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
  1094. print 'task.getRefCount(): %s' % t.getRefCount()
  1095. print '** removeTask'
  1096. obj.removeTask('test')
  1097. print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
  1098. print 'task.getRefCount(): %s' % t.getRefCount()
  1099. print '** step'
  1100. taskMgr.step()
  1101. taskMgr.step()
  1102. taskMgr.step()
  1103. print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
  1104. print 'task.getRefCount(): %s' % t.getRefCount()
  1105. print '** task release'
  1106. t = None
  1107. print 'sys.getrefcount(obj): %s' % sys.getrefcount(obj)
  1108. assert sys.getrefcount(obj) == startRefCount