Details
-
Bug
-
Resolution: Done
-
P3: Somewhat important
-
5.3.1
-
None
-
Windows 7, MSVC 2013 64-bit
Description
I am using QSerialPort to write to a USB serial device on Windows 7, and I was getting the error signal with the QSerialPort::UnknownError error code after doing a write. GetLastError in my error slot was returning ERROR_IO_INCOMPLETE. I believe this is because a QTimer with zero timeout is used on Windows to check the result of the async write operation rather than getting notified by Windows when the operation is actually complete. A side effect of this is that the same data was getting written twice for a single call to write because _q_completeAsyncWrite was setting writeStarted to false when GetOverlappedResult returned FALSE.
See http://msdn.microsoft.com/en-us/library/windows/desktop/ms683209(v=vs.85).aspx for documentation of GetOverlappedResult. It says if the operation is still pending and bWait is FALSE, GetOverlappedResult will return FALSE and GetLastError will be ERROR_IO_COMPLETE.
If I add the following lines right after the call to GetOverlappedResult in _q_completeAsyncWrite, the problem seems to be fixed.
if (ERROR_IO_INCOMPLETE == GetLastError()) { writeStarted = true; return; }
But I'm not sure if this is the proper fix – does this just keep triggering a QTimer to go off until the I/O operation is really complete, or could there be a long delay before it checks the result again (i.e. how is a call to _q_completeAsyncWrite re-triggered if it just returns as above)?
In addition, _q_completeAsyncRead and _q_completeAsyncCommunication both call GetOverlappedResult and do not check for ERROR_IO_INCOMPLETE, so they may need a similar treatment.
Here is the my complete modified _q_completeAsyncWrite for context:
void QSerialPortPrivate::_q_completeAsyncWrite() { Q_Q(QSerialPort); if (writeStarted) { writeStarted = false; DWORD numberOfBytesTransferred = 0; if (!::GetOverlappedResult(handle, &writeCompletionOverlapped, &numberOfBytesTransferred, FALSE)) { if (ERROR_IO_INCOMPLETE == GetLastError()) { writeStarted = true; return; } numberOfBytesTransferred = 0; q->setError(decodeSystemError()); return; } if (numberOfBytesTransferred > 0) { writeBuffer.free(numberOfBytesTransferred); emit q->bytesWritten(numberOfBytesTransferred); } } startAsyncWrite(); }