Is it possible to use IO Completion Ports for Serial I/O? According to Windows via C/C++ it is alluded to that it is possible, and does give an example of using IOCP with physical files showing work with CreateFile, ReadFile, WriteFile, etc. However can this actually work with serial comms - has anyone got it working?
I can't find any examples of this on the web, but I cannot be the first to attempt it?
Yes, using I/O Completion Ports for Serial I/O works fine. There is some setup work needed to create a file handle for a serial port that is appropriate for IOCP. But once the setup is done, you can do asynchronous ReadFile() and WriteFile() operations just like with regular file handles and socket handles.
The setup is basically:
Open serial port with CreateFile() passing in the FILE_FLAG_OVERLAPPED value as the dwFlagsAndAttributes parameter.
Modify the serial port state as desired using GetCommState() and SetCommState(). Do this just like you would do when not using IOCP.
Use GetCommTimeouts() and SetCommTimeouts() to turn off total-timeouts for read operations, since it typically doesn't make sense to have timeouts for asynchronous operations. (You would instead explicitly call CancelIO() to cancel a read operation instead.) Turning off total-timeouts is done by setting the ReadTotalTimeoutMultiplier and ReadTotalTimeoutConstant fields of the COMMTIMEOUTS structure to zero.
Now you can use the handle with IOCP just like you would do with regular file handles and socket handles. I.e. attach the handle to a completion port using CreateIoCompletionPort(), initiate I/O operations with ReadFile() or WriteFile() using an OVERLAPPED structure, dequeue completed, failed or canceled operations from the completion port using the GetQueuedCompletionStatus() function.
Additional serial port specific events can also be retrieved asynchronously using the WaitCommEvent() function.
Related
Just as the title says, I am writing a networking program where I open a handle to a network driver using CreateFile, and I have been experimenting with the NO_BUFFERING flag.
Most documentation won't even mention this being used with communication devices, and the ones that do (AKA the MSDN reference, etc), simply mention that you can.
Does anyone have any idea how this may affect communication with the device?
It is a device driver implementation detail, options you specify in the CreateFile() call are passed in the IRP_MJ_REQUEST request. The one I linked is the one for file systems, it is very fancy one. Click through the IrpSp->Parameters.Create.Options link to IoCreateFileSpecifyDeviceObjectHint()'s Options argument to see FILE_NO_INTERMEDIATE_BUFFERING.
The documentation for the IRP_MJ_REQUEST for serial ports is here. Very simple one, no arguments at all :) In general, the winapi to device driver interface for communication ports is a very straight-forward. There's an (almost) direct mapping between the documented winapi function and its underlying IOCTL. The winapi function doesn't do much beyond basic error checking, then quickly passes the job to the driver.
So there isn't any way to pass the FILE_FLAG_NO_BUFFERING option you specify so it simply doesn't get used.
Otherwise the logical conclusion, serial port I/O is interrupt driven, the driver must buffer in order to not lose bytes and keep an acceptable transfer rate. You can technically tinker with the buffer sizes through SetupComm() but, as documented, it is only a recommendation with pretty high odds that the driver simply ignores very low values.
I'm handling a non-standard modem via serial port in an overlapped manner. Besides reading from and writing to the telecommunication line, I have to check the control lines like CTS and DSR using the WaitCommEvent() function.
DWORD EvtMask;
/// (some scopes/levels ommitted)
const BOOL syncChange = WaitComEvent(hFile, &EvtMask, &overlapped);
if (!syncChange) {
assert(GetLastError() == ERROR_IO_PENDING);
/// *background activity* probably writing into EvtMask
/// until overlapped.hEvent gets signalled
}
In the (practically all) cases the function call indicates *background activity*, I have to wait on the overlapped.hEvent to happen. Since I'm also waiting for events from alternative sources (like IPC caused by user input, program termination), I use the WaitForMuiltipleObjects() function. But, if the blocking wait is finished for other reasons than control line changes, how can I stop the background activity on EvtMask? The code I'm based on, currently uses SetCommMask(hFile, 0), but I did not find a reliable reference for this being appropriate.
I also observe cases where changes to control lines are not supported properly (driver?, VM?), so I have to do a sliced wait with in-between checking.
What must be done to safely leave the scope where the variable EvtMask is declared?
The code you have is correct, and fully supported by the documentation, which clearly says:
If a process attempts to change the device handle's event mask by using the SetCommMask function while an overlapped WaitCommEvent operation is in progress, WaitCommEvent returns immediately.
I've used this fact on both "real" serial ports, and USB virtual serial port emulations, and it works reliably.
(In my particular case, I was watching for EV_TXEMPTY so that I could guarantee a minimal separation between certain transmissions on the wire)
I'm trying to put together a proof of concept for reading/writing to a serial port and I am having problems with design/concept. I don't want to poll, so I'm trying to use Overlapped I/O. In this case, it's not a multi-threaded application - it just uses efficient waits.
Open port (with OVERLAPPED)
SetCommState (set port baud, parity, CTS/RTS, etc)
SetCommMask (EV_BREAK, EV_CTS, EV_DSR,..., EV_RXCHAR, EV_RXFLAG, EV_TXEMPTY)
Create OVERLAPPED structures and Events
WriteFile (to port)
WaitCommEvent (due to write)
WaitCommEvent (due to read)
ReadFile (from port)
Check read buffer
The MSDN example is kind of lame (Monitoring Communications Events). I'm currently using two events, but the ReadFile step is failing with ERROR_INVALID_PARAMETER.
In the steps above, do I need one or two overlapped structures? If one OVERLAPPED structure, are the steps WriteFile, WaitCommEvent (for write), reset event, WaitCommEvent (for read), ReadFile?
Have a look at this article, it is much more thorough about how to use overlapped I/O with serial ports:
Serial Communications
I'm using I/O completion ports on Windows for serial port communication (we will potentially have lots and lots of serial port usage). I've done the usual, creating the IOCP, spinning up the I/O threads, and associating my CreateFile() handle with the IOCP (CreateFile() was called with FILE_FLAG_OVERLAPPED). That's all working fine. I've set the COMMTIMEOUTS all to 0 except ReadIntervalTimeout which is set to MAXDWORD in order to be completely async.
In my I/O thread, I've noticed that GetQueuedCompletionStatus() blocks indefinitely. I'm using an INFINITE timeout. So I put a ReadFile() call right after I associate my handle with the IOCP. Now that causes GetQueuedCompletionStatus() to release immediately for some reason with 0 bytes transferred, but there's no errors (it returns true, GetLastError() reports 0). I obviously want it to block if there's nothing for it to do. If I put another ReadFile() after GetQueuedCompletionStatus(), then another thread in the pool will pick it up with 0 bytes transferred and no errors.
In the examples I've seen and followed, I don't see anyone setting the hEvent on the OVERLAPPED structure when using IOCP. Is that necessary? I don't care to ever block IOCP threads -- so I'll never be interested in CreateEvent(...) | 1.
If it's not necessary, what could be causing the problem? GetQueuedCompletionStatus() needs to block until data arrives on the serial port.
Are there any good IOCP serial port examples out there? I haven't found a complete serial port + IOCP example out there. Most of them are for sockets. In theory, it should work for serial ports, files, sockets, etc.
I figured it out -- I wasn't calling SetCommMask() with EV_RXCHAR | EV_TXEMPTY and then WaitCommEvent() with the OVERLAPPED struct. After I did that, my IOCP threads behaved as expected. GetQueuedCompletionStatus() returned when a new character appeared on the port. I could then call ReadFile().
So to answer the original question: "no, you don't need to set hEvent for IOCP with serial ports."
The usage case is that one application generates an event and sends out a signal that any application that cares to listen for it will get. E.g. an application updates the contents of a file and signals this. On Linux this could be done by the waiters calling inotify on the file. One portable way would be for listeners to register with a well-known server, but I would prefer something simpler if possible. As portable as possible ideally means using only POSIX features which are also widely available.
Option using lock files
You can do this by locking a file.
Signal emitter initial setup:
Create a file with a well-known name and lock it for writing (fcntl(F_SETLK) with F_WRLCK or flock(LOCK_EX)`).
Signal receiver procedure:
Open the file using the well-known filename and try to obtain a read lock on it (fcntl(F_SETLK) with F_RDLCK or flock(LOCK_SH)).
Receiver blocks because the emitter is holding a conflicting write lock.
Signal emission:
Signal emitter creates a new temporary file
Signal emitter obtains a write lock on the new temporary file
Signal emitter renames the new temporary file to the well-known filename. This clobbers the old lock file but the waiting receivers all retain references to it.
Signal emitter closes the old lock file. This also releases the lock.
Signal receivers all wake up because now they can obtain their read locks.
Signal receivers should close the file they've just obtained a lock on. It won't be used again. If they want to wait for the condition to happen again they should reopen the file.
In the signal emitter, the temporary lock file which has been renamed over top of the original lock file now becomes the new current lock file.
Option using network multicast
Have the receivers join a multicast group and wait for packets. Have the signal emitter send UDP packets to that multicast group.
You can bind both the sending and receiving UDP sockets to the loopback interface if you want it to use only host-local communication.
In the end I used a bound unix domain socket. The owner keeps an array of client FDs and sends each a message when there is an event.