Complete Communications Engineering

When developing multi-threaded applications using low-level languages such as C, the concept of thread local storage comes up occasionally.  Thread local storage allows global variables to have a unique instance per thread.  Since it only affects global variables, its usefulness is limited but there are some problems that this feature is particularly suited to solve.  One example is keeping track of the locking depth of a mutex.  In some applications, particularly those using callback functions on a user API, there is a possibility that a thread may attempt to lock the same mutex more than once.  This is undesirable behavior because it will lead to deadlock.  One way to avoid this is to count the number of times a thread has tried to lock a mutex, and only do the lock if that number is zero.  Such a counter must have a unique value for each thread.

There are a few ways to declare a global variable with thread local storage in C.  If the compiler supports C11, then thread local storage is standard through <threads.h>, and variables can be declared as ‘thread_local’, as in the following example:

C11

#include <threads.h>

thread_local int foo = 0;

If C11 support is not available, there are some compiler-specific keywords that might be.  The GNU C compiler is commonly used on Linux systems and many versions support the ‘__thread’ keyword to declare thread local variables:

GNU C

__thread int foo = 0;

On Windows, the Visual C++ compiler supports thread local storage using the following declaration:

Visual C++

__declspec(thread) int foo = 0;

If none of the above methods are available, thread local storage is also supported by the widely used pthread library.  This method is a little more involved than direct compiler support.  The following example shows thread local storage using the pthread library:

pthread

#include <pthread.h>

 

pthread_key_t global_key = 0;

 

/* Must be called only once before any threads use global_key */

void global_init() {

    pthread_key_create(&global_key, NULL);

}

 

/* Called by multiple threads, each has its own unique ptr value */

void thread_function() {

    void *ptr = pthread_getspecific(global_key);

    /* Do some processing */

    pthread_setspecific(global_key, ptr);

}