Little known Win32 APIs: DisableThreadLibraryCalls()

Continuing on the previous theme of cool Win32 APIs that many people ignore, this week’s entry is one of my favorites: DisableThreadLibraryCalls()

DisableThreadLibraryCalls was added in NT 3.5 as a part of the performance enhancements we added to the system.  As we measured the system, it quickly became clear that one major contributor to the overall system working set was the number of pages that were occupied by the DllMain entrypoint in the various system DLL’s. 

The reason for this was that a DLL’s DllMain entrypoint is called whenever a thread is created or destroyed in an application.  This is critical for DLLs that maintain per-thread state like the C runtime library or Winsock.  But for 99% of the DLL’s on the system, the routines simply ignore the DllMain DLL_THREAD_ATTACH and DLL_THREAD_DETACH messages.  Since the system couldn’t determine if a DLL ignores the DLL_THREAD_XXX messages, it always called the DllMain entrypoint whenever a thread was created.

This caused the page that contained the DllMain entrypoint for the DLL to be brought into memory, which increased the application’s working set.

The NT loader guys added the DisableThreadLibraryCalls API to the system to fix this problem.  When an application calls this routine, it lets the system know that the module specified in it’s parameter doesn’t care about DLL_THREAD_XXX messages, and thus the loader won’t call into the DLL on thread creation.

This API is so useful that ATL’s CAtlDllModule.DllMain() method always calls DisableThreadLibraryCalls().  If your DLL doesn’t rely on thread creation/destruction messages, then it should too.


Comments (3)

Cancel reply

  1. Anonymous says:

    Because of the limitations on what you’re allowed to do in DllMain due to the loader lock, and the fact that you don’t get a DLL_THREAD_ATTACH for any threads already running when the DLL is loaded, and that you don’t get DLL_THREAD_DETACH when the DLL is unloaded through FreeLibrary, trying to perform any kind of initialisation or cleanup on a per-thread basis is doomed from the start. You should normally perform lazy initialisation – initialising any required data on demand.

    My DllMain function rarely does anything other than store the HINSTANCE somewhere, in case I need to access the DLL’s resources, and the minimum to initialize any other libraries I’m using (e.g. ATL), then call DisableThreadLibraryCalls. If I didn’t need the instance handle, I’d probably just link /NOENTRY (as long as this doesn’t break the CRT!)

  2. Anonymous says:

    Unfortunately using /NOENTRY usually will break the CRT if you’re using any C++ code, since global constructors are executed in the C runtime library’s entry 🙁

  3. Anonymous says:

    Is this optimization relevant to .NET assemblies? Where can DisableThreadLibraryCalls() be called from?

Skip to main content