summaryrefslogtreecommitdiffstats
path: root/src/widgets/widgets/qmenu.cpp
diff options
context:
space:
mode:
authorTimur Pocheptsov <timur.pocheptsov@qt.io>2020-05-06 16:31:14 +0200
committerTimur Pocheptsov <timur.pocheptsov@qt.io>2020-05-08 18:10:52 +0200
commitf579be96f7ad0362220f710a03a390d1c1139c52 (patch)
tree81dced42065c29fa0a652ce498ef9a01c100dbf2 /src/widgets/widgets/qmenu.cpp
parent8481a9fc974a1f1dd44a9f82decb18fe2290689f (diff)
QMenuPrivate::hideMenu - avoid deleting 'q' too early
This function among other things enters a nested event loop twice. With enough luck processing events may end with posting deferred delete event for 'q' and then ... executing this event leaving the whole call tree with a dangling pointer. This is not very convenient, and we filter out such events to re-post them a bit later. Pick-to: 5.15 Fixes: QTBUG-82349 Change-Id: Ic620273b529b89f2bd57e25df1f91c2754940670 Reviewed-by: Volker Hilsheimer <volker.hilsheimer@qt.io> Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io> Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Diffstat (limited to 'src/widgets/widgets/qmenu.cpp')
-rw-r--r--src/widgets/widgets/qmenu.cpp30
1 files changed, 30 insertions, 0 deletions
diff --git a/src/widgets/widgets/qmenu.cpp b/src/widgets/widgets/qmenu.cpp
index fe5c52ee93..6198b8bfa1 100644
--- a/src/widgets/widgets/qmenu.cpp
+++ b/src/widgets/widgets/qmenu.cpp
@@ -571,6 +571,35 @@ void QMenuPrivate::hideMenu(QMenu *menu)
{
if (!menu)
return;
+
+ // See two execs below. They may trigger an akward situation
+ // when 'menu' (also known as 'q' or 'this' in the many functions
+ // around) to become a dangling pointer if the loop manages
+ // to execute 'deferred delete' ... posted while executing
+ // this same loop. Not good!
+ struct Reposter : QObject
+ {
+ Reposter(QMenu *menu) : q(menu)
+ {
+ Q_ASSERT(q);
+ q->installEventFilter(this);
+ }
+ ~Reposter()
+ {
+ if (deleteLater)
+ q->deleteLater();
+ }
+ bool eventFilter(QObject *obj, QEvent *event) override
+ {
+ if (obj == q && event->type() == QEvent::DeferredDelete)
+ return deleteLater = true;
+
+ return QObject::eventFilter(obj, event);
+ }
+ QMenu *q = nullptr;
+ bool deleteLater = false;
+ };
+
#if QT_CONFIG(effects)
QSignalBlocker blocker(menu);
aboutToHide = true;
@@ -582,6 +611,7 @@ void QMenuPrivate::hideMenu(QMenu *menu)
QAction *activeAction = currentAction;
menu->setActiveAction(nullptr);
+ const Reposter deleteDeleteLate(menu);
QTimer::singleShot(60, &eventLoop, SLOT(quit()));
eventLoop.exec();