PDA

View Full Version : QT4 and GLib



pdoria
12th March 2008, 00:19
Hi all,

About Qt4 and GLib ...

I've written a multi-threaded app with PGSQL support and have noticed that, although having the app heavily and thoroughly scrutinized with the help of Valgrind to cover for any possible memory leaks there's still some segfaults.

Things I've noticed:
- the pgsql lib tends to leak... :(
- no matter how thoroughly you've debugged your app there's always space for the infamous "double free or memory corruption" crash...

I've been launching my app preceded with GSLICE=always_malloc and ever since not a single crash yet...
So I can only assume there's something very wrong with QT and GLib ...

There also a reference about disabling GLib at http://qtnode.net/wiki?title=Glib_Event_Loop

So the point here is:
Should one always resort to the above envvar or simply disable GLib altogether when running multi-threaded apps?

And more importantly:
Could someone share his/hers experience and insight in the matter and possibly some tips? ;)

Thanks in advance,
Pedro Doria Meunier.

jacek
12th March 2008, 01:00
What do you use threads for?

pdoria
12th March 2008, 01:22
Hey Jacek,

Basically for dispatching devices input through sockets...
One thread per processor (ideally), the others remain queued.
The idea here is having the tcp socket server as responsive as possible.

Regards,
Pedro.

jacek
12th March 2008, 13:28
Are these QThreads? Do you create any objects in their constructors?

pdoria
12th March 2008, 13:44
Hi Jacek,

Yes, they're QThreads.

I create objects, such as QSqlDatabase in their declarations, otherwise there's nothing created in their constructors.




class myUnit : public QThread
{
Q_OBJECT
// ...

protected:
QSqlDatabase myDB;
}

myUnit::myUnit ( int socketDescriptor, Server *parent ) : QThread ( parent )
{
// no objects declared here
}


Regards,
Pedro.

jacek
13th March 2008, 00:21
Do you make any connections between myUnit instance and some other object?

pdoria
13th March 2008, 03:54
hey Jacek,

I appreciate you debugging techniques ... and the answer is no.
However I feel we're missing the point here...

Point:
GLib + QT4 still unreliable?

Personal exp: at least with QApplication it *seems* to be, especially so with QSqlDatabase (PGSQL driver)

Ever since I switched to QCoreApplication (console-only app) - no more every-week segfaults.
Yes.. I know... I should have been using it all along... :o

Regards,
Pedro.

jpn
13th March 2008, 06:46
Where do you assign to myDB? Be careful, because myUnit::myUnit() and myUnit::run() are executed in different thread context.

wysota
13th March 2008, 09:55
However I feel we're missing the point here...

Point:
GLib + QT4 still unreliable?
Memory leaks don't cause segfaults. If you experience them, it means there is something wrong with your code.

pdoria
13th March 2008, 12:18
Where do you assign to myDB? Be careful, because myUnit::myUnit() and myUnit::run() are executed in different thread context.

Hi jpn

I initialize myDB parameters (and open it) in myUnit::myUnit() .

Thanks,
Pedro.

jpn
13th March 2008, 12:25
I initialize myDB parameters (and open it) in myUnit::myUnit()
Presumably you use it in myUnit::run() too, which means you're using the same database object in multiple threads (which is a no no). The constructor of myUnit is executed in that thread which creates the myUnit object (eg. main thread). However, myUnit::run() gets executed in the "new" thread. For more details, read the detailed description of QThread docs.

pdoria
13th March 2008, 12:29
Memory leaks don't cause segfaults. If you experience them, it means there is something wrong with your code.

Hi wysota,

Actually I've thoroughly debugged the app using Valgrind. At the end of the (long) run VG reports 0 lost bytes and 0 errors.
The only (possibly lost) leakage comes from QT's fx members and friends. It's neglectable, however (like 160 bytes or so).

I've also been running the app with G_SLICE=always-malloc

Regards,
Pedro.

pdoria
13th March 2008, 12:34
Presumably you use it in myUnit::run() too, which means you're using the same database object in multiple threads (which is a no no). The constructor of myUnit is executed in that thread which creates the myUnit object (eg. main thread). However, myUnit::run() gets executed in the "new" thread. For more details, read the detailed description of QThread docs.

Hi jpn

This is my myUnit::run


void myUnit::run()
{
QString dt = getDateTime();
this->peerAddress = tcpSocket.peerAddress().toString();
textcolor ( DIM, CYAN, BLACK );
printf ("%s: Incoming connection from [%s] - Active clients: %d - DB conn: %s\r\n", qPrintable(dt), qPrintable(peerAddress), server->nbrClients, dbUnique);
textcolor ( BRIGHT, WHITE, BLACK );
exec();
return;
}


As you can see, no access to the DB here...
Are you saying that I should initialize the DB in myUnit::run ?

Thanks,
Pedro.

jpn
13th March 2008, 12:50
So where is myDB used elsewhere than the constructor?

pdoria
13th March 2008, 12:59
So where is myDB used elsewhere than the constructor?

In members
myUnit::updateUnitRecord
myUnit::updateUnitHistory

and only as an argument


QSqlQuery query(myDB);

Thanks,
Pedro.

jpn
13th March 2008, 13:14
And those are called from? :)

pdoria
13th March 2008, 13:23
And those are called from? :)


void myUnit::parseFM4Recs ( quint8 nRecs, QDataStream & records )

Thanks,
Pedro.

jpn
13th March 2008, 13:31
Ok, remember that we know nothing about all the functions you have in myUnit. I was basically tracking where does the execution come from. A slot? An event handler? It might be easier to track the problem if you provide a little bit more information. Could you perhaps attach myunit.h and myunit.cpp?

pdoria
13th March 2008, 14:06
Ok, remember that we know nothing about all the functions you have in myUnit. I was basically tracking where does the execution come from. A slot? An event handler? It might be easier to track the problem if you provide a little bit more information. Could you perhaps attach myunit.h and myunit.cpp?

Hey jpn,

I know that and really appreciate all your care.
As to attaching the code... alas... there are portions of it containing too much proprietary information, which I cannot disclose (protected by NDA).

The app, however, is a simple one:

- A new thread gets fired upon an incoming tcp connection


void Server::incomingConnection ( int socketDescriptor )
{
nbrClients++;
myUnit *tunit = new myUnit ( socketDescriptor,this );
connect ( tunit, SIGNAL ( finished() ), tunit, SLOT ( deleteLater() ) );
tunit->start();
return;
}

- I link signals and slots like this (in myUnit::myUnit):

// link socket signals to this thread's slots.
connect ( &tcpSocket, SIGNAL ( readyRead() ), this, SLOT ( incomingData() ) );
connect ( &tcpSocket, SIGNAL ( disconnected() ), this, SLOT ( disconnected() ) );

- this is my incomingData() :


void myUnit::incomingData ()
{
QString dt = getDateTime();
short zerocount=0; // number of zero header values to check.
quint16 dataLength=0;

unsigned int totalBytes = 0; // quick sanity check...
totalBytes = tcpSocket.bytesAvailable();
if ( totalBytes < 4 ) return; // bogus tx...


QDataStream inData ( &tcpSocket );

while ( zerocount < 4 && dataLength==0 )
{
inData >> dataLength;
if ( dataLength==0 ) zerocount++;
}

// sanity checks...
// exceed number of tries... bogus tx. return.
if ( zerocount>3 )
{
cout << "WAY TOO MUCH ZEROES!" << endl;
return;
}
// we wont have packets bigger than this. so it's a bogus tx.
if ( dataLength == 0xFFFF || dataLength==0 )
{
cout << "DATA LENGTH MISMATCH! [" << dataLength <<"]" << endl;
return;
}

// wait for a maximum of 5 secs for data...
if (tcpSocket.bytesAvailable() < dataLength)
{
tcpSocket.waitForReadyRead ( 5000 );

}
// if 5 secs *still* not enough get out of here!
if (tcpSocket.bytesAvailable() < dataLength) {
this->disconnected();
return;
}


QByteArray avlData;
avlData=tcpSocket.readAll();

if ( !gotImei )
{
checkImei ( avlData );
}
else
{
parseData ( avlData );
}
return;
}

- This is my disconnected() :


void myUnit::disconnected()
{
QString dt = getDateTime();
server->nbrClients--;
textcolor ( BRIGHT, YELLOW, BLACK );
cout << qPrintable(dt) << ": Unit [" << imei.data() << "] disconnected." << " active clients: " << server->nbrClients << endl;
textcolor ( BRIGHT, WHITE, BLACK );
this->quit();
return;
}


- Upon getting data (using QDataStream), the app simply parses the records contained therein in a loop that calls updateUnitRecord (db use here) and updateUnitHistory (another db use here)

- After all is done (unit disconnects) the disconnected() fires up and I take care of closing the DB in


myUnit::~myUnit()
{
this->myDB.close();
}


Again, thanks for taking a look at it.

Regards,
Pedro.

jpn
13th March 2008, 14:20
Well, I'm sorry to say this but you have somewhat severe threading issues there. I strongly recommend reading Multithreading slides from TT DevDays2007 (http://chaos.troll.no/~ahanssen/devdays2007/DevDays2007-Threading.pdf) starting from page 33, about thread affinity.

wysota
13th March 2008, 14:26
I don't know if you are aware of this, but the thread object and its members live not in the thread executed by its run() method but in the thread that created the QThread subclassed object (hence the socket lives in a different thread than the one operating on it). I don't know if it's important here, but it seems to be that you are not aware of that.

pdoria
13th March 2008, 14:28
Well, I'm sorry to say this but you have somewhat severe threading issues there. I strongly recommend reading Multithreading slides from TT DevDays2007 (http://chaos.troll.no/~ahanssen/devdays2007/DevDays2007-Threading.pdf) starting from page 33, about thread affinity.

Thanks for the link. I'll look into it.

Could you please elaborate on "somewhat severe threading issues there"?
My noobiness at Qt doesn't allow me to see it at first glance... :o
Where do you see them?

Regards,
Pedro.

wysota
13th March 2008, 14:43
J-P probably means the same I noticed.

pdoria
13th March 2008, 14:46
I don't know if you are aware of this, but the thread object and its members live not in the thread executed by its run() method but in the thread that created the QThread subclassed object (hence the socket lives in a different thread than the one operating on it). I don't know if it's important here, but it seems to be that you are not aware of that.

Thanks for the heads-up!

I must confess that at first reading I almost got baffled with your input! :o:D
Well... what's wrong with using the socket coming from


class Server : public QTcpServer
{
Q_OBJECT

public:
unsigned int nbrClients;
Server ( QObject *parent = 0 );

protected:
void incomingConnection ( int socketDescriptor );
};
?

and then passing it to

myUnit *tunit = new myUnit ( socketDescriptor,this );

I mean... I have no other discernible way of passing the socket to the thread... :confused:

ADDENDUM:
I create the socket in

class myUnit : public QThread
{
Q_OBJECT

public:
QTcpSocket tcpSocket;
}

and in

myUnit::myUnit ( int socketDescriptor, Server *parent ) : QThread ( parent )
{
// initialize the unit's socket --> provided by Server::incomingConnection()
if ( !tcpSocket.setSocketDescriptor ( socketDescriptor ) )
{
emit error ( tcpSocket.error() );
return;
}
}


I set its descriptor coming from Server::incomingConnection() ...
What could possibly be wrong with this? :confused:

Thanks,
Pedro.

wysota
13th March 2008, 15:21
You have to create the socket in run() or change its affinity (just remember the thread object can't be its parent then, so you'll have to explicitely call delete on the socket).