September 18th, 2025
like2 reactions

Can I close a duplicate handle while I’m waiting on the original?

In our study of the case of the invalid handle error when a handle is closed while a thread is waiting on it, Frederic Benoit described a scenario in which a handle was duplicated and given to another thread. That other thread would operate on the handle and then close it. All of this while the main thread was waiting on the original handle. Is it legal to close a duplicate of a handle that another thread is waiting on?

Yes, this is legal.

The prohibition is against closing a handle while another thread is waiting on that handle. It’s a case of destroying something while it is in use. More generally, you can’t close a handle while another thread is reading from that handle, writing to that handle, using that handle to modify a process or thread’s priority, signaling that handle, whatever.

But if one thread is waiting on the original handle and another thread closes a duplicate, that is not the same handle, so you didn’t break the rule. In fact, closing a duplicate while another thread is waiting on the original is not an uncommon scenario.

Consider this: Suppose there is a helper object whose job it is to set an event handle when something has completed. For example, maybe it’s something similar to ID3D12Fence::SetEventOnCompletion. When you give it the event handle, the object has to duplicate the handle to ensure that it can still get the job done even if the caller later closes the handle. Eventually, the thing completes, and the object calls SetEvent() with the duplicated handle and then closes the duplicate.

Meanwhile, your main thread has done a Wait­For­Multiple­Objects to wait a block of signals.

There is nothing wrong with the helper object closing its private copy of the handle. The point is it didn’t close your copy of the handle, which means that the handle being waited on is not closed while the wait is in progress.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

6 comments

Sort by :
  • GL · Edited

    Edit: This was in reply to Falcon’s top-level comment.

    You’re right. Benoit’s comment meant to express that he relied on the then-de-facto behavior of “closing a console handle unblocks outstanding ReadConsole on that handle” to achieve ReadConsole with timeout. That the code was using a duplicate is to ensure the console is still accessible later via the original handle, since the duplicate is always going to be closed by the new thread.

  • Falcon 4 days ago · Edited

    EDIT: Removed some formatting

    I’m not sure if your interpretation of Frederic’s comment is accurate. Here’s how I understand it:

    Main thread:

    – Existing console handle hConsole
    – Duplicate hConsole to hConsoleDup
    – Create helper thread, passing hConsoleDup to it
    – Call ReadConsole with hConsoleDup handle

    Helper thread:

    – Wait until timeout
    – Close passed handle (hConsoleDup)

    If that’s the case, it would run into the problem described in the other post, since it sounds like it does close a handle that another thread is waiting on (both threads are using the duplicate handle).

    • Michael Taylor 4 days ago

      The scenario you just described is the scenario that isn't supported. You have 2 different threads using the same handle at the same time and one of them closes it. As Raymond mentioned, closing a handle while another thread is doing anything with it is wrong.

      The bug here is with the main thread that is doing things wrong. It creates a duplicate handle and passes that handle on to the child thread. At this point the child thread is responsible for that handle. Yet the main thread uses it anyway. The failure is in the main thread at bullet...

      Read more
      • Falcon

        I’m not disputing that, I was just giving my interpretation of what the referenced comment was saying.

        I believe that the reason for duplicating the handle is to keep the original console handle open. The duplicate is used temporarily while reading; the child thread closes the duplicate handle in order to force ReadConsole to fail and return instead of continuing to wait. (Again, not trying to suggest that it’s a good way of achieving the goal, I was simply “deciphering” the description of what the code was doing.)

  • Kalle Niemitalo 5 days ago · Edited

    This reminds me about traditional POSIX file record locks (fcntl F_SETLK) getting automatically released when the process closes any file descriptor referring to the file.

    • Bwmat 5 days ago

      What a footgun that is…

      Who thought that was a good idea?