Why is it possible to destroy a critical section while it is in use?

Some time back, Stu wondered why it is possible to destroy a critical section while it is in use.

Well, there's nothing stopping you from creating a file that contains these lines:

#include <windows.h>
int __cdecl main(int, char**)
  return 0;

and then telling your compiler to turn it into a program. It's not like a bolt of lightning is going to come out of the sky and zap you before you hit the Enter key.

So obviously, it's possible.

On the other hand, it's a bug, just like closing a handle to a file that another thread is reading from, or like closing an event handle that another thread is waiting on.

Critical sections are one of those low-level I sure hope you know what you're doing because I'm not going to help you if you mess up pieces of functionality. If you use them incorrectly, then you will suffer the consequences, the same as if you tried to free memory twice or write to memory after freeing it or cast a Gdiplus::Color* to a CComBSTR*.

Are there any legitimate cases where you would delete a critical section while it is owned? I sure can't think of any.

If there were a legitimate case for deleting a critical section while it is owned, what could it be? Well, it can't be owned by the thread doing the deleting, because that would imply that you took it in order to prevent somebody else from entering it (while you deleted it), but that just creates another race condition: If you tinker the timing, then you can create this scenario: That other thread gets pre-empted just as it was about to execute the first instruction of the EnterCriticalSection function. Meanwhile, the destroying thread enters the critical section, does whatever other stuff it wants to do, and then deletes the critical section. That other thread finally gets a chance to run and is now attempting to enter a deleted critical section, which is clearly not legal.

Okay, so if there were a legitimate case, it would have to be deleting a critical section owned by some other thread. Maybe that other thread enters the critical section, and then signals the main thread to delete the critical section. Why would it do that? Who knows. Maybe it wants to make sure only one thread signals the main thread. But you still have the same problem as with the previous case: You entered the critical section because you wanted to prevent a third thread from entering the protected region, but that third thread might have been pre-empted just as it transferred control to the first instruction of EnterCriticalSection, and when that third thread finally gets some CPU time, it proceeds to enter a deleted critical section.

So I can't think of a legitimate reason for deleting a critical section while it's in use. Maybe there's a flaw in my logic.

Comments (15)
  1. GregM says:

    "I think the question that Stu was trying to ask was in fact "Why does DeleteCriticalSection() not check for critical sections being in use before deleting them, and refuse to do so if they are?""

    Another possible reason why is that the critical section functions have been around since the time when programmers were trusted to know what they were doing.  They were responsible for making sure that they coded their programs correctly, not the OS.  The check was therefore left out for performance reasons.  Changing the behavior now could break existing programs.

  2. I once had to debug a nasty problem of this sort on the Xbox 360.  Thread 1 was doing this:

    class X {

     volatile int mState;


     HANDLE mEvent;


    X::Wait() {

     while(mState != kDone) {

       WaitForSingleObject(mEvent, INFINITE);



    X::~X() {



    Thread 2 was doing this:

    X::SetState(int state) {


     // do some state logic

     mState = state;




    The problem: if the thread scheduler decided to schedule thread 1 immediately after the event was signaled, it would return from Wait() and execute the object’s destructor shortly thereafter.  Thread 2 exits immediately after SetState() returns.

    However, in this scenario, the destructor in thread 1 calls DestroyCriticalSection() before thread 2 calls ExitCriticalSection(), resulting in a hard crash inside the kernel.

    Solution: surround make sure to grab the critical section while reading mState from thread 1.  This way, thread 2 will have definitely exited the critical section:

    X::Wait() {

     while(1) {


       int state = mState;


       if(state == kDone)


       WaitForSingleObject(mEvent, INFINITE);


    [Also, Raymond, it would be nice if there was a way to post pretty code in the comments (or if you could point to the documentation that says how to do so, if it’s already possible)]

  3. porter says:

    > X::~X()

    You may have bigger problems than that as you are sharing a C++ object between threads without referencing counting it, increasing the chance of the other thread using a reference to a deleted object.

  4. > You may have bigger problems than that as you are sharing a C++ object between threads without referencing counting it, increasing the chance of the other thread using a reference to a deleted object.

    No.  The second thread exits immediately after returning from that call to SetState(), so there are no dangling references.

  5. Alexander Grigoriev says:


    Does checked build of kernel32 issue an assert if you delete an owned CS, or try to enter a deteled one?

    P.S. It would be nice if on “ERROR: The code you entered was invalid, try again.” the browser returned to a #comment bookmark, not to the top of the page.

    [I couldn’t find a reason why you would legitimately want to delete an owned critical section, but that doesn’t mean that no such reason exists. (For example, maybe your logic is complicated enough that the code which deletes the critical section doesn’t know whether or not it has been entered.) And how would the checked build even know that you passed an deleted critical section to EnterCriticalSection? Maybe your block of memory happens to look just like a block of memory that InitializeCriticalSection would have created. -Raymond]
  6. Alexander Grigoriev says:


    I hope you don’t forget to also CloseHandle(hEvent) in the destructor?

  7. ChrisR says:

    @Adam:  I would have coded the read to enter the critical section as well, but in your case, couldn’t you have moved the SetEvent call to after the LeaveCriticalSection.  It doesn’t appear that you need the SetEvent call inside the critical section.

    [But then somebody might delete the critical section right after you leave it, causing the SetEvent(mEvent) to read memory after it was freed. -Raymond]
  8. pete.d says:

    “The third reason is that clean-up functions can’t fail because, well, how do you clean up from a failed clean-up?”

    From the docs for DeleteObject():

    “Return Value

    Nonzero indicates success.

    Zero indicates that the specified handle is not valid or that the handle is currently selected into a device context.”

    The function will fail if the handle is still selected into a DC. Likewise, DestroyCriticalSection() could have been designed to fail if any thread had an unbalanced call to EnterCriticalSection().

    Your previous blog article in fact specifically provides for this exception: “These clean-up functions fall into the category of ‘Must not fail for reasons beyond the program’s control.'”  That is, it’s not that the functions can’t fail. It’s that they can’t fail for reasons that are outside the control of the program calling them.

    Calling DestroyCriticalSection() on a CS that has an unbalanced call to EnterCriticalSection() would be an error under the program’s control, and thus would be perfectly reasonable for the function to return an error instead of succeeding in that case.

    Now, my guess is that there are other legitimate reasons for not returning an error.  For example, the need to destroy the CS in the event that the thread that called EnterCriticalSection() has terminated abnormally, and thus cannot call LeaveCriticalSection() (though, of course the OS could impose additional logic, checking to see if the thread still exists and only generating the error if it does).  Or perhaps it was just antithetical to the API design of that area of the OS to allow for errors.

    But none of that means that it couldn’t be done.

    [I’m still not completely convinced that there is absolutely no scenario where deleting an owned critical section is valid. I vaguely recall that OS/2 had some situations where the standard solution was to exit a thread while it owned the critical section. -Raymond]
  9. Paul Betts says:

    Sounds like something AppVerifier could catch

  10. Perry Lorier says:

    I don’t do any windows coding, so I might be missing something here, but I can imagine a situation where you have something reference counted, with the reference count protected by the critical section.  When the reference count hits zero (which needs to be done from within the critical section), you know tat there cannot be another object waiting on the critical section (since nothing else exists — the count is now 0), so you can free it.  You could release it  then free it just to be polite, but possibly it’s not necessary.

  11. Karellen says:

    “So obviously, it’s possible.”

    Hmmm….obviously it’s possible to *call* DeleteCriticalSection() on a critical section that is in use.

    That does not mean that DeleteCriticalSection() necessarily ought to be required to delete the critical section in such a case, instead of, say, *not* deleting the critical section and returning an error.

    I think the question that Stu was trying to ask was in fact “Why does DeleteCriticalSection() not check for critical sections being in use before deleting them, and refuse to do so if they are?”. Or, “*Why* doesn’t this piece of functionality help you if you mess up?”.

    One answer, as you pointed out, is that there might be a legitimate reason for doing so, even if none are immediately apparent.

    One other is, of course, that operations on critical sections hold locks which you want to be holding for as short an amount of time as possible, and such checks would unnecessarily slow down the ordinary, non-buggy, case.

    [The third reason is that clean-up functions can’t fail because, well, how do you clean up from a failed clean-up? You call DeleteCriticalSection and it fails. Now what? (And given that critical sections are held briefly, what really happens is that you call DeleteCriticalSection and one in 1,000,000 times, it fails. Yay, a nearly-impossible-to-debug memory leak.) -Raymond]
  12. Nicholas says:

    "Or, "*Why* doesn’t this piece of functionality help you if you mess up?"

    1) Well, Windows NT development started around 1989.  In general, programs had to be lean so you did not have luxury checks.  As Raymond wrote, you the programmer was trusted

    2) The general philosophy in programming is that a close/finalize/dtor works.  It doesn’t fail

    This all ties back to the "Worse is better" argument.  The ultimate goal is to keep the implementation as simple as possible, even at the expense of the public API.  Platforms (operating system or code library) cannot be expected to save you from yourself.  Don’t like it?  Perhaps a scripting language would better suit you.

  13. DoesNotMatter says:

    Some extra fancy firewall/AV software ?

    Or anti-cheating measures for online games ?

    I’m basically seeing a triangle formed by:

    Software A, Observer/Verifier, Software B

    Software A has the critical section

    Observer watches Software A and ensures that it does what it designed to do (ie. One mouseclick one action, no scramming the reactor without cause etc.).

    Software B is something that interacts with Software A, be it game client or a hack attempt.

    Problem is I dont see how this setup could really be feasible, I’m thinking that anything that can get to Software A also can get to Observer. And if my Observer is immune against the manipulation attempt against Software A why didn’t I incorporate the immunization directly into Software A ?

  14. Alexander Grigoriev says:

    @Perry Lorier

    InterlockedIncrment/InterlockedDecrement is what everybody uses for the refcount; no critical section necessary.

  15. Kujo says:

    If by "owned", you mean the critical section thinks it has been entered by thread id x, then thread id y could legitimately delete it because it happens to know thread id x is gone.

Comments are closed.