Friday, November 6, 2009

6.6 Using Thread Local Storage



< BACK  NEXT >

[oR]


6.6
Using Thread Local Storage


The 32-bit APIs contains a unique feature called Thread Local Storage (TLS), which you can use in processes and DLLs in a variety of ways. The capability itself is very straightforward, but its extended ramifications are what make it interesting.



The TLS capability consists of four very simple functions: TlsAlloc,
TlsFree,
TlsSetValue, and TlsGetValue. You use TLS by first calling the TlsAlloc function, as shown below:





DWORD tlsIndex; // probably a global variable

...

// allocate a thread local storage index
tlsIndex=TlsAlloc();



The call to TlsAlloc creates a block of memory that contains one 32-bit storage location for each existing thread in a process. Any new threads created after the call to TlsAlloc also receive a unique 32-bit storage location. TlsAlloc returns an index value that the process uses when referring to this particular set of 32-bit thread storage locations. The process can call TlsAlloc as many times as it likes, as long as it uses a different variable each time. See Figure 6.4.





Figure 6.4. The relationship between TLS and the threads in a process. TIsIndex references a block of storage that contains one 32-bit value for each thread in the process








Inside any thread, a call to the function TlsGetValue with the TlsIndex value returns the 32-bit value from TLS storage associated with that particular thread. That is, the value returned by TlsGetValue is context-sensitive based on whatever thread is calling the function. The same goes for TlsSetValue.



The TlsFree function frees up the space occupied by all of the storage locations.



The TLS capability is frequently used inside of DLLs to store pointers to blocks of memory associated with the different threads calling the DLL. See Chapter 17 for details.



Listing 6.11 demonstrates TLS in a simple program. You should compare this program with Listing 6.5, because they do approximately the same thing in different ways.





Listing 6.11 Using Thread Local Storage (TLS). Compare with Listing 6.5


// thread4.cpp

#include <windows.h>
#include <stdlib.h>
#include <iostream.h>

DWORD tlsIndex;

typedef struct
{
DWORD frequency;
DWORD duration;
} honkParams;

void ParamsAlloc()

{
honkParams *params;

params=(honkParams *) GlobalAlloc(GPTR,
sizeof(honkParams));

// generate a randomish value for the frequency
params->frequency=GetTickCount() & 0x00000FFF;
cout << "Using frequency: "
<< params->frequency << endl;
params->duration=100;

// save the pointer value relevant
// for the calling thread
TlsSetValue(tlsIndex, params);
}

void ParamsFree()
{
honkParams *params;

// retrieve the pointer value relevant
// for the calling thread
params=(honkParams *) TlsGetValue(tlsIndex);

GlobalFree(params);
}

void Honk(void)
{
honkParams *params;

// retrieve the pointer value relevant
// for the calling thread
params=(honkParams *) TlsGetValue(tlsIndex);

Beep(params->frequency, params->duration);
}

void HonkThread()
{
DWORD i;

// use a general purpose "params"
// allocation function
ParamsAlloc();

for (i=0; i<8; i++)
{
Honk();
Sleep(1000);
}

// use a general purpose "params" free function
ParamsFree();
}

void main(void)
{
HANDLE honkHandles[3];
DWORD threadID;
DWORD count;

// allocate a thread local storage index
tlsIndex=TlsAlloc();

for (count=0; count<3; count++)
{
// create a thread which beeps
honkHandles[count]=CreateThread(NULL, 0,
(LPTHREAD_START_ROUTINE) HonkThread, 0, 0,
&threadID);
Sleep(1500);
}


WaitForMultipleObjects(3, honkHandles, TRUE,
INFINITE);


// free the thread local storage index
TlsFree(tlsIndex);
}


The code in Listing 6.11 declares a global variable named TlsIndex. The program's first action in the main function is to allocate a TLS storage block pointed to by TlsIndex. The main function then creates three threads and waits for their completion before freeing the TLS storage space.

















TlsAlloc

Allocates a TLS storage structure



DWORD TlsAlloc(VOID)

Returns an index to the TLS storage structure, or 0xFFFFFFFF on failure






















TlsFree

Frees a TLS storage structure



BOOL TlsFree(
DWORD index)

Index

The index of an existing TLS structure

Returns TRUE on success



Each thread calls the ParamsAlloc function, which allocates a parameter structure on the heap and fills it with values for the thread. The pointer to the parameter block is placed in the TLS area using TlsSetValue. TlsSetValue chooses the TLS location in which to place the pointer based on the thread that calls the function. To later free up its parameter storage block, each thread calls ParamsFree. This function retrieves the pointer from the TLS and frees the block.





















TlsGetValue

Retrieves a value from a TLS structure based on the thread making the call



LPVOID TlsGetValue(
DWORD index)

index

The index of an existing TLS structure

Returns the value for the thread on success, or 0 on failure

























TlsSetValue

Sets a value into a TLS structure based on the thread making the call



BOOL TlsSetValue(
DWORD index,
LPVOID value)

index

The index of an existing TLS structure

value

The value to store

Returns TRUE on success



The TLS capability could be used to implement the parameter storage for the multi-threaded Mandelbrot program shown in Listing 6.10, especially since each thread is in charge of cleaning up its own allocated memory in that program. The TLS capability is also frequently used in DLLs to keep memory allocations in the DLL separated on a thread-by-thread basis. See Chapter 17 for details.





< BACK  NEXT >


No comments: