Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. QSerialPort - blocking vs. non blocking confusion

QSerialPort - blocking vs. non blocking confusion

Scheduled Pinned Locked Moved General and Desktop
10 Posts 3 Posters 9.0k Views
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • F Offline
    F Offline
    frankiefrank
    wrote on last edited by
    #1

    I'm working with Qt 5.2 and I'm trying to understand how to best use QSerialPort from the documentation and examples. But I ended up with a bit of a mess in my code and I'd appreciate some clarification.

    I want to use an asynchronous approach for reading, and use the readyRead signal. However, my code is already running inside a worker thread without a Qt event loop. So I'm not sure if I can even use the signals and slots.

    I saw an example where the code used waitForBytesWritten() after the write() call but at this point I'm not sure I understand anymore what approach I'm looking at. I think I'm at some hybrid state which is just plain incorrect.

    Could anyone please explain the two approaches and which methods are used for reading+writing in each one? And also, if I'm in a thread without the event loop, can I use the signals or must I use the waitForxxx methods?

    "Roads? Where we're going, we don't need roads."

    1 Reply Last reply
    0
    • K Offline
      K Offline
      kuzulis
      Qt Champions 2020
      wrote on last edited by
      #2

      It is better - to use an async approach, even in another thread.

      In the theory asynchronous approach implies that the I/O operations are executed asynchronously (without blocking). So, the read() and write() methods alvays returns immediately.

      It is also important to note that QtSerialPort always works in the buffered mode (contains two internal buffers). I.e. the write() will simply copy data to the internal writeBuffer, and read() will simply take data from the internal readBuffer.

      But for data transfering from the writeBuffer to the device and data acquisition from the device to readBuffer is requires an Qt event-loop. Thing in that all I/O operations are executed by system (platform-specific) events from the descriptors of device or descriptors of the I/O operations (depends from host OS).

      Thus the internal subsystem of QtSerialPort automatically is notified, e.g. about that in the Rx FIFO of the device there are data for reading and they can be read, or that the Tx FIFO of the device is empty and data can be filled to write. And will be emitted are readyRead() or bytesWritten() signals.

      So, functioning of the mechanism of events requires an Qt event-loop.
      E.g. this code
      @
      Foo::read() {
      while (1) {
      QByteArray data = port.read();
      }
      }
      @
      will not work because no Qt event-loop entry. And will be returns always empty data.

      But this code is correct:
      @
      ...
      // notify about data available for read
      connect(port, SIGNAL(readyRead()), this, SLOT(handleReadyRead());
      ...
      Foo::handleReadyRead() {
      QByteArray data = port.read();
      }
      @
      or:
      @
      ...
      // polling by timer
      connect(timer, SIGNAL(timeout()), this, SLOT(handleTimeout());
      ...
      Foo::handleTimeout() {
      QByteArray data = port.read();
      }
      @

      But is possible to use the QtSerialPort in blocking (synch) mode when Qt event-loop isn't used for the QtSerialPort. But in this case need do use an separate thread because blocking I/O operations will freeze a main thread.

      A main feature of implementation of the QtSerialPort that for the Qt event-loop's "emulation" in the synchronous mode it is necessary to use the waitForReadyRead() and waitForBytesWritten() methods. This methods are blocking!

      These waitForXX() methods also trace events on descriptors and to do same as event-loop. It should be noted that the read() and write() methods also do nothing, they just do read/write from the internal buffers.

      So, this code is valid:
      @
      Foo::read() {
      while (1) {
      if (waitForReadyRead(500) {
      qint64 av = port.bytesAvailable();
      if (av < expected)
      continue;
      QByteArray data = port.read();
      } else {
      // timeout error (no more data for read or critical failure.
      }
      }
      }
      @

      In this mode use of signals of slots is also possible, e.g.:
      @

      class Bar : public QObject
      {
      public:
      Bar(QObject *parent);
      public slots:
      void handleParsedData(const QByteArray &data);
      }

      class Foo : public QThread
      {
      public:
      Foo(QObject *parent);
      protected:
      void run();
      signals:
      void dataParsed(const QByteArray &data);
      }

      connect(foo, SIGNAL(dataParsed(QByteArray)), bar, SLOT(handleParsedData(QByteArray))));

      Foo::run() {
      while (1) {
      if (waitForReadyRead(500) {
      qint64 av = port.bytesAvailable();
      if (av < expected)
      continue;
      QByteArray data = port.read();
      // parse data
      ...
      // data successfully parsed
      QByteArray pd = ...
      emit dataParsed(pd);
      } else {
      // timeout error (no more data for read or critical failure.
      }
      }
      }
      @
      Of course, the Foo and Bar should be in different threads.

      1 Reply Last reply
      0
      • K Offline
        K Offline
        kuzulis
        Qt Champions 2020
        wrote on last edited by
        #3

        UPD: But in any case I recommend to use async approach, because the waitForXX() methods has a bugs, which is fixed in future Qt 5.2.1 release (or you can manually build sourses from Git). :)

        1 Reply Last reply
        0
        • F Offline
          F Offline
          frankiefrank
          wrote on last edited by
          #4

          First of all, thank you for your detailed answer!

          From what I see online, 5.2.1 should be released within a few weeks so I'm not too bothered by updating then.

          There's still something I didn't get (at least one thing, that is). My starting point was a worker thread without a Qt event loop (started without exec()). Within the context of that thread, I tried to connect to a readyRead signal and when I sent data to my port the slot wasn't called. And reading your explanation I thought that's because of not having the Qt event loop - meaning I should have used the waitFor methods to "Emulate" it as you say. But then I don't understand the part of the code you have as "but this code is correct". Without an event loop, that code still wouldn't work, no?

          "Roads? Where we're going, we don't need roads."

          1 Reply Last reply
          0
          • K Offline
            K Offline
            kuzulis
            Qt Champions 2020
            wrote on last edited by
            #5

            bq. I tried to connect to a readyRead signal and when I sent data to my port the slot wasn’t called.

            Please provide your code, what you want to do?

            bq. But then I don’t understand the part of the code you have as “but this code is correct”.

            I mean usual asynch way (with event-loop) with use readyRead() to notify about new data:
            @
            class Foo : public QObject
            {
            Q_OBJECT
            public:
            explicit Foo(QObject *parent)
            : QObject(parent)
            {
            connect(&p, SIGNAL(readyRead()), this, SLOT(handleReadyRead()));
            ...
            }

            private slots:
            void handleReadyRead()
            {
            if (p.bytesAvailable() < expexted)
            return;
            QByteArray date = p.readAll();
            ...
            }
            private:
            QSerialPort p;
            }
            @

            I mean usual asynch way (with event-loop) with use timeout() to polling a new data:
            @
            class Foo : public QObject
            {
            Q_OBJECT
            public:
            explicit Foo(QObject *parent)
            : QObject(parent)
            {
            connect(&t, SIGNAL(timeout()), this, SLOT(handleTimeout()));
            t.start(100);
            ...
            }

            private slots:
            void handleTimeout()
            {
            if (p.bytesAvailable() < expexted)
            return;
            QByteArray date = p.readAll();
            ...
            }
            private:
            QSerialPort p;
            QTimer t;
            }
            @

            Or I do not understand what to you do not understand? :)

            1 Reply Last reply
            0
            • F Offline
              F Offline
              frankiefrank
              wrote on last edited by
              #6

              I think I understand it now, thank you.

              "Roads? Where we're going, we don't need roads."

              1 Reply Last reply
              0
              • M Offline
                M Offline
                mmasur
                wrote on last edited by
                #7

                I am using Qt 5.1.1 on both a 64-bit Windows 7 and 32-bit XP systems. I was having trouble receiving with the synchronous method. I used the following to get WaitForReadyRead to work correctly:

                @QByteArray qbuffmsg;

                if(serial.waitForReadyRead(200)
                {
                qbuffmsg = serial.readAll();
                while(serial.waitForReadyRead(50))
                {
                qbuffmsg.append(serial.readAll());
                }
                }
                @

                The key is the "while(serial.waitForReadyRead(50))". The Windows 7 machine needed at least a 30ms timeout to capture the entire message consistently and the XP machine required 50ms timeout value. This works very consistently. I can tolerate these timeout values for now, but I hope they fix the underlying problem soon. :)

                1 Reply Last reply
                0
                • K Offline
                  K Offline
                  kuzulis
                  Qt Champions 2020
                  wrote on last edited by
                  #8

                  bq. I can tolerate these timeout values for now, but I hope they fix the underlying problem soon. :)

                  Who are they? MS? :)

                  It is normal because such small values (30 - 50 ms) are on border of switching of a context in Windows OS.

                  You can try to use QSerialPort::handle() and to setup necessary read timeouts to the driver through "SetCommTimeouts":http://msdn.microsoft.com/en-us/library/windows/desktop/aa363437(v=vs.85).aspx.

                  1 Reply Last reply
                  0
                  • M Offline
                    M Offline
                    mmasur
                    wrote on last edited by
                    #9

                    Thanks for the suggestion. I posted the timeouts that worked because all the Qt examples I have seen use waitForReadyRead(10). A 10ms timeout fails much of the time. My app is used for a Modbus interface and the 50ms timeout is pretty long when I only need to see a 2ms period of no characters to detect a Modbus message. I will eventually try your suggestions for improving the performance. I am stuck with Windows for now. I want my QNX back. :)

                    1 Reply Last reply
                    0
                    • K Offline
                      K Offline
                      kuzulis
                      Qt Champions 2020
                      wrote on last edited by
                      #10

                      Yes, I know, that the Modbus spec described about timeouts between chars (an 3.5 chars as I remember??). I simply would try to measure a timeout on the whole package instead of each character. But I agree, it also isn't the good decision.

                      UPD: Also you can try to increase a thread priority..

                      1 Reply Last reply
                      0

                      • Login

                      • Login or register to search.
                      • First post
                        Last post
                      0
                      • Categories
                      • Recent
                      • Tags
                      • Popular
                      • Users
                      • Groups
                      • Search
                      • Get Qt Extensions
                      • Unsolved