Windows Dynamic-Link Libraries 12

 

 

 

 

 

Using Thread Local Storage in a Dynamic-Link Library Program Example

 

This section shows the use of a DLL entry-point function to set up a thread local storage (TLS) index to provide private storage for each thread of a multithreaded process. The TLS index is stored in a global variable, making it available to all of the DLL functions. This example assumes that the DLL's global data is not shared, because the TLS index is not necessarily the same for each process that loads the DLL. The entry-point function uses the TlsAlloc() function to allocate a TLS index whenever a process loads the DLL. Each thread can then use this index to store a pointer to its own block of memory. When the entry-point function is called with the DLL_PROCESS_ATTACH value, the code performs the following actions:

 

  1. Uses the TlsAlloc() function to allocate a TLS index.
  2. Allocates a block of memory to be used exclusively by the initial thread of the process.
  3. Uses the TLS index in a call to the TlsSetValue() function to store the address of the memory block in the TLS slot associated with the index.

 

Each time the process creates a new thread, the entry-point function is called with the DLL_THREAD_ATTACH value. The entry-point function then allocates a block of memory for the new thread and stores a pointer to it by using the TLS index. When a function requires access to the data associated with a TLS index, specify the index in a call to the TlsGetValue() function. This retrieves the contents of the TLS slot for the calling thread, which in this case is a pointer to the memory block for the data. When a process uses load-time linking with this DLL, the entry-point function is sufficient to manage the thread local storage. Problems can occur with a process that uses run-time linking because the entry-point function is not called for threads that exist before the LoadLibrary() function is called, so TLS memory is not allocated for these threads. This example solves this problem by checking the value returned by the TlsGetValue() function and allocating memory if the value indicates that the TLS slot for this thread is not set. When each thread no longer needs to use a TLS index, it must free the memory whose pointer is stored in the TLS slot. When all threads have finished using a TLS index, use the TlsFree() function to release the index. When a thread terminates, the entry-point function is called with the DLL_THREAD_DETACH value and the memory for that thread is freed. When a process terminates, the entry-point function is called with the DLL_PROCESS_DETACH value and the memory referenced by the pointer in the TLS index is freed.

Create a new empty Win32 console application project. Give a suitable project name and change the project location if needed.

 

Using Thread Local Storage in a Dynamic-Link Library Program Example: Creating new Win32 C++ console application project in Visual C++ .NET

 

Select the DLL and empty project as shown below.

 

Using Thread Local Storage in a Dynamic-Link Library Program Example: Selecting the DLL and empty Win32 Visual C++ project template

 

Then, add the source file and give it a suitable name.

 

Using Thread Local Storage in a Dynamic-Link Library Program Example: Adding new C++ source file for C++ source code to the existing C++ project

 

Next, add the following source code.

 

// The DLL code

#include <windows.h>

 

static DWORD dwTlsIndex; // address of shared memory

 

// DllMain() is the entry-point function for this DLL

BOOL WINAPI DllMain(HINSTANCE hinstDLL, // DLL module handle

    DWORD fdwReason,                    // reason called

    LPVOID lpvReserved)                 // reserved

{

    LPVOID lpvData;

    BOOL fIgnore;

 

    switch (fdwReason)

    {

        // The DLL is loading due to process

        // initialization or a call to LoadLibrary

        case DLL_PROCESS_ATTACH:

                  // Allocate a TLS index

                  if ((dwTlsIndex = TlsAlloc()) == TLS_OUT_OF_INDEXES)

                        return FALSE;

                  // No break: Initialize the index for first thread.

                  // The attached process creates a new thread.

            case DLL_THREAD_ATTACH:

                  // Initialize the TLS index for this thread.

                  lpvData = (LPVOID) LocalAlloc(LPTR, 256);

                  if (lpvData != NULL)

                        fIgnore = TlsSetValue(dwTlsIndex, lpvData);

                  break;

                  // The thread of the attached process terminates.

            case DLL_THREAD_DETACH:

                  // Release the allocated memory for this thread

                  lpvData = TlsGetValue(dwTlsIndex);

                  if (lpvData != NULL)

                        LocalFree((HLOCAL) lpvData);

                  break;

                  // DLL unload due to process termination or FreeLibrary

            case DLL_PROCESS_DETACH:

                  // Release the allocated memory for this thread

                  lpvData = TlsGetValue(dwTlsIndex);

                  if (lpvData != NULL)

                        LocalFree((HLOCAL) lpvData);

                  // Release the TLS index

                  TlsFree(dwTlsIndex);

                  break;

            default:

                  break;

    }

 

    return TRUE;

    UNREFERENCED_PARAMETER(hinstDLL);

    UNREFERENCED_PARAMETER(lpvReserved);

}

 

// The export mechanism used here is the __declspec(export)

// method supported by Microsoft Visual Studio, but any

// other export method supported by your development

// environment may be substituted.

#ifdef __cplusplus    // If used by C++ code,

extern "C" {          // we need to export the C interface

#endif

 

__declspec(dllexport) BOOL WINAPI StoreData(DWORD dw)

{

   LPVOID lpvData;

   DWORD * pData;  // The stored memory pointer

 

   lpvData = TlsGetValue(dwTlsIndex);

   if (lpvData == NULL)

   {

      lpvData = (LPVOID) LocalAlloc(LPTR, 256);

      if (lpvData == NULL)

         return FALSE;

      if (!TlsSetValue(dwTlsIndex, lpvData))

         return FALSE;

   }

 

   pData = (DWORD *) lpvData; // Cast to my data type.

   // In this example, it is only a pointer to a DWORD

   // but it can be a structure pointer to contain more complicated data.

   (*pData) = dw;

   return TRUE;

}

 

__declspec(dllexport) BOOL WINAPI GetData(DWORD *pdw)

{

   LPVOID lpvData;

   DWORD * pData;  // The stored memory pointer

 

   lpvData = TlsGetValue(dwTlsIndex);

   if (lpvData == NULL)

      return FALSE;

 

   pData = (DWORD *) lpvData;

   (*pdw) = (*pData);

   return TRUE;

}

#ifdef __cplusplus

}

#endif

 

Build the project. The DLL and lib files should be generated.

 

Using Thread Local Storage in a Dynamic-Link Library Program Example: The generated DLL and LIB files

 

 

 

 

< Windows Dynamic Link Library 11 | Windows Dynamic Link Library | Win32 Programming | Windows Dynamic Link Library 13 >