Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-40769

QSerialPort does not handle ERROR_IO_INCOMPLETE on Windows

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P3: Somewhat important
    • 5.3.2
    • 5.3.1
    • Serial Port
    • None
    • Windows 7, MSVC 2013 64-bit
    • Windows

    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();
      }
      

      Attachments

        Activity

          People

            kuzulis Denis Shienkov
            pnoffke Patrick Noffke
            Votes:
            0 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: