On Unix, the read notification maps EAGAIN
, EIO
, and EBADF
to ResourceError
and stops processing of further read notifications until you invoke a waitFor
function or until you reopen the port. This makes the QSerialPort
useless for reads until you reopen or waitFor
. It makes sense to map EIO
and EBADF
to the same error perhaps. But, since EAGAIN
is benign, it looks like a Qt bug to treat it that way.
To see if that's the problem, you could clear the error and reinvoke waitForReadyRead(0)
or waitForBytesWritten(0)
and see if the error reappears:
bool retryWaitForBytesWritten(int n, int retries = 10) {
if (!dev.bytesToWrite()) return false;
while (retries-- && !dev.waitForBytesWritten(n)) {
if (dev.error() != QSerialPort::ResourceError)
return false;
dev.clearError(); // retry if it was a resource error
}
return true;
}
You mainly should not use waitForBytesWritten
because:
It doesn't do what you perhaps think it does. All that it ensures is that the internal write buffer in the QSerialPort
has been emptied by feeding the data to the operating system using the write
syscall. This doesn't mean that the data has been physically sent.
It potentially blocks. It's not guaranteed to block.
Getting informed of when a write is finished is usually unnecessary. If you have a half-duplex (essentially) command-response protocol, you don't care when you finished sending, but when the response has arrived or timed out.
If you have a streaming protocol where you send poll commands and don't want to generate them faster than the device can consume them, you should issue the commands (and mark them as "issued" in a data structure) until the bytesToWrite
is past a high watermark. You then suspend adding new commands until you're informed by the bytesWritten
signal that the send buffer is at a low watermark. The readyRead
slot matches up the responses to the issued commands and indicates them appropriately.
So, ideally, your code should contain zero waitXxx
calls. The world around us is asynchronous. You don't wait for things, you react to them. Don't stop the event loop until something happens - that's backwards. Other things may happen before that thing you wait on, and you're ignoring them till then. Even if you push the I/O to a separate thread, you're still wasting a whole thread just blocking. Handling events that redirect flow of control is also hard in sequential pseudo-synchronous code - everything ends up littered with (necessary!) error checks. Sometimes people resort to using exceptions for that, which might be OK, but gets very hard to do correctly in large code bases, and anytime you wish to redirect control to code that wasn't on the call stack. It's much easier to reason about the correctness of your code when faced with the asynchronous events, such as incoming data or errors, when you explicitly express it as a hierarchical state machine.
Design your system so that it progresses based on the events that happen asynchronously. If some aspect of your UI must indicate the state of the data being sent, just make it so: don't wait, don't block the thread.
This answer demonstrates how to implement asynchronous I/O for a device based on QIODevice
, using the state machine framework. Once you've got your states set up, it's a relatively easy matter to tie them to UI behaviors. See these answers: one, two, three, four, five, six, seven, eight.
Also, I was wrong and I fixed the other answer. QSerialPort
does not re-enter the event loop, neither do the sockets in the Network module.