TaskNew.py 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  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']
  6. from direct.directnotify.DirectNotifyGlobal import *
  7. from direct.showbase import ExceptionVarDump
  8. import signal
  9. import types
  10. import time
  11. from pandac.PandaModules import *
  12. def print_exc_plus():
  13. """
  14. Print the usual traceback information, followed by a listing of all the
  15. local variables in each frame.
  16. """
  17. import sys
  18. import traceback
  19. tb = sys.exc_info()[2]
  20. while 1:
  21. if not tb.tb_next:
  22. break
  23. tb = tb.tb_next
  24. stack = []
  25. f = tb.tb_frame
  26. while f:
  27. stack.append(f)
  28. f = f.f_back
  29. stack.reverse()
  30. traceback.print_exc()
  31. print "Locals by frame, innermost last"
  32. for frame in stack:
  33. print
  34. print "Frame %s in %s at line %s" % (frame.f_code.co_name,
  35. frame.f_code.co_filename,
  36. frame.f_lineno)
  37. for key, value in frame.f_locals.items():
  38. print "\t%20s = " % key,
  39. #We have to be careful not to cause a new error in our error
  40. #printer! Calling str() on an unknown object could cause an
  41. #error we don't want.
  42. try:
  43. print value
  44. except:
  45. print "<ERROR WHILE PRINTING VALUE>"
  46. # For historical purposes, we remap the C++-defined enumeration to
  47. # these Python names, and define them both at the module level, here,
  48. # and at the class level (below). The preferred access is via the
  49. # class level.
  50. done = AsyncTask.DSDone
  51. cont = AsyncTask.DSCont
  52. again = AsyncTask.DSAgain
  53. pickup = AsyncTask.DSPickup
  54. # Alias PythonTask to Task for historical purposes.
  55. Task = PythonTask
  56. # Copy the module-level enums above into the class level. This funny
  57. # syntax is necessary because it's a C++-wrapped extension type, not a
  58. # true Python class.
  59. Task.DtoolClassDict['done'] = done
  60. Task.DtoolClassDict['cont'] = cont
  61. Task.DtoolClassDict['again'] = again
  62. Task.DtoolClassDict['pickup'] = pickup
  63. class TaskManager:
  64. notify = directNotify.newCategory("TaskManager")
  65. extendedExceptions = False
  66. MaxEpochSpeed = 1.0/30.0
  67. def __init__(self):
  68. self.mgr = AsyncTaskManager('TaskManager')
  69. self.resumeFunc = None
  70. self.globalClock = self.mgr.getClock()
  71. self.stepping = False
  72. self.running = False
  73. self.fKeyboardInterrupt = False
  74. self.interruptCount = 0
  75. def destroy(self):
  76. self.mgr.cleanup()
  77. def setClock(self, clockObject):
  78. self.mgr.setClock(clockObject)
  79. self.globalClock = clockObject
  80. def keyboardInterruptHandler(self, signalNumber, stackFrame):
  81. self.fKeyboardInterrupt = 1
  82. self.interruptCount += 1
  83. if self.interruptCount == 1:
  84. print '* interrupt by keyboard'
  85. elif self.interruptCount == 2:
  86. print '** waiting for end of frame before interrupting...'
  87. # The user must really want to interrupt this process
  88. # Next time around invoke the default handler
  89. signal.signal(signal.SIGINT, self.invokeDefaultHandler)
  90. def setupTaskChain(self, chainName, numThreads = None, tickClock = None,
  91. threadPriority = None, frameBudget = None,
  92. timeslicePriority = None):
  93. """Defines a new task chain. Each task chain executes tasks
  94. potentially in parallel with all of the other task chains (if
  95. numThreads is more than zero). When a new task is created, it
  96. may be associated with any of the task chains, by name (or you
  97. can move a task to another task chain with
  98. task.setTaskChain()). You can have any number of task chains,
  99. but each must have a unique name.
  100. numThreads is the number of threads to allocate for this task
  101. chain. If it is 1 or more, then the tasks on this task chain
  102. will execute in parallel with the tasks on other task chains.
  103. If it is greater than 1, then the tasks on this task chain may
  104. execute in parallel with themselves (within tasks of the same
  105. sort value).
  106. If tickClock is True, then this task chain will be responsible
  107. for ticking the global clock each frame (and thereby
  108. incrementing the frame counter). There should be just one
  109. task chain responsible for ticking the clock, and usually it
  110. is the default, unnamed task chain.
  111. threadPriority specifies the priority level to assign to
  112. threads on this task chain. It may be one of TPLow, TPNormal,
  113. TPHigh, or TPUrgent. This is passed to the underlying
  114. threading system to control the way the threads are scheduled.
  115. frameBudget is the maximum amount of time (in seconds) to
  116. allow this task chain to run per frame. Set it to -1 to mean
  117. no limit (the default). It's not directly related to
  118. threadPriority.
  119. timeslicePriority is False in the default mode, in which each
  120. task runs exactly once each frame, round-robin style,
  121. regardless of the task's priority value; or True to change the
  122. meaning of priority so that certain tasks are run less often,
  123. in proportion to their time used and to their priority value.
  124. See AsyncTaskManager.setTimeslicePriority() for more.
  125. """
  126. chain = self.mgr.makeTaskChain(chainName)
  127. if numThreads is not None:
  128. chain.setNumThreads(numThreads)
  129. if tickClock is not None:
  130. chain.setTickClock(tickClock)
  131. if threadPriority is not None:
  132. chain.setThreadPriority(threadPriority)
  133. if frameBudget is not None:
  134. chain.setFrameBudget(frameBudget)
  135. if timeslicePriority is not None:
  136. chain.setTimeslicePriority(timeslicePriority)
  137. def hasTaskNamed(self, taskName):
  138. """Returns true if there is at least one task, active or
  139. sleeping, with the indicated name. """
  140. return bool(self.mgr.findTask(taskName))
  141. def getTasksNamed(self, taskName):
  142. """Returns a list of all tasks, active or sleeping, with the
  143. indicated name. """
  144. return self.__makeTaskList(self.mgr.findTasks(taskName))
  145. def getTasks(self):
  146. """Returns list of all active tasks in arbitrary order. """
  147. return self.__makeTaskList(self.mgr.getActiveTasks())
  148. def getDoLaters(self):
  149. """Returns list of all sleeping tasks in arbitrary order. """
  150. return self.__makeTaskList(self.mgr.getSleepingTasks())
  151. def __makeTaskList(self, taskCollection):
  152. l = []
  153. for i in range(taskCollection.getNumTasks()):
  154. l.append(taskCollection.getTask(i))
  155. return l
  156. def doMethodLater(self, delayTime, funcOrTask, name, extraArgs = None,
  157. sort = None, priority = None, taskChain = None,
  158. uponDeath = None, appendTask = False, owner = None):
  159. """Adds a task to be performed at some time in the future.
  160. This is identical to add(), except that the specified
  161. delayTime is applied to the Task object first, which means
  162. that the task will not begin executing until at least the
  163. indicated delayTime (in seconds) has elapsed.
  164. After delayTime has elapsed, the task will become active, and
  165. will run in the soonest possible frame thereafter. If you
  166. wish to specify a task that will run in the next frame, use a
  167. delayTime of 0.
  168. """
  169. if delayTime < 0:
  170. assert self.notify.warning('doMethodLater: added task: %s with negative delay: %s' % (name, delayTime))
  171. task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
  172. task.setDelay(delayTime)
  173. self.mgr.add(task)
  174. return task
  175. def add(self, funcOrTask, name, sort = None, extraArgs = None,
  176. priority = None, uponDeath = None, appendTask = False,
  177. taskChain = None, owner = None):
  178. """
  179. Add a new task to the taskMgr. The task will begin executing
  180. immediately, or next frame if its sort value has already
  181. passed this frame.
  182. The parameters are:
  183. funcOrTask - either an existing Task object (not already added
  184. to the task manager), or a callable function object. If this
  185. is a function, a new Task object will be created and returned.
  186. name - the name to assign to the Task.
  187. sort - the sort value to assign the task. The default sort is
  188. 0. Within a particular task chain, it is guaranteed that the
  189. tasks with a lower sort value will all run before tasks with a
  190. higher sort value run.
  191. extraArgs - the list of arguments to pass to the task
  192. function. If this is omitted, the list is just the task
  193. object itself.
  194. priority - the priority at which to run the task. The default
  195. priority is 0. For historical purposes, if you specify a
  196. priority without also specifying a sort, the priority value is
  197. understood to actually be a sort value. (Previously, there
  198. was no priority value, only a sort value, and it was called
  199. "priority".)
  200. uponDeath - a function to call when the task terminates,
  201. either because it has run to completion, or because
  202. appendTask - a boolean flag. If this is true, then the task
  203. object itself will be appended to the extraArgs list before
  204. calling the function.
  205. taskChain - the name of the task chain to assign the task to.
  206. owner - an option Python object that is declared as the
  207. "owner" of this task for maintenance purposes. The owner must
  208. have two methods: owner._addTask(self, task), which is called
  209. when the task begins, and owner._clearTask(self, task), which
  210. is called when the task terminates.
  211. The return value is the new Task object that has been added.
  212. """
  213. task = self.__setupTask(funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath)
  214. self.mgr.add(task)
  215. return task
  216. def __setupTask(self, funcOrTask, name, priority, sort, extraArgs, taskChain, appendTask, owner, uponDeath):
  217. if isinstance(funcOrTask, PythonTask):
  218. task = funcOrTask
  219. elif callable(funcOrTask):
  220. task = PythonTask(funcOrTask)
  221. else:
  222. self.notify.error(
  223. 'add: Tried to add a task that was not a Task or a func')
  224. assert isinstance(name, types.StringTypes), 'Name must be a string type'
  225. if extraArgs is None:
  226. extraArgs = []
  227. appendTask = True
  228. task.setArgs(extraArgs, appendTask)
  229. task.setName(name)
  230. # For historical reasons, if priority is specified but not
  231. # sort, it really means sort.
  232. if priority is not None and sort is None:
  233. task.setSort(priority)
  234. else:
  235. if priority is not None:
  236. task.setPriority(priority)
  237. if sort is not None:
  238. task.setSort(sort)
  239. if extraArgs is None:
  240. extraArgs = []
  241. appendTask = True
  242. task.setArgs(extraArgs, appendTask)
  243. if taskChain is not None:
  244. task.setTaskChain(taskChain)
  245. if owner is not None:
  246. task.setOwner(owner)
  247. if uponDeath is not None:
  248. task.setUponDeath(uponDeath)
  249. return task
  250. def remove(self, taskOrName):
  251. """Removes a task from the task manager. The task is
  252. immediately stopped, almost as if it had returned task.done.
  253. You may specify either an explicit Task object, or the name of
  254. a task. If you specify a name, all tasks with the indicated
  255. name are removed. Returns the number of tasks removed. """
  256. if isinstance(taskOrName, types.StringTypes):
  257. tasks = self.mgr.findTasks(taskOrName)
  258. return self.mgr.remove(tasks)
  259. elif isinstance(taskOrName, AsyncTask):
  260. return self.mgr.remove(taskOrName)
  261. else:
  262. self.notify.error('remove takes a string or a Task')
  263. def removeTasksMatching(self, taskPattern):
  264. """Removes all tasks whose names match the pattern, which can
  265. include standard shell globbing characters like *, ?, and [].
  266. See also remove().
  267. Returns the number of tasks removed.
  268. """
  269. tasks = self.mgr.findTasksMatching(GlobPattern(taskPattern))
  270. return self.mgr.remove(tasks)
  271. def step(self):
  272. """Invokes the task manager for one frame, and then returns.
  273. Normally, this executes each task exactly once, though task
  274. chains that are in sub-threads or that have frame budgets
  275. might execute their tasks differently. """
  276. self.__doStep()
  277. self.mgr.stopThreads()
  278. def __doStep(self):
  279. # Replace keyboard interrupt handler during task list processing
  280. # so we catch the keyboard interrupt but don't handle it until
  281. # after task list processing is complete.
  282. self.fKeyboardInterrupt = 0
  283. self.interruptCount = 0
  284. signal.signal(signal.SIGINT, self.keyboardInterruptHandler)
  285. startFrameTime = self.globalClock.getRealTime()
  286. self.mgr.poll()
  287. # This is the spot for an internal yield function
  288. nextTaskTime = self.mgr.getNextWakeTime()
  289. self.doYield(startFrameTime, nextTaskTime)
  290. # Restore default interrupt handler
  291. signal.signal(signal.SIGINT, signal.default_int_handler)
  292. if self.fKeyboardInterrupt:
  293. raise KeyboardInterrupt
  294. def run(self):
  295. """Starts the task manager running. Does not return until an
  296. exception is encountered (including KeyboardInterrupt). """
  297. # Set the clock to have last frame's time in case we were
  298. # Paused at the prompt for a long time
  299. t = self.globalClock.getFrameTime()
  300. timeDelta = t - globalClock.getRealTime()
  301. self.globalClock.setRealTime(t)
  302. messenger.send("resetClock", [timeDelta])
  303. if self.resumeFunc != None:
  304. self.resumeFunc()
  305. if self.stepping:
  306. self.__doStep()
  307. else:
  308. self.running = True
  309. while self.running:
  310. try:
  311. self.__doStep()
  312. except KeyboardInterrupt:
  313. self.stop()
  314. except IOError, ioError:
  315. code, message = self._unpackIOError(ioError)
  316. # Since upgrading to Python 2.4.1, pausing the execution
  317. # often gives this IOError during the sleep function:
  318. # IOError: [Errno 4] Interrupted function call
  319. # So, let's just handle that specific exception and stop.
  320. # All other IOErrors should still get raised.
  321. # Only problem: legit IOError 4s will be obfuscated.
  322. if code == 4:
  323. self.stop()
  324. else:
  325. raise
  326. except Exception, e:
  327. if self.extendedExceptions:
  328. self.stop()
  329. print_exc_plus()
  330. else:
  331. if (ExceptionVarDump.wantVariableDump and
  332. ExceptionVarDump.dumpOnExceptionInit):
  333. ExceptionVarDump._varDump__print(e)
  334. raise
  335. except:
  336. if self.extendedExceptions:
  337. self.stop()
  338. print_exc_plus()
  339. else:
  340. raise
  341. self.mgr.stopThreads()
  342. def _unpackIOError(self, ioError):
  343. # IOError unpack from http://www.python.org/doc/essays/stdexceptions/
  344. # this needs to be in its own method, exceptions that occur inside
  345. # a nested try block are not caught by the inner try block's except
  346. try:
  347. (code, message) = ioError
  348. except:
  349. code = 0
  350. message = ioError
  351. return code, message
  352. def stop(self):
  353. # Set a flag so we will stop before beginning next frame
  354. self.running = False
  355. def __repr__(self):
  356. return str(self.mgr)
  357. # In the event we want to do frame time managment, this is the
  358. # function to replace or overload.
  359. def doYield(self, frameStartTime, nextScheduledTaskTime):
  360. pass
  361. """
  362. def doYieldExample(self, frameStartTime, nextScheduledTaskTime):
  363. minFinTime = frameStartTime + self.MaxEpochSpeed
  364. if nextScheduledTaskTime > 0 and nextScheduledTaskTime < minFinTime:
  365. print ' Adjusting Time'
  366. minFinTime = nextScheduledTaskTime
  367. delta = minFinTime - self.globalClock.getRealTime()
  368. while(delta > 0.002):
  369. print ' sleep %s'% (delta)
  370. time.sleep(delta)
  371. delta = minFinTime - self.globalClock.getRealTime()
  372. """
  373. if __debug__:
  374. # to catch memory leaks during the tests at the bottom of the file
  375. def _startTrackingMemLeaks(self):
  376. pass
  377. def _stopTrackingMemLeaks(self):
  378. pass
  379. def _checkMemLeaks(self):
  380. pass
  381. def _runTests(self):
  382. if __debug__:
  383. tm = TaskManager()
  384. tm.setClock(ClockObject())
  385. tm.setupTaskChain("default", tickClock = True)
  386. # check for memory leaks after every test
  387. tm._startTrackingMemLeaks()
  388. tm._checkMemLeaks()
  389. # run-once task
  390. l = []
  391. def _testDone(task, l=l):
  392. l.append(None)
  393. return task.done
  394. tm.add(_testDone, 'testDone')
  395. tm.step()
  396. assert len(l) == 1
  397. tm.step()
  398. assert len(l) == 1
  399. _testDone = None
  400. tm._checkMemLeaks()
  401. # remove by name
  402. def _testRemoveByName(task):
  403. return task.done
  404. tm.add(_testRemoveByName, 'testRemoveByName')
  405. assert tm.remove('testRemoveByName') == 1
  406. assert tm.remove('testRemoveByName') == 0
  407. _testRemoveByName = None
  408. tm._checkMemLeaks()
  409. # duplicate named tasks
  410. def _testDupNamedTasks(task):
  411. return task.done
  412. tm.add(_testDupNamedTasks, 'testDupNamedTasks')
  413. tm.add(_testDupNamedTasks, 'testDupNamedTasks')
  414. assert tm.remove('testRemoveByName') == 0
  415. _testDupNamedTasks = None
  416. tm._checkMemLeaks()
  417. # continued task
  418. l = []
  419. def _testCont(task, l = l):
  420. l.append(None)
  421. return task.cont
  422. tm.add(_testCont, 'testCont')
  423. tm.step()
  424. assert len(l) == 1
  425. tm.step()
  426. assert len(l) == 2
  427. tm.remove('testCont')
  428. _testCont = None
  429. tm._checkMemLeaks()
  430. # continue until done task
  431. l = []
  432. def _testContDone(task, l = l):
  433. l.append(None)
  434. if len(l) >= 2:
  435. return task.done
  436. else:
  437. return task.cont
  438. tm.add(_testContDone, 'testContDone')
  439. tm.step()
  440. assert len(l) == 1
  441. tm.step()
  442. assert len(l) == 2
  443. tm.step()
  444. assert len(l) == 2
  445. assert not tm.hasTaskNamed('testContDone')
  446. _testContDone = None
  447. tm._checkMemLeaks()
  448. # hasTaskNamed
  449. def _testHasTaskNamed(task):
  450. return task.done
  451. tm.add(_testHasTaskNamed, 'testHasTaskNamed')
  452. assert tm.hasTaskNamed('testHasTaskNamed')
  453. tm.step()
  454. assert not tm.hasTaskNamed('testHasTaskNamed')
  455. _testHasTaskNamed = None
  456. tm._checkMemLeaks()
  457. # task sort
  458. l = []
  459. def _testPri1(task, l = l):
  460. l.append(1)
  461. return task.cont
  462. def _testPri2(task, l = l):
  463. l.append(2)
  464. return task.cont
  465. tm.add(_testPri1, 'testPri1', sort = 1)
  466. tm.add(_testPri2, 'testPri2', sort = 2)
  467. tm.step()
  468. assert len(l) == 2
  469. assert l == [1, 2,]
  470. tm.step()
  471. assert len(l) == 4
  472. assert l == [1, 2, 1, 2,]
  473. tm.remove('testPri1')
  474. tm.remove('testPri2')
  475. _testPri1 = None
  476. _testPri2 = None
  477. tm._checkMemLeaks()
  478. # task extraArgs
  479. l = []
  480. def _testExtraArgs(arg1, arg2, l=l):
  481. l.extend([arg1, arg2,])
  482. return done
  483. tm.add(_testExtraArgs, 'testExtraArgs', extraArgs=[4,5])
  484. tm.step()
  485. assert len(l) == 2
  486. assert l == [4, 5,]
  487. _testExtraArgs = None
  488. tm._checkMemLeaks()
  489. # task appendTask
  490. l = []
  491. def _testAppendTask(arg1, arg2, task, l=l):
  492. l.extend([arg1, arg2,])
  493. return task.done
  494. tm.add(_testAppendTask, '_testAppendTask', extraArgs=[4,5], appendTask=True)
  495. tm.step()
  496. assert len(l) == 2
  497. assert l == [4, 5,]
  498. _testAppendTask = None
  499. tm._checkMemLeaks()
  500. # task uponDeath
  501. l = []
  502. def _uponDeathFunc(task, l=l):
  503. l.append(task.name)
  504. def _testUponDeath(task):
  505. return done
  506. tm.add(_testUponDeath, 'testUponDeath', uponDeath=_uponDeathFunc)
  507. tm.step()
  508. assert len(l) == 1
  509. assert l == ['testUponDeath']
  510. _testUponDeath = None
  511. _uponDeathFunc = None
  512. tm._checkMemLeaks()
  513. # task owner
  514. class _TaskOwner:
  515. def _addTask(self, task):
  516. self.addedTaskName = task.name
  517. def _clearTask(self, task):
  518. self.clearedTaskName = task.name
  519. to = _TaskOwner()
  520. l = []
  521. def _testOwner(task):
  522. return done
  523. tm.add(_testOwner, 'testOwner', owner=to)
  524. tm.step()
  525. assert getattr(to, 'addedTaskName', None) == 'testOwner'
  526. assert getattr(to, 'clearedTaskName', None) == 'testOwner'
  527. _testOwner = None
  528. del to
  529. _TaskOwner = None
  530. tm._checkMemLeaks()
  531. doLaterTests = [0,]
  532. # doLater
  533. l = []
  534. def _testDoLater1(task, l=l):
  535. l.append(1)
  536. def _testDoLater2(task, l=l):
  537. l.append(2)
  538. def _monitorDoLater(task, tm=tm, l=l, doLaterTests=doLaterTests):
  539. if task.time > .03:
  540. assert l == [1, 2,]
  541. doLaterTests[0] -= 1
  542. return task.done
  543. return task.cont
  544. tm.doMethodLater(.01, _testDoLater1, 'testDoLater1')
  545. tm.doMethodLater(.02, _testDoLater2, 'testDoLater2')
  546. doLaterTests[0] += 1
  547. # make sure we run this task after the doLaters if they all occur on the same frame
  548. tm.add(_monitorDoLater, 'monitorDoLater', sort=10)
  549. _testDoLater1 = None
  550. _testDoLater2 = None
  551. _monitorDoLater = None
  552. # don't check until all the doLaters are finished
  553. #tm._checkMemLeaks()
  554. # doLater sort
  555. l = []
  556. def _testDoLaterPri1(task, l=l):
  557. l.append(1)
  558. def _testDoLaterPri2(task, l=l):
  559. l.append(2)
  560. def _monitorDoLaterPri(task, tm=tm, l=l, doLaterTests=doLaterTests):
  561. if task.time > .02:
  562. assert l == [1, 2,]
  563. doLaterTests[0] -= 1
  564. return task.done
  565. return task.cont
  566. tm.doMethodLater(.01, _testDoLaterPri1, 'testDoLaterPri1', sort=1)
  567. tm.doMethodLater(.01, _testDoLaterPri2, 'testDoLaterPri2', sort=2)
  568. doLaterTests[0] += 1
  569. # make sure we run this task after the doLaters if they all occur on the same frame
  570. tm.add(_monitorDoLaterPri, 'monitorDoLaterPri', sort=10)
  571. _testDoLaterPri1 = None
  572. _testDoLaterPri2 = None
  573. _monitorDoLaterPri = None
  574. # don't check until all the doLaters are finished
  575. #tm._checkMemLeaks()
  576. # doLater extraArgs
  577. l = []
  578. def _testDoLaterExtraArgs(arg1, l=l):
  579. l.append(arg1)
  580. def _monitorDoLaterExtraArgs(task, tm=tm, l=l, doLaterTests=doLaterTests):
  581. if task.time > .02:
  582. assert l == [3,]
  583. doLaterTests[0] -= 1
  584. return task.done
  585. return task.cont
  586. tm.doMethodLater(.01, _testDoLaterExtraArgs, 'testDoLaterExtraArgs', extraArgs=[3,])
  587. doLaterTests[0] += 1
  588. # make sure we run this task after the doLaters if they all occur on the same frame
  589. tm.add(_monitorDoLaterExtraArgs, 'monitorDoLaterExtraArgs', sort=10)
  590. _testDoLaterExtraArgs = None
  591. _monitorDoLaterExtraArgs = None
  592. # don't check until all the doLaters are finished
  593. #tm._checkMemLeaks()
  594. # doLater appendTask
  595. l = []
  596. def _testDoLaterAppendTask(arg1, task, l=l):
  597. assert task.name == 'testDoLaterAppendTask'
  598. l.append(arg1)
  599. def _monitorDoLaterAppendTask(task, tm=tm, l=l, doLaterTests=doLaterTests):
  600. if task.time > .02:
  601. assert l == [4,]
  602. doLaterTests[0] -= 1
  603. return task.done
  604. return task.cont
  605. tm.doMethodLater(.01, _testDoLaterAppendTask, 'testDoLaterAppendTask',
  606. extraArgs=[4,], appendTask=True)
  607. doLaterTests[0] += 1
  608. # make sure we run this task after the doLaters if they all occur on the same frame
  609. tm.add(_monitorDoLaterAppendTask, 'monitorDoLaterAppendTask', sort=10)
  610. _testDoLaterAppendTask = None
  611. _monitorDoLaterAppendTask = None
  612. # don't check until all the doLaters are finished
  613. #tm._checkMemLeaks()
  614. # doLater uponDeath
  615. l = []
  616. def _testUponDeathFunc(task, l=l):
  617. assert task.name == 'testDoLaterUponDeath'
  618. l.append(10)
  619. def _testDoLaterUponDeath(arg1, l=l):
  620. return done
  621. def _monitorDoLaterUponDeath(task, tm=tm, l=l, doLaterTests=doLaterTests):
  622. if task.time > .02:
  623. assert l == [10,]
  624. doLaterTests[0] -= 1
  625. return task.done
  626. return task.cont
  627. tm.doMethodLater(.01, _testDoLaterUponDeath, 'testDoLaterUponDeath',
  628. uponDeath=_testUponDeathFunc)
  629. doLaterTests[0] += 1
  630. # make sure we run this task after the doLaters if they all occur on the same frame
  631. tm.add(_monitorDoLaterUponDeath, 'monitorDoLaterUponDeath', sort=10)
  632. _testUponDeathFunc = None
  633. _testDoLaterUponDeath = None
  634. _monitorDoLaterUponDeath = None
  635. # don't check until all the doLaters are finished
  636. #tm._checkMemLeaks()
  637. # doLater owner
  638. class _DoLaterOwner:
  639. def _addTask(self, task):
  640. self.addedTaskName = task.name
  641. def _clearTask(self, task):
  642. self.clearedTaskName = task.name
  643. doLaterOwner = _DoLaterOwner()
  644. l = []
  645. def _testDoLaterOwner(l=l):
  646. pass
  647. def _monitorDoLaterOwner(task, tm=tm, l=l, doLaterOwner=doLaterOwner,
  648. doLaterTests=doLaterTests):
  649. if task.time > .02:
  650. assert getattr(doLaterOwner, 'addedTaskName', None) == 'testDoLaterOwner'
  651. assert getattr(doLaterOwner, 'clearedTaskName', None) == 'testDoLaterOwner'
  652. doLaterTests[0] -= 1
  653. return task.done
  654. return task.cont
  655. tm.doMethodLater(.01, _testDoLaterOwner, 'testDoLaterOwner',
  656. owner=doLaterOwner)
  657. doLaterTests[0] += 1
  658. # make sure we run this task after the doLaters if they all occur on the same frame
  659. tm.add(_monitorDoLaterOwner, 'monitorDoLaterOwner', sort=10)
  660. _testDoLaterOwner = None
  661. _monitorDoLaterOwner = None
  662. del doLaterOwner
  663. _DoLaterOwner = None
  664. # don't check until all the doLaters are finished
  665. #tm._checkMemLeaks()
  666. # run the doLater tests
  667. while doLaterTests[0] > 0:
  668. tm.step()
  669. del doLaterTests
  670. tm._checkMemLeaks()
  671. # getTasks
  672. def _testGetTasks(task):
  673. return task.cont
  674. # No doLaterProcessor in the new world.
  675. assert len(tm.getTasks()) == 0
  676. tm.add(_testGetTasks, 'testGetTasks1')
  677. assert len(tm.getTasks()) == 1
  678. assert (tm.getTasks()[0].name == 'testGetTasks1' or
  679. tm.getTasks()[1].name == 'testGetTasks1')
  680. tm.add(_testGetTasks, 'testGetTasks2')
  681. tm.add(_testGetTasks, 'testGetTasks3')
  682. assert len(tm.getTasks()) == 3
  683. tm.remove('testGetTasks2')
  684. assert len(tm.getTasks()) == 2
  685. tm.remove('testGetTasks1')
  686. tm.remove('testGetTasks3')
  687. assert len(tm.getTasks()) == 0
  688. _testGetTasks = None
  689. tm._checkMemLeaks()
  690. # getDoLaters
  691. def _testGetDoLaters():
  692. pass
  693. assert len(tm.getDoLaters()) == 0
  694. tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater1')
  695. assert len(tm.getDoLaters()) == 1
  696. assert tm.getDoLaters()[0].name == 'testDoLater1'
  697. tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater2')
  698. tm.doMethodLater(.1, _testGetDoLaters, 'testDoLater3')
  699. assert len(tm.getDoLaters()) == 3
  700. tm.remove('testDoLater2')
  701. assert len(tm.getDoLaters()) == 2
  702. tm.remove('testDoLater1')
  703. tm.remove('testDoLater3')
  704. assert len(tm.getDoLaters()) == 0
  705. _testGetDoLaters = None
  706. tm._checkMemLeaks()
  707. # duplicate named doLaters removed via taskMgr.remove
  708. def _testDupNameDoLaters():
  709. pass
  710. # the doLaterProcessor is always running
  711. tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
  712. tm.doMethodLater(.1, _testDupNameDoLaters, 'testDupNameDoLater')
  713. assert len(tm.getDoLaters()) == 2
  714. tm.remove('testDupNameDoLater')
  715. assert len(tm.getDoLaters()) == 0
  716. _testDupNameDoLaters = None
  717. tm._checkMemLeaks()
  718. # duplicate named doLaters removed via remove()
  719. def _testDupNameDoLatersRemove():
  720. pass
  721. # the doLaterProcessor is always running
  722. dl1 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
  723. dl2 = tm.doMethodLater(.1, _testDupNameDoLatersRemove, 'testDupNameDoLaterRemove')
  724. assert len(tm.getDoLaters()) == 2
  725. dl2.remove()
  726. assert len(tm.getDoLaters()) == 1
  727. dl1.remove()
  728. assert len(tm.getDoLaters()) == 0
  729. _testDupNameDoLatersRemove = None
  730. # nameDict etc. isn't cleared out right away with task.remove()
  731. tm._checkMemLeaks()
  732. # getTasksNamed
  733. def _testGetTasksNamed(task):
  734. return task.cont
  735. assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
  736. tm.add(_testGetTasksNamed, 'testGetTasksNamed')
  737. assert len(tm.getTasksNamed('testGetTasksNamed')) == 1
  738. assert tm.getTasksNamed('testGetTasksNamed')[0].name == 'testGetTasksNamed'
  739. tm.add(_testGetTasksNamed, 'testGetTasksNamed')
  740. tm.add(_testGetTasksNamed, 'testGetTasksNamed')
  741. assert len(tm.getTasksNamed('testGetTasksNamed')) == 3
  742. tm.remove('testGetTasksNamed')
  743. assert len(tm.getTasksNamed('testGetTasksNamed')) == 0
  744. _testGetTasksNamed = None
  745. tm._checkMemLeaks()
  746. # removeTasksMatching
  747. def _testRemoveTasksMatching(task):
  748. return task.cont
  749. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching')
  750. assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 1
  751. tm.removeTasksMatching('testRemoveTasksMatching')
  752. assert len(tm.getTasksNamed('testRemoveTasksMatching')) == 0
  753. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1')
  754. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2')
  755. assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 1
  756. assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 1
  757. tm.removeTasksMatching('testRemoveTasksMatching*')
  758. assert len(tm.getTasksNamed('testRemoveTasksMatching1')) == 0
  759. assert len(tm.getTasksNamed('testRemoveTasksMatching2')) == 0
  760. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching1a')
  761. tm.add(_testRemoveTasksMatching, 'testRemoveTasksMatching2a')
  762. assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 1
  763. assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 1
  764. tm.removeTasksMatching('testRemoveTasksMatching?a')
  765. assert len(tm.getTasksNamed('testRemoveTasksMatching1a')) == 0
  766. assert len(tm.getTasksNamed('testRemoveTasksMatching2a')) == 0
  767. _testRemoveTasksMatching = None
  768. tm._checkMemLeaks()
  769. # create Task object and add to mgr
  770. l = []
  771. def _testTaskObj(task, l=l):
  772. l.append(None)
  773. return task.cont
  774. t = Task(_testTaskObj)
  775. tm.add(t, 'testTaskObj')
  776. tm.step()
  777. assert len(l) == 1
  778. tm.step()
  779. assert len(l) == 2
  780. tm.remove('testTaskObj')
  781. tm.step()
  782. assert len(l) == 2
  783. _testTaskObj = None
  784. tm._checkMemLeaks()
  785. # remove Task via task.remove()
  786. l = []
  787. def _testTaskObjRemove(task, l=l):
  788. l.append(None)
  789. return task.cont
  790. t = Task(_testTaskObjRemove)
  791. tm.add(t, 'testTaskObjRemove')
  792. tm.step()
  793. assert len(l) == 1
  794. tm.step()
  795. assert len(l) == 2
  796. t.remove()
  797. tm.step()
  798. assert len(l) == 2
  799. del t
  800. _testTaskObjRemove = None
  801. tm._checkMemLeaks()
  802. """
  803. # this test fails, and it's not clear what the correct behavior should be.
  804. # sort passed to Task.__init__ is always overridden by taskMgr.add()
  805. # even if no sort is specified, and calling Task.setSort() has no
  806. # effect on the taskMgr's behavior.
  807. # set/get Task sort
  808. l = []
  809. def _testTaskObjSort(arg, task, l=l):
  810. l.append(arg)
  811. return task.cont
  812. t1 = Task(_testTaskObjSort, sort=1)
  813. t2 = Task(_testTaskObjSort, sort=2)
  814. tm.add(t1, 'testTaskObjSort1', extraArgs=['a',], appendTask=True)
  815. tm.add(t2, 'testTaskObjSort2', extraArgs=['b',], appendTask=True)
  816. tm.step()
  817. assert len(l) == 2
  818. assert l == ['a', 'b']
  819. assert t1.getSort() == 1
  820. assert t2.getSort() == 2
  821. t1.setSort(3)
  822. assert t1.getSort() == 3
  823. tm.step()
  824. assert len(l) == 4
  825. assert l == ['a', 'b', 'b', 'a',]
  826. t1.remove()
  827. t2.remove()
  828. tm.step()
  829. assert len(l) == 4
  830. del t1
  831. del t2
  832. _testTaskObjSort = None
  833. tm._checkMemLeaks()
  834. """
  835. del l
  836. tm.destroy()
  837. del tm