diff options
author | Timur Pocheptsov <timur.pocheptsov@qt.io> | 2020-05-06 16:31:14 +0200 |
---|---|---|
committer | Timur Pocheptsov <timur.pocheptsov@qt.io> | 2020-05-08 18:10:52 +0200 |
commit | f579be96f7ad0362220f710a03a390d1c1139c52 (patch) | |
tree | 81dced42065c29fa0a652ce498ef9a01c100dbf2 /src | |
parent | 8481a9fc974a1f1dd44a9f82decb18fe2290689f (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')
-rw-r--r-- | src/widgets/widgets/qmenu.cpp | 30 |
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(); |