kachofool
4th December 2009, 19:31
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.
class DeviceThread : public QThread
{
Q_OBJECT
public:
AxisCouple *_axis;
...
protected:
void DeviceThread::run()
{
switch(_device)
{
case SENSOR:
{
// connect to sensor serial port
QextSerialPort* devPort = _axis->_sensor.GetSerialPort();
QObject::connect(devPort, SIGNAL(readyRead()),
_axis, SLOT(SensorReady()) );
dataRow dRow;
while (_keepRunning) // START SENSOR LOOP
{
// request sensor data
_axis->_sensor.RequestPos();
// wait until serial port signals rx data
_axis->_lockSensor.lock();
_axis->_sensorReady.wait(&(_axis->_lockSensor));
// atomic on (ignore signals)
QObject::disconnect(devPort, SIGNAL(readyRead()),
_axis, SLOT(SensorReady()) );
// read encoder and get timestamp
//dRow.mstime_ = _axis->getElapsedTime();
dRow.mstime_ = 0;
dRow.data_ = _axis->_sensor.RecvPos();
std::cerr << "sensor position: " << dRow.data_ << std::endl;
_axis->_lockSensor.unlock();
_axis->_lockSensorBuffer.lock();
_axis->_sensorBuff.push_back(dRow);
_axis->_lockSensorBuffer.unlock();
// atomic off (allow signals)
QObject::connect(devPort, SIGNAL(readyRead()),
_axis, SLOT(SensorReady()) );
}
}
break;
case MOVER:
{
// connect to mover serial port
QextSerialPort* mvPort = _axis->_mover.GetSerialPort();
QObject::connect(mvPort, SIGNAL(readyRead()),
_axis, SLOT(MoverReady()) );
int sSize(0), minDataPoints(0);
double nextPos(0), prevPos(0);
std::vector<dataRow> sn;
while (_keepRunning) // START MOVER LOOP
{
// copy sensor buffer and clear before releasing
_axis->_lockSensorBuffer.lock();
sSize = _axis->_sensorBuff.size();
if (sSize > minDataPoints)
{
sn.assign( _axis->_sensorBuff.begin(),
_axis->_sensorBuff.end());
_axis->_sensorBuff.clear();
}
_axis->_lockSensorBuffer.unlock();
if (sSize > minDataPoints)
{
prevPos = nextPos;
nextPos = (sn[0]).data_;
}
// update mover position data
_axis->_lockMoverData.lock();
_axis->_moverPrevPos = prevPos;
_axis->_moverNextPos = nextPos;
_axis->_lockMoverData.unlock();
// send mover new position data
_axis->_mover.PositionMotor(nextPos, prevPos);
std::cerr << "sent something!" << std::endl;
// wait until serial port signals rx data
_axis->_lockMover.lock();
_axis->_moverReady.wait(&(_axis->_lockMover));
// atomic on (ignore signals) after (1) event recvd
QObject::disconnect(mvPort, SIGNAL(readyRead()),
_axis, SLOT(MoverReady()) );
try
{ _axis->_mover.VerifyMotor(nextPos); }
catch(ErrorMVPRead&)
{ _axis->_mover.StopMotor(); }
_axis->_lockMover.unlock();
// atomic off (allow signals)
QObject::connect(mvPort, SIGNAL(readyRead()),
_axis, SLOT(MoverReady()) );
}
} // END MOVER LOOP
break;
...
...
class AxisCouple : public QObject
{
Q_OBJECT
...
AxisCouple::AxisCouple(std::string type, QObject* parent)
: QObject(parent)
{
// setup threads
_sensorThread = new DeviceThread(this, SENSOR);
_sensor.InitializeSensor();
_sensorThread->RunDevice();
_moverThread = new DeviceThread(this, MOVER);
_mover.InitializeMover();
_moverThread->RunDevice();
}
// public slots
void AxisCouple::SensorReady()
{
// announce data available
_sensorReady.wakeOne();
}
void AxisCouple::MoverReady()
{
// announce data available
_moverReady.wakeOne();
}
I've tried to add specific comments to my code to highlight what I'm talking about.
Regards,
-KF
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.
class DeviceThread : public QThread
{
Q_OBJECT
public:
AxisCouple *_axis;
...
protected:
void DeviceThread::run()
{
switch(_device)
{
case SENSOR:
{
// connect to sensor serial port
QextSerialPort* devPort = _axis->_sensor.GetSerialPort();
QObject::connect(devPort, SIGNAL(readyRead()),
_axis, SLOT(SensorReady()) );
dataRow dRow;
while (_keepRunning) // START SENSOR LOOP
{
// request sensor data
_axis->_sensor.RequestPos();
// wait until serial port signals rx data
_axis->_lockSensor.lock();
_axis->_sensorReady.wait(&(_axis->_lockSensor));
// atomic on (ignore signals)
QObject::disconnect(devPort, SIGNAL(readyRead()),
_axis, SLOT(SensorReady()) );
// read encoder and get timestamp
//dRow.mstime_ = _axis->getElapsedTime();
dRow.mstime_ = 0;
dRow.data_ = _axis->_sensor.RecvPos();
std::cerr << "sensor position: " << dRow.data_ << std::endl;
_axis->_lockSensor.unlock();
_axis->_lockSensorBuffer.lock();
_axis->_sensorBuff.push_back(dRow);
_axis->_lockSensorBuffer.unlock();
// atomic off (allow signals)
QObject::connect(devPort, SIGNAL(readyRead()),
_axis, SLOT(SensorReady()) );
}
}
break;
case MOVER:
{
// connect to mover serial port
QextSerialPort* mvPort = _axis->_mover.GetSerialPort();
QObject::connect(mvPort, SIGNAL(readyRead()),
_axis, SLOT(MoverReady()) );
int sSize(0), minDataPoints(0);
double nextPos(0), prevPos(0);
std::vector<dataRow> sn;
while (_keepRunning) // START MOVER LOOP
{
// copy sensor buffer and clear before releasing
_axis->_lockSensorBuffer.lock();
sSize = _axis->_sensorBuff.size();
if (sSize > minDataPoints)
{
sn.assign( _axis->_sensorBuff.begin(),
_axis->_sensorBuff.end());
_axis->_sensorBuff.clear();
}
_axis->_lockSensorBuffer.unlock();
if (sSize > minDataPoints)
{
prevPos = nextPos;
nextPos = (sn[0]).data_;
}
// update mover position data
_axis->_lockMoverData.lock();
_axis->_moverPrevPos = prevPos;
_axis->_moverNextPos = nextPos;
_axis->_lockMoverData.unlock();
// send mover new position data
_axis->_mover.PositionMotor(nextPos, prevPos);
std::cerr << "sent something!" << std::endl;
// wait until serial port signals rx data
_axis->_lockMover.lock();
_axis->_moverReady.wait(&(_axis->_lockMover));
// atomic on (ignore signals) after (1) event recvd
QObject::disconnect(mvPort, SIGNAL(readyRead()),
_axis, SLOT(MoverReady()) );
try
{ _axis->_mover.VerifyMotor(nextPos); }
catch(ErrorMVPRead&)
{ _axis->_mover.StopMotor(); }
_axis->_lockMover.unlock();
// atomic off (allow signals)
QObject::connect(mvPort, SIGNAL(readyRead()),
_axis, SLOT(MoverReady()) );
}
} // END MOVER LOOP
break;
...
...
class AxisCouple : public QObject
{
Q_OBJECT
...
AxisCouple::AxisCouple(std::string type, QObject* parent)
: QObject(parent)
{
// setup threads
_sensorThread = new DeviceThread(this, SENSOR);
_sensor.InitializeSensor();
_sensorThread->RunDevice();
_moverThread = new DeviceThread(this, MOVER);
_mover.InitializeMover();
_moverThread->RunDevice();
}
// public slots
void AxisCouple::SensorReady()
{
// announce data available
_sensorReady.wakeOne();
}
void AxisCouple::MoverReady()
{
// announce data available
_moverReady.wakeOne();
}
I've tried to add specific comments to my code to highlight what I'm talking about.
Regards,
-KF