[Qt-interest] BlockingQueuedConnection... or QtConcurrent BUG ?!
Mihail Naydenov
mlists at ymail.com
Sat Dec 12 17:44:39 CET 2009
Some time ago I send a mail " BlockingQueuedConnection where and how to ?! "
about a crash that is avoided by using BlockingQueuedConnection signal-slot.
I also added a test code that uses QtConcurrent to spawn the background threads the ui is trying to communicate with (through signals and slots).
I recently encountered the issues again (in a different scenario), but this time, using QtCreator, I managed to get
a stacktrace of the error (Eclipse failed at that the first time). So I decided to run my old test in QtCreator/Debug
Here is the result:
SIGSEGV
0 QVector<int>::at qvector.h 323 0x00414c27
1 QtConcurrent::ResultIterator<int>::pointer qtconcurrentresultstore.h 117 0x0041491a
2 QtConcurrent::ResultIterator<int>::value qtconcurrentresultstore.h 111 0x004148d9
3 QFutureInterface<int>::resultReference qfutureinterface.h 250 0x00414acb
4 QFuture<int>::resultAt qfuture.h 182 0x00414bc4
5 QFutureWatcher<int>::resultAt qfuturewatcher.h 132 0x004149e7
6 worker::workdoneAt main.cpp 133 0x00411f17
7 worker::qt_metacall main.moc 145 0x0040161a
8 QMetaObject::metacall qmetaobject.cpp 237 0x6a200e14
9 QMetaCallEvent::placeMetaCall qobject.cpp 574 0x6a20b0cf
10 QObject::event qobject.cpp 1252 0x6a20d0f2
11 QApplicationPrivate::notify_helper qapplication.cpp 4242 0x00c32252
12 QApplication::notify qapplication.cpp 3661 0x00c2fdb7
13 QCoreApplication::notifyInternal qcoreapplication.cpp 704 0x6a1fc512
14 QCoreApplication::sendEvent qcoreapplication.h 215 0x6a2604a4
15 QCoreApplicationPrivate::sendPostedEvents qcoreapplication.cpp 1345 0x6a1fd5a3
16 qt_internal_proc qeventdispatcher_win.cpp 507 0x6a21ea87
17 USER32!IsWindowVisible C:\Windows\system32\user32.dll 0 0x759486ef
18 ?? 0 0x00080680
19 ?? 0 0x00000401
20 ?? 0 0x00000000
... <More>
Although the crash can be avoided throgh blocked connection, the real problem seams to be indeed in QtConcurrent after all!
Running my real code, I noticed the crash rises when the concurrent job is restarted (this is also true for this test app)... now
considering the stacktrace, Im pretty convinced there some sort of race condition or something,
accessing the result from the QFuture variable and setting/starting a newjpb through QtConcurrent::mapped(...) call.
As I said the crash before is also avoided when not getting the result from the job.
Again, here is the test code.
I might be wrong what causes it, but please test it and enlighten me.
thank you
MihaiNaydenov
----------------------------
#include <QtGui>
// lame work doing function
int work(int item)
{
int timespend = qrand();
int result = 0;
for(int i=0; i < timespend*2; i++)
result = i;
return result;
}
class view : public QListWidget
{
Q_OBJECT
public:
view(QWidget* parent=0) : QListWidget(parent)
{
connect(this, SIGNAL(itemSelectionChanged()), SLOT(onitemSelectionChanged()));
for(int i=0; i < 1000; i++)
{
insertItem(i, new QListWidgetItem("item_" + QString::number(i)));
}
startTimer(0);
}
void timerEvent (QTimerEvent* e)
{
clearSelection();
killTimer(e->timerId());
// select randomly some items
QItemSelection selection;
int rowL = qrand();
int max = model()->rowCount() - 1;
if(max < 0)
max = 0;
while (rowL > max )
{
rowL = qrand();
}
int rowR = qrand();
while (rowR > max )
{
rowR = qrand();
}
if(rowR > rowL)
qSwap(rowR, rowL);
selection.select(model()->index(rowR, 0), model()->index(rowL, 0));
selectionModel()->select(selection, QItemSelectionModel::ClearAndSelect);
// every 0 to 1500 ms we will submit another job
int timeout = qrand();
while (timeout > 1500 )
{
timeout = qrand();
}
startTimer(timeout);
}
signals:
void needWorkdone(const QVector<int>& items);
private slots:
void onitemSelectionChanged ()
{
QVector<int> dummy(selectedItems().count());
emit needWorkdone(dummy);
}
};
// a worker (living in another thread)
class worker : public QObject
{
Q_OBJECT
public:
worker(QObject* parent=0)
{
// for simplicity the work result is handled in the worker
connect(&_workWatcher, SIGNAL(resultReadyAt(int)), SLOT(workdoneAt(int)));
}
~worker() { _workWatcher.cancel(), _workWatcher.waitForFinished(); }
public slots:
void dowork(const QVector<int>& items)
{
if(!items.isEmpty())
{
if(_workWatcher.isRunning())
{
_workWatcher.cancel();
_workWatcher.waitForFinished();
}
qDebug()<<"work on";
_workWatcher.setFuture(QtConcurrent::mapped(items, work));
}
}
void workdoneAt(int at)
{
qDebug()<<"ready at"<<at;
int result = 0;
// >>> commenting this line will avoid the crash <<<
result = _workWatcher.resultAt(at);
qDebug()<<"result: "<<result;
}
private:
QFutureWatcher<int> _workWatcher;
};
#include "main.moc"
Q_DECLARE_METATYPE(QVector<int>)
int main(int argc, char *argv[])
{
qRegisterMetaType< QVector<int> >();
QApplication a(argc, argv);
QThread th;
th.connect(&a, SIGNAL(aboutToQuit()), SLOT(quit()));
worker w;
w.moveToThread(&th);
th.start();
view v;
w.connect(&v, SIGNAL(needWorkdone(const QVector<int>&)), SLOT(dowork(const QVector<int>&))
//, Qt::BlockingQueuedConnection // uncommnet to avoid the crash, but block the ui.
);
v.show();
return a.exec();
}
More information about the Qt-interest-old
mailing list