Task.py 50 KB

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