Fix sending deferred delete events when posted before outermost loop

QDeferredDeleteEvent has the loopLevel field, which is a sum of
scope and loop levels found at posting. In sendPostedEvents however,
it is impossible to only use this information to find delete events
posted before the outermost loop (which should be handled by any loop)
based solely on this information, as the scope level essentialy removes
the information on loop level.

Break the loopLevel in two, storing both loop and scope levels in
QDeferredDeleteEvent, so that we can check whether an event was posted
before the outermost event loop (for which we need to compare only the
loop level).

QDeferredDeleteEvent was also made private as it should - it is an
implementation detail that wasn't hidden properly.

Change-Id: I0a607a0bd3a2deb5024acad67f740dbf4338574c
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
Reviewed-by: Mikołaj Boc <Mikolaj.Boc@qt.io>
This commit is contained in:
Mikolaj Boc 2023-05-08 08:21:49 +02:00
parent 30e5ff3ff2
commit 78acaf4fb6
10 changed files with 89 additions and 30 deletions

View File

@ -152,7 +152,7 @@ qt_internal_add_module(Core
kernel/qcoreapplication.cpp kernel/qcoreapplication.h kernel/qcoreapplication_p.h kernel/qcoreapplication.cpp kernel/qcoreapplication.h kernel/qcoreapplication_p.h
kernel/qcoreapplication_platform.h kernel/qcoreapplication_platform.h
kernel/qcorecmdlineargs_p.h kernel/qcorecmdlineargs_p.h
kernel/qcoreevent.cpp kernel/qcoreevent.h kernel/qcoreevent.cpp kernel/qcoreevent.h kernel/qcoreevent_p.h
kernel/qdeadlinetimer.cpp kernel/qdeadlinetimer.h kernel/qdeadlinetimer.cpp kernel/qdeadlinetimer.h
kernel/qelapsedtimer.cpp kernel/qelapsedtimer.h kernel/qelapsedtimer.cpp kernel/qelapsedtimer.h
kernel/qeventloop.cpp kernel/qeventloop.h kernel/qeventloop_p.h kernel/qeventloop.cpp kernel/qeventloop.h kernel/qeventloop_p.h

View File

@ -8,6 +8,7 @@
#ifndef QT_NO_QOBJECT #ifndef QT_NO_QOBJECT
#include "qabstracteventdispatcher.h" #include "qabstracteventdispatcher.h"
#include "qcoreevent.h" #include "qcoreevent.h"
#include "qcoreevent_p.h"
#include "qeventloop.h" #include "qeventloop.h"
#endif #endif
#include "qmetaobject.h" #include "qmetaobject.h"
@ -1660,7 +1661,10 @@ void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
int scopeLevel = data->scopeLevel; int scopeLevel = data->scopeLevel;
if (scopeLevel == 0 && loopLevel != 0) if (scopeLevel == 0 && loopLevel != 0)
scopeLevel = 1; scopeLevel = 1;
static_cast<QDeferredDeleteEvent *>(event)->level = loopLevel + scopeLevel;
QDeferredDeleteEvent *deleteEvent = static_cast<QDeferredDeleteEvent *>(event);
deleteEvent->m_loopLevel = loopLevel;
deleteEvent->m_scopeLevel = scopeLevel;
} }
// 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
@ -1849,13 +1853,15 @@ void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type
// events posted by the current event loop; or // events posted by the current event loop; or
// 3) if the event was posted before the outermost event loop. // 3) if the event was posted before the outermost event loop.
int eventLevel = static_cast<QDeferredDeleteEvent *>(pe.event)->loopLevel(); const int eventLoopLevel = static_cast<QDeferredDeleteEvent *>(pe.event)->loopLevel();
int loopLevel = data->loopLevel + data->scopeLevel; const int eventScopeLevel = static_cast<QDeferredDeleteEvent *>(pe.event)->scopeLevel();
const bool postedBeforeOutermostLoop = eventLoopLevel == 0;
const bool allowDeferredDelete = const bool allowDeferredDelete =
(eventLevel > loopLevel (eventLoopLevel + eventScopeLevel > data->loopLevel + data->scopeLevel
|| (!eventLevel && loopLevel > 0) || (postedBeforeOutermostLoop && data->loopLevel > 0)
|| (event_type == QEvent::DeferredDelete || (event_type == QEvent::DeferredDelete
&& eventLevel == loopLevel)); && eventLoopLevel + eventScopeLevel == data->loopLevel + data->scopeLevel));
if (!allowDeferredDelete) { if (!allowDeferredDelete) {
// cannot send deferred delete // cannot send deferred delete
if (!event_type && !receiver) { if (!event_type && !receiver) {

View File

@ -3,6 +3,7 @@
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qcoreevent.h" #include "qcoreevent.h"
#include "qcoreevent_p.h"
#include "qcoreapplication.h" #include "qcoreapplication.h"
#include "qcoreapplication_p.h" #include "qcoreapplication_p.h"
@ -637,19 +638,10 @@ Q_IMPL_EVENT_COMMON(QDynamicPropertyChangeEvent)
*/ */
QDeferredDeleteEvent::QDeferredDeleteEvent() QDeferredDeleteEvent::QDeferredDeleteEvent()
: QEvent(QEvent::DeferredDelete) : QEvent(QEvent::DeferredDelete)
, level(0)
{ } { }
Q_IMPL_EVENT_COMMON(QDeferredDeleteEvent) Q_IMPL_EVENT_COMMON(QDeferredDeleteEvent)
/*! \fn int QDeferredDeleteEvent::loopLevel() const
Returns the loop-level in which the event was posted. The
loop-level is set by QCoreApplication::postEvent().
\sa QObject::deleteLater()
*/
QT_END_NAMESPACE QT_END_NAMESPACE
#include "moc_qcoreevent.cpp" #include "moc_qcoreevent.cpp"

View File

@ -395,18 +395,6 @@ private:
QByteArray n; QByteArray n;
}; };
class Q_CORE_EXPORT QDeferredDeleteEvent : public QEvent
{
Q_DECL_EVENT_COMMON(QDeferredDeleteEvent)
public:
explicit QDeferredDeleteEvent();
int loopLevel() const { return level; }
private:
int level;
friend class QCoreApplication;
};
QT_END_NAMESPACE QT_END_NAMESPACE
#endif // QCOREEVENT_H #endif // QCOREEVENT_H

View File

@ -0,0 +1,40 @@
// Copyright (C) 2023 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QCOREEVENT_P_H
#define QCOREEVENT_P_H
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
#include "QtCore/qcoreevent.h"
QT_BEGIN_NAMESPACE
class QCoreApplication;
class Q_AUTOTEST_EXPORT QDeferredDeleteEvent : public QEvent
{
Q_DECL_EVENT_COMMON(QDeferredDeleteEvent)
public:
explicit QDeferredDeleteEvent();
int loopLevel() const { return m_loopLevel; }
int scopeLevel() const { return m_scopeLevel; }
private:
int m_loopLevel = 0;
int m_scopeLevel = 0;
friend class QCoreApplication;
};
QT_END_NAMESPACE
#endif // QCOREEVENT_P_H

View File

@ -12,6 +12,7 @@
#include "qabstracteventdispatcher_p.h" #include "qabstracteventdispatcher_p.h"
#include "qcoreapplication.h" #include "qcoreapplication.h"
#include "qcoreapplication_p.h" #include "qcoreapplication_p.h"
#include "qcoreevent_p.h"
#include "qloggingcategory.h" #include "qloggingcategory.h"
#include "qvariant.h" #include "qvariant.h"
#include "qmetaobject.h" #include "qmetaobject.h"

View File

@ -9,6 +9,7 @@
#include <private/qabstracteventdispatcher_p.h> // for qGlobalPostedEventsCount() #include <private/qabstracteventdispatcher_p.h> // for qGlobalPostedEventsCount()
#include <private/qcoreapplication_p.h> #include <private/qcoreapplication_p.h>
#include <private/qcoreevent_p.h>
#include <private/qeventloop_p.h> #include <private/qeventloop_p.h>
#include <private/qthread_p.h> #include <private/qthread_p.h>
@ -24,9 +25,12 @@ class EventSpy : public QObject
public: public:
QList<int> recordedEvents; QList<int> recordedEvents;
bool eventFilter(QObject *, QEvent *event) override std::function<void(QObject *, QEvent *)> eventCallback;
bool eventFilter(QObject *target, QEvent *event) override
{ {
recordedEvents.append(event->type()); recordedEvents.append(event->type());
if (eventCallback)
eventCallback(target, event);
return false; return false;
} }
}; };
@ -1081,6 +1085,29 @@ static void createQObjectOnDestruction()
} }
Q_DESTRUCTOR_FUNCTION(createQObjectOnDestruction) Q_DESTRUCTOR_FUNCTION(createQObjectOnDestruction)
void tst_QCoreApplication::testDeleteLaterFromBeforeOutermostEventLoop()
{
int argc = 0;
QCoreApplication app(argc, nullptr);
EventSpy *spy = new EventSpy();
QPointer<QObject> spyPointer = spy;
app.installEventFilter(spy);
spy->eventCallback = [spy](QObject *, QEvent *event) {
if (event->type() == QEvent::User + 1)
spy->deleteLater();
};
QCoreApplication::postEvent(&app, new QEvent(QEvent::Type(QEvent::User + 1)));
QCoreApplication::processEvents();
QEventLoop loop;
QTimer::singleShot(0, &loop, &QEventLoop::quit);
loop.exec();
QVERIFY(!spyPointer);
}
#ifndef QT_QGUIAPPLICATIONTEST #ifndef QT_QGUIAPPLICATIONTEST
QTEST_APPLESS_MAIN(tst_QCoreApplication) QTEST_APPLESS_MAIN(tst_QCoreApplication)
#endif #endif

View File

@ -45,6 +45,7 @@ private slots:
void addRemoveLibPaths(); void addRemoveLibPaths();
#endif #endif
void theMainThread(); void theMainThread();
void testDeleteLaterFromBeforeOutermostEventLoop();
}; };
#endif // TST_QCOREAPPLICATION_H #endif // TST_QCOREAPPLICATION_H

View File

@ -14,7 +14,6 @@
X(QTimerEvent, (42)) \ X(QTimerEvent, (42)) \
X(QChildEvent, (QEvent::ChildAdded, nullptr)) \ X(QChildEvent, (QEvent::ChildAdded, nullptr)) \
X(QDynamicPropertyChangeEvent, ("size")) \ X(QDynamicPropertyChangeEvent, ("size")) \
X(QDeferredDeleteEvent, ()) \
/* qfutureinterface_p.h */ \ /* qfutureinterface_p.h */ \
X(QFutureCallOutEvent, ()) \ X(QFutureCallOutEvent, ()) \
/* end */ /* end */

View File

@ -19,6 +19,7 @@
#if QT_CONFIG(process) #if QT_CONFIG(process)
# include <QtCore/QProcess> # include <QtCore/QProcess>
#endif #endif
#include <QtCore/private/qcoreevent_p.h>
#include <QtCore/private/qeventloop_p.h> #include <QtCore/private/qeventloop_p.h>
#include <QtGui/QFontDatabase> #include <QtGui/QFontDatabase>
@ -94,7 +95,9 @@ private slots:
void libraryPaths_qt_plugin_path_2(); void libraryPaths_qt_plugin_path_2();
#endif #endif
#ifdef QT_BUILD_INTERNAL
void sendPostedEvents(); void sendPostedEvents();
#endif // ifdef QT_BUILD_INTERNAL
void thread(); void thread();
void desktopSettingsAware(); void desktopSettingsAware();
@ -1126,6 +1129,7 @@ void tst_QApplication::libraryPaths_qt_plugin_path_2()
} }
#endif #endif
#ifdef QT_BUILD_INTERNAL
class SendPostedEventsTester : public QObject class SendPostedEventsTester : public QObject
{ {
Q_OBJECT Q_OBJECT
@ -1171,6 +1175,7 @@ void tst_QApplication::sendPostedEvents()
(void) QCoreApplication::exec(); (void) QCoreApplication::exec();
QVERIFY(p.isNull()); QVERIFY(p.isNull());
} }
#endif
void tst_QApplication::thread() void tst_QApplication::thread()
{ {