Results 1 to 10 of 10

Thread: QThread, signalling and readyRead()

  1. #1
    Join Date
    Oct 2009
    Posts
    33
    Thanks
    2

    Default QThread, signalling and readyRead()

    Hi all,

    I'm trying to develop a multi-threaded application that controls serial i/o. I'm having some odd behaviour that I think is due to incorrectly using QThread (between run and exec maybe).

    * I have two threads, a subclass of QThread, that send requests to devices connected through the serial port (one thread / port).

    * Both threads send a request, and then block using QWaitCondition until the device sends a response, which is indicated with a readyRead() signal (I'm using QextSerial in EventDriven mode).

    * The readyRead() signal is connected to an object that controls the two threads. The object has public slots that call QWaitCondition.wakeOne() when the readyRead() signal is received for each thread.

    Everything works as it should when there is only one thread running. Once a second thread is run, the other thread will block. But the threads aren't really ever interdependent in that sense, and they should never wait on each other. So it just seems like there's no concurrency here and everything is linear. I've attached relevant parts of my code and would appreciate any insight.

    Qt Code:
    1. class DeviceThread : public QThread
    2. {
    3. Q_OBJECT
    4.  
    5. public:
    6. AxisCouple *_axis;
    7. ...
    8. protected:
    9. void DeviceThread::run()
    10. {
    11. switch(_device)
    12. {
    13. case SENSOR:
    14. {
    15. // connect to sensor serial port
    16. QextSerialPort* devPort = _axis->_sensor.GetSerialPort();
    17. QObject::connect(devPort, SIGNAL(readyRead()),
    18. _axis, SLOT(SensorReady()) );
    19.  
    20. dataRow dRow;
    21. while (_keepRunning) // START SENSOR LOOP
    22. {
    23. // request sensor data
    24. _axis->_sensor.RequestPos();
    25.  
    26. // wait until serial port signals rx data
    27. _axis->_lockSensor.lock();
    28. _axis->_sensorReady.wait(&(_axis->_lockSensor));
    29.  
    30. // atomic on (ignore signals)
    31. QObject::disconnect(devPort, SIGNAL(readyRead()),
    32. _axis, SLOT(SensorReady()) );
    33.  
    34. // read encoder and get timestamp
    35. //dRow.mstime_ = _axis->getElapsedTime();
    36. dRow.mstime_ = 0;
    37. dRow.data_ = _axis->_sensor.RecvPos();
    38. std::cerr << "sensor position: " << dRow.data_ << std::endl;
    39.  
    40. _axis->_lockSensor.unlock();
    41.  
    42. _axis->_lockSensorBuffer.lock();
    43. _axis->_sensorBuff.push_back(dRow);
    44. _axis->_lockSensorBuffer.unlock();
    45.  
    46. // atomic off (allow signals)
    47. QObject::connect(devPort, SIGNAL(readyRead()),
    48. _axis, SLOT(SensorReady()) );
    49. }
    50. }
    51. break;
    52.  
    53. case MOVER:
    54. {
    55. // connect to mover serial port
    56. QextSerialPort* mvPort = _axis->_mover.GetSerialPort();
    57. QObject::connect(mvPort, SIGNAL(readyRead()),
    58. _axis, SLOT(MoverReady()) );
    59.  
    60. int sSize(0), minDataPoints(0);
    61. double nextPos(0), prevPos(0);
    62. std::vector<dataRow> sn;
    63.  
    64.  
    65. while (_keepRunning) // START MOVER LOOP
    66. {
    67. // copy sensor buffer and clear before releasing
    68. _axis->_lockSensorBuffer.lock();
    69. sSize = _axis->_sensorBuff.size();
    70. if (sSize > minDataPoints)
    71. {
    72. sn.assign( _axis->_sensorBuff.begin(),
    73. _axis->_sensorBuff.end());
    74.  
    75. _axis->_sensorBuff.clear();
    76. }
    77. _axis->_lockSensorBuffer.unlock();
    78.  
    79. if (sSize > minDataPoints)
    80. {
    81. prevPos = nextPos;
    82. nextPos = (sn[0]).data_;
    83. }
    84.  
    85. // update mover position data
    86. _axis->_lockMoverData.lock();
    87. _axis->_moverPrevPos = prevPos;
    88. _axis->_moverNextPos = nextPos;
    89. _axis->_lockMoverData.unlock();
    90.  
    91. // send mover new position data
    92. _axis->_mover.PositionMotor(nextPos, prevPos);
    93. std::cerr << "sent something!" << std::endl;
    94.  
    95. // wait until serial port signals rx data
    96. _axis->_lockMover.lock();
    97. _axis->_moverReady.wait(&(_axis->_lockMover));
    98.  
    99. // atomic on (ignore signals) after (1) event recvd
    100. QObject::disconnect(mvPort, SIGNAL(readyRead()),
    101. _axis, SLOT(MoverReady()) );
    102.  
    103. try
    104. { _axis->_mover.VerifyMotor(nextPos); }
    105.  
    106. catch(ErrorMVPRead&)
    107. { _axis->_mover.StopMotor(); }
    108.  
    109. _axis->_lockMover.unlock();
    110.  
    111. // atomic off (allow signals)
    112. QObject::connect(mvPort, SIGNAL(readyRead()),
    113. _axis, SLOT(MoverReady()) );
    114.  
    115. }
    116. } // END MOVER LOOP
    117. break;
    118.  
    119. ...
    120. ...
    121.  
    122. class AxisCouple : public QObject
    123. {
    124. Q_OBJECT
    125. ...
    126.  
    127. AxisCouple::AxisCouple(std::string type, QObject* parent)
    128. : QObject(parent)
    129. {
    130. // setup threads
    131. _sensorThread = new DeviceThread(this, SENSOR);
    132. _sensor.InitializeSensor();
    133. _sensorThread->RunDevice();
    134.  
    135. _moverThread = new DeviceThread(this, MOVER);
    136. _mover.InitializeMover();
    137. _moverThread->RunDevice();
    138.  
    139. }
    140.  
    141. // public slots
    142. void AxisCouple::SensorReady()
    143. {
    144. // announce data available
    145. _sensorReady.wakeOne();
    146. }
    147.  
    148. void AxisCouple::MoverReady()
    149. {
    150. // announce data available
    151. _moverReady.wakeOne();
    152. }
    To copy to clipboard, switch view to plain text mode 

    I've tried to add specific comments to my code to highlight what I'm talking about.

    Regards,

    -KF

  2. #2
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QThread, signalling and readyRead()

    Where do you create the _axis object? Also it looks like you are accessing shared data without proper synchronization.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  3. #3
    Join Date
    Oct 2009
    Posts
    33
    Thanks
    2

    Default Re: QThread, signalling and readyRead()

    Hi wysota,

    I've initialized an AxisCouple object in my main.cpp file.
    It's the default file with no changes except the init:

    Qt Code:
    1. int main(int argc, char *argv[])
    2. {
    3. QCoreApplication a(argc, argv);
    4.  
    5. AxisCouple newRCNSAxis("Rotation");
    6.  
    7. return a.exec();
    8. }
    To copy to clipboard, switch view to plain text mode 

    I use a (this) pointer when launching the DeviceThreads as in the above post to link the two.

    With regards to improper synchronization, I'm unable to locate where shared data isn't safely being accessed; The only shared variable between threads is 'sensorBuffer' and I'm using a mutex to protect this variable.
    Last edited by kachofool; 4th December 2009 at 20:43.

  4. #4
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QThread, signalling and readyRead()

    The whole _axis object is shared, isn't it? The QExtSerialPort variable is also shared and it can't be protected with a mutex as it's a QObject so you don't have total control over it.
    Its slots will be executed in the main thread, by the way - I don't know if this is something that you intended. You can read its data only from the main thread.

    Also get rid of the connect and disconnect statements. I'm not entirely sure what you meant them to do but I'm pretty sure it's not a good design.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  5. #5
    Join Date
    Oct 2009
    Posts
    33
    Thanks
    2

    Default Re: QThread, signalling and readyRead()

    Hi wysota,

    Thanks for your help. The axis object is shared, but I'm mostly using this object to call member functions that are used by my system. The two device threads shouldn't be manipulating common shared memory except for the shared variable I'm protecting with the mutex (I could totally be wrong since I'm new to multithreading, so feel free to school me ).

    There are two seperate QextSerialPorts instances, one for each device. The two serial port pointers I set up in the thread class point to different memory locations.

    My reasoning for disconnecting and reconnecting the signals and slots is to ignore signals while the program is doing something specific. Since I'm using event driven I/O over the serial port, I can get multiple events from the device for something that actually represents one proper response I need to process. My event handler should only be called once for every 'proper response'...to do this I wait until I get a response from the device after requesting something, and then 'ignore' subsequent events by disconnecting the associated signals and slots until I'm done addressing the response. I'm used to thinking in terms of system level interrupts where interrupts are ignored during specific blocks of code.

    It's fine if the slots for the axis couple object are executed in the main thread as long as it properly wakes up the blocked device threads, which is where I'm having the problem.

  6. #6
    Join Date
    Oct 2009
    Posts
    33
    Thanks
    2

    Default Re: QThread, signalling and readyRead()

    After going through my code some more, I think part of the problem is that I get multiple signals (ie. the signals my threads are waiting on) from QextSerial's readyRead() event so rapidly, that they preempt my attempt to disconnect the signals. I'm not sure how to implement the functionality to ignore these events during certain blocks of code.

    I was thinking I could try to run exec() loops for the threads themselves (I'm not sure how to do this -- do I just overload exec() like I overloaded run() previously?) and add slots to my thread objects that tell the thread to ignore signals from QextSerialPort. I still foresee the same problem though -- as in, I'll receive a signal, and before I finish running my slot, I get another signal (ad inifinitum).

    As a very last ditch attempt I'm thinking I can modify QextSerialPort's code so it only sends 1 readReady signal and then stops until my class sends QextSerialPort some sort of acknowledge.

    Does anyone have any other ideas? Or maybe I'm going about this whole thing wrong?

  7. #7
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QThread, signalling and readyRead()

    I think you are going for the whole thing wrong First you are assuming that by the time you receive readyRead() all the data you need will be available, which is incorrect, only part of it may be there. And to overcome this issue you should read the data from the port when it comes in and only when there is enough to process it, signal the wait condition. This way you'll get rid of two problems at once. And no need to disconnect signals.

    Next thing - by groupping everything into one object, your program has become everything but a good designed object oriented application. Instead of aggregating, separate the data used by different tasks. Make them two classes containing only the data they need. There is a question whether you need the threads at all - from what I see, you don't, you can do everything in one thread. Once you have clear design, everything will become easier.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  8. #8
    Join Date
    Oct 2009
    Posts
    33
    Thanks
    2

    Default Re: QThread, signalling and readyRead()

    Part of the reason I feel totally lost is that I'm migrating from pthreads, where I don't need to create a separate class for threads, and where I can run things concurrently in a way that seems coherent. If I have different devices that need to talk to each other and to my application, I would think that threads would be involved.

    I don't understand how I can emit a signal only when there are enough bytes available to indicate a proper response from my device.

    I don't understand how I could run the GUI, and several devices that all interact which each other with a single thread. I couldn't even run the GUI by itself doing mundane operations like opening up a dialog box without multiple threads. So could you please point me in the right direction?

  9. #9
    Join Date
    Jan 2006
    Location
    Warsaw, Poland
    Posts
    33,359
    Thanks
    3
    Thanked 5,015 Times in 4,792 Posts
    Qt products
    Qt3 Qt4 Qt5 Qt/Embedded
    Platforms
    Unix/X11 Windows Android Maemo/MeeGo
    Wiki edits
    10

    Default Re: QThread, signalling and readyRead()

    Quote Originally Posted by kachofool View Post
    Part of the reason I feel totally lost is that I'm migrating from pthreads, where I don't need to create a separate class for threads,
    Probably because pthreads has a C interface

    and where I can run things concurrently in a way that seems coherent.
    I don't think this has anything to do with your current situation.

    If I have different devices that need to talk to each other and to my application, I would think that threads would be involved.
    Only if i/o operations are blocking. Otherwise you wouldn't need threads.

    I don't understand how I can emit a signal only when there are enough bytes available to indicate a proper response from my device.
    I didn't say you should. I said you should react on readyRead() in a different manner than you are doing now. Buffer the data until there is enough to perform your task. Then you can emit a signal or do whatever you like.

    For instance assuming a "datagram" would be a complete line of text, you could do this in your slot connected to readyRead():
    Qt Code:
    1. void Class::onReadyRead() {
    2. while(device->canReadLine()) {
    3. QByteArray data = device->readLine();
    4. doSomethingWithThe(data);
    5. }
    6. }
    To copy to clipboard, switch view to plain text mode 


    So could you please point me in the right direction?
    Signals and slots are your friends.
    Your biological and technological distinctiveness will be added to our own. Resistance is futile.

    Please ask Qt related questions on the forum and not using private messages or visitor messages.


  10. #10
    Join Date
    Oct 2009
    Posts
    33
    Thanks
    2

    Default Re: QThread, signalling and readyRead()

    My 'class::OnReadyRead' never runs to completion -- that's the first problem.

    The second issue is I don't understand how to set up the structure of my program using Qt. I have i/o going on with several devices. The i/o uses software 'handshaking', in that I talk to my device, wait for a response, verify the response (take corrective action if necessary), rinse and repeat. The thread communicating to the device should block while waiting for a response (so in this sense there is blocking involved).

    So I'm really trying to figure out what method to set this sort of system up in. What I did initially was set up threads to talk to my devices, and block them using wait conditions after they had sent something to the device. I then tried to use signals (to and from the main application thread) to tell these threads to unblock, but this didn't work.

    When you say 'signals and slots are your friend' could you please emphasize in the context of what I'm trying to accomplish? I really appreciate the help so far.

    -KF

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.