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