windowswinapiserial-portiocpio-completion-ports

CloseHandle() returns before the serial port is actually closed


I'm pulling my hair trying to figure out when a serial port finishes closing so I can reopen it. It turns out that CloseHandle() returns before the port is actually unlocked.

I am opening a serial port using CreateFile(FILE_FLAG_OVERLAPPED), associating it with a CompletionPort using CreateIoCompletionPort(), reading/writing to it using ReadFile(), WriteFile() and closing it using CloseHandle().

I've noticed that if I close and reopen a serial port quickly enough, I get an ERROR_ACCESS_DENIED back from CreateFile(). This is happening in spite of the fact that I'm waiting for CloseHandle() to return, then waiting for all outstanding read/write operations associated with that handle to come back from the completion port. Surely there is a better way :)

How do I close a serial port synchronously? Please no retry loops, sleep() or some other cheap hacks.

EDIT: Maybe this has something to do with my use of completion ports and FILE_FLAG_OVERLAPPED. I get a callback when the read/write operations complete. Is there some sort of callback for the port closing?


Solution

  • I believe the problem is with the driver that serves the COM port. Hence - there'll be no API to "actually close" the COM port.

    BTW, after you close the file handle, there's no need to wait for all oustanding I/Os to complete with errors. At the time CloseHandle returns all outstanding I/Os are already completed/cancelled, you just receive the callbacks asynchronously (be it via completion port or APC queue, no matter).

    Specifically FTDI drivers (those that emulate COM->USB) are known to be very glitchy.

    I may only recommend to try flushing the data before closing the handle. You may wait for all I/Os to complete before closing the COM port (if this is applicable for your case). Alternatively you may call SetCommMask and WaitCommEvent to ensure there's no send data pending. Hopefully this may help.

    EDIT:

    Does CloseHandle immediately (before it returns) cancel all the pending I/Os on the file handle?

    Strictly speaking - no.

    There may be other references left to the file object. For example, a user-mode code may call DuplicateHandle, or a kernel-mode driver may call ObReferenceObjectByXXXX. In such a case the object the handle refers to is not necessarily released.

    When the last handle is closed the driver's DispatchCleanup is called. It must cancel all the outstanding I/Os according to this.

    However while one thread is within a CloseHandle - you may theoretically issue another I/O from another thread (if you're lucky - the handle will still be valid). While you're in the call to an I/O function (such as WriteFile or etc.) the OS temporarily increases the reference counter to the object. This in turn may start another I/O, which won't be cancelled directly by the CloseHandle invocation.

    However in this case the handle will be closed by the O/S immediately after the new I/O is issued, because the reference count to the object reached 0 again.

    So that in such a perverted scenario there may happen a situation where immediately after a call to CloseHandle you're unable to reopen the file again.