CloseHandle() returns before the serial port is actually closed
Asked Answered
H

3

7

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?

Hadsall answered 16/1, 2012 at 20:16 Comment(7)
Maybe related to your specific serial port driver. Have you seen this here on SO: #2948928Mckinzie
Just to be sure.... you haven't used DuplicateHandle anywhere?Bedwell
I'm not sure if the completion port is related, I've always used APC callbacks with ReadFileEx and WriteFileEx. That's also simpler, because APCs only run then the thread enters an alertable wait, so no cross-thread synchronization issues (just be careful about re-entrancy).Bedwell
I suspect it is caused by an incomplete I/O request. But tackle the problem at the root, there's no point in closing a serial port and re-opening it again.Celka
@BenVoigt: No, I'm not using DuplicateHandle anywhere.Hadsall
@SimonMourier: I'm using an authentic comport it's unlikely a driver issue.Hadsall
Actually, this isn't unique to serial ports and I don't think it's anything you're doing wrong. I've seen the same issue with file handles. There is a significant lag between the time CloseHandle returns and the object actually being released. I suspect that in the end you'll find that some cheap hack involving a retry loop and sleep will be the only solution.Laflamme
C
1

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.

Chainey answered 17/1, 2012 at 15:3 Comment(3)
I am using the serial port that comes off the motherboard (using the built-in Microsoft drivers). There is no COM to USB adapter at play. The problem with flushing the data is that it could block forever if hardware flow control is enabled. Ideally I'd like to abort all outstanding commands, close the port and return from my function once the close is really done. That's all a moot point if there is no API for ensuring a close. PS: How do you know that CloseHandle() cancels all outstanding I/Os before returning? I don't see such a guarantee in the specification.Hadsall
Please see my edit. You may also try to use CancelIoEx. And, of course, don't do any I/O on another thread maenwhile (I hope you don't do this anyway)Chainey
Okay, I ended up using a retry loop when trying to open the port. Thanks for the reference information.Hadsall
D
0

Ensure that your reader and writer fail (with invalid handle) after you have issued the CloseHandle() request (presumably on some other thread) before attempting to touch the device again. You can use this as a cue to clean up all your io handling before attempting to issue another CreateFile. I must say completion ports do seem needlessly baroque.

Drool answered 20/9, 2012 at 14:28 Comment(2)
Nice trick, but unreliable. Because the same handle may be (theoretically) reused.Chainey
Plus you may not have any outstanding reads/writes so now you have to keep track of that as well.Hadsall
F
0

When you open a COM Port in a thread, the operating system opens another hidden thread to do the I/O. I am trying to resolve closing a port as well. Near as I can tell one method which may work is: 1) Suspend the thread which opened the COM Port, 2) PurgeComm() to stop all I/O and empty all read/write I/O buffers, and 3) then attempt to close the COM Port. There may be a waitforsingleobject involved to determine when the thread with the COM Port actually suspends. Unless the COM Port absolutely needs to be closed, I am considering leaving the COM Port open and letting the WinProc process the IDM_Exit command to close the COM Port, threads, and the application. Not what I wanted but an option I could live with. I am using a USB to Serial Port connection and not sure what problems this is causing me. I know if you are using an USB to Serial Port interface you need to set pin 20 high (DTR). Pin 20 helps power the interface, but is only providing around 30ma or so. If all this works I will make an update to this post and let you know how if I was able to close the COM Port. Windows has made a mess of the Serial Port driver and seem to be content to leave things in disarray!

Fritzfritze answered 6/7, 2016 at 0:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.