The Scenario
Given the following code fragment from a project that builds to a DLL:
inline ShellItem2Pointer get_desktopItem()
{
ShellItem2Pointer ret;
auto desktopSf = desktopFolder(); //a similar function that calls SHGetDesktopFolder
::SHGetItemFromObject(desktopSf.asUnknown(), IID_PPV_ARGS(ret.addressOf()));
Q_ASSERT(ret);
return ret;
}
//! Retrieves a `ShellItem2Pointer` for the desktop.
QE_WINDOWS_EXPORT ShellItem2Pointer desktopItem()
{
static ShellItem2Pointer ret = get_desktopItem();
return ret;
}
Where ShellItem2Pointer is a specialization of yet-another basic smart pointer class for IUnknown interfaces. The specialization is for UnknownPointer<IShellItem2>, and it provides the usual basic automatic AddRef() and Release() calls plus some helper functions, including asUnknown() and addressOf().
In context, the main thread maintains a cache of data from the Shell namespace to display in a tree or list view. Icons, thumbnails, and data for remote objects will be loaded in background threads (ITEMIDLISTs are copied and safely passed between threads instead of IShellItem2Pointers).
The question
If I change the declaration of the static variable to
thread_local ShellItem2Pointer ret ...
to ensure my threads aren't sharing a COM object improperly, what pitfalls are there and what protections must I assume regarding the change? I am looking both from the perspective of thread_local variables in general in DLLs, as well as the fact that it holds a pointer to an IShellItem2.
Things we already know because we went on MSDN
- Dynamically initialized
thread-localvariables in DLLs may not be correctly initialized on all calling threads. [not applicable, but good to know]- ...
You can specify
thread_localonly on data items with static storage duration. This includes global data objects (bothstaticandextern), local static objects, and static data members of classes. Any local variable declaredthread_localis implicitly static if no other storage class is provided; in other words, at block scopethread_localis equivalent tothread_local static....
On Windows, thread_local is functionally equivalent to __declspec(thread) except that [compiler-specific extensions]...
And from the docs for __declspec(thread):
If the variable is initialized with a function call (including constructors), this function will only be called for the thread that caused the binary/DLL to load into the process, and for those threads that started after the binary/DLL was loaded. The initialization functions are not called for any other thread that was already running when the DLL was loaded. Dynamic initialization occurs on the DllMain call for DLL_THREAD_ATTACH, but the DLL never gets that message if the DLL isn't in the process when the thread starts.
Thread-local variables that are initialized statically with constant values are generally initialized properly on all threads. However, as of December 2017 there is a known conformance issue in the Microsoft Visual C++ compiler whereby constexpr variables receive dynamic rather than static initialization. [also not applicable, also good to know]
Note: Both of these issues are expected to be fixed in future updates of the compiler.
I dug up a good overview of the subject, but it dates to 2007. The Old New Thing also has a good article that's too dated, as well.
Is this a dupe?
I've found several questions that have answers related to this topic, but I am unable to find an SO question that directly answers my question. A sample:
- Can thread_local be used to provide a static global variable in a dll for each thread? is specific to
staticglobal variables - How do you properly implement (c++) thread local storage in a dynamically loaded DLL? is a great question, but pre-dates C++11's wide implementation and adoption (including
thread_local). - And quite a few tangentially-related-but-unanswered questions.
Aucun commentaire:
Enregistrer un commentaire