Question

for example, in a multi-thread program:

struct TLSObject;

void foo()
{
    TLSObject* p = TlsGetValue(slot);
    if (p == 0) {
        p = new TLSObject;
        TlsSetValue(slot, p);
    }
    // doing something with p
}

the first time to call foo() in any thread will makes a new TLSObject.

my question is: How to delete a TLSObject(if I don't use boost::thread and boost::thread_specific_ptr) ?

boost::thread_specific_ptr can do cleanup work at thread exit, but it depends on boost::thread I guess, not for normal OS thread, and it's slow.

Was it helpful?

Solution 2

Alright. For Windows Vista and above, as James McNellis said - we could use FlsCallback.

For a DLL, we could just use DllMain, if reason parameter equals to DLL_THREAD_DETACH, we do the cleanup. An alternative might be to use _pRawDllMain, it's just like another DllMain, you could find it from boost source.

For an EXE, we could use TLS callback, please have a look at here and here, and, of course, boost source. In practice, it works on Windows XP, but I found that optimizations may make it ineffective, so be careful with optimizations, or make a explicit reference to the pointer of your callback function.

Save the code below to tls.cpp and add it to your project, no matter it's exe or dll, it will work. Note that for a DLL on Windows Vista and above the onThreadExit function may be called twice - one from dll_callback and one from tls_callback.

#include <windows.h>

extern void onThreadExit();

static void NTAPI tls_callback(PVOID, DWORD reason, PVOID)
{
    if (reason == DLL_THREAD_DETACH) {
        onThreadExit();
    }
}

static BOOL WINAPI dll_callback(LPVOID, DWORD reason, LPVOID)
{
    if (reason == DLL_THREAD_DETACH) {
        onThreadExit();
    }
    return TRUE;
}

#pragma section(".CRT$XLY",long,read)
extern "C" __declspec(allocate(".CRT$XLY")) PIMAGE_TLS_CALLBACK _xl_y = tls_callback;

extern "C"
{
    extern BOOL (WINAPI * const _pRawDllMain)(HANDLE, DWORD, LPVOID) = &dll_callback;
}

#pragma comment(linker, "/INCLUDE:__tls_used")
#pragma comment(linker, "/INCLUDE:__xl_y")

If you think it's obscure, use boost's at_thread_exit, the complexity is hidden. In fact the code above is a simplified version of boost tls. And if you do not want to use boost, on Windows system this is a alternative.

Or, a more generic way: thread_local.

OTHER TIPS

Instead of TlsAlloc, use FlsAlloc (and related Fls* functions). With FLS, you register a cleanup callback which the OS will call on the thread before the thread terminates, giving you the opportunity to clean up.

A 'boost::thread_specific_ptr' should work on any thread (according to the answer to my question: Check if thread is a boost thread)

About it being slow, yes, it isn't ideal. However, what you can do is use whatever normal TLS mechanism you wish (I used the GCC specific modifier) and then create an additional thread_specific_ptr which cleans up the data (create a wrapper to your true TLS pointer). So creation and deletion of the TLS is a bit expensive, but access is unaffected.

You should be able use one of the many scope exit mechanisms to achieve this, for example this one.

Another alternative would be to wrap your TLSObject into an RAII class that releases the object when the RAII wrapper is destroyed. This is a very common resource management pattern and definitely applicable here.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top