Prevent O(n^2) behavior when calling QObject::deleteLater

When a deleteLater event is queued, a check if done if the same event
for the same receiver is queued before by scanning all pending events.
This leads to quadratic behavior, which is quite noticeable. By using
an unused bit in QObjectData, this can be prevented. Now the duplicate
event scanning in QCoreApplication is only done for the quit event.

Task-number: QTBUG-65712
Change-Id: Ie505acbbec802f91ebd0b94ac067e362c2476113
Reviewed-by: Erik Verbruggen <erik.verbruggen@qt.io>
Reviewed-by: Simon Hausmann <simon.hausmann@qt.io>
Reviewed-by: Lars Knoll <lars.knoll@qt.io>
This commit is contained in:
Erik Verbruggen 2018-01-12 12:19:20 +01:00 committed by Simon Hausmann
parent cb714248a8
commit 99b89d30fa
3 changed files with 30 additions and 13 deletions

View File

@ -1426,6 +1426,9 @@ void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
return; return;
} }
if (event->type() == QEvent::DeferredDelete)
receiver->d_ptr->deleteLaterCalled = true;
if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) { if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) {
// remember the current running eventloop for DeferredDelete // remember the current running eventloop for DeferredDelete
// events posted in the receiver's thread. // events posted in the receiver's thread.
@ -1485,22 +1488,34 @@ bool QCoreApplication::compressEvent(QEvent *event, QObject *receiver, QPostEven
return true; return true;
} }
} }
} else return false;
}
#endif #endif
if ((event->type() == QEvent::DeferredDelete
|| event->type() == QEvent::Quit) if (event->type() == QEvent::DeferredDelete) {
&& receiver->d_func()->postedEvents > 0) { if (receiver->d_ptr->deleteLaterCalled) {
for (int i = 0; i < postedEvents->size(); ++i) { // there was a previous DeferredDelete event, so we can drop the new one
const QPostEvent &cur = postedEvents->at(i); delete event;
if (cur.receiver != receiver return true;
}
// deleteLaterCalled is set to true in postedEvents when queueing the very first
// deferred deletion event.
return false;
}
if (event->type() == QEvent::Quit && receiver->d_func()->postedEvents > 0) {
for (int i = 0; i < postedEvents->size(); ++i) {
const QPostEvent &cur = postedEvents->at(i);
if (cur.receiver != receiver
|| cur.event == 0 || cur.event == 0
|| cur.event->type() != event->type()) || cur.event->type() != event->type())
continue; continue;
// found an event for this receiver // found an event for this receiver
delete event; delete event;
return true; return true;
}
} }
}
return false; return false;
} }

View File

@ -230,6 +230,7 @@ QObjectPrivate::QObjectPrivate(int version)
connectedSignals[0] = connectedSignals[1] = 0; connectedSignals[0] = connectedSignals[1] = 0;
metaObject = 0; metaObject = 0;
isWindow = false; isWindow = false;
deleteLaterCalled = false;
} }
QObjectPrivate::~QObjectPrivate() QObjectPrivate::~QObjectPrivate()

View File

@ -106,7 +106,8 @@ public:
uint sendChildEvents : 1; uint sendChildEvents : 1;
uint receiveChildEvents : 1; uint receiveChildEvents : 1;
uint isWindow : 1; //for QWindow uint isWindow : 1; //for QWindow
uint unused : 25; uint deleteLaterCalled : 1;
uint unused : 24;
int postedEvents; int postedEvents;
QDynamicMetaObjectData *metaObject; QDynamicMetaObjectData *metaObject;
QMetaObject *dynamicMetaObject() const; QMetaObject *dynamicMetaObject() const;