__declspec(thread) bites me in the ass yet again

Sometimes I find myself writing code that needs thread-local variables; that is, variables that have a separate value in every thread in the process. This is handy when guarding against recursion while preserving thread-safety, for example.

In the latest case, I have some logging code, and if the logging fails, it uses the same logging code to report the failure (the code is based on log4cplus, where each log attempt writes to multiple outputs, so the logic isn’t as stupid as it sounds). Obviously, if the attempt to log the failure itself fails, I don’t want the code to keep logging meta-failures forever, so I need a bool that says “You’re logging an error in logging, so if you fail don’t try to log that failure”.

However, I’m writing code that runs in a server with multiple concurrent threads, and I’d hate to miss log events written from other threads that happen to take place while my bool is set, so I want the bool to be thread local, so each thread has its own copy of it.

Windows provides the TLS API for this purpose, but Visual C++ makes it so much easier with the __declspec(thread) modifier, which automatically does the TLS stuff under the covers. It’s so easy that I often forget it has a huge caveat.

If a DLL declares any nonlocal data or object as _declspec( thread ), it can cause a protection fault if dynamically loaded. After the DLL is loaded with LoadLibrary, it causes system failure whenever the code references the nonlocal _declspec( thread ) data. Because the global variable space for a thread is allocated at run time, the size of this space is based on a calculation of the requirements of the application plus the requirements of all the DLLs that are statically linked. When you use LoadLibrary, there is no way to extend this space to allow for the thread local variables declared with __declspec( thread ). Use the TLS APIs, such as TlsAlloc, in your DLL to allocate TLS if the DLL might be loaded with LoadLibrary.

Did you get that? If you’re writing code that might be compiled to a DLL, and that DLL might want to someday be loaded with LoadLibrary, don’t even think about using __declspec(thread). As it happens, that’s exactly the case I’m facing. Fuck!

Tags: , , ,

Leave a Reply