PDA

View Full Version : Using QUdpSocket within a QThread



db
29th October 2007, 18:51
Hopefully someone can help with this problem. I have created a QThread to work in conjunction with a GUI app. When runing the executable everything works great. However, after shutting down the application and trying to restart it within 5 seconds or so I get the following error:

GThread-ERROR **: GThread system may only be initialized once.
aborting...
Segmentation fault

I thought from the way may code was written that everything (includeing the thread) are terminated, but it appears that the thread is still running. How do I get it to stop completely. Using RedHat EL 4


Thanks for the help.

Here is my code:


// Main -----------------------------------------------------------------

int main(int argc, char *argv[])
{
// creates class that contains packets and methods
CHostIFTools * hostifTools = new CHostIFTools();

// create threads to handle UDP recieves and tranmits
CHostIF hostifReceive( hostifTools );

// start independant threads
hostifReceive.start();

// create application class
QApplication app(argc,argv);
CHostIF * hostif = new CHostIF( 0, hostifTools );

// display main widget
hostif->show();

// run application
app.exec();

// stop independant threads
hostifReceive.quit();

// wait for thread to finish
hostifReceive.wait();

// delay to ensure that threads are really terminated
printf("Waiting for thread to shutdown");
fflush(stdout);

for (int sleepIdx=0; sleepIdx<5; sleepIdx++)
{
printf("..");
fflush(stdout);
sleep (1);
}
printf("done!!\n");
fflush(stdout);

// need to remove the tools class
delete hostifTools;

return 0;
}


// Class definition ----------------------------------------------------------------------

class CHostIFReceive : public QThread
{
public:

// CHostIFReceive : class initialization method
CHostIFReceive( CHostIFTools * tools=0 );

// ~CHostIFReceive : class destrcutor method
~CHostIFReceive();

// run : local implementation of the QThread run method
void run();

// stop : used to set the stop variable
void quit();

protected:

// defines the tools class that is to be used
CHostIFTools * hostifTools;

// provides clean approch to terminting the thread
volatile bool stopThreads;
};

// Class code ----------------------------------------------------------------------------

CHostIFReceive::CHostIFReceive(CHostIFTools * tools)
: QThread()
{
// instantiate configuration tools class
hostifTools = tools;

// indicate thread is in a running state
stopThreads = false;
}

void CHostIFReceive::run()
{
// as long as thread has not been terminated
while ( ! stopThreads )
{
// update the system value
hostifTools->updateSystemInformation1();

// wait for a little while
sleep( 1 );

}
}

void CHostIFReceive::quit()
{
stopThreads = true;
QThread::quit();
}

marcel
29th October 2007, 19:34
The first mistake is that you create QObjects(the thread) before you instantiate the QApplication.

Correct that problem and see if something changes.

EDIT: and calling QThread::quit has no effect since your implementation does not start and event loop, so you might as well remove that call.

db
29th October 2007, 21:15
OK. So I tried your corrections and they worked. So it will now create the thread, now I have one last issue with the code. I have added a UDP socket to it but when is starts up I get the following:

Object::connect: No such slot QThread::dataPending()

How do I get it to recognize the dataPending to be associacted with the socket?

Thanks, you have be a greate help

Here is an update to my code with the UDP in it.


class CHostIFReceive : public QThread
{
public:

… same code

private slots:
// receives the data from the UDP packet
void dataPending();

protected:

… same code

// declare the UDP socket
QUdpSocket * receiveSocket;

// indicates the UDP port that is bound
unsigned int portNumber;

};

//----------------------------------------------

CHostIFReceive::CHostIFReceive(CHostIFTools * tools)
: QThread()
{
… same code

// get port number to be used
portNumber = atoi( getenv("HOSTIFPORT") );
qDebug("Port number: %d",portNumber);

// create the UDP class for receiving data
receiveSocket = new QUdpSocket( 0 );
receiveSocket->bind( portNumber );

connect( receiveSocket, SIGNAL(readyRead()), this, SLOT(dataPending()) );
}

jacek
29th October 2007, 22:46
Object::connect: No such slot QThread::dataPending()
Did you remember about Q_OBJECT macro?


// create the UDP class for receiving data
receiveSocket = new QUdpSocket( 0 );
You shouldn't create any objects in QThread constructor, if you are going to use them in a new thread --- instantiate them in run() instead.


connect( receiveSocket, SIGNAL(readyRead()), this, SLOT(dataPending()) );
You have to use a direct connection here, because "this" lives in the GUI thread and if you use an automatic connection, your slot will be executed by GUI thread. Pass Qt::DirectConnection as the fifth parameter.

db
30th October 2007, 14:25
I am trying to create a thread class that contains a QUdpSocket object within it. My code builds and executes, but when it starts up I get the following error:

"Object::connect: No such slot QThread::processPendingDatagrams."

So obviously the dataReady signal will not get processed. How do I resolve the error??

I have tried several approaches:

1) Previously, it was suggested that I may need a Q_OBJECT in the Thread class; I didn’t think so because I’m not using a widget (may be an incorrect assumption on my part). I put it in the code and got linker errors:

"Undefined reference to vtable for CHostIFReceive."

My understanding is that it thinks I’m using a virtual object that hasn’t been declared, I didn’t think I was.

2) Another suggesting was that I should not create the QUpdSocket in the QThread constructor but in the run() method (all the examples I’ve seen have it in the constructor). I got the following runtime errors:

"QObject can not create children for a parent that is in a different thread.
(Parent is QThread(0xbfefb140), parent’s thread is QThread(0x9e9c2e8), current thread is QThread(0xbfefb140)
Object::connect: No such slot QThread::processPendingDatagrams."

3) Final suggestion was to make sure that I pass in Qt::DirectConnection as the fifth parameter for the connect() method. That had no affect.

I’m thinking that it should be straight forward soloution but don’t see it. Any other suggestions?

My code:



// Main code ----------------------------------------------------------------------

int main(int argc, char *argv[])
{
// create application class
QApplication app(argc,argv);

// creates class that contains packets and methods
CHostIFTools * hostifTools = new CHostIFTools();

// create threads to handle UDP recieves and tranmits
CHostIF hostifReceive( hostifTools );

// start independant threads
hostifReceive.start();

// create the application's gui class
CHostIF * hostif = new CHostIF( 0, hostifTools );

// display main widget
hostif->show();

// run application
app.exec();

// stop independant threads
hostifReceive.stop();

// wait for thread to finish
hostifReceive.wait();

// need to remove the tools class
delete hostifTools;

return 0;
}


// Class definition ----------------------------------------------------------------------

class CHostIFReceive : public QThread
{
public:

// CHostIFReceive : class initialization method
CHostIFReceive( CHostIFTools * tools=0 );

// ~CHostIFReceive : class destrcutor method
~CHostIFReceive();

// run : local implementation of the QThread run method
void run();

// stop : used to set the stop variable
void stop();

private slots:
// receives the data from the UDP packet
void processPendingDatagrams();

protected:

// defines the tools class that is to be used
CHostIFTools * hostifTools;

// provides clean approch to terminting the thread
volatile bool stopThreads;

// declare the UDP socket
QUdpSocket * receiveSocket;

// indicates the UDP port that is bound
unsigned int portNumber;
};

// Class code ----------------------------------------------------------------------------

CHostIFReceive::CHostIFReceive(CHostIFTools * tools)
: QThread()
{
// instantiate configuration tools class
hostifTools = tools;

// indicate thread is in a running state
stopThreads = false;

// get port number to be used
portNumber = atoi( getenv("HOSTIFPORT") );
qDebug("Port number: %d",portNumber);

// create the UDP class for receiving data
receiveSocket = new QUdpSocket( 0 );
receiveSocket->bind( portNumber );

connect( receiveSocket, SIGNAL(readyRead()), this, SLOT(processPendingDatagrams()) );
}

// -----------

CHostIFReceive::~CHostIFReceive() {}

// -----------

void CHostIFReceive::run()
{
// as long as thread has not been terminated
while ( ! stopThreads )
{
// update the system value
hostifTools->updateSystemInformation1();

// wait for a little while
sleep( 1 );

}
}

// -----------

void CHostIFReceive::stop()
{
stopThreads = true;
}

// -----------

void CHostIFReceive::processPendingDatagrams()
{
; // nothing for now since error happes before actual processing
}

high_flyer
30th October 2007, 15:15
make the slot public

db
30th October 2007, 16:24
I have tried the different approaches, and none of them solved the problem.

1) It was suggested that I may need a Q_OBJECT in the Thread class; I didn’t think so because I’m not using a widget (may be an incorrect assumption on my part). I put it in the code and got linker errors:

"Undefined reference to vtable for CHostIFReceive."

My understanding is that it thinks I’m using a virtual object that hasn’t been declared, I didn’t think I was.

2) Another suggesting was that I should not create the QUpdSocket in the QThread constructor but in the run() method (all the examples I’ve seen have it in the constructor). I got the following runtime errors:

"QObject can not create children for a parent that is in a different thread.
(Parent is QThread(0xbfefb140), parent’s thread is QThread(0x9e9c2e8), current thread is QThread(0xbfefb140)
Object::connect: No such slot QThread:rocessPendingDatagrams."

3) Final suggestion was to make sure that I pass in Qt:irectConnection as the fifth parameter for the connect() method. That had no affect.

db
30th October 2007, 16:26
I tried the public (and protected) slot approach. Still get the same error.

jpn
30th October 2007, 17:18
First of all, you're missing the Q_OBJECT (http://doc.trolltech.com/4.3/qobject.html#Q_OBJECT) macro. Secondly, create the socket in QThread::run(), not in constructor. And finally, make sure you enforce the connection as direct because otherwise the slot will get called in main thread's context (that's where the thread object itself "lives" in):

connect( receiveSocket, SIGNAL(readyRead()), this, SLOT(processPendingDatagrams()), Qt::DirectConnection);

db
30th October 2007, 17:41
Actually if you read the top portion of my post you would see that I tried all thoses suggestions already and it did not work. In fact I got even more error messages.

Thanks

jpn
30th October 2007, 17:50
1) Previously, it was suggested that I may need a Q_OBJECT in the Thread class; I didn’t think so because I’m not using a widget (may be an incorrect assumption on my part). I put it in the code and got linker errors:

"Undefined reference to vtable for CHostIFReceive."

My understanding is that it thinks I’m using a virtual object that hasn’t been declared, I didn’t think I was.

Make sure the declaration is in a header file and that the header file is listed in .pro file. Then, re-run qmake to get new Makefiles with correct rules for running moc as required.



2) Another suggesting was that I should not create the QUpdSocket in the QThread constructor but in the run() method (all the examples I’ve seen have it in the constructor). I got the following runtime errors:

"QObject can not create children for a parent that is in a different thread.
(Parent is QThread(0xbfefb140), parent’s thread is QThread(0x9e9c2e8), current thread is QThread(0xbfefb140)
Object::connect: No such slot QThread::processPendingDatagrams."

Yes, QThread::run() is the correct place to create it. Just don't pass any parent. Again, the QThread object itself aka "this" lives in main thread which is different thread which is being executed in QThread::run().



3) Final suggestion was to make sure that I pass in Qt::DirectConnection as the fifth parameter for the connect() method. That had no affect.

Yes, you'll need to pass it. See the explanation in my previous post.

marcel
30th October 2007, 19:31
It was suggested that I may need a Q_OBJECT in the Thread class; I didn’t think so because I’m not using a widget (may be an incorrect assumption on my part). I put it in the code and got linker errors:
Not only QWidget sibclasses but all classes that use/implement signals and slots have to have the Q_OBJECT macro in order to be processed by moc. So, it is mandatory in your case.



"Undefined reference to vtable for CHostIFReceive."

My understanding is that it thinks I’m using a virtual object that hasn’t been declared, I didn’t think I was.
Do you implement two classes in the same cpp? There was a similar problem on the forum, still unsolved unfortunately. Try splitting the declarations and implementations for classes containing the Q_OBJECT macro.



2) Another suggesting was that I should not create the QUpdSocket in the QThread constructor but in the run() method (all the examples I’ve seen have it in the constructor). I got the following runtime errors:

"QObject can not create children for a parent that is in a different thread.
(Parent is QThread(0xbfefb140), parent’s thread is QThread(0x9e9c2e8), current thread is QThread(0xbfefb140)
Object::connect: No such slot QThread:rocessPendingDatagrams."
You'll have to post the thread code for this. Probably something got mixed up.
It might be that the worker thread object lives in the GUI thread. If you move it to itself after it is started could solve the problem.



Final suggestion was to make sure that I pass in Qt:irectConnection as the fifth parameter for the connect() method. That had no affect.
If the socket lives in the worker thread there will be no need to specify the connection type. It will default to direct.

Also, make sure you start the event loop in the thread with exec, otherwise thread slots won't work.

jacek
30th October 2007, 20:28
"QObject can not create children for a parent that is in a different thread.
(Parent is QThread(0xbfefb140), parent’s thread is QThread(0x9e9c2e8), current thread is QThread(0xbfefb140)
You shouldn't create the socket with a parent --- create it just like in the code you have posted earlier, but in run().


Final suggestion was to make sure that I pass in Qt:irectConnection as the fifth parameter for the connect() method. That had no affect.
If you stay with automatic connections, don't be surprised when you discover that the GUI thread does all of the work.

jacek
30th October 2007, 20:34
If the socket lives in the worker thread there will be no need to specify the connection type. It will default to direct.
On the contrary. The receiver, i.e. QThread, lives in the GUI thread and the socket lives in the worker thread, so the connection will be queued and all signals will land in GUI thread's event queue.

jacek
30th October 2007, 20:36
Please, don't start more than one thread on the same problem.

{Threads merged}

marcel
30th October 2007, 20:40
Yes but you can always move the QThread object from to GUI thread to the worker thread to solve this. This way connect will default to Qt::DirectConnection and the slot gets executed by the worker thread.

Or, as you said, can leave it like it already is and force a direct connection.
EDIT: both approaches work, but I prefer the one with changing the object's affinity. Don't really know why, though...

jacek
30th October 2007, 20:48
Yes but you can always move the QThread object from to GUI thread to the worker thread to solve this. This way connect will default to Qt::DirectConnection and the slot gets executed by the worker thread.
Of course, but won't that create problems after the thread stops? You will end with an object that lives in a non-existent thread.

marcel
30th October 2007, 20:53
You're right! Never thought of that.
I wonder if there's a solution for this. Maybe the finished() signal can be used to delete the QThread object.
Indeed, changing affinities sucks.

db
30th October 2007, 23:59
JPN, Thanks for walking me through the change with explanations... I did miss the qmake and of course removing the parent in the instaniation help also.

Marcel, Jacek, Thanks for your inputs also. I got lost in some of the stuff but being a newbie I'm sure it will eventually make sense.

also my bad about the 2 threads but I thought that I was starting a new topic with the QUdpSocket so...

Again, thanks guys