Task.py 50 KB

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