[For complete, up-to-date TBB information visit: http://www.ThreadingBuildingBlocks.org]
Summary
Base class for tasks.
Syntax
class task;
Header
#include "tbb/task.h"
Description
Class task is the base class for tasks. Programmers are expected to derive classes from task, and override the virtual method task* task::execute().
Each instance of task has associated attributes, that while not directly visible, must be understood to fully grasp how task objects are used. The attributes are described in Table 24.
Table 24: Task Attributes
Attribute
Description
owner
the worker thread that is currently in charge of the task.
parent
either null, or a pointer to another task whose refcount field will be decremented after the present task completes. Typically, the other task is the parent or a continuation of the parent.
depth
the depth of the task in the task tree.
refcount
the number of Tasks that have this is their parent. Increments and decrement of refcount are always atomic.
TIP: Always allocate memory for task objects using special overloaded new operators ( 8.3.2) provided by the library, otherwise the results are undefined. Destruction of a task is normally implicit. The copy constructor and assignment operators for task are not accessible. This prevents accidental copying of a task, which would be ill-defined and corrupt internal data structures.
Notation
Some member descriptions illustrate effects by diagrams such as Figure 4.
// Synchronization
void set_ref_count( int count );
void wait_for_all();
void spawn( task& child );
void spawn( task_list& list );
void spawn_and_wait_for_all( task& child );
void spawn_and_wait_for_all( task_list& list );
static void spawn_root_and_wait( task& root );
static void spawn_root_and_wait( task_list& root );
// task context
static task& self();
task* parent() const;
bool is_stolen_task() const;
// task debugging
enum state_type {
executing,
reexecute,
ready,
allocated,
freed
};
int ref_count() const;
state_type state() const;
};
} // namespace tbb
void *operator new( size_t bytes, const proxy1& p );
void operator delete( void* task, const proxy1& p );
void *operator new( size_t bytes, const proxy2& p );
void operator delete( void* task, const proxy2& p );
void *operator new( size_t bytes, const proxy3& p );
void operator delete( void* task, const proxy3& p );
void *operator new( size_t bytes, proxy4& p );
void operator delete( void* task, proxy4& p );
Class task is an abstract base class. You must override method task::execute. Method execute should perform the necessary actions for running the task, and then return the next task to execute, or NULL if the scheduler should choose the next task to execute. Typically, if non-NULL, the returned task is one of the children of this. Unless one of the recycle/reschedule methods described in Section ( 8.3.4) is called while method execute() is running, the this object will be implicitly destroyed after method execute returns.
The derived class should override the virtual destructor if necessary to release resources allocated by the constructor.
depth null result 0
Figure 5: Effect of task::allocate_root()
Use method spawn_root_and_wait ( 8.3.6.7) to execute the task.
8.3.2.2 new( this. allocate_continuation() ) T
Allocate and construct a task of type T at the same depth as this, and transfers the parent from this to the new task. No reference counts change. Figure 6 summarizes the state transition.
depth depth depth parent parent null this this resu0 refcount refcount
Figure 6: Effect of allocate_continuation()
8.3.2.3 new( this. allocate_child() ) T
Effect
Allocates a task with a depth one more than this, with this as its parent. Figure 7 summarizes the state transition.
depth depth depth+1 this this result refcount refcount 0 parent parent
Figure 7: Effect of allocate_child()
If using explicit continuation passing, then the continuation, not the parent, should call the allocation method, so that parent is set correctly. The task this must be owned by the current thread.
If the number of tasks is not a small fixed number, consider building a task_list ( 8.5) of the children first, and spawning them with a single call to task::spawn (530H 8.3.6.3). If a task must spawn some children before all are constructed, it should use task::allocate_additional_child_of(*this) instead, because that method atomically increments refcount, so that the additional child is properly accounted. However, if doing so, the task must protect against premature zeroing of refcount by using a blocking-style task pattern.
8.3.2.4 new( this.task::allocate_additional_child_of( parent ))
Effect
Allocates a task as a child of another task parent. The result becomes a child of parent, not this. The parent may be owned by another thread, and may be already running or have other children running. The task object this must be owned by the current thread, and the result has the same owner as the current thread, not the parent. Figure 8 summarizes the state transition.
depth depth+1 parent result refcount+1 0 grandparentdepth parent refcount grandparent(result.owner=this. owner) this this
Figure 8: Effect of allocate_additional_child_of(parent)
Because parent may already have running children, the increment of parent.refcount is thread safe (unlike the other allocation methods, where the increment is not thread safe). When adding a child to a parent with other children running, it is up to the programmer to ensure that the parent’s refcount does not prematurely reach 0 and trigger execution of the parent before the child is added.
Usually, a task is automatically destroyed by the scheduler after its method execute returns. But sometimes task objects are used idiomatically (e.g. for reference counting) without ever running execute. Such tasks should be disposed of with method destroy.
8.3.3.1 void destroy( task& victim )
Requirements
The reference count of victim should be 0. This requirement is checked in the debug version of the library. The calling thread must own this.
Effects
Calls destructor and deallocates memory for victim. If this has non-null parent, atomically decrements parent->refcount. The parent is not put into the ready pool if parent->refcount becomes zero. Figure 9 summarizes the state transition.
The implicit argument this is used internally, but not visibly affected. A task is allowed to destroy itself; e.g., “this->destroy(*this)” is permitted unless the task has been spawned but has not yet completed method execute.
depth victim 0 this parent refcount this parent refcount-1 refcount adjustment skipped if if parent is null (can be null)
Figure 9: Effect of destroy(victim)
It is often more efficient to recycle a task object rather than reallocate one from scratch. Often the parent can become the continuation, or one of the children.
8.3.4.1 void recycle_as_continuation()
Requirements
Must be called while method execute() is running.
The refcount for the recycled task should be set to n, where n is the number of children of the continuation task.
NOTE: The caller must guarantee that the task’s refcount does not become zero until after the method execute() returns. If this is not possible, use the method recycle_as_safe_continuation() instead, and set refcount to n+1.
Effects
Causes this to not be destroyed when method execute() returns.
8.3.4.2 Preview Feature: void recycle_as_safe_continuation()
Requirements
Must be called while method execute() is running.
The refcount for the recycled task should be set to n+1, where n is the number of children of the continuation task. The additional +1 represents the task to be recycled.
Effects
Causes this to not be destroyed when method execute() returns.
This method avoids race conditions that can arise from using the method recycle_as_continuation. The race occurs when:
1. The method execute() recycles this as a continuation.
2. The continuation creates children.
3. All the children finish before method execute() completes, so the continuation executes before the scheduler is done running this, which corrupts the scheduler.
Method recycle_as_safe_continuation avoids this race because the additional +1 in the refcount prevents the continuation from executing until the task completes.
8.3.4.3 void recycle_as_child_of( task& parent )
Requirements
Must be called while method execute() is running.
Effects
Causes this to become a child of parent, and not be destroyed when method execute() returns.
8.3.4.4 void recycle _to_reexecute()
Requirements
Must be called while method execute() is running. Method execute() must return a pointer to another task.
Effects
Causes this to be automatically spawned after execute() returns.
For general fork-join parallelism, there is no need to explicitly set the depth of a task. However, in specialized task patterns that do not follow the fork-join pattern, it may be useful to explicitly set or adjust the depth of a task.
8.3.5.1 depth_type
The type task::depth_type is an implementation-defined signed integral type.
8.3.5.2 depth_type depth() const
Returns
Current depth attribute for the task.
8.3.5.3 void set_depth( depth_type new_depth )
Requirements
The value new_depth must be non-negative.
Effects
Set the depth attribute of the task to new_depth. Figure 10 shows the update.
new_depth depth this this refcount refcount parent parent
Figure 10: Effect of set_depth
8.3.5.4 void add_to_depth( int delta )
Requirements
The task must not be in the ready pool. The sum depth+delta must be non-negative.
Effects
Set the depth attribute of the task to depth+delta. Figure 11 illustrates the effect. The update is not atomic.
depth+delta depth this this refcount refcount parent parent
Figure 11: Effect of add_to_depth(delta)
Spawning a task task either causes the calling thread to invoke task.execute(), or causes task to be put into the ready pool. Any thread participating in task scheduling may then acquire the task and invoke task.execute(). Section 8.1 describes the structure of the ready pool.
The calls that spawn come in two forms:
1. Spawn a single task
2. Spawn multiple task objects specified by a task_list and clear task_list.
The calls distinguish between spawning root tasks and child tasks. A root task is one that was created using method allocate_root.
Important
A task should not spawn any child until it has called method set_ref_count to indicate both the number of children and whether it intends to use one of the "wait_for_all" methods.
8.3.6.1 void set_ref_count( int count )
Requirements
count>0. If the intent is to subsequently spawn n children and wait, then count should be n+1. Otherwise count should be n.
Effects
Sets the refcount attribute to count.
8.3.6.2 void wait_for_all()
Requirements
refcount=n+1, where n is the number of children who are still running.
Effects
Executes tasks in ready pool until refcount is 1. Afterwards sets refcount to 0. Figure 12 summarizes the state transitions.
depth depth this this 0 n+1 dependent dependent n = previously spawned children who are still running
Figure 12: Effect of wait_for_all
8.3.6.3 void spawn( task& child )
Requirements
child.refcount>0
The calling thread must own this and child.
Effects
Puts the task into the ready pool and immediately returns. The this task that does the spawning must be owned by the caller thread. A task may spawn itself if it is owned by the caller thread. If no convenient task owned by the current thread is handy, use task::self().spawn(task) to spawn task.
The parent must call set_ref_count before spawning any child tasks, because once the child tasks are going, their completion will cause refcount to be decremented asynchronously. The debug version of the library detects when a required call to set_ref_count is not made, or is made too late.
8.3.6.4 void spawn ( task_list& list )
Requirements
For each task in list, refcount>0. The calling thread must own this and each task in list. Each task in list must be the same value for its depth attribute.
Effects
Equivalent to executing spawn on each task in list and clearing list, but more efficient. If list is empty, there is no effect.
8.3.6.5 void spawn_and_wait_for_all( task& child )
Requirements
Any other children of this must already be spawned. The task child must have a non-null attribute parent. There must be a chain of parent links from the child to the calling task. Typically, this chain contains a single link. That is, child is typically a child of this.
Effects
Similar to {spawn(task); wait_for_all();}, but often more efficient. Furthermore, it guarantees that task is executed by the current thread. This constraint can sometimes simplify synchronization. Figure 13 illustrates the state transitions.
depth depth this this 0 refcount depth+1 child 0 dependent dependent previously spawned children who have not completed.
Figure 13: Effect of spawn_and_wait_for_all
8.3.6.6 void spawn_and_wait_for_all( task_list& list )
Effects
Similar to {spawn(list); wait_for_all();}, but often more efficient.
8.3.6.7 static void spawn_root_and_wait( task& root )
Requirements
The memory for task root was allocated by task::allocate_root(). The calling thread must own root.
Effects
Sets parent attribute of root to an undefined value and execute root as described in Section 8.3.1.1. Destroys root afterwards unless root was recycled.
8.3.6.8 static void spawn_root_and_wait( task_list& root_list )
Requirements
each task object t in root_list must meet the requirements in Section 8.3.6.7..
Effects
For each task object t in root_list, performs spawn_root_and_wait(t), possibly in parallel. Section 8.3.6.7 describes the actions of spawn_root_and_wait(t).
These methods expose relationships between task objects, and between task objects and the underlying physical threads.
8.3.7.1 static task& self()
Returns
Reference to innermost task that calling thread is executing.
8.3.7.2 task* parent() const
Returns
Value of the attribute parent. The result is an undefined value if the task was allocated by allocate_root and is currently running under control of spawn_root_and_wait.
8.3.7.3 bool is_stolen_task() const
Requirements
The attribute parent is not null and this.execute() is running. The calling task must not have been allocated with allocate_root.
Returns
true if the attribute owner of this is unequal to owner of parent.
Methods in this subsection are useful for debugging. They may change in future implementations.
8.3.8.1 state_type state() const
CAUTION: This method is intended for debugging only. Its behavior or performance may change in future implementations. The definition of task::state_type may change in future implementations. This information is being provided because it can be useful for diagnosing problems during debugging.
Returns
Current state of the task. Table 25 describes valid states. Any other value is the result of memory corruption, such as using a task whose memory has been deallocated.
Table 25: Values returned by task::state()
Value
Description
allocated
task is freshly allocated or recycled.
ready
task is in ready pool, or is in process of being transferred to/from there.
executing
task is running, and will be destroyed after method execute() returns.
freed
task is on internal free list, or is in process of being transferred to/from there.
reexecute
task is running, and will be respawned after method execute() returns.
Figure 14 summarizes possible state transitions for a task.
freed allocated reexecute allocate_...(t) (implicit)spawn(t)spawn_and_wait_for_all(t) return from t.execute()return from t.execute()t.recycle_to_reexecute ready executingt.recycle_as... (implicit)storage returned to heapdestroy(t)allocate_...(t)storage from heap
Figure 14: Typical task::state() transitions
8.3.8.2 int ref_count() const
CAUTION: This method is intended for debugging only. Its behavior or performance may change in future implementations.
Returns
The value of the attribute refcount.
[For complete, up-to-date TBB information visit: http://www.ThreadingBuildingBlocks.org]