Linux threading seems to be broken with Qt 4.8.0
-
I've got a simple database application.
It launches two threads. Each thread attempts to update the same record in a Postgres database using a transaction.
The transaction logic is this:
Start the transaction
Update the record
Sleep for a few seconds
Commit the transaction(See code down down at the bottom of the post)
The problem I'm having is that when I launch two threads to do this, my application hangs.
What happens is this:
Thread one Starts the transaction
Thread one updates the record
Thread one sleeps
Thread two starts the transaction
Thread two attempts to update the recordThen, nothing else. The whole program locks. I have determined that Thread two cannot update the db record because the record is locked by the transaction from Thread one. However, for a threaded application, Thread one should be able to complete its work and then after it commits Thread two can carry on.
I've tested it on Windows and, indeed, it works like such on Windows...
Thread one Starts the transaction
Thread one updates the record
Thread one sleeps
Thread two starts the transaction
Thread two attempts to update the record (and blocks)
Thread one wakes up from sleeping
Thread one commits the transaction
Thread two successfully updates the database record.
Thread two commits the transaction.
Program exits normally.And, when I run two separate instances of the program (ie. separate processes) on Linux, each process only executing a single thread, they work in lock-step just as the threads worked on Windows.
So, separate processes behave correctly on Linux, but the threads aren't working as I'd expect.
Can someone straighten me out on Linux threading? Is my application somehow being compiled without threading? I can see in the make output that it's using -lpthread...
Make output
@
make debug
make -f Makefile.Debug
make[1]: Entering directory/media/TMP/TestODBC' g++ -c -pipe -g -O0 -Wall -W -D_REENTRANT -DQT_SQL_LIB -DQT_CORE_LIB -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/mkspecs/default -I. -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/include/QtCore -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/include/QtSql -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/include -Idebug -o debug/ODBCExample.o ODBCExample.cpp g++ -c -pipe -g -O0 -Wall -W -D_REENTRANT -DQT_SQL_LIB -DQT_CORE_LIB -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/mkspecs/default -I. -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/include/QtCore -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/include/QtSql -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/include -Idebug -o debug/main.o main.cpp main.cpp:7: warning: unused parameter ‘argc’ /opt/QtSDK/Desktop/Qt/4.8.0/gcc/bin/moc -DQT_SQL_LIB -DQT_CORE_LIB -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/mkspecs/default -I. -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/include/QtCore -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/include/QtSql -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/include -Idebug ODBCExample.h -o debug/moc_ODBCExample.cpp g++ -c -pipe -g -O0 -Wall -W -D_REENTRANT -DQT_SQL_LIB -DQT_CORE_LIB -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/mkspecs/default -I. -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/include/QtCore -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/include/QtSql -I/opt/QtSDK/Desktop/Qt/4.8.0/gcc/include -Idebug -o debug/moc_ODBCExample.o debug/moc_ODBCExample.cpp g++ -Wl,-rpath,/opt/QtSDK/Desktop/Qt/4.8.0/gcc/lib -o ODBCExampleD debug/ODBCExample.o debug/main.o debug/moc_ODBCExample.o -L/opt/QtSDK/Desktop/Qt/4.8.0/gcc/lib -lQtSql -lQtCore -lpthread { test -n "" && DESTDIR="" || DESTDIR=.; } && test $(gdb --version | sed -e 's,[^0-9]\+\([0-9]\)\.\([0-9]\).*,\1\2,;q') -gt 72 && gdb --nx --batch --quiet -ex 'set confirm off' -ex "save gdb-index $DESTDIR" -ex quit 'ODBCExampleD' && test -f ODBCExampleD.gdb-index && objcopy --add-section '.gdb_index=ODBCExampleD.gdb-index' --set-section-flags '.gdb_index=readonly' 'ODBCExampleD' 'ODBCExampleD' && rm -f ODBCExampleD.gdb-index || true make[1]: Leaving directory
/media/TMP/TestODBC'
@Transaction logic
@
bool ODBCExample::RunThreadedTransaction(QString connectionName,
int sleepSeconds)
{
QSqlDatabase::addDatabase("QODBC3", connectionName);qDebug() << QSqlDatabase::connectionNames().join("|");
QSqlDatabase db = QSqlDatabase::database(connectionName, false);
qDebug() << "[" << connectionName << "] db address: " << &db;
qDebug() << "[" << connectionName << "] db.isValid: " << db.isValid();
qDebug() << "[" << connectionName << "] connect options: "
<< db.connectOptions();
qDebug() << "[" << connectionName << "] connection name: "
<< db.connectionName();
qDebug() << "[" << connectionName << "] driver name: " << db.driverName();
QSqlDriver *dbd = db.driver();
qDebug() << "[" << connectionName << "] db.driver: " << dbd;db.setDatabaseName("PostgresDSN;BoolsAsChar=0");
db.setUserName("myuser");
db.setPassword("mypassword");if (!db.open())
{
qDebug() << "[" << connectionName << "] database wasn't opened";
qDebug() << "[" << connectionName << "] " << db.lastError();
return false;
}qDebug() << "[" << connectionName
<< "] database successfully opened for connection 1";// bool trans = dbd->beginTransaction();
bool trans = db.transaction();
qDebug() << "[" << connectionName << "] trans: " << ((trans) ? "true"
: "false");if (trans)
{
QSqlQuery query(db);
QString strUpdateUsers =
"UPDATE mytable SET mynumber = mynumber + 1 WHERE myid = 102";
query.exec(strUpdateUsers);
qDebug() << "[" << connectionName << "] update finished";
sleep(sleepSeconds);
db.commit();
qDebug() << "[" << connectionName << "] trans commited";
}
else
{
qDebug() << "[" << connectionName << "] trans could not be started";
}return true;
}
@ -
Qt is well-tested, so, in my opinion something wrong in your code instead of Qt code
-
You may need to add thread support in your .pro file;
CONFIG += thread
Documentation:
http://qt-project.org/doc/qt-4.8/qmake-project-files.html -
Make sure your using QMutex's since your using multiple threads. I have used multiple threads on Linux for Qt on 4.8.0 and have had no problems...
-
If you use the very same database connection on two different threads, you might run into problems. The database classes are not thread safe! You will need distinct connections for each thread.