[For complete, up-to-date TBB information visit: http://www.ThreadingBuildingBlocks.org]

TbbRef (Ver. 20): 8.3 task Class

8.3 task Class

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 );

8.3.1 task Derivation

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.

8.3.3 Explicit task Destruction

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)

8.3.4 Recycling Tasks

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.

8.3.5 task Depth

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)

8.3.6 Synchronization

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).

8.3.7 task Context

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.

8.3.8 task Debugging

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]