Task.py 49 KB

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