QSerialPort - Is it possible to read() and write() on separate threads?
We have a DLL that provides an API for a USB device we make that can appear as a USB CDC com port. We actually use a custom driver on windows for best performance along with async i/o, but we have also used serial port async file i/o in the past with reasonable success as well.
Latency is very important in this API when it is communicating with our device, so we have structured our library so that when applications make API calls to execute commands on the device, those commands turn directly into writes on the API caller's thread so that there is no waiting for a context switch. The library also maintains a listening thread which is always waiting using wait objects on an async read for new responses. These responses get parsed and inserted into thread-safe queues for the API user to read at their convenience.
So basically, we do most of our writing in the API caller's thread, and all of our reading in a listening thread. I have tried porting a version of our code over to using QSerialPort instead of native serial file i/o for Windows and OSX, but I am running into an error whenever I try to write() from the caller's thread (the QSerialPort is created in the listening thread):
@QObject: Cannot create children for a parent that is in a different thread.@
which seems to be due to the creation of another QObject-based WriteOverlappedCompletionNotifier for the notifiers pool used by QSerialPortPrivate::startAsyncWrite().
Is the current 5.2 version of QSerialPort limited to only doing reads and writes on the same thread? This seems very unfortunate as the underlying operating systems do not have any such thread limitations for serial port file i/o. As far as I can tell, the issue mainly has to do with the fact that all of QSerialPort's notifier classes are based on QObject.
Does anyone have a good work around to this? I might try building my own QSerialPort that uses notifiers not based on QObject to see how far that gets me. The only real advantage QObject seems to be giving here is in the destruction of the notifiers when the port closes.
It's not possible, unfortunately.
QObject is not thread-safe, and QSerialPort is a QObject. Therefore, QSerialPort is not thread-safe, which means that its member functions must only be called from the thread that the QSerialPort lives in.
For your use case, I believe you will need a solution that does not involve QObject.
Agreed - it just seems unfortunate to wrap what is fundamentally multithreaded file i/o in something that forces it be single threaded. For things like actual files, this isn't really a problem since you can open multiple single threaded handles and have one for reading and one for writing, but since the serial ports must be opened exclusively on a single file handle you can't do this.
Qt engineers, including the ones who are working on QSerialPort, are active at the "Interest mailing list":http://lists.qt-project.org/mailman/listinfo/interest. If you subscribe and post there, I'm sure they will be interested in hearing your use case. They might even have a solution for you that doesn't involve writing new serial port classes from scratch.
That thing about what you ask, can't be implemented (it is problematic and hard) in context of Qt philosophy. Because QSerialPort was designed so that it uses internal subsystems of I/O of QtCore and is completely integrated with it. The QSerialPort it is pure the Qt decision which uses all features of Qt, including signals/slots and other. I.e. when using QSerialPort it is necessary to adhere to the general concept of Qt.
Also, when using QSerialPort there is no need for various threads for reading and writing. Since everything is carried out with asynchronous and non-blocking approach. I.e. QSerialPort is a decision for the general case of use (also as well as QFile, QxxSocket, etc.). Because impossible to implement in one decision all available use-cases (e.g. as in your case). Btw, it lead to change the design of internal Qt core and so forth.. So, it is difficult in common scenario.
So, IMHO, it is simple - to implement your specific feature by using the native API of each platform himself, without QSerialPort,
(or ... make changes and re-design of your approach for your shared library to near to Qt approach). :)
As a follow up, shortly after this discussion I did implement my own thread-safe serial port code for POSIX systems using select() and the like and it is working well on multiple threads in conjunction with a Qt application.