Task Chains
When you add tasks to the TaskManager, you are actually adding them to the default Task Chain. The TaskManager maintains one or more task chains; each chain is a list of tasks that are available to be executed.
You are free to create additional task chains as you see the need. Normally, though, there is no reason to have more than the default task chain, unless you wish to take advantage of threaded tasks: each task chain has the option of being serviced by one or more sub-threads, which allows the tasks on that chain to run in parallel with (or at a lower priority than) the main tasks.
Note that threading is an advanced topic, and the use of threading inherently
comes with risks. In particular, it is easy to introduce race conditions or
deadlocks in code that involves multiple threads. You are responsible for
protecting critical sections of your code from mutual access with proper use of
synchronization primitives, such as provided by Panda’s Mutex and ConditionVar
classes, and for Python users, the direct.stdpy.threading
module. For
the purposes of this discussion, we will assume that you are already familiar
with the proper use of synchronization primitives in threading.
Note also that Panda may be compiled with a special threading mode (called
“simple threads”) that is designed to be low overhead, but which is
fundamentally incompatible with true threads as provided by the system library.
Thus, in any Panda application, you must always use Panda’s synchronization
primitives, and not the system-provided ones; and you must use Panda’s thread
primitives and not call into the system thread library directly, or you will
risk a terrible crash. That is, you should use Panda’s Thread and Mutex
classes (or for Python users, the direct.stdpy.threading
module), and
not any system thread or mutex implementation. See Threading for more.
Defining task chains
To set up a new task chain, you simply call:
taskMgr.setupTaskChain('chain_name', numThreads = None, tickClock = None,
threadPriority = None, frameBudget = None,
frameSync = None, timeslicePriority = None)
Task chains are identified by their unique name. Repeated calls to setupTaskChain() with the same task chain name will reconfigure the same task chain.
The task chain parameters are:
- numThreads
Specifies the number of threads that will service this task chain. The default is zero, which means the task chain will be handled by the main thread. If you set this to 1, then a single thread will be spawned to handle all of the tasks in the chain one at a time, in the normal order. If you set this to some number higher than 1, then multiple threads will be spawned to handle the tasks on the chain. In this case, some of the tasks may be run in parallel with each other, and task ordering is difficult to guarantee.
- tickClock
If this is true, then this task chain will be responsible for ticking the global clock each frame (and thereby incrementing the frame counter). There should be just one task chain responsible for ticking the clock, and usually it is the default task chain.
- threadPriority
This specifies the priority level to assign to threads on this task chain. It may be one of TP_low, TP_normal, TP_high, or TP_urgent. This is passed to the underlying threading system to control the way the threads are scheduled. It only has meaning for a threaded task chain, of course.
- frameBudget
This is the maximum amount of time (in seconds) to allow this task chain to run per frame. Set it to -1 to mean no limit (the default). It’s not directly related to threadPriority.
- frameSync
Set this true to force the task chain to sync to the clock. When this flag is false, the default, the task chain will finish all of its tasks and then immediately start from the first task again, regardless of the clock frame. When it is true, the task chain will finish all of its tasks and then wait for the clock to tick to the next frame before resuming the first task. This only makes sense for threaded tasks chains; non-threaded task chains are automatically synchronous.
- timeslicePriority
This is false in the default mode, in which each task runs exactly once each frame, round-robin style, regardless of the task’s priority value. Set it to true to change the meaning of priority so that certain tasks are run less often, in proportion to their time used and to their priority value. See
AsyncTaskManager.set_timeslice_priority()
for more.
Using task chains
You may add any tasks to the task chain of your choosing with the optional
taskChain parameter to taskMgr.add()
or
taskMgr.doMethodLater()
. This parameter should receive the name of
the task chain to add the task to; this is the ‘chain_name’ you specified in
the above call to taskMgr.setupTaskChain()
. For example:
taskMgr.add(self.myTaskFunc, 'myTaskName', taskChain = 'myChain')