Performance Tips for DllMains

The DllMain is an optional entry point into a dynamic link library (DLL) which is called by the system when the DLL is first loaded into a process or when it is unloaded from a process. Think of it as a constructor and destructor of the DLL. In addition, each DLL’s entry point is also called when the system starts or terminates a thread within a process with the appropriate reason. This tip is with regards to the thread attach callouts.

From MSDN documentation:

DLL_THREAD_ATTACH

2

The current process is creating a new thread. When this occurs, the system calls the entry-point function of all DLLs currently attached to the process. The call is made in the context of the new thread. DLLs can use this opportunity to initialize a TLS slot for the thread. A thread calling the DLL entry-point function with DLL_PROCESS_ATTACH does not call the DLL entry-point function with DLL_THREAD_ATTACH.

Note that a DLL's entry-point function is called with this value only by threads created after the DLL is loaded by the process. When a DLL is loaded using LoadLibrary, existing threads do not call the entry-point function of the newly loaded DLL.

DLL_THREAD_DETACH

3

A thread is exiting cleanly. If the DLL has stored a pointer to allocated memory in a TLS slot, it should use this opportunity to free the memory. The system calls the entry-point function of all currently loaded DLLs with this value. The call is made in the context of the exiting thread.

 

Let’s examine the default case where all DLLs receive these thread attach callouts:

  • Let’s say you have a process with a 50 DLLs loaded in its memory and the process creates a new thread. In the default case, the system will have to send the DLL_THREAD_ATTACH callout to each of the 50 DLLs.
  • It’s possible that for some of those DLLs, their DllMain code has been paged out from memory. So, when a thread is created you would now have to pay the cost to page in their DllMain code.
  • And if it turns out that your DllMain is not interested in the DLL_THREAD_* reason and falls through the switch statement, then it was called unnecessarily.

This is an unnecessary overhead. I have seen only a few instances of DLLs having a genuine reason to track when a thread is created in the process space. Majority of the DLLs don’t need to know when a thread is created or terminated in the process. Also, note the remark in the above table for DLL_THREAD_ATTACH, you will not receive the callout for any threads created before the DLL is dynamically loaded. This implies you will not be able to accurately track the number of threads in a process using this technique. 

DisableThreadLibraryCalls

To avoid receiving these thread attach/detach callouts for your DLLs, you can call DisableThreadLibraryCalls in your DllMain.

https://msdn.microsoft.com/en-us/library/ms682579(VS.85).aspx

Example Usage:

BOOL WINAPI

DllMain(HINSTANCE hinst, DWORD reason, VOID *reserved)

{

    switch (reason)

    {

        case DLL_PROCESS_ATTACH:

        {

            DisableThreadLibraryCalls(hinst);

            // other code

            break;

        }

    // Other code

} // End of DllMain

 

And that’s it, your DLL will no longer will receive the thread attach/detach callouts.