How can I atomically leave a critical section and delete it?


A customer had a thread which has entered a critical section. It has finished whatever it needs to do, and now it wants to leave the critical section and delete it. But wait, there's a race condition here: Between the call to Leave­Critical­Section and Delete­Critical­Section, another thread might sneak in and enter the critical section. This means that the original thread is going to delete a critical section while there are threads waiting for it, which the documentation explicitly calls out as leaving the waiting threads in an undefined state.

The question then is how to close this race window by performing the leave and delete atomically?

Actually, that's the wrong question, because having such an atomic operation doesn't fix anything. Suppose your thread calls that atomic leave-and-delete function. Now, the other interloper thread cannot enter the critical section after you leave and before you delete it. Yay, the race condition is gone!

But wait, you jumped out of the frying pan and landed in the fire: What's going to happen is that the interloper thread will instead try to enter the critical section after it has been deleted, which also results in undefined behavior.

All you did was trade one undefined behavior for another. You didn't actually fix anything.

If you have a system set up where there's the possibility of a thread entering a critical section that is about to be deleted, then it means that you also have the possibility of a thread entering a critical section after it has been deleted.

So fix your design so that second problem no longer exists. Once you fix that, you'll see that the original problem also vanishes.

Comments (7)
  1. Yuri Khan says:

    How can I atomically leave a critical section and delete it? Same as doing any other two operations atomically: wrap them in a critical section. Oh wait…

  2. GL says:

    What that customer is trying to do is `delete`ing something that some other guy is holding reference to. Guess s/he never heard of “dangling pointer”.

  3. Alex Cohn says:

    I am afraid I cannot imagine the motivation to delete such CS.

    If the idea is that after the job is done, other threads can cut the overhead of dealing with an obsolete CS, it’s enough to introduce a simple boolean flag that says, “everybody is welcome to proceed without waisting time on CS”.

    But there is another, terrifying possiblility: maybe they create so many Critical Sections that they run out of handles and need to delete obsolete ones…

  4. Joshua says:

    If the problem can’t exist (that is, you ensured that nobody can take it and you’re only worried about threads in it):

    EnterCriticalSection(&criticalsection);
    DeleteCriticalSection(&criticalsection);

    And yeah, I know why you might want to delete CS. This comes up from time to time when you have CS objects protecting dynamically allocated objects.

    1. Joshua says:

      Whoops NVM I forgot EnterCriticalSection is not a queue.

    2. GL says:

      This isn’t any kind of solution… First, you still have the problem that others might be awaiting the CS when you have entered it. Second, it is mandated that the CS to be deleted must be unowned (owned = entered for this case). In this case, your thread owns the CS. See https://msdn.microsoft.com/en-us/library/windows/desktop/ms682552(v=vs.85).aspx

  5. jeff cassar says:

    in a well designed app, a synchronization object lifespan will ALWAYS be one to one in relation to the objects lifespan, in which it protects. this sounds like extremely poor code/design. who just arbitrarily wants to go and delete an object/synchronization object in a multi-threaded environment without knowing how many references the object/synchronization object has to it currently, AND without preventing further references to that object, so that it can be deleted in the future, when there are no more references?

Comments are closed.

Skip to main content