I've been doing serial communications lately so I prepared a class being a simple interface to all those Windows API functions responsible for reading, writing, etc. All I/O operations inside this class are handled asynchronously.
Before I go to my question, let me show you how I write and read data from serial port (this is only the reading function, because the structure of the writing one is exactly the same so there is no point in presenting both of them).
function TSerialPort.Read(var pBuffer; const lBufferSize: Cardinal): Cardinal;
var
lOverlapped: OVERLAPPED;
lLastError: Cardinal;
lEvent: TEvent;
begin
lEvent := TEvent.Create(nil, True, False, '');
try
FillChar(lOverlapped, SizeOf(lOverlapped), 0);
lOverlapped.hEvent := lEvent.Handle;
if not ReadFile(FSerialPortHandle, pBuffer, lBufferSize, Result, @lOverlapped) then
begin
lLastError := GetLastError;
if (lLastError <> ERROR_IO_PENDING) and (lLastError <> ERROR_SUCCESS) then
raise Exception.Create(SysErrorMessage(lLastError));
case lEvent.WaitFor(INFINITE) of
wrSignaled:
if not GetOverlappedResult(FSerialPortHandle, lOverlapped, Result, False) then
raise Exception.Create(SysErrorMessage(GetLastError));
wrError:
begin
lLastError := lEvent.LastError;
//this is a call to Windows.CancelIo(FSerialPortHandle);
if Self.CancelIO() then
lEvent.WaitFor(INFINITE);
raise Exception.Create(SysErrorMessage(lLastError));
end;
end;
end;
finally
FreeAndNil(lEvent);
end;
end;
Before you ask me why I open serial port for overlapped operations while this function waits for the read operation to finish, here's my explanation - only when opening serial port this way can I specify the time WaitCommEvent() method waits for events. If I opened the port for non-overlapped operations, WaitCommEvent() would block until an event appears on serial port which could not always happen causing the calling thread to block forever.
Nevertheless, let us concentrate on the above Read() function.
1) First of all, I wait without any time limit for the event to be set. Is there a possibility that the current thread would block forever for some reason because of that? I do not know whether I can be 100% sure that the event will sooner or later be set by the thread performing the read operation asynchronously. I know that when serial port's read timeouts are all set to zero, the read operation will not finish until the given number of bytes is read, but this is a behaviour I'm aware of. My question concerns unexpected situations that would cause the event never to be set and the WaitFor() method to wait forever - is it likely to happen?
2) WaitFor() may return wrError which informs that some error occured during the wait operation (but this is not connected with the overlapped read operation at all, right?). Therefore I think I should no longer wait for the read operation to finish then, for the event handle might no longer be usable, right? So I call the CancelIO() method to cancel the read operation, wait for the event to be set by the thread asynchronously performing the cancelled reading and only then raise exception. I wait for the reading to be cancelled by that thread because If I left my Read() method immediately (without cancelling I/O), I would cause that thread to write its data (the overlapped record data) to the local variables that would be no longer valid then, right? On the other hand, is there a danger that the current thread would be blocked forever because of the WaitFor(INFINITE) call before raising exception?
I will be grateful if you tell me if the above statements are true or not and comment on them, please.
Thank you very much in advance.