Fix event delevery order
Some functions (such as QObject::moveToThread) did not keep the event ordered by priority. And because qUpperBound is used to add events, that mean new events would not be inserted in order. Task-number: QTBUG19637 Change-Id: I38eb9addb1cdd45b8566e000361ac6e5f1f2c2b8 Reviewed-on: http://codereview.qt.nokia.com/733 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Bradley T. Hughes <bradley.hughes@nokia.com> Reviewed-by: Olivier Goffart <olivier.goffart@nokia.com>
This commit is contained in:
parent
89459bc7cf
commit
7eeabcf70d
@ -1272,20 +1272,7 @@ void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
|
|||||||
// delete the event on exceptions to protect against memory leaks till the event is
|
// delete the event on exceptions to protect against memory leaks till the event is
|
||||||
// properly owned in the postEventList
|
// properly owned in the postEventList
|
||||||
QScopedPointer<QEvent> eventDeleter(event);
|
QScopedPointer<QEvent> eventDeleter(event);
|
||||||
if (data->postEventList.isEmpty() || data->postEventList.last().priority >= priority) {
|
data->postEventList.addEvent(QPostEvent(receiver, event, priority));
|
||||||
// optimization: we can simply append if the last event in
|
|
||||||
// the queue has higher or equal priority
|
|
||||||
data->postEventList.append(QPostEvent(receiver, event, priority));
|
|
||||||
} else {
|
|
||||||
// insert event in descending priority order, using upper
|
|
||||||
// bound for a given priority (to ensure proper ordering
|
|
||||||
// of events with the same priority)
|
|
||||||
QPostEventList::iterator begin = data->postEventList.begin()
|
|
||||||
+ data->postEventList.insertionOffset,
|
|
||||||
end = data->postEventList.end();
|
|
||||||
QPostEventList::iterator at = qUpperBound(begin, end, priority);
|
|
||||||
data->postEventList.insert(at, QPostEvent(receiver, event, priority));
|
|
||||||
}
|
|
||||||
eventDeleter.take();
|
eventDeleter.take();
|
||||||
event->posted = true;
|
event->posted = true;
|
||||||
++receiver->d_func()->postedEvents;
|
++receiver->d_func()->postedEvents;
|
||||||
@ -1445,7 +1432,7 @@ void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type
|
|||||||
// cannot send deferred delete
|
// cannot send deferred delete
|
||||||
if (!event_type && !receiver) {
|
if (!event_type && !receiver) {
|
||||||
// don't lose the event
|
// don't lose the event
|
||||||
data->postEventList.append(pe);
|
data->postEventList.addEvent(pe);
|
||||||
const_cast<QPostEvent &>(pe).event = 0;
|
const_cast<QPostEvent &>(pe).event = 0;
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -1482,7 +1482,7 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData
|
|||||||
continue;
|
continue;
|
||||||
if (pe.receiver == q) {
|
if (pe.receiver == q) {
|
||||||
// move this post event to the targetList
|
// move this post event to the targetList
|
||||||
targetData->postEventList.append(pe);
|
targetData->postEventList.addEvent(pe);
|
||||||
const_cast<QPostEvent &>(pe).event = 0;
|
const_cast<QPostEvent &>(pe).event = 0;
|
||||||
++eventsMoved;
|
++eventsMoved;
|
||||||
}
|
}
|
||||||
|
@ -93,6 +93,8 @@ inline bool operator<(const QPostEvent &pe, int priority)
|
|||||||
return priority < pe.priority;
|
return priority < pe.priority;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This class holds the list of posted events.
|
||||||
|
// The list has to be kept sorted by priority
|
||||||
class QPostEventList : public QList<QPostEvent>
|
class QPostEventList : public QList<QPostEvent>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
@ -109,6 +111,25 @@ public:
|
|||||||
inline QPostEventList()
|
inline QPostEventList()
|
||||||
: QList<QPostEvent>(), recursion(0), startOffset(0), insertionOffset(0)
|
: QList<QPostEvent>(), recursion(0), startOffset(0), insertionOffset(0)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
void addEvent(const QPostEvent &ev) {
|
||||||
|
int priority = ev.priority;
|
||||||
|
if (isEmpty() || last().priority >= priority) {
|
||||||
|
// optimization: we can simply append if the last event in
|
||||||
|
// the queue has higher or equal priority
|
||||||
|
append(ev);
|
||||||
|
} else {
|
||||||
|
// insert event in descending priority order, using upper
|
||||||
|
// bound for a given priority (to ensure proper ordering
|
||||||
|
// of events with the same priority)
|
||||||
|
QPostEventList::iterator at = qUpperBound(begin() + insertionOffset, end(), priority);
|
||||||
|
insert(at, ev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
//hides because they do not keep that list sorted. addEvent must be used
|
||||||
|
using QList<QPostEvent>::append;
|
||||||
|
using QList<QPostEvent>::insert;
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifndef QT_NO_THREAD
|
#ifndef QT_NO_THREAD
|
||||||
|
@ -1288,8 +1288,8 @@ bool QApplication::compressEvent(QEvent *event, QObject *receiver, QPostEventLis
|
|||||||
|| event->type() == QEvent::LanguageChange
|
|| event->type() == QEvent::LanguageChange
|
||||||
|| event->type() == QEvent::UpdateSoftKeys
|
|| event->type() == QEvent::UpdateSoftKeys
|
||||||
|| event->type() == QEvent::InputMethod)) {
|
|| event->type() == QEvent::InputMethod)) {
|
||||||
for (int i = 0; i < postedEvents->size(); ++i) {
|
for (QPostEventList::const_iterator it = postedEvents->constBegin(); it != postedEvents->constEnd(); ++it) {
|
||||||
const QPostEvent &cur = postedEvents->at(i);
|
const QPostEvent &cur = *it;
|
||||||
if (cur.receiver != receiver || cur.event == 0 || cur.event->type() != event->type())
|
if (cur.receiver != receiver || cur.event == 0 || cur.event->type() != event->type())
|
||||||
continue;
|
continue;
|
||||||
if (cur.event->type() == QEvent::LayoutRequest
|
if (cur.event->type() == QEvent::LayoutRequest
|
||||||
|
@ -59,6 +59,8 @@
|
|||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "../../shared/util.h"
|
||||||
|
|
||||||
//TESTED_CLASS=
|
//TESTED_CLASS=
|
||||||
//TESTED_FILES=
|
//TESTED_FILES=
|
||||||
|
|
||||||
@ -208,6 +210,7 @@ private slots:
|
|||||||
void quit();
|
void quit();
|
||||||
void processEventsExcludeSocket();
|
void processEventsExcludeSocket();
|
||||||
void processEventsExcludeTimers();
|
void processEventsExcludeTimers();
|
||||||
|
void deliverInDefinedOrder_QTBUG19637();
|
||||||
|
|
||||||
// keep this test last:
|
// keep this test last:
|
||||||
void nestedLoops();
|
void nestedLoops();
|
||||||
@ -837,5 +840,77 @@ void tst_QEventLoop::symbianNestedActiveSchedulerLoop()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Q_DECLARE_METATYPE(QThread*)
|
||||||
|
|
||||||
|
namespace DeliverInDefinedOrder_QTBUG19637 {
|
||||||
|
enum { NbThread = 3, NbObject = 500, NbEventQueue = 5, NbEvent = 50 };
|
||||||
|
|
||||||
|
struct CustomEvent : public QEvent {
|
||||||
|
CustomEvent(int q, int v) : QEvent(Type(User + q)), value(v) {}
|
||||||
|
int value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Object : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
Object() : count(0) {
|
||||||
|
for (int i = 0; i < NbEventQueue; i++)
|
||||||
|
lastReceived[i] = -1;
|
||||||
|
}
|
||||||
|
int lastReceived[NbEventQueue];
|
||||||
|
int count;
|
||||||
|
virtual void customEvent(QEvent* e) {
|
||||||
|
QVERIFY(e->type() >= QEvent::User);
|
||||||
|
QVERIFY(e->type() < QEvent::User + 5);
|
||||||
|
uint idx = e->type() - QEvent::User;
|
||||||
|
int value = static_cast<CustomEvent *>(e)->value;
|
||||||
|
QVERIFY(lastReceived[idx] < value);
|
||||||
|
lastReceived[idx] = value;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void moveToThread(QThread *t) {
|
||||||
|
QObject::moveToThread(t);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_QEventLoop::deliverInDefinedOrder_QTBUG19637()
|
||||||
|
{
|
||||||
|
using namespace DeliverInDefinedOrder_QTBUG19637;
|
||||||
|
qMetaTypeId<QThread*>();
|
||||||
|
QThread threads[NbThread];
|
||||||
|
Object objects[NbObject];
|
||||||
|
for (int t = 0; t < NbThread; t++) {
|
||||||
|
threads[t].start();
|
||||||
|
}
|
||||||
|
|
||||||
|
int event = 0;
|
||||||
|
|
||||||
|
for (int o = 0; o < NbObject; o++) {
|
||||||
|
objects[o].moveToThread(&threads[o % NbThread]);
|
||||||
|
for (int e = 0; e < NbEvent; e++) {
|
||||||
|
int q = e % NbEventQueue;
|
||||||
|
QCoreApplication::postEvent(&objects[o], new CustomEvent(q, ++event) , q);
|
||||||
|
if (e % 7)
|
||||||
|
QMetaObject::invokeMethod(&objects[o], "moveToThread", Qt::QueuedConnection, Q_ARG(QThread*, &threads[(e+o)%NbThread]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QTest::qWait(30);
|
||||||
|
for (int o = 0; o < NbObject; o++) {
|
||||||
|
QTRY_COMPARE(objects[o].count, int(NbEvent));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int t = 0; t < NbThread; t++) {
|
||||||
|
threads[t].quit();
|
||||||
|
threads[t].wait();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
QTEST_MAIN(tst_QEventLoop)
|
QTEST_MAIN(tst_QEventLoop)
|
||||||
#include "tst_qeventloop.moc"
|
#include "tst_qeventloop.moc"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user