Open Inventor Release 2024.2.0
 
Loading...
Searching...
No Matches
Class Static Variables and Static Variables

There are a few cases where class static variables can be accessed without protection or can be simply protected by a mutex. For example, if a class static variable stores an option setting for the entire class, then typically the option is only set (modified) by the application when no traversal is occurring. So traversal methods that only use the value should be able to do this safely with no protection.

The problem occurs when a node class uses a class static variable to store information that may be different for each thread. For example, a node might allocate memory for “scratch space” or it might need an instance of an action class for internal use. It would be inefficient to allocate the memory or create the action on each traversal, so the node wants to allocate or create only once. However, clearly only one thread can use this memory or action at one time. We can avoid this problem by protecting use of the memory or action with a mutex. This may be appropriate in some cases, but in general this would be a performance problem because it forces multiple render threads to execute sequentially in this traversal method.

A solution is to use Instance Thread Storage or Thread Local Storage (TLS), which basically provides each thread with its own copy of the class static variables, at the cost of some minor additional code for getting a pointer to the variable. Open Inventor provides a platform-independent mechanism for using TLS. Accessing TLS variables is slightly more complicated than accessing state variables directly, but the code changes are small.

Example : TLS use for a node with class static variables numCoords and coordsArray This single thread code:

C++ :

if ( numCoords > 0 )
delete[] coordsArray;
numCoords = numCoordsNeeded;
coordsArray = new SbVec2f[numCoordsNeeded];

Might become this tread-safe code:

C++ :

struct MyNode::MTstruct* mtstruct = ( struct MyNode::MTstruct* )GET_THREAD_LOCAL_STORAGE( MyNode );
if ( mtstruct->numCoords > 0 )
delete[] mtstruct->coordsArray;
mtstruct->numCoords = numCoordsNeeded;
mtstruct->coordsArray = new SbVec2f[numCoordsNeeded];

See Thread Local Storage (TLS) for the details of setting up Thread Local Storage for a new node class.

NOTE: You should also check the node’s source file for static variables. Some built-in nodes were implemented with code like this:

C++ :

{
...
static SoGetBoundingBoxAction* pGBBA = NULL;
if ( pGBBA == NULL )
pGBBA = new SoGetBoundingBoxAction;
pGBBA->apply( someNode );
}

It would be a very bad idea for multiple threads traversing this node to all use the BBoxAction at the same time. You could protect this block of code with a mutex, but then you would force multiple threads to traverse this code sequentially. The best solution is to put the pointer in Thread Local Storage and let each thread have its own copy of the action, like this:

C++ :

{
...
struct MyNode::MTStruct* mtstruct = ( struct MyNode::MTStruct* )GET_THREAD_LOCAL_STORAGE( MyNode );
if ( mtstruct->pGBBA == NULL )
mtstruct->pGBBA = new SoGetBoundingBoxAction;
mtstruct->pGBBA->apply( someNode );
}

Handling class static and local static variables with TLS will be sufficient to make many nodes thread safe.