INF: Use of Thread Local Storage in an Ext. Stored ProcedureID: Q163449
|
Thread Local Storage (TLS) can be a very tricky subject. When working in a
thread pooling environment, it is very important that you understand the
ramifications of TLS and the thread interactions.
It is highly suggested you avoid TLS in a thread pooling environment such
as Microsoft SQL Server. However, if you must use TLS, please carefully
read this document and consult the online documentation in the Win32 SDK
for the TLS and DLL main functions.
Also, the compiler directive __declspec(thread) is not supported in an
extended stored procedure. When LoadLibarary takes place, the
__declspec(thread) definition is not properly initialized. See "Advanced
Widows" by Jeffrey Ricter for more details.
NOTE: Any extension to SQL Server, including extended stored procedures,
may never make a call to DisableThreadLibraryCalls().
As documented, the TlsAlloc function returns an index value that is used in
function calls to TlsSetValue and TlsGetValue. The documentation suggests
that you call the TlsAlloc function in your DLLMain function when
ul_reason_for_call = DLL_PROCESS_ATTACH. By default, DLL_PROCESS_ATTACH is
called when the DLL is initially loaded.
In DLL_PROCESS_ATTACH and DLL_THREAD_ATTACH, you should allocate memory and
call TlsSetValue. This is specifically where the thread pooling starts to
cause some problems in the scenario.
Below is an example scenario that applies to any application that does
thread pooling. This example uses the Microsoft SQL Server application.
The following are a few basics for the example that you must understand.
These are all documented in more detail in the Win32 SDK documentation
under the DLLMain function.
Thread ATTACH CALLED COMMAND USER
------------------------------------------------
1 NO select Joe
2 YES xp_test Mary
3 YES select Adam
1 NO xp_test Lynn
//
// 1. Start SQL Server from the command prompt to see the output:
// ...\Mssql\Binn\Sqlservr -c
//
// 2. Run Xproctst.cmd to show the behavior.
//
#include "windows.h"
#include "stdio.h"
#include "srv.h"
#define TLS_FAIL 0xFFFFFFFF
DWORD dwTlsIndex = TLS_FAIL;
DWORD dwCounter = 0;
CRITICAL_SECTION csSync;
//
// Cleanup TLS memory
//
void vCleanUpTls(void)
{
char * strData = NULL;
if(TLS_FAIL != dwTlsIndex)
{
strData = TlsGetValue(dwTlsIndex);
if(NULL != strData)
{
free(strData);
printf("\n >>> Tls memory released by thread %ld",
GetCurrentThreadId());
}
}
}
//
// Setup the TLS pointer
//
void vSetUpTls(void)
{
char * strData = NULL;
if(TLS_FAIL == dwTlsIndex)
{
dwTlsIndex = TlsAlloc();
}
//
// Are we ready to go
//
if(TLS_FAIL != dwTlsIndex)
{
strData = (char *) calloc(256,1);
if(strData)
{
printf("\n >>> Tls memory allocated by thread %ld",
GetCurrentThreadId());
if(TRUE == TlsSetValue(dwTlsIndex, strData))
{
//
// Protect the counter.
//
EnterCriticalSection(&csSync);
sprintf(strData, "Counter = %ld", ++dwCounter);
LeaveCriticalSection(&csSync);
}
else
{
printf("\n >>> *** Serious error *** TlsSetValue
failed.\n");
}
}
else
{
printf("\n >>> *** Serious error *** can not allocate
memory.\n");
}
}
else
{
printf("\n >>> *** Serious error *** TlsAlloc failed.\n");
}
}
//
// DLLMain
//
BOOL APIENTRY DllMain(HANDLE hInst, ULONG ul_reason_for_call, LPVOID
lpReserved)
{
char strInfo[256] = "";
switch(ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
InitializeCriticalSection(&csSync);
vSetUpTls();
sprintf(strInfo, "\n >>> DLL_PROCESS_ATTACH Thread: %ld",
GetCurrentThreadId());
break;
case DLL_THREAD_ATTACH:
vSetUpTls();
sprintf(strInfo, "\n >>> DLL_THREAD_ATTACH Thread: %ld",
GetCurrentThreadId());
break;
case DLL_PROCESS_DETACH:
vCleanUpTls();
if(TLS_FAIL != dwTlsIndex)
TlsFree(dwTlsIndex);
DeleteCriticalSection(&csSync);
sprintf(strInfo, "\n >>> DLL_PROCESS_DETACH Thread: %ld",
GetCurrentThreadId());
break;
case DLL_THREAD_DETACH:
vCleanUpTls();
sprintf(strInfo, "\n >>> DLL_THREAD_DETACH Thread: %ld",
GetCurrentThreadId());
break;
}
printf(strInfo);
return TRUE;
}
//
// DLL function in the extended stored procedure to show TLS trap
//
__declspec(dllexport) SRVRETCODE xp_Tls(SRV_PROC *pSrvProc)
{
char strInfo[256] = "";
char * strData = NULL;
sprintf(strInfo, "\n >>> Invoking xp_Tls on Thread: %ld",
GetCurrentThreadId());
printf(strInfo);
if(TLS_FAIL != dwTlsIndex)
{
strData = TlsGetValue(dwTlsIndex);
if(NULL != strData)
{
sprintf(strInfo, "\n >>> %s", strData);
printf(strInfo);
}
else
{
printf("\n >>> *** Serious error *** TlsGetValue returned NULL,
thread pooling not handled correctly.\n");
}
}
return 1;
}
Additional query words: ODS
Keywords : kbother kbprg kbusage SSrvODS SSrvProg SSrvStProc
Version : 6.0 6.5
Platform : WINDOWS
Issue type : kbhowto
Last Reviewed: April 8, 1999