What is the point of FreeLibraryAndExitThread?

The Free­Library­And­Exit­Thread function seems pointless. I mean, all the function does is

void WINAPI FreeLibraryAndExitThread(
    HMODULE hLibModule,
    DWORD dwExitCode)

Who needs such a trivial function? If I wanted to do that, I could just write it myself.

DWORD CALLBACK MyThreadProc(void *lpParameter)
    ... blah blah blah ...
    // FreeLibraryAndExitThread(g_hinstSelf, 0);

And then you discover that occasionally your program crashes. What's going on?

Let's rewind and look at the original problem.

Originally, you had code that did something like this:

DWORD CALLBACK SomethingThreadProc(void *lpParameter)
 ... do something ...
 return 0;

void DoSomethingInTheBackground()
 DWORD dwThreadId;
 HANDLE hThread = CreateThread(nullptr, 0, SomethingThreadProc,
                  nullptr, 0, &dwThreadId);
 if (hThread) CloseHandle(hThread);

This worked great, until somebody did this to your DLL:

HMODULE hmodDll = LoadLibrary(TEXT("awesome.dll"));
if (hmodDll) {
 auto pfn = reinterpret_cast<decltype(DoSomethingInTheBackground)*>
            (GetProcAddress(hmodDll, "DoSomethingInTheBackground"));
 if (pfn) pfn();

This code fragment calls your Do­Something­In­The­Background function and then immediately unloads the DLL, presumably because all they wanted to do was call that one function.

Now you have a problem: That Free­Library call frees your DLL, while your Something­Thread­Proc is still running! Result: A crash at an address where there is no code. Older debuggers reported this as a crash in ⟨unknown⟩; newer ones can dig into the recently-unloaded modules list and report it as a crash in awesome_unloaded.

This is a very common class of error. When I helped out the application compatibility team by looking at crashes in third-party code, the majority of the crashes I looked at in Internet Explorer were of this sort, where a plug-in got unloaded while it still had a running thread.

How do you prevent your DLL from being unloaded while you still have code running (or have registered callbacks)? You perform a bonus Load­Library on yourself, thereby bumping your DLL reference count by one.

If you don't need to support Windows 2000, you can use the new Get­Module­Handle­Ex function, which is much more convenient and probably a lot faster, too.

BOOL IncrementDLLReferenceCount(HINSTANCE hinst)
 HMODULE hmod;

Bumping the DLL reference count means that when the original person who called Load­Library finally calls Free­Library, your DLL will still remain in memory because the reference count has not yet dropped all the way to zero because you have taken a reference to the DLL yourself.

When you unregister your callback or your background thread finishes, you call Free­Library to release your reference to the DLL, and if that's the last reference, then the DLL will be unloaded.

But wait, now we have a problem. When you call Free­Library to release your reference to the DLL, that call might end up unloading the code that is making the call. When the call returns, there is no more code there. This most commonly happens when you are calling Free­Library on yourself and that was the last reference. In rarer circumstances, it happens indirectly through a chain of final references.

Let's walk through that scenario again, since understanding it is central to solving the problem.

  1. Some application calls Load­Library on your DLL. The reference count on your DLL is now 1.

  2. The application calls a function in your DLL that uses a background thread.

  3. Your DLL prepares for the background thread by doing a Get­Module­Handle­Ex on itself, to avoid a premature unload. The reference count on your DLL is now 2.

  4. Your DLL starts the background thread.
  5. The application decides that it doesn't need your DLL any more, so it calls Free­Library. The reference count on your DLL is now 1.

  6. Your DLL background thread finishes its main work. The thread procedure ends with the lines

        return 0;
  7. The thread procedure calls Free­Library(g_hinst­Self) to drop its reference count.

  8. The Free­Library function frees your DLL.

  9. The Free­Library function returns to its caller, namely your thread procedure.

  10. Crash, because your thread procedure was unloaded!

This is why you need Free­Library­And­Exit­Thread: So that the return address of the Free­Library is not in code that's being unloaded by the Free­Library itself.

Change the last two lines of the thread procedure to Free­Library­AndExit­Thread(g_hinstSelf, 0); and watch what happens. The first five steps are the same, and then we take a turn:

  1. Your DLL background thread finishes its main work. The thread procedure ends with a call to

        FreeLibraryAndExitThread(g_hinstSelf, 0);
  2. The Free­Library­And­Exit­Thread function calls Free­Library(g_hinst­Self).

  3. The Free­Library function frees your DLL.

  4. The Free­Library function returns to its caller, which is not your thread procedure but rather the Free­Library­And­Exit­Thread function, which was not unloaded.

  5. The Free­Library­And­Exit­Thread function calls Exit­Thread(0).

  6. The thread exits and no further code is executed.

That's why the Free­Library­And­Exit­Thread function exists: So you don't pull the rug out from underneath yourself. Instead, you have somebody else pull the rug for you.

This issue of keeping your DLL from unloading prematurely rears its head in several ways. We'll look at some of them in the next few days.

Bonus chatter: The thread pool version of Free­Library­And­Exit­Thread is Free­Library­When­Callback­Returns.

Comments (24)
  1. Joshua says:

    Neat trick. I almost never have to worry about unloading DLLs though (except one case where this is inadequate)

  2. Definitely a nice trick, I didn't have any clue of why it could be useful.

    (On an unrelated note, the <ol> are misaligned on Firefox 24/Windows XP, in the "post view" only the last digit of each list number is shown, and in the blog home page only the dot remains visible)

    [It's a bug in my style sheet that's been around for a decade. Fixed. -Raymond]
  3. CarlD says:

    I remember building a mechanism like FreeLibraryAndExitThread years ago – back in the days NT4 or earlier – to handle essentially this very scenario.  As I recall, the lack of such a feature did make for some difficult to debug bugs until we figured out what was happening.

  4. I had to debug similar crashes when writing a plugin for an application; there was no threading involved, but the application managed to unload my dll before I destroyed its window; Visual Studio was quite confused trying to debug the crashes in the now-disappeared window procedure.

  5. Joshua Ganes says:

    Wow, this seems like something that would take days of pounding my head against my desk, wall, and other hard objects. Wonderful advice that I, unfortunately, will probably forget just in time to shoot myself in the foot.

  6. > How do you prevent your DLL from being unloaded while you still have code running (or have registered callbacks)?

    You have a clear contract for your clients which enforces that all of your background threads are shut down, and all of your callbacks are unregistered, before your .dll is unloaded.

    [And what if you are adding a feature to an existing component, and the existing contract for that component didn't cover background threads? Or if you don't control the contract at all (e.g. COM)? -Raymond]
  7. Mario Vilas says:

    One could build a similar mechanism by manipulating the stack and pointing the return address of LoadLibrary directly to the entry point of ExitThread. Of course, while it'd be clever, it'd never be portable! :)

  8. parkrrrr says:

    Hypothetically, one could set one's own thread procedure parameter to the HMODULE of the library, move ebp to esp to clear the local variables off the stack, and then jump to FreeLibrary rather than calling it. Also not at all portable, but far easier than also manipulating FreeLibrary's return address.

    Not that I've ever done this.

  9. Joshua says:

    > Not that I've ever done this.

    Hmmm that's an interesting idea. If FreeLibrary is tail called (note that most archs allow tail call), this would work. It might be possible by passing a certain set of optimization flags to convince the compiler to generate the call as a jmp instruction; but now code correctness depends on exact optimizer flags (a terrible proposition).

    Better to write such fragments in assembly. Then somebody reading it either gives up or can see that it is correct.

  10. JM says:

    Huh. The blog sees fit to censor what I consider to be quite a mild insult in English, involving the questioning of the legally wedded status of one's parents. Just in case anyone thought I'd written something much worse there.

  11. > what if you are adding a feature to an existing component, and the existing contract for that component didn't cover background threads? Or if you don't control the contract at all (e.g. COM)?

    I don't understand the question.

    If a contract already exists, you try to work within it and still implement the feature. If the feature is fundamentally incompatible with the contract, you either define a new contract or you change the feature.

    What else can you do?

    [I asked "How do you prevent your DLL from being unloaded prematurely?" and you said, "You write the contract so that is not allowed." Then I asked, "What if you can't rewrite the contract?" And you said, "I don't understand the question. If there is an existing contract, then you try to work within the contract." And that's what this article is about: How to work within an existing contract that does not cover background threads. -Raymond]
  12. Joshua says:

    @Myra (*): It was actually possible on NT4 and 2000 to completely unload the .EXE. Closing handle 3 after calling UnmapViewOfFile would get rid of it. On XP and up, the handle isn't 3 anymore, and I'm told it's now in-kernel only so userspace cannot close it (if it were merely in another process I'd try the safe variant of the unlocker trick that I have posted here).

  13. Joshua says:

    (e.g. COM)?

    Out-of-process COM. /me ducks

  14. Myria says:

    @Joshua: I'd love a /SWAPRUN:ALWAYS linker option.

  15. Henke37 says:

    The scary part is that I guessed the correct answer just from the title of the post.

  16. JM says:

    @Maurits: from the years I've read Raymond's blog, I've come to think that contracts are almost entirely mythological in Windows programming. There is Stuff That Works and Stuff That Does Not Work (with shades of Stuff That Nearly Always Works and Stuff That Shouldn't Work But We've Made Damn Sure It Does thrown in, of course), but contracts, no. The kindest thing you can do (for end users) is to assume everyone else is a *** and be as unobtrusive yourself as possible. Forget "trust, but verify" — make it "assume hostile intent".

  17. Myria says:

    Too bad there is no _freelibraryandendthreadex to avoid leaking C runtime memory if you use _beginthreadex.  Such a thing could be made.

    To avoid the possibility of msvcr*.dll getting unloaded before the calling DLL (because the calling DLL might be the last reference to msvcr*.dll), a hypothetical _freelibraryandendthreadex could increase its own reference count first via the GetModuleHandleExW trick above.  msvcr*.dll could free the C runtime memory, FreeLibrary the calling DLL, then FreeLibraryAndExitThread itself to terminate the thread and remove its last reference, possibly also unloading msvcr*.dll.

  18. Myria says:

    @myself: OK, I'm dumb.  If X depends on Y, X will get unloaded(*) before Y, so no special trick is required.  Therefore, _freelibraryandendthreadex could be implemented in a straightforward manner.

    (*) If X is the process EXE, neither X nor Y ever actually get unloaded prior to process termination.  Instead, they merely get deinitialized with DLL_PROCESS_DETACH as in the DLL case, but then NtTerminateProcess gets called to make the process go "poof".

  19. Jonathan says:

    Of course, one must beware of additional stuff that the language adds at scope end. For example, if you have some auto-class that releases on exit (CComPtr, CCoInitialize, etc), its destructor won't get called, and you'll have a nasty memory leak on you hands. Solution: Put the "Thread API funny stuff" and nothing else in the thread proc, and call DoSomething() from it – inside you can use whatever scope-dependent stuff you want.

  20. Neil says:

    XP's version of Verdana doesn't seem to have the ⟨ or ⟩ characters. IE8 just gives up while the browser I'm using substitutes a different font where the characters seem to have lots of ascent and descent for some reason. (I couldn't work out which font it was though.)

  21. DWalker says:

    @Neil:  And that is related to this issue how, exactly?  I'm confused….  Surely those aren't the greater-than and less-than characters you're mentioning.

  22. parkrrrr says:

    > Better to write such fragments in assembly.

    Which is exactly what I did when I never did that.

  23. aaronla says:

    Threads are awfully expensive. There are times in which you could instead call "FreeLibraryAndSwitchToFiber(hmodule, fiber);"

  24. Paul M. Parks says:

    @aaronla: The statement "Threads are awfully expensive" is meaningless without context, or at least examples. Fibers are also more trouble than they're worth, if you're suggesting using them in place of threads as a general solution. See blogs.msdn.com/…/9969664.aspx

Comments are closed.

Skip to main content