PDA

View Full Version : Calling QSqlDatabase::addDatabase from not main thread causes SIGSEGV



ander.pijoan
6th May 2015, 07:58
Hi all,

Im having trouble with QSqlDatabase::addDatabase. I have a PostgresDriver class which I use for connecting to DB and making some queries. I can create any of these in the main cpp file. However I also have an HTTPServer which depending on the request, also creates a PostgresDriver to . This is where the QSqlDatabase::addDatabase() (and for what I can see in the stack trace, in its inner moveToThread) crashes.

I've read all the posts and documentation saying that connections are not thread safe and that should not be reused in other threads. I also presume that the HTTPSERVER will handle the requests spreaded in different threads and therefore the place where I am creating the new PostgresDriver is no the main thread. But for what I understood there should not be any problem if what I am doing is creating a new connection (not reusing one from another thread).





///////////////////////////////////////////////////////// POSTGRESDRIVER.H

class PostgresDriver : public QObject
{
public:
PostgresDriver(QString url, int port, QString db_name, QString db_user, QString db_pass, QString connection_name = "shared_connection");
~PostgresDriver();
QList<QSqlRecord> executeQuery(QString sql);

private:
QSqlDatabase database;
QString connection_name;
};

///////////////////////////////////////////////////////// POSTGRESDRIVER.CPP

#include "PostgresDriver.h"

PostgresDriver::PostgresDriver(QString url, int port, QString db_name, QString db_user, QString db_pass, QString connection_name) : QObject()
{
this->connection_name = connection_name;

this->database = QSqlDatabase::addDatabase("QPSQL" , this->connection_name); // HERE IS WHERE IT BREAKS IF DRIVER IS CREATED NOT IN THE MAIN THREAD
this->database.setHostName( url );
this->database.setPort( port );
this->database.setDatabaseName( db_name );
this->database.setUserName( db_user );
this->database.setPassword( db_pass );

while( !this->database.open() ){
this->thread()->sleep( 2 ); // Wait 2 seconds
qWarning() << "[PostgresDriver::connectDB] Unable to connect to DB : " << this->database.hostName() << this->database.port() << this->database.databaseName() << " waiting..." << endl;
}
}

PostgresDriver::~PostgresDriver(){
this->database.close();
this->database.removeDatabase( this->connection_name );
}

QList<QSqlRecord> PostgresDriver::executeQuery( QString sql ){

QList<QSqlRecord> list;

if( this->database.isOpen() ){
QSqlQuery query( this->database.database( this->connection_name ) );

query.exec( sql );

QSqlError error = query.lastError();
if (error.type() != QSqlError::NoError){
qWarning() << error.text() << endl << sql << endl;
}

while( query.next() ){
list.append(query.record());
}

} else {
qWarning() << "[PostgresDriver::executeQuery] Could not execute query because database is closed." << endl;
}

return list;
}



STACK TRACE:
0 QObject::moveToThread(QThread *) 0x7fb1885167a7
1 QFactoryLoader::instance(int) const 0x7fb1884c9df5
2 ?? 0x7fb188d42c74
3 QSqlDatabase::addDatabase(QString const&, QString const&) 0x7fb188d431f1
4 PostgresDriver::PostgresDriver PostgresDriver.cpp 12 0x4233aa // MY POSTGRESDRIVER CLASS
5 RequestHandler::HTTP_Create RequestHandler.cpp 175 0x41667e // MY HTTPSERVER REQUEST HANDLER


I've even tried creating a centralized DBController which was created in the main thread and dispatched connections to other threads but same thing happened. Any clues about this?

Thanks in advance

Lesiok
6th May 2015, 09:52
SQL module is NOT thread safe (http://doc.qt.io/qt-4.8/threads-modules.html#threads-and-the-sql-module).
Connection name must be unique for every thread.

P.S.

While loop waiting for db open is "never ending loop" if they are having connecting problems.

ander.pijoan
6th May 2015, 10:21
Even if the connection_name is unique for every thread It still throws the SIGSEGV. :(

Lesiok
6th May 2015, 10:39
Show how are You using this class.

ander.pijoan
6th May 2015, 11:02
///////////////////////////////////////////////////////// MAIN.CPP

PostgresDriver* driver = new PostgresDriver("localhost" , 5432 , "database" , "user" , "user" , "main");

QListIterator<QSqlRecord> i( driver->executeQuery( "SELECT * FROM WHATEVER" ) );
while( i.hasNext() ){
}
driver->deleteLater();

Works perfectly.

Now:



///////////////////////////////////////////////////////// AGENT.H

class Agent : public QObject
{
Q_OBJECT
public:
Agent(QString class_name = "Agent");
~Agent();

// GETTERS
unsigned int getId();
QString getClass();

slots:
virtual void start();
virtual void end(){this->deleteLater();}

private:

// INCREMENTAL FOR IDS
static unsigned int counter;

unsigned int id;
QString class_name;
};

///////////////////////////////////////////////////////// AGENT.CPP

#include "Agent.h"

unsigned int Agent::counter = 0;

Agent::Agent(QString class_name) : QObject()
{
this->id = ++Agent::counter;
this->class_name = class_name;
this->setObjectName( QString("%1%2").arg( this->getClass() ).arg( this->getId() ) );
}

Agent::~Agent() {
this->thread()->deleteLater();
}

unsigned int Agent::getId(){
return this->id;
}

QString Agent::getClass(){
return this->class_name;
}

void Agent::start(){
PostgresDriver* db = new PostgresDriver("localhost" , 5432 , "database" , "user" , "user" , this->objectName() ); //CRASHES
}

///////////////////////////////////////////////////////// MAIN.CPP

Agent* agent = new Agent("Car");
QThread *thread = new QThread( );
agent->moveToThread( thread );
agent->connect( thread, SIGNAL( started() ), agent, SLOT( start() ) );
thread->start();

Crashes when creating a PostgresDriver inside the Agent.

jefftee
6th May 2015, 19:37
Why don't you create the connection from your thread? i.e. your start slot?