VC++ 6.0 ebook chapter index
Free counters!

Thread Local Storage

Global variables in a multithreaded program, as well as any allocated memory, are shared among all the threads in the program. Local static variables in a function are also shared among all threads using that function. Local automatic variables in a function are unique to each thread because they are stored on the stack and each thread has its own stack.

It might be necessary to have persistent storage that is unique to each thread. For example, the C strtok function I mentioned earlier in this chapter requires this type of storage. Unfortunately, the C language does not support such a variable. But Windows includes four functions that implement a mechanism to do it, and the Microsoft extensions to C also support it. As we've seen, this is called thread local storage.

Here's how the APIs work:

First define a structure that contains all the data that needs to be unique among the threads. For example,

typedef struct
     int a ;
     int b ;

The primary thread calls TlsAlloc to obtain an index value:

dwTlsIndex = TlsAlloc () ;

This index value can be stored in a global variable or passed to the Thread function in the argument structure.

The Thread function begins by allocating memory for the data structure and calling TlsSetValue using the index obtained above:

TlsSetValue (dwTlsIndex, GlobalAlloc (GPTR, sizeof (DATA)) ;

This associates a pointer with a particular thread and a particular thread index. Now any function that needs to use this pointer, including the original Thread function itself, can include code like so:

PDATA pdata ;
pdata = (PDATA) TlsGetValue (dwTlsIndex) ;

Now it can set or use pdata->a and pdata->b. Before the Thread function terminates, it frees the allocated memory:

GlobalFree (TlsGetValue (dwTlsIndex)) ;

When all the threads using this data have terminated, the primary thread frees the index:

TlsFree (dwTlsIndex) ;

This process might be confusing at first, so perhaps it might be helpful to see how thread local storage might be implemented. (I have no knowledge of how Windows actually does it, but the following is plausible.) First, TlsAlloc might simply allocate a block of memory (zero bytes in length) and return an index value that is a pointer to this block. Every time TlsSetValue is called with that index, the block of memory is increased by 8 bytes by reallocating it. Stored in these 8 bytes is the ID of the thread calling the function—obtained by calling GetCurrentThreadId—and the pointer passed to the TlsSetValue function. TlsGetValue simply uses the thread ID to search the table and then return the pointer. TlsFree frees up the block of memory. So, as you see, this is something you could probably easily implement yourself, but it's nice to have the facility already done for us.

A Microsoft extension to C makes this even more simple. Just preface any variable that needs to be different for each thread with __declspec (thread), like so

__declspec (thread) int iGlobal = 1 ;

for static variables external to any function, or like so

__declspec (thread) static int iLocal = 2 ;

for static variables within functions.